2011-05-18 13:17:29 +00:00
|
|
|
// Copyright 2011 the V8 project authors. All rights reserved.
|
2008-07-03 15:10:15 +00:00
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are
|
|
|
|
// met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above
|
|
|
|
// copyright notice, this list of conditions and the following
|
|
|
|
// disclaimer in the documentation and/or other materials provided
|
|
|
|
// with the distribution.
|
|
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
|
|
// contributors may be used to endorse or promote products derived
|
|
|
|
// from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
|
|
#include "v8.h"
|
|
|
|
|
2009-03-24 12:47:53 +00:00
|
|
|
#include "bootstrapper.h"
|
2010-08-30 11:48:07 +00:00
|
|
|
#include "code-stubs.h"
|
2013-07-03 15:39:18 +00:00
|
|
|
#include "cpu-profiler.h"
|
2010-12-07 11:31:57 +00:00
|
|
|
#include "deoptimizer.h"
|
2009-11-20 12:15:46 +00:00
|
|
|
#include "global-handles.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
#include "log.h"
|
2013-07-18 17:19:31 +00:00
|
|
|
#include "log-utils.h"
|
2009-03-24 12:47:53 +00:00
|
|
|
#include "macro-assembler.h"
|
2012-03-12 13:56:56 +00:00
|
|
|
#include "platform.h"
|
2010-12-07 11:31:57 +00:00
|
|
|
#include "runtime-profiler.h"
|
2009-03-24 12:47:53 +00:00
|
|
|
#include "serialize.h"
|
2008-10-15 08:50:22 +00:00
|
|
|
#include "string-stream.h"
|
2010-12-07 11:31:57 +00:00
|
|
|
#include "vm-state-inl.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-25 10:05:56 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2013-07-15 11:35:39 +00:00
|
|
|
|
2013-07-19 14:58:53 +00:00
|
|
|
#define DECLARE_EVENT(ignore1, name) name,
|
|
|
|
static const char* const kLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
|
|
|
|
LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT)
|
|
|
|
};
|
|
|
|
#undef DECLARE_EVENT
|
|
|
|
|
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
#define PROFILER_LOG(Call) \
|
|
|
|
do { \
|
|
|
|
CpuProfiler* cpu_profiler = isolate_->cpu_profiler(); \
|
|
|
|
if (cpu_profiler->is_profiling()) { \
|
|
|
|
cpu_profiler->Call; \
|
|
|
|
} \
|
|
|
|
} while (false);
|
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
// ComputeMarker must only be used when SharedFunctionInfo is known.
|
|
|
|
static const char* ComputeMarker(Code* code) {
|
|
|
|
switch (code->kind()) {
|
|
|
|
case Code::FUNCTION: return code->optimizable() ? "~" : "";
|
|
|
|
case Code::OPTIMIZED_FUNCTION: return "*";
|
|
|
|
default: return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-19 14:58:53 +00:00
|
|
|
class CodeEventLogger {
|
|
|
|
public:
|
|
|
|
virtual ~CodeEventLogger() { }
|
|
|
|
|
|
|
|
void CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
const char* comment);
|
|
|
|
void CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
Name* name);
|
|
|
|
void CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
int args_count);
|
|
|
|
void CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
CompilationInfo* info,
|
|
|
|
Name* name);
|
|
|
|
void CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
CompilationInfo* info,
|
|
|
|
Name* source,
|
|
|
|
int line);
|
|
|
|
void RegExpCodeCreateEvent(Code* code, String* source);
|
|
|
|
|
|
|
|
protected:
|
|
|
|
class NameBuffer {
|
|
|
|
public:
|
|
|
|
NameBuffer() { Reset(); }
|
|
|
|
|
|
|
|
void Reset() {
|
|
|
|
utf8_pos_ = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Init(Logger::LogEventsAndTags tag) {
|
|
|
|
Reset();
|
|
|
|
AppendBytes(kLogEventsNames[tag]);
|
|
|
|
AppendByte(':');
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppendName(Name* name) {
|
|
|
|
if (name->IsString()) {
|
|
|
|
AppendString(String::cast(name));
|
|
|
|
} else {
|
|
|
|
Symbol* symbol = Symbol::cast(name);
|
|
|
|
AppendBytes("symbol(");
|
|
|
|
if (!symbol->name()->IsUndefined()) {
|
|
|
|
AppendBytes("\"");
|
|
|
|
AppendString(String::cast(symbol->name()));
|
|
|
|
AppendBytes("\" ");
|
|
|
|
}
|
|
|
|
AppendBytes("hash ");
|
|
|
|
AppendHex(symbol->Hash());
|
|
|
|
AppendByte(')');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppendString(String* str) {
|
|
|
|
if (str == NULL) return;
|
|
|
|
int uc16_length = Min(str->length(), kUtf16BufferSize);
|
|
|
|
String::WriteToFlat(str, utf16_buffer, 0, uc16_length);
|
|
|
|
int previous = unibrow::Utf16::kNoPreviousCharacter;
|
|
|
|
for (int i = 0; i < uc16_length && utf8_pos_ < kUtf8BufferSize; ++i) {
|
|
|
|
uc16 c = utf16_buffer[i];
|
|
|
|
if (c <= unibrow::Utf8::kMaxOneByteChar) {
|
|
|
|
utf8_buffer_[utf8_pos_++] = static_cast<char>(c);
|
|
|
|
} else {
|
|
|
|
int char_length = unibrow::Utf8::Length(c, previous);
|
|
|
|
if (utf8_pos_ + char_length > kUtf8BufferSize) break;
|
|
|
|
unibrow::Utf8::Encode(utf8_buffer_ + utf8_pos_, c, previous);
|
|
|
|
utf8_pos_ += char_length;
|
|
|
|
}
|
|
|
|
previous = c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppendBytes(const char* bytes, int size) {
|
|
|
|
size = Min(size, kUtf8BufferSize - utf8_pos_);
|
|
|
|
OS::MemCopy(utf8_buffer_ + utf8_pos_, bytes, size);
|
|
|
|
utf8_pos_ += size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppendBytes(const char* bytes) {
|
|
|
|
AppendBytes(bytes, StrLength(bytes));
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppendByte(char c) {
|
|
|
|
if (utf8_pos_ >= kUtf8BufferSize) return;
|
|
|
|
utf8_buffer_[utf8_pos_++] = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppendInt(int n) {
|
|
|
|
Vector<char> buffer(utf8_buffer_ + utf8_pos_,
|
|
|
|
kUtf8BufferSize - utf8_pos_);
|
|
|
|
int size = OS::SNPrintF(buffer, "%d", n);
|
|
|
|
if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) {
|
|
|
|
utf8_pos_ += size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AppendHex(uint32_t n) {
|
|
|
|
Vector<char> buffer(utf8_buffer_ + utf8_pos_,
|
|
|
|
kUtf8BufferSize - utf8_pos_);
|
|
|
|
int size = OS::SNPrintF(buffer, "%x", n);
|
|
|
|
if (size > 0 && utf8_pos_ + size <= kUtf8BufferSize) {
|
|
|
|
utf8_pos_ += size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* get() { return utf8_buffer_; }
|
|
|
|
int size() const { return utf8_pos_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
static const int kUtf8BufferSize = 512;
|
|
|
|
static const int kUtf16BufferSize = 128;
|
|
|
|
|
|
|
|
int utf8_pos_;
|
|
|
|
char utf8_buffer_[kUtf8BufferSize];
|
|
|
|
uc16 utf16_buffer[kUtf16BufferSize];
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
|
|
|
virtual void LogRecordedBuffer(Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
NameBuffer* name_buffer) = 0;
|
|
|
|
|
|
|
|
NameBuffer name_buffer_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
const char* comment) {
|
|
|
|
name_buffer_.Init(tag);
|
|
|
|
name_buffer_.AppendBytes(comment);
|
|
|
|
LogRecordedBuffer(code, NULL, &name_buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
Name* name) {
|
|
|
|
name_buffer_.Init(tag);
|
|
|
|
name_buffer_.AppendName(name);
|
|
|
|
LogRecordedBuffer(code, NULL, &name_buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
CompilationInfo* info,
|
|
|
|
Name* name) {
|
|
|
|
name_buffer_.Init(tag);
|
|
|
|
name_buffer_.AppendBytes(ComputeMarker(code));
|
|
|
|
name_buffer_.AppendName(name);
|
|
|
|
LogRecordedBuffer(code, shared, &name_buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
CompilationInfo* info,
|
|
|
|
Name* source, int line) {
|
|
|
|
name_buffer_.Init(tag);
|
|
|
|
name_buffer_.AppendBytes(ComputeMarker(code));
|
|
|
|
name_buffer_.AppendString(shared->DebugName());
|
|
|
|
name_buffer_.AppendByte(' ');
|
|
|
|
if (source->IsString()) {
|
|
|
|
name_buffer_.AppendString(String::cast(source));
|
|
|
|
} else {
|
|
|
|
name_buffer_.AppendBytes("symbol(hash ");
|
|
|
|
name_buffer_.AppendHex(Name::cast(source)->Hash());
|
|
|
|
name_buffer_.AppendByte(')');
|
|
|
|
}
|
|
|
|
name_buffer_.AppendByte(':');
|
|
|
|
name_buffer_.AppendInt(line);
|
|
|
|
LogRecordedBuffer(code, shared, &name_buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CodeEventLogger::CodeCreateEvent(Logger::LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
int args_count) {
|
|
|
|
name_buffer_.Init(tag);
|
|
|
|
name_buffer_.AppendInt(args_count);
|
|
|
|
LogRecordedBuffer(code, NULL, &name_buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void CodeEventLogger::RegExpCodeCreateEvent(Code* code, String* source) {
|
|
|
|
name_buffer_.Init(Logger::REG_EXP_TAG);
|
|
|
|
name_buffer_.AppendString(source);
|
|
|
|
LogRecordedBuffer(code, NULL, &name_buffer_);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-15 11:35:39 +00:00
|
|
|
// Low-level logging support.
|
2013-07-19 14:58:53 +00:00
|
|
|
class LowLevelLogger : public CodeEventLogger {
|
2013-07-15 11:35:39 +00:00
|
|
|
public:
|
|
|
|
explicit LowLevelLogger(const char* file_name);
|
2013-07-19 14:58:53 +00:00
|
|
|
virtual ~LowLevelLogger();
|
2013-07-15 11:35:39 +00:00
|
|
|
|
|
|
|
void CodeMoveEvent(Address from, Address to);
|
|
|
|
void CodeDeleteEvent(Address from);
|
|
|
|
void SnapshotPositionEvent(Address addr, int pos);
|
|
|
|
void CodeMovingGCEvent();
|
|
|
|
|
|
|
|
private:
|
2013-07-19 14:58:53 +00:00
|
|
|
virtual void LogRecordedBuffer(Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
NameBuffer* name_buffer);
|
2013-07-15 11:35:39 +00:00
|
|
|
|
2013-07-19 14:58:53 +00:00
|
|
|
// Low-level profiling event structures.
|
2013-07-15 11:35:39 +00:00
|
|
|
struct CodeCreateStruct {
|
|
|
|
static const char kTag = 'C';
|
|
|
|
|
|
|
|
int32_t name_size;
|
|
|
|
Address code_address;
|
|
|
|
int32_t code_size;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct CodeMoveStruct {
|
|
|
|
static const char kTag = 'M';
|
|
|
|
|
|
|
|
Address from_address;
|
|
|
|
Address to_address;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct CodeDeleteStruct {
|
|
|
|
static const char kTag = 'D';
|
|
|
|
|
|
|
|
Address address;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct SnapshotPositionStruct {
|
|
|
|
static const char kTag = 'P';
|
|
|
|
|
|
|
|
Address address;
|
|
|
|
int32_t position;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static const char kCodeMovingGCTag = 'G';
|
|
|
|
|
|
|
|
|
|
|
|
// Extension added to V8 log file name to get the low-level log name.
|
|
|
|
static const char kLogExt[];
|
|
|
|
|
|
|
|
// File buffer size of the low-level log. We don't use the default to
|
|
|
|
// minimize the associated overhead.
|
|
|
|
static const int kLogBufferSize = 2 * MB;
|
|
|
|
|
|
|
|
void LogCodeInfo();
|
|
|
|
void LogWriteBytes(const char* bytes, int size);
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void LogWriteStruct(const T& s) {
|
|
|
|
char tag = T::kTag;
|
|
|
|
LogWriteBytes(reinterpret_cast<const char*>(&tag), sizeof(tag));
|
|
|
|
LogWriteBytes(reinterpret_cast<const char*>(&s), sizeof(s));
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE* ll_output_handle_;
|
|
|
|
};
|
|
|
|
|
|
|
|
const char LowLevelLogger::kLogExt[] = ".ll";
|
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
LowLevelLogger::LowLevelLogger(const char* name)
|
|
|
|
: ll_output_handle_(NULL) {
|
|
|
|
// Open the low-level log file.
|
|
|
|
size_t len = strlen(name);
|
|
|
|
ScopedVector<char> ll_name(static_cast<int>(len + sizeof(kLogExt)));
|
|
|
|
OS::MemCopy(ll_name.start(), name, len);
|
|
|
|
OS::MemCopy(ll_name.start() + len, kLogExt, sizeof(kLogExt));
|
|
|
|
ll_output_handle_ = OS::FOpen(ll_name.start(), OS::LogFileOpenMode);
|
|
|
|
setvbuf(ll_output_handle_, NULL, _IOFBF, kLogBufferSize);
|
|
|
|
|
|
|
|
LogCodeInfo();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
LowLevelLogger::~LowLevelLogger() {
|
|
|
|
fclose(ll_output_handle_);
|
|
|
|
ll_output_handle_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LowLevelLogger::LogCodeInfo() {
|
|
|
|
#if V8_TARGET_ARCH_IA32
|
|
|
|
const char arch[] = "ia32";
|
|
|
|
#elif V8_TARGET_ARCH_X64
|
|
|
|
const char arch[] = "x64";
|
|
|
|
#elif V8_TARGET_ARCH_ARM
|
|
|
|
const char arch[] = "arm";
|
|
|
|
#elif V8_TARGET_ARCH_MIPS
|
|
|
|
const char arch[] = "mips";
|
|
|
|
#else
|
|
|
|
const char arch[] = "unknown";
|
|
|
|
#endif
|
|
|
|
LogWriteBytes(arch, sizeof(arch));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LowLevelLogger::LogRecordedBuffer(Code* code,
|
|
|
|
SharedFunctionInfo*,
|
|
|
|
NameBuffer* name_buffer) {
|
|
|
|
CodeCreateStruct event;
|
|
|
|
event.name_size = name_buffer->size();
|
|
|
|
event.code_address = code->instruction_start();
|
|
|
|
ASSERT(event.code_address == code->address() + Code::kHeaderSize);
|
|
|
|
event.code_size = code->instruction_size();
|
|
|
|
LogWriteStruct(event);
|
|
|
|
LogWriteBytes(name_buffer->get(), name_buffer->size());
|
|
|
|
LogWriteBytes(
|
|
|
|
reinterpret_cast<const char*>(code->instruction_start()),
|
|
|
|
code->instruction_size());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LowLevelLogger::CodeMoveEvent(Address from, Address to) {
|
|
|
|
CodeMoveStruct event;
|
|
|
|
event.from_address = from + Code::kHeaderSize;
|
|
|
|
event.to_address = to + Code::kHeaderSize;
|
|
|
|
LogWriteStruct(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LowLevelLogger::CodeDeleteEvent(Address from) {
|
|
|
|
CodeDeleteStruct event;
|
|
|
|
event.address = from + Code::kHeaderSize;
|
|
|
|
LogWriteStruct(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LowLevelLogger::SnapshotPositionEvent(Address addr, int pos) {
|
|
|
|
SnapshotPositionStruct event;
|
|
|
|
event.address = addr + Code::kHeaderSize;
|
|
|
|
event.position = pos;
|
|
|
|
LogWriteStruct(event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LowLevelLogger::LogWriteBytes(const char* bytes, int size) {
|
|
|
|
size_t rv = fwrite(bytes, 1, size, ll_output_handle_);
|
|
|
|
ASSERT(static_cast<size_t>(size) == rv);
|
|
|
|
USE(rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void LowLevelLogger::CodeMovingGCEvent() {
|
|
|
|
const char tag = kCodeMovingGCTag;
|
|
|
|
|
|
|
|
LogWriteBytes(&tag, sizeof(tag));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-15 11:35:39 +00:00
|
|
|
#define LL_LOG(Call) if (ll_logger_) ll_logger_->Call;
|
|
|
|
|
|
|
|
|
2013-07-19 14:58:53 +00:00
|
|
|
class CodeAddressMap: public CodeEventLogger {
|
|
|
|
public:
|
|
|
|
CodeAddressMap() { }
|
|
|
|
virtual ~CodeAddressMap() { }
|
|
|
|
|
|
|
|
void CodeMoveEvent(Address from, Address to) {
|
|
|
|
address_to_name_map_.Move(from, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CodeDeleteEvent(Address from) {
|
|
|
|
address_to_name_map_.Remove(from);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* Lookup(Address address) {
|
|
|
|
return address_to_name_map_.Lookup(address);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
class NameMap {
|
|
|
|
public:
|
|
|
|
NameMap() : impl_(&PointerEquals) {}
|
|
|
|
|
|
|
|
~NameMap() {
|
|
|
|
for (HashMap::Entry* p = impl_.Start(); p != NULL; p = impl_.Next(p)) {
|
|
|
|
DeleteArray(static_cast<const char*>(p->value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Insert(Address code_address, const char* name, int name_size) {
|
|
|
|
HashMap::Entry* entry = FindOrCreateEntry(code_address);
|
|
|
|
if (entry->value == NULL) {
|
|
|
|
entry->value = CopyName(name, name_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* Lookup(Address code_address) {
|
|
|
|
HashMap::Entry* entry = FindEntry(code_address);
|
|
|
|
return (entry != NULL) ? static_cast<const char*>(entry->value) : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Remove(Address code_address) {
|
|
|
|
HashMap::Entry* entry = FindEntry(code_address);
|
|
|
|
if (entry != NULL) {
|
|
|
|
DeleteArray(static_cast<char*>(entry->value));
|
|
|
|
RemoveEntry(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Move(Address from, Address to) {
|
|
|
|
if (from == to) return;
|
|
|
|
HashMap::Entry* from_entry = FindEntry(from);
|
|
|
|
ASSERT(from_entry != NULL);
|
|
|
|
void* value = from_entry->value;
|
|
|
|
RemoveEntry(from_entry);
|
|
|
|
HashMap::Entry* to_entry = FindOrCreateEntry(to);
|
|
|
|
ASSERT(to_entry->value == NULL);
|
|
|
|
to_entry->value = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
static bool PointerEquals(void* lhs, void* rhs) {
|
|
|
|
return lhs == rhs;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char* CopyName(const char* name, int name_size) {
|
|
|
|
char* result = NewArray<char>(name_size + 1);
|
|
|
|
for (int i = 0; i < name_size; ++i) {
|
|
|
|
char c = name[i];
|
|
|
|
if (c == '\0') c = ' ';
|
|
|
|
result[i] = c;
|
|
|
|
}
|
|
|
|
result[name_size] = '\0';
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
HashMap::Entry* FindOrCreateEntry(Address code_address) {
|
|
|
|
return impl_.Lookup(code_address, ComputePointerHash(code_address), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
HashMap::Entry* FindEntry(Address code_address) {
|
|
|
|
return impl_.Lookup(code_address,
|
|
|
|
ComputePointerHash(code_address),
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RemoveEntry(HashMap::Entry* entry) {
|
|
|
|
impl_.Remove(entry->key, entry->hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
HashMap impl_;
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(NameMap);
|
|
|
|
};
|
|
|
|
|
|
|
|
virtual void LogRecordedBuffer(Code* code,
|
|
|
|
SharedFunctionInfo*,
|
|
|
|
NameBuffer* name_buffer) {
|
|
|
|
address_to_name_map_.Insert(code->address(),
|
|
|
|
name_buffer->get(),
|
|
|
|
name_buffer->size());
|
|
|
|
}
|
|
|
|
|
|
|
|
NameMap address_to_name_map_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#define CODE_ADDRESS_MAP_LOG(Call)\
|
|
|
|
if (Serializer::enabled()) code_address_map_->Call;
|
|
|
|
|
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
class JitLogger : public CodeEventLogger {
|
|
|
|
public:
|
|
|
|
explicit JitLogger(JitCodeEventHandler code_event_handler);
|
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
void CodeMoveEvent(Address from, Address to);
|
2013-07-19 15:57:18 +00:00
|
|
|
void CodeDeleteEvent(Address from);
|
|
|
|
void AddCodeLinePosInfoEvent(
|
|
|
|
void* jit_handler_data,
|
|
|
|
int pc_offset,
|
|
|
|
int position,
|
|
|
|
JitCodeEvent::PositionType position_type);
|
|
|
|
void* StartCodePosInfoEvent();
|
|
|
|
void EndCodePosInfoEvent(Code* code, void* jit_handler_data);
|
|
|
|
|
|
|
|
private:
|
|
|
|
virtual void LogRecordedBuffer(Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
CodeEventLogger::NameBuffer* name_buffer);
|
|
|
|
|
|
|
|
JitCodeEventHandler code_event_handler_;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define JIT_LOG(Call) if (jit_logger_) jit_logger_->Call;
|
|
|
|
|
|
|
|
|
|
|
|
JitLogger::JitLogger(JitCodeEventHandler code_event_handler)
|
|
|
|
: code_event_handler_(code_event_handler) {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void JitLogger::LogRecordedBuffer(Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
|
|
|
CodeEventLogger::NameBuffer* name_buffer) {
|
|
|
|
JitCodeEvent event;
|
|
|
|
memset(&event, 0, sizeof(event));
|
|
|
|
event.type = JitCodeEvent::CODE_ADDED;
|
|
|
|
event.code_start = code->instruction_start();
|
|
|
|
event.code_len = code->instruction_size();
|
|
|
|
Handle<Script> script_handle;
|
|
|
|
if (shared && shared->script()->IsScript()) {
|
|
|
|
script_handle = Handle<Script>(Script::cast(shared->script()));
|
|
|
|
}
|
|
|
|
event.script = ToApiHandle<v8::Script>(script_handle);
|
|
|
|
event.name.str = name_buffer->get();
|
|
|
|
event.name.len = name_buffer->size();
|
|
|
|
code_event_handler_(&event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
void JitLogger::CodeMoveEvent(Address from, Address to) {
|
2013-07-19 15:57:18 +00:00
|
|
|
Code* from_code = Code::cast(HeapObject::FromAddress(from));
|
|
|
|
|
|
|
|
JitCodeEvent event;
|
|
|
|
event.type = JitCodeEvent::CODE_MOVED;
|
|
|
|
event.code_start = from_code->instruction_start();
|
|
|
|
event.code_len = from_code->instruction_size();
|
|
|
|
|
|
|
|
// Calculate the header size.
|
|
|
|
const size_t header_size =
|
|
|
|
from_code->instruction_start() - reinterpret_cast<byte*>(from_code);
|
|
|
|
|
|
|
|
// Calculate the new start address of the instructions.
|
|
|
|
event.new_code_start =
|
|
|
|
reinterpret_cast<byte*>(HeapObject::FromAddress(to)) + header_size;
|
|
|
|
|
|
|
|
code_event_handler_(&event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void JitLogger::CodeDeleteEvent(Address from) {
|
|
|
|
Code* from_code = Code::cast(HeapObject::FromAddress(from));
|
|
|
|
|
|
|
|
JitCodeEvent event;
|
|
|
|
event.type = JitCodeEvent::CODE_REMOVED;
|
|
|
|
event.code_start = from_code->instruction_start();
|
|
|
|
event.code_len = from_code->instruction_size();
|
|
|
|
|
|
|
|
code_event_handler_(&event);
|
|
|
|
}
|
|
|
|
|
|
|
|
void JitLogger::AddCodeLinePosInfoEvent(
|
|
|
|
void* jit_handler_data,
|
|
|
|
int pc_offset,
|
|
|
|
int position,
|
|
|
|
JitCodeEvent::PositionType position_type) {
|
|
|
|
JitCodeEvent event;
|
|
|
|
memset(&event, 0, sizeof(event));
|
|
|
|
event.type = JitCodeEvent::CODE_ADD_LINE_POS_INFO;
|
|
|
|
event.user_data = jit_handler_data;
|
|
|
|
event.line_info.offset = pc_offset;
|
|
|
|
event.line_info.pos = position;
|
|
|
|
event.line_info.position_type = position_type;
|
|
|
|
|
|
|
|
code_event_handler_(&event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void* JitLogger::StartCodePosInfoEvent() {
|
|
|
|
JitCodeEvent event;
|
|
|
|
memset(&event, 0, sizeof(event));
|
|
|
|
event.type = JitCodeEvent::CODE_START_LINE_INFO_RECORDING;
|
|
|
|
|
|
|
|
code_event_handler_(&event);
|
|
|
|
return event.user_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void JitLogger::EndCodePosInfoEvent(Code* code, void* jit_handler_data) {
|
|
|
|
JitCodeEvent event;
|
|
|
|
memset(&event, 0, sizeof(event));
|
|
|
|
event.type = JitCodeEvent::CODE_END_LINE_INFO_RECORDING;
|
|
|
|
event.code_start = code->instruction_start();
|
|
|
|
event.user_data = jit_handler_data;
|
|
|
|
|
|
|
|
code_event_handler_(&event);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// The Profiler samples pc and sp values for the main thread.
|
|
|
|
// Each sample is appended to a circular buffer.
|
|
|
|
// An independent thread removes data and writes it to the log.
|
|
|
|
// This design minimizes the time spent in the sampler.
|
|
|
|
//
|
|
|
|
class Profiler: public Thread {
|
|
|
|
public:
|
2011-06-10 09:42:08 +00:00
|
|
|
explicit Profiler(Isolate* isolate);
|
2008-07-03 15:10:15 +00:00
|
|
|
void Engage();
|
|
|
|
void Disengage();
|
|
|
|
|
|
|
|
// Inserts collected profiling data into buffer.
|
|
|
|
void Insert(TickSample* sample) {
|
2009-01-29 19:47:00 +00:00
|
|
|
if (paused_)
|
|
|
|
return;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
if (Succ(head_) == tail_) {
|
|
|
|
overflow_ = true;
|
|
|
|
} else {
|
|
|
|
buffer_[head_] = *sample;
|
|
|
|
head_ = Succ(head_);
|
|
|
|
buffer_semaphore_->Signal(); // Tell we have an element.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Waits for a signal and removes profiling data.
|
|
|
|
bool Remove(TickSample* sample) {
|
|
|
|
buffer_semaphore_->Wait(); // Wait for an element.
|
|
|
|
*sample = buffer_[tail_];
|
|
|
|
bool result = overflow_;
|
|
|
|
tail_ = Succ(tail_);
|
|
|
|
overflow_ = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Run();
|
|
|
|
|
2009-01-29 19:47:00 +00:00
|
|
|
// Pause and Resume TickSample data collection.
|
2011-03-18 20:35:07 +00:00
|
|
|
bool paused() const { return paused_; }
|
|
|
|
void pause() { paused_ = true; }
|
|
|
|
void resume() { paused_ = false; }
|
2009-01-29 19:47:00 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
private:
|
|
|
|
// Returns the next index in the cyclic buffer.
|
|
|
|
int Succ(int index) { return (index + 1) % kBufferSize; }
|
|
|
|
|
2011-06-10 09:54:04 +00:00
|
|
|
Isolate* isolate_;
|
2008-07-03 15:10:15 +00:00
|
|
|
// Cyclic buffer for communicating profiling samples
|
|
|
|
// between the signal handler and the worker thread.
|
|
|
|
static const int kBufferSize = 128;
|
|
|
|
TickSample buffer_[kBufferSize]; // Buffer storage.
|
|
|
|
int head_; // Index to the buffer head.
|
|
|
|
int tail_; // Index to the buffer tail.
|
|
|
|
bool overflow_; // Tell whether a buffer overflow has occurred.
|
|
|
|
Semaphore* buffer_semaphore_; // Sempahore used for buffer synchronization.
|
|
|
|
|
2009-10-28 09:13:11 +00:00
|
|
|
// Tells whether profiler is engaged, that is, processing thread is stated.
|
|
|
|
bool engaged_;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Tells whether worker thread should continue running.
|
|
|
|
bool running_;
|
2009-01-29 19:47:00 +00:00
|
|
|
|
|
|
|
// Tells whether we are currently recording tick samples.
|
2011-03-18 20:35:07 +00:00
|
|
|
bool paused_;
|
2008-07-03 15:10:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Ticker used to provide ticks to the profiler and the sliding state
|
|
|
|
// window.
|
|
|
|
//
|
2008-08-22 13:33:59 +00:00
|
|
|
class Ticker: public Sampler {
|
2008-07-03 15:10:15 +00:00
|
|
|
public:
|
Simplify isolates access during stack iteration (WAS: Move SafeStackFrameIterator::active_count_...)
While trying to fix Mac and Windows versions for this change:
http://codereview.chromium.org/6771047/, I figured out, that we
already store an isolate in StackFrameIterator, so we can use it in
frame objects, instead of requiring it from caller.
I've changed iterators usage to the following scheme: whenever a
caller maintains an isolate pointer, it just passes it to stack
iterator, and no more worries about passing it to frame content
accessors. If a caller uses current isolate, it can omit passing it
to iterator, in this case, an iterator will use the current isolate,
too.
There was a special case with LiveEdit, which creates
detached copies of frame objects.
R=vitalyr@chromium.org
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6794019
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7499 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2011-04-05 09:01:47 +00:00
|
|
|
Ticker(Isolate* isolate, int interval):
|
2011-03-18 20:35:07 +00:00
|
|
|
Sampler(isolate, interval),
|
2010-12-07 11:31:57 +00:00
|
|
|
profiler_(NULL) {}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
~Ticker() { if (IsActive()) Stop(); }
|
|
|
|
|
2010-10-18 12:37:07 +00:00
|
|
|
virtual void Tick(TickSample* sample) {
|
2008-07-03 15:10:15 +00:00
|
|
|
if (profiler_) profiler_->Insert(sample);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetProfiler(Profiler* profiler) {
|
2010-12-07 11:31:57 +00:00
|
|
|
ASSERT(profiler_ == NULL);
|
2008-07-03 15:10:15 +00:00
|
|
|
profiler_ = profiler;
|
2010-12-07 11:31:57 +00:00
|
|
|
IncreaseProfilingDepth();
|
2009-05-25 08:25:36 +00:00
|
|
|
if (!FLAG_prof_lazy && !IsActive()) Start();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ClearProfiler() {
|
2010-12-07 11:31:57 +00:00
|
|
|
DecreaseProfilingDepth();
|
2008-07-03 15:10:15 +00:00
|
|
|
profiler_ = NULL;
|
2012-12-07 13:47:42 +00:00
|
|
|
if (IsActive()) Stop();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Profiler* profiler_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Profiler implementation.
|
|
|
|
//
|
2011-06-10 09:42:08 +00:00
|
|
|
Profiler::Profiler(Isolate* isolate)
|
2011-06-10 09:54:04 +00:00
|
|
|
: Thread("v8:Profiler"),
|
|
|
|
isolate_(isolate),
|
2011-01-04 09:09:50 +00:00
|
|
|
head_(0),
|
2009-10-28 09:13:11 +00:00
|
|
|
tail_(0),
|
|
|
|
overflow_(false),
|
|
|
|
buffer_semaphore_(OS::CreateSemaphore(0)),
|
|
|
|
engaged_(false),
|
2011-03-18 20:35:07 +00:00
|
|
|
running_(false),
|
|
|
|
paused_(false) {
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Profiler::Engage() {
|
2009-10-28 09:13:11 +00:00
|
|
|
if (engaged_) return;
|
|
|
|
engaged_ = true;
|
|
|
|
|
2013-01-29 14:39:09 +00:00
|
|
|
OS::LogSharedLibraryAddresses();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Start thread processing the profiler buffer.
|
|
|
|
running_ = true;
|
|
|
|
Start();
|
|
|
|
|
|
|
|
// Register to get ticks.
|
2013-04-08 15:16:55 +00:00
|
|
|
Logger* logger = isolate_->logger();
|
|
|
|
logger->ticker_->SetProfiler(this);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2013-04-08 15:16:55 +00:00
|
|
|
logger->ProfilerBeginEvent();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Profiler::Disengage() {
|
2009-10-28 09:13:11 +00:00
|
|
|
if (!engaged_) return;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Stop receiving ticks.
|
2013-07-19 15:57:18 +00:00
|
|
|
isolate_->logger()->ticker_->ClearProfiler();
|
2012-08-28 14:43:28 +00:00
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
// Terminate the worker thread by setting running_ to false,
|
|
|
|
// inserting a fake element in the queue and then wait for
|
|
|
|
// the thread to terminate.
|
|
|
|
running_ = false;
|
|
|
|
TickSample sample;
|
|
|
|
// Reset 'paused_' flag, otherwise semaphore may not be signalled.
|
|
|
|
resume();
|
|
|
|
Insert(&sample);
|
|
|
|
Join();
|
2012-08-28 14:43:28 +00:00
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
LOG(ISOLATE, UncheckedStringEvent("profiler", "end"));
|
2012-08-28 14:43:28 +00:00
|
|
|
}
|
|
|
|
|
2013-02-18 18:06:12 +00:00
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
void Profiler::Run() {
|
|
|
|
TickSample sample;
|
|
|
|
bool overflow = Remove(&sample);
|
|
|
|
while (running_) {
|
|
|
|
LOG(isolate_, TickEvent(&sample, overflow));
|
|
|
|
overflow = Remove(&sample);
|
|
|
|
}
|
2013-02-18 18:06:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
//
|
|
|
|
// Logger class implementation.
|
|
|
|
//
|
2013-02-18 18:06:12 +00:00
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
Logger::Logger(Isolate* isolate)
|
|
|
|
: isolate_(isolate),
|
|
|
|
ticker_(NULL),
|
|
|
|
profiler_(NULL),
|
|
|
|
log_events_(NULL),
|
|
|
|
logging_nesting_(0),
|
|
|
|
cpu_profiler_nesting_(0),
|
|
|
|
log_(new Log(this)),
|
|
|
|
ll_logger_(NULL),
|
|
|
|
jit_logger_(NULL),
|
|
|
|
code_address_map_(new CodeAddressMap),
|
|
|
|
is_initialized_(false),
|
|
|
|
last_address_(NULL),
|
|
|
|
prev_sp_(NULL),
|
|
|
|
prev_function_(NULL),
|
|
|
|
prev_to_(NULL),
|
|
|
|
prev_code_(NULL),
|
|
|
|
epoch_(0) {
|
2013-02-18 18:06:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2013-07-19 15:57:18 +00:00
|
|
|
Logger::~Logger() {
|
|
|
|
delete code_address_map_;
|
|
|
|
delete log_;
|
2013-02-18 18:06:12 +00:00
|
|
|
}
|
2012-08-28 14:43:28 +00:00
|
|
|
|
2009-05-05 15:57:47 +00:00
|
|
|
|
2009-05-29 06:56:23 +00:00
|
|
|
void Logger::ProfilerBeginEvent() {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-05-29 06:56:23 +00:00
|
|
|
msg.Append("profiler,\"begin\",%d\n", kSamplingIntervalMs);
|
2009-06-08 13:39:48 +00:00
|
|
|
msg.WriteToLogFile();
|
|
|
|
}
|
|
|
|
|
2009-02-17 08:21:24 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Logger::StringEvent(const char* name, const char* value) {
|
2009-02-11 07:38:31 +00:00
|
|
|
if (FLAG_log) UncheckedStringEvent(name, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::UncheckedStringEvent(const char* name, const char* value) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("%s,\"%s\"\n", name, value);
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::IntEvent(const char* name, int value) {
|
2010-03-01 10:41:34 +00:00
|
|
|
if (FLAG_log) UncheckedIntEvent(name, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-30 07:22:53 +00:00
|
|
|
void Logger::IntPtrTEvent(const char* name, intptr_t value) {
|
|
|
|
if (FLAG_log) UncheckedIntPtrTEvent(name, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-01 10:41:34 +00:00
|
|
|
void Logger::UncheckedIntEvent(const char* name, int value) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("%s,%d\n", name, value);
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-09-30 07:22:53 +00:00
|
|
|
void Logger::UncheckedIntPtrTEvent(const char* name, intptr_t value) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2010-09-30 07:22:53 +00:00
|
|
|
msg.Append("%s,%" V8_PTR_PREFIX "d\n", name, value);
|
|
|
|
msg.WriteToLogFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Logger::HandleEvent(const char* name, Object** location) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_handles) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-05-15 09:58:40 +00:00
|
|
|
msg.Append("%s,0x%" V8PRIxPTR "\n", name, location);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ApiEvent is private so all the calls come from the Logger class. It is the
|
2009-05-05 15:57:47 +00:00
|
|
|
// caller's responsibility to ensure that log is enabled and that
|
2008-07-03 15:10:15 +00:00
|
|
|
// FLAG_log_api is true.
|
|
|
|
void Logger::ApiEvent(const char* format, ...) {
|
2011-03-18 20:35:07 +00:00
|
|
|
ASSERT(log_->IsEnabled() && FLAG_log_api);
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2008-07-03 15:10:15 +00:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
2009-06-15 15:49:03 +00:00
|
|
|
msg.AppendVA(format, ap);
|
2009-02-17 12:49:35 +00:00
|
|
|
va_end(ap);
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::ApiNamedSecurityCheck(Object* key) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_api) return;
|
2008-07-03 15:10:15 +00:00
|
|
|
if (key->IsString()) {
|
2011-09-09 22:39:47 +00:00
|
|
|
SmartArrayPointer<char> str =
|
2008-07-03 15:10:15 +00:00
|
|
|
String::cast(key)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
|
|
|
ApiEvent("api,check-security,\"%s\"\n", *str);
|
2013-03-04 15:00:57 +00:00
|
|
|
} else if (key->IsSymbol()) {
|
2013-03-22 16:51:28 +00:00
|
|
|
Symbol* symbol = Symbol::cast(key);
|
|
|
|
if (symbol->name()->IsUndefined()) {
|
|
|
|
ApiEvent("api,check-security,symbol(hash %x)\n",
|
|
|
|
Symbol::cast(key)->Hash());
|
|
|
|
} else {
|
|
|
|
SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
|
|
|
|
DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
|
|
|
ApiEvent("api,check-security,symbol(\"%s\" hash %x)\n",
|
|
|
|
*str,
|
|
|
|
Symbol::cast(key)->Hash());
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
} else if (key->IsUndefined()) {
|
|
|
|
ApiEvent("api,check-security,undefined\n");
|
|
|
|
} else {
|
|
|
|
ApiEvent("api,check-security,['no-name']\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::SharedLibraryEvent(const char* library_path,
|
2009-06-16 12:52:02 +00:00
|
|
|
uintptr_t start,
|
|
|
|
uintptr_t end) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_prof) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-06-16 12:52:02 +00:00
|
|
|
msg.Append("shared-library,\"%s\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR "\n",
|
|
|
|
library_path,
|
|
|
|
start,
|
|
|
|
end);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::SharedLibraryEvent(const wchar_t* library_path,
|
2009-06-16 12:52:02 +00:00
|
|
|
uintptr_t start,
|
|
|
|
uintptr_t end) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_prof) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-06-16 12:52:02 +00:00
|
|
|
msg.Append("shared-library,\"%ls\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR "\n",
|
|
|
|
library_path,
|
|
|
|
start,
|
|
|
|
end);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2008-09-12 17:25:38 +00:00
|
|
|
|
2013-07-03 10:10:27 +00:00
|
|
|
void Logger::CodeDeoptEvent(Code* code) {
|
|
|
|
if (!log_->IsEnabled()) return;
|
|
|
|
ASSERT(FLAG_log_internal_timer_events);
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-07-03 10:10:27 +00:00
|
|
|
int since_epoch = static_cast<int>(OS::Ticks() - epoch_);
|
|
|
|
msg.Append("code-deopt,%ld,%d\n", since_epoch, code->CodeSize());
|
|
|
|
msg.WriteToLogFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-28 13:55:40 +00:00
|
|
|
void Logger::TimerEvent(StartEnd se, const char* name) {
|
2012-11-26 08:56:59 +00:00
|
|
|
if (!log_->IsEnabled()) return;
|
2012-12-05 16:22:14 +00:00
|
|
|
ASSERT(FLAG_log_internal_timer_events);
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-01-28 13:55:40 +00:00
|
|
|
int since_epoch = static_cast<int>(OS::Ticks() - epoch_);
|
|
|
|
const char* format = (se == START) ? "timer-event-start,\"%s\",%ld\n"
|
|
|
|
: "timer-event-end,\"%s\",%ld\n";
|
|
|
|
msg.Append(format, name, since_epoch);
|
2012-11-22 13:04:11 +00:00
|
|
|
msg.WriteToLogFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-24 14:44:08 +00:00
|
|
|
void Logger::EnterExternal(Isolate* isolate) {
|
|
|
|
LOG(isolate, TimerEvent(START, TimerEventScope::v8_external));
|
|
|
|
ASSERT(isolate->current_vm_state() == JS);
|
|
|
|
isolate->set_current_vm_state(EXTERNAL);
|
2012-11-28 15:11:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-24 14:44:08 +00:00
|
|
|
void Logger::LeaveExternal(Isolate* isolate) {
|
|
|
|
LOG(isolate, TimerEvent(END, TimerEventScope::v8_external));
|
|
|
|
ASSERT(isolate->current_vm_state() == EXTERNAL);
|
|
|
|
isolate->set_current_vm_state(JS);
|
2012-11-28 15:11:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-28 13:55:40 +00:00
|
|
|
void Logger::TimerEventScope::LogTimerEvent(StartEnd se) {
|
|
|
|
LOG(isolate_, TimerEvent(se, name_));
|
2012-11-26 08:56:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-22 13:04:11 +00:00
|
|
|
const char* Logger::TimerEventScope::v8_recompile_synchronous =
|
|
|
|
"V8.RecompileSynchronous";
|
|
|
|
const char* Logger::TimerEventScope::v8_recompile_parallel =
|
|
|
|
"V8.RecompileParallel";
|
|
|
|
const char* Logger::TimerEventScope::v8_compile_full_code =
|
|
|
|
"V8.CompileFullCode";
|
|
|
|
const char* Logger::TimerEventScope::v8_execute = "V8.Execute";
|
2013-01-28 13:55:40 +00:00
|
|
|
const char* Logger::TimerEventScope::v8_external = "V8.External";
|
2012-11-22 13:04:11 +00:00
|
|
|
|
|
|
|
|
2008-09-23 11:45:43 +00:00
|
|
|
void Logger::LogRegExpSource(Handle<JSRegExp> regexp) {
|
2008-09-11 12:57:27 +00:00
|
|
|
// Prints "/" + re.source + "/" +
|
2008-09-11 11:24:45 +00:00
|
|
|
// (re.global?"g":"") + (re.ignorecase?"i":"") + (re.multiline?"m":"")
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2008-09-11 11:24:45 +00:00
|
|
|
|
|
|
|
Handle<Object> source = GetProperty(regexp, "source");
|
|
|
|
if (!source->IsString()) {
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("no source");
|
2008-09-11 11:24:45 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-10-24 08:40:02 +00:00
|
|
|
switch (regexp->TypeTag()) {
|
2008-10-02 15:35:28 +00:00
|
|
|
case JSRegExp::ATOM:
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('a');
|
2008-10-02 15:35:28 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('/');
|
|
|
|
msg.AppendDetailed(*Handle<String>::cast(source), false);
|
|
|
|
msg.Append('/');
|
2008-09-11 11:24:45 +00:00
|
|
|
|
|
|
|
// global flag
|
|
|
|
Handle<Object> global = GetProperty(regexp, "global");
|
|
|
|
if (global->IsTrue()) {
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('g');
|
2008-09-11 11:24:45 +00:00
|
|
|
}
|
|
|
|
// ignorecase flag
|
|
|
|
Handle<Object> ignorecase = GetProperty(regexp, "ignoreCase");
|
|
|
|
if (ignorecase->IsTrue()) {
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('i');
|
2008-09-11 11:24:45 +00:00
|
|
|
}
|
|
|
|
// multiline flag
|
|
|
|
Handle<Object> multiline = GetProperty(regexp, "multiline");
|
|
|
|
if (multiline->IsTrue()) {
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('m');
|
2008-09-11 11:24:45 +00:00
|
|
|
}
|
2009-02-17 12:49:35 +00:00
|
|
|
|
|
|
|
msg.WriteToLogFile();
|
2008-09-11 11:24:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-24 08:40:02 +00:00
|
|
|
void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_regexp) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("regexp-compile,");
|
2008-09-11 11:24:45 +00:00
|
|
|
LogRegExpSource(regexp);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append(in_cache ? ",hit\n" : ",miss\n");
|
|
|
|
msg.WriteToLogFile();
|
2008-09-11 11:24:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-08 15:16:55 +00:00
|
|
|
void Logger::LogRuntime(Vector<const char> format,
|
2013-02-15 09:27:10 +00:00
|
|
|
JSArray* args) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_runtime) return;
|
2013-04-08 15:16:55 +00:00
|
|
|
HandleScope scope(isolate_);
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-01-06 13:24:52 +00:00
|
|
|
for (int i = 0; i < format.length(); i++) {
|
|
|
|
char c = format[i];
|
|
|
|
if (c == '%' && i <= format.length() - 2) {
|
|
|
|
i++;
|
|
|
|
ASSERT('0' <= format[i] && format[i] <= '9');
|
2010-10-25 15:22:03 +00:00
|
|
|
MaybeObject* maybe = args->GetElement(format[i] - '0');
|
|
|
|
Object* obj;
|
|
|
|
if (!maybe->ToObject(&obj)) {
|
|
|
|
msg.Append("<exception>");
|
|
|
|
continue;
|
|
|
|
}
|
2009-01-06 13:24:52 +00:00
|
|
|
i++;
|
|
|
|
switch (format[i]) {
|
|
|
|
case 's':
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.AppendDetailed(String::cast(obj), false);
|
2009-01-06 13:24:52 +00:00
|
|
|
break;
|
|
|
|
case 'S':
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.AppendDetailed(String::cast(obj), true);
|
2009-01-06 13:24:52 +00:00
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
Logger::LogRegExpSource(Handle<JSRegExp>(JSRegExp::cast(obj)));
|
|
|
|
break;
|
|
|
|
case 'x':
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("0x%x", Smi::cast(obj)->value());
|
2009-01-06 13:24:52 +00:00
|
|
|
break;
|
|
|
|
case 'i':
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("%i", Smi::cast(obj)->value());
|
2009-01-06 13:24:52 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
} else {
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append(c);
|
2009-01-06 13:24:52 +00:00
|
|
|
}
|
|
|
|
}
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
2008-09-11 11:24:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Logger::ApiIndexedSecurityCheck(uint32_t index) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_api) return;
|
2008-07-03 15:10:15 +00:00
|
|
|
ApiEvent("api,check-security,%u\n", index);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::ApiNamedPropertyAccess(const char* tag,
|
|
|
|
JSObject* holder,
|
|
|
|
Object* name) {
|
2013-03-04 15:00:57 +00:00
|
|
|
ASSERT(name->IsName());
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_api) return;
|
2008-07-03 15:10:15 +00:00
|
|
|
String* class_name_obj = holder->class_name();
|
2011-09-09 22:39:47 +00:00
|
|
|
SmartArrayPointer<char> class_name =
|
2008-07-03 15:10:15 +00:00
|
|
|
class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
2013-03-04 15:00:57 +00:00
|
|
|
if (name->IsString()) {
|
|
|
|
SmartArrayPointer<char> property_name =
|
|
|
|
String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
|
|
|
ApiEvent("api,%s,\"%s\",\"%s\"\n", tag, *class_name, *property_name);
|
|
|
|
} else {
|
2013-03-22 16:51:28 +00:00
|
|
|
Symbol* symbol = Symbol::cast(name);
|
|
|
|
uint32_t hash = symbol->Hash();
|
|
|
|
if (symbol->name()->IsUndefined()) {
|
|
|
|
ApiEvent("api,%s,\"%s\",symbol(hash %x)\n", tag, *class_name, hash);
|
|
|
|
} else {
|
|
|
|
SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
|
|
|
|
DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
|
|
|
ApiEvent("api,%s,\"%s\",symbol(\"%s\" hash %x)\n",
|
|
|
|
tag, *class_name, *str, hash);
|
|
|
|
}
|
2013-03-04 15:00:57 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Logger::ApiIndexedPropertyAccess(const char* tag,
|
|
|
|
JSObject* holder,
|
|
|
|
uint32_t index) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_api) return;
|
2008-07-03 15:10:15 +00:00
|
|
|
String* class_name_obj = holder->class_name();
|
2011-09-09 22:39:47 +00:00
|
|
|
SmartArrayPointer<char> class_name =
|
2008-07-03 15:10:15 +00:00
|
|
|
class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
2011-03-28 08:44:30 +00:00
|
|
|
ApiEvent("api,%s,\"%s\",%u\n", tag, *class_name, index);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Logger::ApiObjectAccess(const char* tag, JSObject* object) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_api) return;
|
2008-07-03 15:10:15 +00:00
|
|
|
String* class_name_obj = object->class_name();
|
2011-09-09 22:39:47 +00:00
|
|
|
SmartArrayPointer<char> class_name =
|
2008-07-03 15:10:15 +00:00
|
|
|
class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
2011-03-28 08:44:30 +00:00
|
|
|
ApiEvent("api,%s,\"%s\"\n", tag, *class_name);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::ApiEntryCall(const char* name) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_api) return;
|
2011-03-28 08:44:30 +00:00
|
|
|
ApiEvent("api,%s\n", name);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::NewEvent(const char* name, void* object, size_t size) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-05-15 09:58:40 +00:00
|
|
|
msg.Append("new,%s,0x%" V8PRIxPTR ",%u\n", name, object,
|
2009-02-17 12:49:35 +00:00
|
|
|
static_cast<unsigned int>(size));
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::DeleteEvent(const char* name, void* object) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-05-15 09:58:40 +00:00
|
|
|
msg.Append("delete,%s,0x%" V8PRIxPTR "\n", name, object);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-18 20:35:07 +00:00
|
|
|
void Logger::NewEventStatic(const char* name, void* object, size_t size) {
|
2013-04-08 15:16:55 +00:00
|
|
|
Isolate::Current()->logger()->NewEvent(name, object, size);
|
2011-03-18 20:35:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::DeleteEventStatic(const char* name, void* object) {
|
2013-04-08 15:16:55 +00:00
|
|
|
Isolate::Current()->logger()->DeleteEvent(name, object);
|
2011-03-18 20:35:07 +00:00
|
|
|
}
|
|
|
|
|
2013-07-18 17:19:31 +00:00
|
|
|
|
2013-03-04 15:00:57 +00:00
|
|
|
void Logger::CallbackEventInternal(const char* prefix, Name* name,
|
2009-11-25 16:39:18 +00:00
|
|
|
Address entry_point) {
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-06-24 12:55:19 +00:00
|
|
|
msg.Append("%s,%s,-2,",
|
2010-12-07 13:24:22 +00:00
|
|
|
kLogEventsNames[CODE_CREATION_EVENT],
|
|
|
|
kLogEventsNames[CALLBACK_TAG]);
|
2009-11-23 09:19:24 +00:00
|
|
|
msg.AppendAddress(entry_point);
|
2013-03-04 15:00:57 +00:00
|
|
|
if (name->IsString()) {
|
|
|
|
SmartArrayPointer<char> str =
|
|
|
|
String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
|
|
|
msg.Append(",1,\"%s%s\"", prefix, *str);
|
|
|
|
} else {
|
2013-03-22 16:51:28 +00:00
|
|
|
Symbol* symbol = Symbol::cast(name);
|
|
|
|
if (symbol->name()->IsUndefined()) {
|
|
|
|
msg.Append(",1,symbol(hash %x)", prefix, symbol->Hash());
|
|
|
|
} else {
|
|
|
|
SmartArrayPointer<char> str = String::cast(symbol->name())->ToCString(
|
|
|
|
DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
|
|
|
msg.Append(",1,symbol(\"%s\" hash %x)", prefix, *str, symbol->Hash());
|
|
|
|
}
|
2013-03-04 15:00:57 +00:00
|
|
|
}
|
2009-11-23 09:19:24 +00:00
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
2009-11-25 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-04 15:00:57 +00:00
|
|
|
void Logger::CallbackEvent(Name* name, Address entry_point) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CallbackEvent(name, entry_point));
|
2013-03-04 15:00:57 +00:00
|
|
|
CallbackEventInternal("", name, entry_point);
|
2009-11-25 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-04 15:00:57 +00:00
|
|
|
void Logger::GetterCallbackEvent(Name* name, Address entry_point) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(GetterCallbackEvent(name, entry_point));
|
2013-03-04 15:00:57 +00:00
|
|
|
CallbackEventInternal("get ", name, entry_point);
|
2009-11-25 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-04 15:00:57 +00:00
|
|
|
void Logger::SetterCallbackEvent(Name* name, Address entry_point) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(SetterCallbackEvent(name, entry_point));
|
2013-03-04 15:00:57 +00:00
|
|
|
CallbackEventInternal("set ", name, entry_point);
|
2009-11-23 09:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-18 17:19:31 +00:00
|
|
|
static void AppendCodeCreateHeader(Log::MessageBuilder* msg,
|
|
|
|
Logger::LogEventsAndTags tag,
|
|
|
|
Code* code) {
|
2013-06-24 12:55:19 +00:00
|
|
|
ASSERT(msg);
|
|
|
|
msg->Append("%s,%s,%d,",
|
2013-07-18 17:19:31 +00:00
|
|
|
kLogEventsNames[Logger::CODE_CREATION_EVENT],
|
2013-06-24 12:55:19 +00:00
|
|
|
kLogEventsNames[tag],
|
|
|
|
code->kind());
|
|
|
|
msg->AppendAddress(code->address());
|
|
|
|
msg->Append(",%d,", code->ExecutableSize());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-24 08:40:38 +00:00
|
|
|
void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
const char* comment) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CodeCreateEvent(tag, code, comment));
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!is_logging_code_events()) return;
|
2013-07-19 14:58:53 +00:00
|
|
|
JIT_LOG(CodeCreateEvent(tag, code, comment));
|
|
|
|
LL_LOG(CodeCreateEvent(tag, code, comment));
|
|
|
|
CODE_ADDRESS_MAP_LOG(CodeCreateEvent(tag, code, comment));
|
2013-06-24 08:40:38 +00:00
|
|
|
|
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-06-24 12:55:19 +00:00
|
|
|
AppendCodeCreateHeader(&msg, tag, code);
|
|
|
|
msg.AppendDoubleQuotedString(comment);
|
2009-02-17 08:21:24 +00:00
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-22 16:31:24 +00:00
|
|
|
void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
|
|
|
Code* code,
|
2013-03-04 15:00:57 +00:00
|
|
|
Name* name) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CodeCreateEvent(tag, code, name));
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!is_logging_code_events()) return;
|
2013-07-19 14:58:53 +00:00
|
|
|
JIT_LOG(CodeCreateEvent(tag, code, name));
|
|
|
|
LL_LOG(CodeCreateEvent(tag, code, name));
|
|
|
|
CODE_ADDRESS_MAP_LOG(CodeCreateEvent(tag, code, name));
|
2013-06-24 08:40:38 +00:00
|
|
|
|
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-06-24 12:55:19 +00:00
|
|
|
AppendCodeCreateHeader(&msg, tag, code);
|
2013-03-04 15:00:57 +00:00
|
|
|
if (name->IsString()) {
|
|
|
|
msg.Append('"');
|
|
|
|
msg.AppendDetailed(String::cast(name), false);
|
|
|
|
msg.Append('"');
|
|
|
|
} else {
|
2013-07-18 17:19:31 +00:00
|
|
|
msg.AppendSymbolName(Symbol::cast(name));
|
2013-03-04 15:00:57 +00:00
|
|
|
}
|
2011-04-29 16:06:25 +00:00
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
2011-02-22 16:31:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
2013-05-14 22:51:33 +00:00
|
|
|
CompilationInfo* info,
|
2013-03-04 15:00:57 +00:00
|
|
|
Name* name) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CodeCreateEvent(tag, code, shared, info, name));
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!is_logging_code_events()) return;
|
2013-07-19 14:58:53 +00:00
|
|
|
JIT_LOG(CodeCreateEvent(tag, code, shared, info, name));
|
|
|
|
LL_LOG(CodeCreateEvent(tag, code, shared, info, name));
|
|
|
|
CODE_ADDRESS_MAP_LOG(CodeCreateEvent(tag, code, shared, info, name));
|
2013-06-24 08:40:38 +00:00
|
|
|
|
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-02 15:44:30 +00:00
|
|
|
if (code == isolate_->builtins()->builtin(
|
2011-03-23 13:40:07 +00:00
|
|
|
Builtins::kLazyCompile))
|
2011-03-18 20:35:07 +00:00
|
|
|
return;
|
|
|
|
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-06-24 12:55:19 +00:00
|
|
|
AppendCodeCreateHeader(&msg, tag, code);
|
2013-03-04 15:00:57 +00:00
|
|
|
if (name->IsString()) {
|
|
|
|
SmartArrayPointer<char> str =
|
|
|
|
String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
|
|
|
msg.Append("\"%s\"", *str);
|
|
|
|
} else {
|
2013-07-18 17:19:31 +00:00
|
|
|
msg.AppendSymbolName(Symbol::cast(name));
|
2013-03-04 15:00:57 +00:00
|
|
|
}
|
|
|
|
msg.Append(',');
|
2011-02-22 16:31:24 +00:00
|
|
|
msg.AppendAddress(shared->address());
|
|
|
|
msg.Append(",%s", ComputeMarker(code));
|
2009-06-18 07:59:13 +00:00
|
|
|
msg.Append('\n');
|
2009-02-17 08:21:24 +00:00
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-22 16:31:24 +00:00
|
|
|
// Although, it is possible to extract source and line from
|
|
|
|
// the SharedFunctionInfo object, we left it to caller
|
|
|
|
// to leave logging functions free from heap allocations.
|
2009-06-08 13:39:48 +00:00
|
|
|
void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
2011-02-22 16:31:24 +00:00
|
|
|
Code* code,
|
|
|
|
SharedFunctionInfo* shared,
|
2013-05-14 22:51:33 +00:00
|
|
|
CompilationInfo* info,
|
2013-03-04 15:00:57 +00:00
|
|
|
Name* source, int line) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CodeCreateEvent(tag, code, shared, info, source, line));
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!is_logging_code_events()) return;
|
2013-07-19 14:58:53 +00:00
|
|
|
JIT_LOG(CodeCreateEvent(tag, code, shared, info, source, line));
|
|
|
|
LL_LOG(CodeCreateEvent(tag, code, shared, info, source, line));
|
|
|
|
CODE_ADDRESS_MAP_LOG(CodeCreateEvent(tag, code, shared, info, source, line));
|
2013-06-24 08:40:38 +00:00
|
|
|
|
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-06-24 12:55:19 +00:00
|
|
|
AppendCodeCreateHeader(&msg, tag, code);
|
2011-09-09 22:39:47 +00:00
|
|
|
SmartArrayPointer<char> name =
|
2011-02-22 16:31:24 +00:00
|
|
|
shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
2013-06-24 12:55:19 +00:00
|
|
|
msg.Append("\"%s ", *name);
|
2013-03-04 15:00:57 +00:00
|
|
|
if (source->IsString()) {
|
|
|
|
SmartArrayPointer<char> sourcestr =
|
|
|
|
String::cast(source)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
|
|
|
msg.Append("%s", *sourcestr);
|
|
|
|
} else {
|
2013-07-18 17:19:31 +00:00
|
|
|
msg.AppendSymbolName(Symbol::cast(source));
|
2013-03-04 15:00:57 +00:00
|
|
|
}
|
|
|
|
msg.Append(":%d\",", line);
|
2011-02-22 16:31:24 +00:00
|
|
|
msg.AppendAddress(shared->address());
|
|
|
|
msg.Append(",%s", ComputeMarker(code));
|
2009-06-18 07:59:13 +00:00
|
|
|
msg.Append('\n');
|
2009-02-17 08:21:24 +00:00
|
|
|
msg.WriteToLogFile();
|
2009-02-03 10:52:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-19 14:58:53 +00:00
|
|
|
void Logger::CodeCreateEvent(LogEventsAndTags tag,
|
|
|
|
Code* code,
|
|
|
|
int args_count) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CodeCreateEvent(tag, code, args_count));
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!is_logging_code_events()) return;
|
2013-07-19 14:58:53 +00:00
|
|
|
JIT_LOG(CodeCreateEvent(tag, code, args_count));
|
|
|
|
LL_LOG(CodeCreateEvent(tag, code, args_count));
|
|
|
|
CODE_ADDRESS_MAP_LOG(CodeCreateEvent(tag, code, args_count));
|
2013-06-24 08:40:38 +00:00
|
|
|
|
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-06-24 12:55:19 +00:00
|
|
|
AppendCodeCreateHeader(&msg, tag, code);
|
|
|
|
msg.Append("\"args_count: %d\"", args_count);
|
2009-06-18 07:59:13 +00:00
|
|
|
msg.Append('\n');
|
2009-02-17 08:21:24 +00:00
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-19 16:45:11 +00:00
|
|
|
void Logger::CodeMovingGCEvent() {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CodeMovingGCEvent());
|
|
|
|
|
|
|
|
if (!is_logging_code_events()) return;
|
2011-04-29 16:06:25 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_ll_prof) return;
|
2013-07-15 11:35:39 +00:00
|
|
|
LL_LOG(CodeMovingGCEvent());
|
2010-10-19 16:45:11 +00:00
|
|
|
OS::SignalCodeMovingGC();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-31 09:06:37 +00:00
|
|
|
void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(RegExpCodeCreateEvent(code, source));
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!is_logging_code_events()) return;
|
2013-07-19 14:58:53 +00:00
|
|
|
JIT_LOG(RegExpCodeCreateEvent(code, source));
|
|
|
|
LL_LOG(RegExpCodeCreateEvent(code, source));
|
|
|
|
CODE_ADDRESS_MAP_LOG(RegExpCodeCreateEvent(code, source));
|
2013-06-24 08:40:38 +00:00
|
|
|
|
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-06-24 12:55:19 +00:00
|
|
|
AppendCodeCreateHeader(&msg, REG_EXP_TAG, code);
|
|
|
|
msg.Append('"');
|
2009-03-31 09:06:37 +00:00
|
|
|
msg.AppendDetailed(source, false);
|
2013-06-24 12:55:19 +00:00
|
|
|
msg.Append('"');
|
2009-06-18 07:59:13 +00:00
|
|
|
msg.Append('\n');
|
2009-03-31 09:06:37 +00:00
|
|
|
msg.WriteToLogFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Logger::CodeMoveEvent(Address from, Address to) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CodeMoveEvent(from, to));
|
|
|
|
|
|
|
|
if (!is_logging_code_events()) return;
|
|
|
|
JIT_LOG(CodeMoveEvent(from, to));
|
2013-07-15 11:35:39 +00:00
|
|
|
LL_LOG(CodeMoveEvent(from, to));
|
2013-07-19 14:58:53 +00:00
|
|
|
CODE_ADDRESS_MAP_LOG(CodeMoveEvent(from, to));
|
Fix issue 553: function frame is skipped in profile when compare stub is called.
The problem appeared due to a fact that stubs doesn't create a stack
frame, reusing the stack frame of the caller function. When building
stack traces, the current function is retrieved from PC, and its
callees are retrieved by traversing the stack backwards. Thus, for
stubs, the stub itself was discovered via PC, and then stub's caller's
caller was retrieved from stack.
To fix this problem, a pointer to JSFunction object is now captured
from the topmost stack frame, and is saved into stack trace log
record. Then a simple heuristics is applied whether a referred
function should be added to decoded stack, or not, to avoid reporting
the same function twice (from PC and from the pointer.)
BUG=553
TEST=added to mjsunit/tools/tickprocessor
Review URL: http://codereview.chromium.org/546089
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3673 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2010-01-21 16:42:41 +00:00
|
|
|
MoveEventInternal(CODE_MOVE_EVENT, from, to);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::CodeDeleteEvent(Address from) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(CodeDeleteEvent(from));
|
|
|
|
|
|
|
|
if (!is_logging_code_events()) return;
|
2013-07-19 14:58:53 +00:00
|
|
|
JIT_LOG(CodeDeleteEvent(from));
|
2013-07-15 11:35:39 +00:00
|
|
|
LL_LOG(CodeDeleteEvent(from));
|
2013-07-19 14:58:53 +00:00
|
|
|
CODE_ADDRESS_MAP_LOG(CodeDeleteEvent(from));
|
2013-07-19 15:57:18 +00:00
|
|
|
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-19 15:57:18 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
|
|
|
msg.Append("%s,", kLogEventsNames[CODE_DELETE_EVENT]);
|
|
|
|
msg.AppendAddress(from);
|
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
Fix issue 553: function frame is skipped in profile when compare stub is called.
The problem appeared due to a fact that stubs doesn't create a stack
frame, reusing the stack frame of the caller function. When building
stack traces, the current function is retrieved from PC, and its
callees are retrieved by traversing the stack backwards. Thus, for
stubs, the stub itself was discovered via PC, and then stub's caller's
caller was retrieved from stack.
To fix this problem, a pointer to JSFunction object is now captured
from the topmost stack frame, and is saved into stack trace log
record. Then a simple heuristics is applied whether a referred
function should be added to decoded stack, or not, to avoid reporting
the same function twice (from PC and from the pointer.)
BUG=553
TEST=added to mjsunit/tools/tickprocessor
Review URL: http://codereview.chromium.org/546089
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3673 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2010-01-21 16:42:41 +00:00
|
|
|
}
|
|
|
|
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-02-18 18:06:12 +00:00
|
|
|
void Logger::CodeLinePosInfoAddPositionEvent(void* jit_handler_data,
|
|
|
|
int pc_offset,
|
|
|
|
int position) {
|
2013-07-18 13:18:46 +00:00
|
|
|
JIT_LOG(AddCodeLinePosInfoEvent(jit_handler_data,
|
|
|
|
pc_offset,
|
|
|
|
position,
|
|
|
|
JitCodeEvent::POSITION));
|
2013-02-18 18:06:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-02-18 18:06:12 +00:00
|
|
|
void Logger::CodeLinePosInfoAddStatementPositionEvent(void* jit_handler_data,
|
|
|
|
int pc_offset,
|
|
|
|
int position) {
|
2013-07-18 13:18:46 +00:00
|
|
|
JIT_LOG(AddCodeLinePosInfoEvent(jit_handler_data,
|
|
|
|
pc_offset,
|
|
|
|
position,
|
|
|
|
JitCodeEvent::STATEMENT_POSITION));
|
2013-02-18 18:06:12 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
2013-02-18 18:06:12 +00:00
|
|
|
void Logger::CodeStartLinePosInfoRecordEvent(PositionsRecorder* pos_recorder) {
|
2013-07-18 13:18:46 +00:00
|
|
|
if (jit_logger_ != NULL) {
|
|
|
|
pos_recorder->AttachJITHandlerData(jit_logger_->StartCodePosInfoEvent());
|
2013-02-18 18:06:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-19 14:58:53 +00:00
|
|
|
|
2013-02-18 18:06:12 +00:00
|
|
|
void Logger::CodeEndLinePosInfoRecordEvent(Code* code,
|
|
|
|
void* jit_handler_data) {
|
2013-07-18 13:18:46 +00:00
|
|
|
JIT_LOG(EndCodePosInfoEvent(code, jit_handler_data));
|
2013-02-18 18:06:12 +00:00
|
|
|
}
|
Fix issue 553: function frame is skipped in profile when compare stub is called.
The problem appeared due to a fact that stubs doesn't create a stack
frame, reusing the stack frame of the caller function. When building
stack traces, the current function is retrieved from PC, and its
callees are retrieved by traversing the stack backwards. Thus, for
stubs, the stub itself was discovered via PC, and then stub's caller's
caller was retrieved from stack.
To fix this problem, a pointer to JSFunction object is now captured
from the topmost stack frame, and is saved into stack trace log
record. Then a simple heuristics is applied whether a referred
function should be added to decoded stack, or not, to avoid reporting
the same function twice (from PC and from the pointer.)
BUG=553
TEST=added to mjsunit/tools/tickprocessor
Review URL: http://codereview.chromium.org/546089
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3673 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2010-01-21 16:42:41 +00:00
|
|
|
|
2013-07-05 09:52:11 +00:00
|
|
|
|
Fix issue 553: function frame is skipped in profile when compare stub is called.
The problem appeared due to a fact that stubs doesn't create a stack
frame, reusing the stack frame of the caller function. When building
stack traces, the current function is retrieved from PC, and its
callees are retrieved by traversing the stack backwards. Thus, for
stubs, the stub itself was discovered via PC, and then stub's caller's
caller was retrieved from stack.
To fix this problem, a pointer to JSFunction object is now captured
from the topmost stack frame, and is saved into stack trace log
record. Then a simple heuristics is applied whether a referred
function should be added to decoded stack, or not, to avoid reporting
the same function twice (from PC and from the pointer.)
BUG=553
TEST=added to mjsunit/tools/tickprocessor
Review URL: http://codereview.chromium.org/546089
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3673 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2010-01-21 16:42:41 +00:00
|
|
|
void Logger::SnapshotPositionEvent(Address addr, int pos) {
|
2011-04-29 16:06:25 +00:00
|
|
|
if (!log_->IsEnabled()) return;
|
2013-07-15 11:35:39 +00:00
|
|
|
LL_LOG(SnapshotPositionEvent(addr, pos));
|
2013-07-19 14:58:53 +00:00
|
|
|
if (Serializer::enabled()) {
|
|
|
|
const char* code_name = code_address_map_->Lookup(addr);
|
2011-04-29 16:06:25 +00:00
|
|
|
if (code_name == NULL) return; // Not a code object.
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2013-06-24 12:55:19 +00:00
|
|
|
msg.Append("%s,%d,", kLogEventsNames[SNAPSHOT_CODE_NAME_EVENT], pos);
|
|
|
|
msg.AppendDoubleQuotedString(code_name);
|
|
|
|
msg.Append("\n");
|
2011-04-29 16:06:25 +00:00
|
|
|
msg.WriteToLogFile();
|
|
|
|
}
|
|
|
|
if (!FLAG_log_snapshot_positions) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2010-12-07 13:24:22 +00:00
|
|
|
msg.Append("%s,", kLogEventsNames[SNAPSHOT_POSITION_EVENT]);
|
Fix issue 553: function frame is skipped in profile when compare stub is called.
The problem appeared due to a fact that stubs doesn't create a stack
frame, reusing the stack frame of the caller function. When building
stack traces, the current function is retrieved from PC, and its
callees are retrieved by traversing the stack backwards. Thus, for
stubs, the stub itself was discovered via PC, and then stub's caller's
caller was retrieved from stack.
To fix this problem, a pointer to JSFunction object is now captured
from the topmost stack frame, and is saved into stack trace log
record. Then a simple heuristics is applied whether a referred
function should be added to decoded stack, or not, to avoid reporting
the same function twice (from PC and from the pointer.)
BUG=553
TEST=added to mjsunit/tools/tickprocessor
Review URL: http://codereview.chromium.org/546089
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3673 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2010-01-21 16:42:41 +00:00
|
|
|
msg.AppendAddress(addr);
|
|
|
|
msg.Append(",%d", pos);
|
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-10 12:00:27 +00:00
|
|
|
void Logger::SharedFunctionInfoMoveEvent(Address from, Address to) {
|
2013-07-23 08:12:15 +00:00
|
|
|
PROFILER_LOG(SharedFunctionInfoMoveEvent(from, to));
|
|
|
|
|
|
|
|
if (!is_logging_code_events()) return;
|
2011-03-10 12:00:27 +00:00
|
|
|
MoveEventInternal(SHARED_FUNC_MOVE_EVENT, from, to);
|
Fix issue 553: function frame is skipped in profile when compare stub is called.
The problem appeared due to a fact that stubs doesn't create a stack
frame, reusing the stack frame of the caller function. When building
stack traces, the current function is retrieved from PC, and its
callees are retrieved by traversing the stack backwards. Thus, for
stubs, the stub itself was discovered via PC, and then stub's caller's
caller was retrieved from stack.
To fix this problem, a pointer to JSFunction object is now captured
from the topmost stack frame, and is saved into stack trace log
record. Then a simple heuristics is applied whether a referred
function should be added to decoded stack, or not, to avoid reporting
the same function twice (from PC and from the pointer.)
BUG=553
TEST=added to mjsunit/tools/tickprocessor
Review URL: http://codereview.chromium.org/546089
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3673 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2010-01-21 16:42:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::MoveEventInternal(LogEventsAndTags event,
|
|
|
|
Address from,
|
|
|
|
Address to) {
|
2013-07-23 08:12:15 +00:00
|
|
|
if (!FLAG_log_code || !log_->IsEnabled()) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2010-12-07 13:24:22 +00:00
|
|
|
msg.Append("%s,", kLogEventsNames[event]);
|
2009-06-15 09:37:50 +00:00
|
|
|
msg.AppendAddress(from);
|
Fix issue 553: function frame is skipped in profile when compare stub is called.
The problem appeared due to a fact that stubs doesn't create a stack
frame, reusing the stack frame of the caller function. When building
stack traces, the current function is retrieved from PC, and its
callees are retrieved by traversing the stack backwards. Thus, for
stubs, the stub itself was discovered via PC, and then stub's caller's
caller was retrieved from stack.
To fix this problem, a pointer to JSFunction object is now captured
from the topmost stack frame, and is saved into stack trace log
record. Then a simple heuristics is applied whether a referred
function should be added to decoded stack, or not, to avoid reporting
the same function twice (from PC and from the pointer.)
BUG=553
TEST=added to mjsunit/tools/tickprocessor
Review URL: http://codereview.chromium.org/546089
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3673 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2010-01-21 16:42:41 +00:00
|
|
|
msg.Append(',');
|
2010-12-07 13:24:22 +00:00
|
|
|
msg.AppendAddress(to);
|
2009-06-15 09:37:50 +00:00
|
|
|
msg.Append('\n');
|
2009-02-17 08:21:24 +00:00
|
|
|
msg.WriteToLogFile();
|
2008-12-19 13:12:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Logger::ResourceEvent(const char* name, const char* tag) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("%s,%s,", name, tag);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
uint32_t sec, usec;
|
|
|
|
if (OS::GetUserTime(&sec, &usec) != -1) {
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("%d,%d,", sec, usec);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("%.0f", OS::TimeCurrentMillis());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-04 15:00:57 +00:00
|
|
|
void Logger::SuspectReadEvent(Name* name, Object* obj) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_suspect) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2008-12-09 09:19:02 +00:00
|
|
|
String* class_name = obj->IsJSObject()
|
|
|
|
? JSObject::cast(obj)->class_name()
|
2013-02-15 09:27:10 +00:00
|
|
|
: isolate_->heap()->empty_string();
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("suspect-read,");
|
|
|
|
msg.Append(class_name);
|
|
|
|
msg.Append(',');
|
2013-03-04 15:00:57 +00:00
|
|
|
if (name->IsString()) {
|
|
|
|
msg.Append('"');
|
|
|
|
msg.Append(String::cast(name));
|
|
|
|
msg.Append('"');
|
|
|
|
} else {
|
2013-07-18 17:19:31 +00:00
|
|
|
msg.AppendSymbolName(Symbol::cast(name));
|
2013-03-04 15:00:57 +00:00
|
|
|
}
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::HeapSampleBeginEvent(const char* space, const char* kind) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_gc) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-07-20 09:38:44 +00:00
|
|
|
// Using non-relative system time in order to be able to synchronize with
|
|
|
|
// external memory profiling events (e.g. DOM memory size).
|
|
|
|
msg.Append("heap-sample-begin,\"%s\",\"%s\",%.0f\n",
|
|
|
|
space, kind, OS::TimeCurrentMillis());
|
|
|
|
msg.WriteToLogFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Logger::HeapSampleEndEvent(const char* space, const char* kind) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_gc) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("heap-sample-end,\"%s\",\"%s\"\n", space, kind);
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::HeapSampleItemEvent(const char* type, int number, int bytes) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log_gc) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("heap-sample-item,%s,%d,%d\n", type, number, bytes);
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-08-06 10:02:49 +00:00
|
|
|
void Logger::DebugTag(const char* call_site_tag) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("debug-tag,%s\n", call_site_tag);
|
|
|
|
msg.WriteToLogFile();
|
2008-08-06 10:02:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Logger::DebugEvent(const char* event_type, Vector<uint16_t> parameter) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_log) return;
|
2008-08-06 10:02:49 +00:00
|
|
|
StringBuilder s(parameter.length() + 1);
|
|
|
|
for (int i = 0; i < parameter.length(); ++i) {
|
|
|
|
s.AddCharacter(static_cast<char>(parameter[i]));
|
|
|
|
}
|
|
|
|
char* parameter_string = s.Finalize();
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append("debug-queue-event,%s,%15.3f,%s\n",
|
|
|
|
event_type,
|
|
|
|
OS::TimeCurrentMillis(),
|
|
|
|
parameter_string);
|
2008-08-06 10:02:49 +00:00
|
|
|
DeleteArray(parameter_string);
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.WriteToLogFile();
|
2008-08-06 10:02:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
void Logger::TickEvent(TickSample* sample, bool overflow) {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled() || !FLAG_prof) return;
|
2013-07-18 17:19:31 +00:00
|
|
|
Log::MessageBuilder msg(log_);
|
2010-12-07 13:24:22 +00:00
|
|
|
msg.Append("%s,", kLogEventsNames[TICK_EVENT]);
|
|
|
|
msg.AppendAddress(sample->pc);
|
2012-12-05 16:22:14 +00:00
|
|
|
msg.Append(",%ld", static_cast<int>(OS::Ticks() - epoch_));
|
2013-04-19 11:55:01 +00:00
|
|
|
if (sample->has_external_callback) {
|
|
|
|
msg.Append(",1,");
|
|
|
|
msg.AppendAddress(sample->external_callback);
|
|
|
|
} else {
|
|
|
|
msg.Append(",0,");
|
|
|
|
msg.AppendAddress(sample->tos);
|
|
|
|
}
|
2009-06-15 09:37:50 +00:00
|
|
|
msg.Append(",%d", static_cast<int>(sample->state));
|
2009-02-17 12:49:35 +00:00
|
|
|
if (overflow) {
|
|
|
|
msg.Append(",overflow");
|
|
|
|
}
|
2009-03-06 13:07:57 +00:00
|
|
|
for (int i = 0; i < sample->frames_count; ++i) {
|
2009-06-15 09:37:50 +00:00
|
|
|
msg.Append(',');
|
2010-12-07 13:24:22 +00:00
|
|
|
msg.AppendAddress(sample->stack[i]);
|
2009-06-11 14:08:34 +00:00
|
|
|
}
|
2009-02-17 12:49:35 +00:00
|
|
|
msg.Append('\n');
|
|
|
|
msg.WriteToLogFile();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-01-29 19:47:00 +00:00
|
|
|
|
|
|
|
|
2011-07-01 09:35:16 +00:00
|
|
|
bool Logger::IsProfilerPaused() {
|
|
|
|
return profiler_ == NULL || profiler_->paused();
|
2009-01-29 19:47:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-01 09:35:16 +00:00
|
|
|
void Logger::PauseProfiler() {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled()) return;
|
2011-07-01 09:35:16 +00:00
|
|
|
if (profiler_ != NULL) {
|
2010-02-17 13:23:46 +00:00
|
|
|
// It is OK to have negative nesting.
|
|
|
|
if (--cpu_profiler_nesting_ == 0) {
|
|
|
|
profiler_->pause();
|
|
|
|
if (FLAG_prof_lazy) {
|
2012-12-07 13:47:42 +00:00
|
|
|
ticker_->Stop();
|
2010-02-17 13:23:46 +00:00
|
|
|
FLAG_log_code = false;
|
2011-03-18 20:35:07 +00:00
|
|
|
LOG(ISOLATE, UncheckedStringEvent("profiler", "pause"));
|
2010-02-17 13:23:46 +00:00
|
|
|
}
|
2010-03-01 10:41:34 +00:00
|
|
|
--logging_nesting_;
|
2009-08-04 14:14:01 +00:00
|
|
|
}
|
2009-05-29 06:56:23 +00:00
|
|
|
}
|
2009-01-29 19:47:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-01 09:35:16 +00:00
|
|
|
void Logger::ResumeProfiler() {
|
2011-03-18 20:35:07 +00:00
|
|
|
if (!log_->IsEnabled()) return;
|
2011-07-01 09:35:16 +00:00
|
|
|
if (profiler_ != NULL) {
|
2010-02-17 13:23:46 +00:00
|
|
|
if (cpu_profiler_nesting_++ == 0) {
|
2010-03-01 10:41:34 +00:00
|
|
|
++logging_nesting_;
|
2010-02-17 13:23:46 +00:00
|
|
|
if (FLAG_prof_lazy) {
|
|
|
|
profiler_->Engage();
|
2011-03-18 20:35:07 +00:00
|
|
|
LOG(ISOLATE, UncheckedStringEvent("profiler", "resume"));
|
2010-02-17 13:23:46 +00:00
|
|
|
FLAG_log_code = true;
|
|
|
|
LogCompiledFunctions();
|
|
|
|
LogAccessorCallbacks();
|
2012-12-07 13:47:42 +00:00
|
|
|
if (!ticker_->IsActive()) ticker_->Start();
|
2010-02-17 13:23:46 +00:00
|
|
|
}
|
|
|
|
profiler_->resume();
|
2009-08-04 14:14:01 +00:00
|
|
|
}
|
2009-05-29 06:56:23 +00:00
|
|
|
}
|
2009-01-29 19:47:00 +00:00
|
|
|
}
|
2009-05-05 15:57:47 +00:00
|
|
|
|
|
|
|
|
2009-05-28 13:56:32 +00:00
|
|
|
// This function can be called when Log's mutex is acquired,
|
|
|
|
// either from main or Profiler's thread.
|
2011-03-18 20:35:07 +00:00
|
|
|
void Logger::LogFailure() {
|
2011-07-01 09:35:16 +00:00
|
|
|
PauseProfiler();
|
2009-05-28 13:56:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-07 11:31:57 +00:00
|
|
|
class EnumerateOptimizedFunctionsVisitor: public OptimizedFunctionVisitor {
|
|
|
|
public:
|
|
|
|
EnumerateOptimizedFunctionsVisitor(Handle<SharedFunctionInfo>* sfis,
|
|
|
|
Handle<Code>* code_objects,
|
|
|
|
int* count)
|
|
|
|
: sfis_(sfis), code_objects_(code_objects), count_(count) { }
|
|
|
|
|
|
|
|
virtual void EnterContext(Context* context) {}
|
|
|
|
virtual void LeaveContext(Context* context) {}
|
|
|
|
|
|
|
|
virtual void VisitFunction(JSFunction* function) {
|
2011-06-17 08:40:30 +00:00
|
|
|
SharedFunctionInfo* sfi = SharedFunctionInfo::cast(function->shared());
|
|
|
|
Object* maybe_script = sfi->script();
|
|
|
|
if (maybe_script->IsScript()
|
|
|
|
&& !Script::cast(maybe_script)->HasValidSource()) return;
|
2010-12-07 11:31:57 +00:00
|
|
|
if (sfis_ != NULL) {
|
2011-06-17 08:40:30 +00:00
|
|
|
sfis_[*count_] = Handle<SharedFunctionInfo>(sfi);
|
2010-12-07 11:31:57 +00:00
|
|
|
}
|
|
|
|
if (code_objects_ != NULL) {
|
|
|
|
ASSERT(function->code()->kind() == Code::OPTIMIZED_FUNCTION);
|
|
|
|
code_objects_[*count_] = Handle<Code>(function->code());
|
|
|
|
}
|
|
|
|
*count_ = *count_ + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Handle<SharedFunctionInfo>* sfis_;
|
|
|
|
Handle<Code>* code_objects_;
|
|
|
|
int* count_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-02-11 13:02:20 +00:00
|
|
|
static int EnumerateCompiledFunctions(Heap* heap,
|
|
|
|
Handle<SharedFunctionInfo>* sfis,
|
2010-12-07 11:31:57 +00:00
|
|
|
Handle<Code>* code_objects) {
|
2013-02-11 13:02:20 +00:00
|
|
|
HeapIterator iterator(heap);
|
2013-06-03 15:32:22 +00:00
|
|
|
DisallowHeapAllocation no_gc;
|
2009-05-20 09:04:13 +00:00
|
|
|
int compiled_funcs_count = 0;
|
2010-12-07 11:31:57 +00:00
|
|
|
|
|
|
|
// Iterate the heap to find shared function info objects and record
|
|
|
|
// the unoptimized code for them.
|
2010-01-25 22:53:18 +00:00
|
|
|
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
2009-10-07 12:20:02 +00:00
|
|
|
if (!obj->IsSharedFunctionInfo()) continue;
|
|
|
|
SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
|
|
|
|
if (sfi->is_compiled()
|
|
|
|
&& (!sfi->script()->IsScript()
|
|
|
|
|| Script::cast(sfi->script())->HasValidSource())) {
|
2010-12-07 11:31:57 +00:00
|
|
|
if (sfis != NULL) {
|
2009-10-07 12:20:02 +00:00
|
|
|
sfis[compiled_funcs_count] = Handle<SharedFunctionInfo>(sfi);
|
2010-12-07 11:31:57 +00:00
|
|
|
}
|
|
|
|
if (code_objects != NULL) {
|
|
|
|
code_objects[compiled_funcs_count] = Handle<Code>(sfi->code());
|
|
|
|
}
|
2009-10-07 12:20:02 +00:00
|
|
|
++compiled_funcs_count;
|
2009-05-20 09:04:13 +00:00
|
|
|
}
|
2009-10-07 12:20:02 +00:00
|
|
|
}
|
2010-12-07 11:31:57 +00:00
|
|
|
|
|
|
|
// Iterate all optimized functions in all contexts.
|
|
|
|
EnumerateOptimizedFunctionsVisitor visitor(sfis,
|
|
|
|
code_objects,
|
|
|
|
&compiled_funcs_count);
|
2013-03-18 13:57:49 +00:00
|
|
|
Deoptimizer::VisitAllOptimizedFunctions(heap->isolate(), &visitor);
|
2010-12-07 11:31:57 +00:00
|
|
|
|
2009-10-07 12:20:02 +00:00
|
|
|
return compiled_funcs_count;
|
|
|
|
}
|
2009-05-20 09:04:13 +00:00
|
|
|
|
|
|
|
|
2009-11-11 12:35:50 +00:00
|
|
|
void Logger::LogCodeObject(Object* object) {
|
2013-06-03 09:46:32 +00:00
|
|
|
Code* code_object = Code::cast(object);
|
|
|
|
LogEventsAndTags tag = Logger::STUB_TAG;
|
|
|
|
const char* description = "Unknown code from the snapshot";
|
|
|
|
switch (code_object->kind()) {
|
|
|
|
case Code::FUNCTION:
|
|
|
|
case Code::OPTIMIZED_FUNCTION:
|
|
|
|
return; // We log this later using LogCompiledFunctions.
|
|
|
|
case Code::UNARY_OP_IC: // fall through
|
|
|
|
case Code::BINARY_OP_IC: // fall through
|
|
|
|
case Code::COMPARE_IC: // fall through
|
|
|
|
case Code::COMPARE_NIL_IC: // fall through
|
|
|
|
case Code::TO_BOOLEAN_IC: // fall through
|
|
|
|
case Code::STUB:
|
|
|
|
description =
|
|
|
|
CodeStub::MajorName(CodeStub::GetMajorKey(code_object), true);
|
|
|
|
if (description == NULL)
|
|
|
|
description = "A stub from the snapshot";
|
|
|
|
tag = Logger::STUB_TAG;
|
|
|
|
break;
|
2013-06-24 12:55:19 +00:00
|
|
|
case Code::REGEXP:
|
|
|
|
description = "Regular expression code";
|
|
|
|
tag = Logger::REG_EXP_TAG;
|
|
|
|
break;
|
2013-06-03 09:46:32 +00:00
|
|
|
case Code::BUILTIN:
|
|
|
|
description = "A builtin from the snapshot";
|
|
|
|
tag = Logger::BUILTIN_TAG;
|
|
|
|
break;
|
|
|
|
case Code::KEYED_LOAD_IC:
|
|
|
|
description = "A keyed load IC from the snapshot";
|
|
|
|
tag = Logger::KEYED_LOAD_IC_TAG;
|
|
|
|
break;
|
|
|
|
case Code::LOAD_IC:
|
|
|
|
description = "A load IC from the snapshot";
|
|
|
|
tag = Logger::LOAD_IC_TAG;
|
|
|
|
break;
|
|
|
|
case Code::STORE_IC:
|
|
|
|
description = "A store IC from the snapshot";
|
|
|
|
tag = Logger::STORE_IC_TAG;
|
|
|
|
break;
|
|
|
|
case Code::KEYED_STORE_IC:
|
|
|
|
description = "A keyed store IC from the snapshot";
|
|
|
|
tag = Logger::KEYED_STORE_IC_TAG;
|
|
|
|
break;
|
|
|
|
case Code::CALL_IC:
|
|
|
|
description = "A call IC from the snapshot";
|
|
|
|
tag = Logger::CALL_IC_TAG;
|
|
|
|
break;
|
|
|
|
case Code::KEYED_CALL_IC:
|
|
|
|
description = "A keyed call IC from the snapshot";
|
|
|
|
tag = Logger::KEYED_CALL_IC_TAG;
|
|
|
|
break;
|
2013-06-25 09:34:22 +00:00
|
|
|
case Code::NUMBER_OF_KINDS:
|
|
|
|
break;
|
2009-11-11 12:35:50 +00:00
|
|
|
}
|
2013-06-03 09:46:32 +00:00
|
|
|
PROFILE(isolate_, CodeCreateEvent(tag, code_object, description));
|
2009-11-11 12:35:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-04 21:34:03 +00:00
|
|
|
void Logger::LogCodeObjects() {
|
2013-02-15 09:27:10 +00:00
|
|
|
Heap* heap = isolate_->heap();
|
2013-02-11 13:02:20 +00:00
|
|
|
heap->CollectAllGarbage(Heap::kMakeHeapIterableMask,
|
2012-02-03 14:16:40 +00:00
|
|
|
"Logger::LogCodeObjects");
|
2013-02-11 13:02:20 +00:00
|
|
|
HeapIterator iterator(heap);
|
2013-06-03 15:32:22 +00:00
|
|
|
DisallowHeapAllocation no_gc;
|
2010-02-04 21:34:03 +00:00
|
|
|
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
|
|
|
if (obj->IsCode()) LogCodeObject(obj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-14 11:47:03 +00:00
|
|
|
void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
|
|
|
|
Handle<Code> code) {
|
|
|
|
Handle<String> func_name(shared->DebugName());
|
|
|
|
if (shared->script()->IsScript()) {
|
|
|
|
Handle<Script> script(Script::cast(shared->script()));
|
2013-07-07 11:42:30 +00:00
|
|
|
int line_num = GetScriptLineNumber(script, shared->start_position()) + 1;
|
2011-09-14 11:47:03 +00:00
|
|
|
if (script->name()->IsString()) {
|
2013-06-13 11:09:19 +00:00
|
|
|
Handle<String> script_name(String::cast(script->name()));
|
|
|
|
if (line_num > 0) {
|
2013-04-02 07:53:50 +00:00
|
|
|
PROFILE(isolate_,
|
2011-09-14 11:47:03 +00:00
|
|
|
CodeCreateEvent(
|
|
|
|
Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
|
2013-05-14 22:51:33 +00:00
|
|
|
*code, *shared, NULL,
|
2013-07-07 11:42:30 +00:00
|
|
|
*script_name, line_num));
|
2011-09-14 11:47:03 +00:00
|
|
|
} else {
|
|
|
|
// Can't distinguish eval and script here, so always use Script.
|
2013-04-02 07:53:50 +00:00
|
|
|
PROFILE(isolate_,
|
2011-09-14 11:47:03 +00:00
|
|
|
CodeCreateEvent(
|
|
|
|
Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
|
2013-05-14 22:51:33 +00:00
|
|
|
*code, *shared, NULL, *script_name));
|
2011-09-14 11:47:03 +00:00
|
|
|
}
|
|
|
|
} else {
|
2013-04-02 07:53:50 +00:00
|
|
|
PROFILE(isolate_,
|
2011-09-14 11:47:03 +00:00
|
|
|
CodeCreateEvent(
|
|
|
|
Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
|
2013-07-07 11:42:30 +00:00
|
|
|
*code, *shared, NULL,
|
|
|
|
isolate_->heap()->empty_string(), line_num));
|
2011-09-14 11:47:03 +00:00
|
|
|
}
|
|
|
|
} else if (shared->IsApiFunction()) {
|
|
|
|
// API function.
|
|
|
|
FunctionTemplateInfo* fun_data = shared->get_api_func_data();
|
|
|
|
Object* raw_call_data = fun_data->call_code();
|
|
|
|
if (!raw_call_data->IsUndefined()) {
|
|
|
|
CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
|
|
|
|
Object* callback_obj = call_data->callback();
|
|
|
|
Address entry_point = v8::ToCData<Address>(callback_obj);
|
2013-04-02 07:53:50 +00:00
|
|
|
PROFILE(isolate_, CallbackEvent(*func_name, entry_point));
|
2011-09-14 11:47:03 +00:00
|
|
|
}
|
|
|
|
} else {
|
2013-04-02 07:53:50 +00:00
|
|
|
PROFILE(isolate_,
|
2011-09-14 11:47:03 +00:00
|
|
|
CodeCreateEvent(
|
2013-05-14 22:51:33 +00:00
|
|
|
Logger::LAZY_COMPILE_TAG, *code, *shared, NULL, *func_name));
|
2011-09-14 11:47:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-07 12:20:02 +00:00
|
|
|
void Logger::LogCompiledFunctions() {
|
2013-02-15 09:27:10 +00:00
|
|
|
Heap* heap = isolate_->heap();
|
2013-02-11 13:02:20 +00:00
|
|
|
heap->CollectAllGarbage(Heap::kMakeHeapIterableMask,
|
2012-02-03 14:16:40 +00:00
|
|
|
"Logger::LogCompiledFunctions");
|
2013-02-15 09:27:10 +00:00
|
|
|
HandleScope scope(isolate_);
|
2013-02-11 13:02:20 +00:00
|
|
|
const int compiled_funcs_count = EnumerateCompiledFunctions(heap, NULL, NULL);
|
2010-05-05 12:25:58 +00:00
|
|
|
ScopedVector< Handle<SharedFunctionInfo> > sfis(compiled_funcs_count);
|
2010-12-07 11:31:57 +00:00
|
|
|
ScopedVector< Handle<Code> > code_objects(compiled_funcs_count);
|
2013-02-11 13:02:20 +00:00
|
|
|
EnumerateCompiledFunctions(heap, sfis.start(), code_objects.start());
|
2009-05-20 09:04:13 +00:00
|
|
|
|
|
|
|
// During iteration, there can be heap allocation due to
|
|
|
|
// GetScriptLineNumber call.
|
|
|
|
for (int i = 0; i < compiled_funcs_count; ++i) {
|
2013-07-02 15:44:30 +00:00
|
|
|
if (*code_objects[i] == isolate_->builtins()->builtin(
|
2011-03-23 13:40:07 +00:00
|
|
|
Builtins::kLazyCompile))
|
2011-03-18 20:35:07 +00:00
|
|
|
continue;
|
2011-09-14 11:47:03 +00:00
|
|
|
LogExistingFunction(sfis[i], code_objects[i]);
|
2009-05-20 09:04:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-25 16:39:18 +00:00
|
|
|
|
|
|
|
void Logger::LogAccessorCallbacks() {
|
2013-02-15 09:27:10 +00:00
|
|
|
Heap* heap = isolate_->heap();
|
2013-02-11 13:02:20 +00:00
|
|
|
heap->CollectAllGarbage(Heap::kMakeHeapIterableMask,
|
2012-02-03 14:16:40 +00:00
|
|
|
"Logger::LogAccessorCallbacks");
|
2013-02-11 13:02:20 +00:00
|
|
|
HeapIterator iterator(heap);
|
2013-06-03 15:32:22 +00:00
|
|
|
DisallowHeapAllocation no_gc;
|
2010-01-25 22:53:18 +00:00
|
|
|
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
2013-02-12 14:33:08 +00:00
|
|
|
if (!obj->IsExecutableAccessorInfo()) continue;
|
|
|
|
ExecutableAccessorInfo* ai = ExecutableAccessorInfo::cast(obj);
|
2013-03-04 15:00:57 +00:00
|
|
|
if (!ai->name()->IsName()) continue;
|
2009-11-25 16:39:18 +00:00
|
|
|
Address getter_entry = v8::ToCData<Address>(ai->getter());
|
2013-03-04 15:00:57 +00:00
|
|
|
Name* name = Name::cast(ai->name());
|
2009-11-25 16:39:18 +00:00
|
|
|
if (getter_entry != 0) {
|
2013-04-02 07:53:50 +00:00
|
|
|
PROFILE(isolate_, GetterCallbackEvent(name, getter_entry));
|
2009-11-25 16:39:18 +00:00
|
|
|
}
|
|
|
|
Address setter_entry = v8::ToCData<Address>(ai->setter());
|
|
|
|
if (setter_entry != 0) {
|
2013-04-02 07:53:50 +00:00
|
|
|
PROFILE(isolate_, SetterCallbackEvent(name, setter_entry));
|
2009-11-25 16:39:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2013-07-15 11:35:39 +00:00
|
|
|
static void AddIsolateIdIfNeeded(StringStream* stream) {
|
|
|
|
Isolate* isolate = Isolate::Current();
|
|
|
|
if (isolate->IsDefaultIsolate()) return;
|
|
|
|
stream->Add("isolate-%p-", isolate);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static SmartArrayPointer<const char> PrepareLogFileName(const char* file_name) {
|
|
|
|
if (strchr(file_name, '%') != NULL ||
|
|
|
|
!Isolate::Current()->IsDefaultIsolate()) {
|
|
|
|
// If there's a '%' in the log file name we have to expand
|
|
|
|
// placeholders.
|
|
|
|
HeapStringAllocator allocator;
|
|
|
|
StringStream stream(&allocator);
|
|
|
|
AddIsolateIdIfNeeded(&stream);
|
|
|
|
for (const char* p = file_name; *p; p++) {
|
|
|
|
if (*p == '%') {
|
|
|
|
p++;
|
|
|
|
switch (*p) {
|
|
|
|
case '\0':
|
|
|
|
// If there's a % at the end of the string we back up
|
|
|
|
// one character so we can escape the loop properly.
|
|
|
|
p--;
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
stream.Add("%d", OS::GetCurrentProcessId());
|
|
|
|
break;
|
|
|
|
case 't': {
|
|
|
|
// %t expands to the current time in milliseconds.
|
|
|
|
double time = OS::TimeCurrentMillis();
|
|
|
|
stream.Add("%.0f", FmtElm(time));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case '%':
|
|
|
|
// %% expands (contracts really) to %.
|
|
|
|
stream.Put('%');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// All other %'s expand to themselves.
|
|
|
|
stream.Put('%');
|
|
|
|
stream.Put(*p);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
stream.Put(*p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return SmartArrayPointer<const char>(stream.ToCString());
|
|
|
|
}
|
|
|
|
int length = StrLength(file_name);
|
|
|
|
char* str = NewArray<char>(length + 1);
|
|
|
|
OS::MemCopy(str, file_name, length);
|
|
|
|
str[length] = '\0';
|
|
|
|
return SmartArrayPointer<const char>(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-24 14:44:08 +00:00
|
|
|
bool Logger::SetUp(Isolate* isolate) {
|
2011-03-18 20:35:07 +00:00
|
|
|
// Tests and EnsureInitialize() can call this twice in a row. It's harmless.
|
|
|
|
if (is_initialized_) return true;
|
|
|
|
is_initialized_ = true;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2010-10-19 16:45:11 +00:00
|
|
|
// --ll-prof implies --log-code and --log-snapshot-positions.
|
|
|
|
if (FLAG_ll_prof) {
|
|
|
|
FLAG_log_snapshot_positions = true;
|
|
|
|
}
|
|
|
|
|
2009-05-25 08:25:36 +00:00
|
|
|
// --prof_lazy controls --log-code, implies --noprof_auto.
|
|
|
|
if (FLAG_prof_lazy) {
|
|
|
|
FLAG_log_code = false;
|
|
|
|
FLAG_prof_auto = false;
|
|
|
|
}
|
|
|
|
|
2013-07-15 11:35:39 +00:00
|
|
|
SmartArrayPointer<const char> log_file_name =
|
|
|
|
PrepareLogFileName(FLAG_logfile);
|
|
|
|
log_->Initialize(*log_file_name);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2013-07-15 11:35:39 +00:00
|
|
|
if (FLAG_ll_prof) {
|
|
|
|
ll_logger_ = new LowLevelLogger(*log_file_name);
|
|
|
|
}
|
2010-10-19 16:45:11 +00:00
|
|
|
|
2011-06-10 09:54:04 +00:00
|
|
|
ticker_ = new Ticker(isolate, kSamplingIntervalMs);
|
|
|
|
|
2013-04-24 14:44:08 +00:00
|
|
|
if (Log::InitLogAtStart()) {
|
2010-03-01 10:41:34 +00:00
|
|
|
logging_nesting_ = 1;
|
|
|
|
}
|
2009-06-24 13:09:34 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
if (FLAG_prof) {
|
2011-06-10 09:42:08 +00:00
|
|
|
profiler_ = new Profiler(isolate);
|
2009-06-24 13:09:34 +00:00
|
|
|
if (!FLAG_prof_auto) {
|
2009-01-29 19:47:00 +00:00
|
|
|
profiler_->pause();
|
2009-06-24 13:09:34 +00:00
|
|
|
} else {
|
2010-03-01 10:41:34 +00:00
|
|
|
logging_nesting_ = 1;
|
2009-06-24 13:09:34 +00:00
|
|
|
}
|
2009-10-28 09:13:11 +00:00
|
|
|
if (!FLAG_prof_lazy) {
|
|
|
|
profiler_->Engage();
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2012-12-05 16:22:14 +00:00
|
|
|
if (FLAG_log_internal_timer_events || FLAG_prof) epoch_ = OS::Ticks();
|
2012-11-22 13:04:11 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-28 14:43:28 +00:00
|
|
|
void Logger::SetCodeEventHandler(uint32_t options,
|
|
|
|
JitCodeEventHandler event_handler) {
|
2013-07-18 13:18:46 +00:00
|
|
|
if (jit_logger_) {
|
|
|
|
delete jit_logger_;
|
|
|
|
jit_logger_ = NULL;
|
|
|
|
}
|
2012-08-28 14:43:28 +00:00
|
|
|
|
2013-07-18 13:18:46 +00:00
|
|
|
if (event_handler) {
|
|
|
|
jit_logger_ = new JitLogger(event_handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jit_logger_ != NULL && (options & kJitCodeEventEnumExisting)) {
|
2013-07-02 15:44:30 +00:00
|
|
|
HandleScope scope(isolate_);
|
2012-08-28 14:43:28 +00:00
|
|
|
LogCodeObjects();
|
|
|
|
LogCompiledFunctions();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-18 20:35:07 +00:00
|
|
|
Sampler* Logger::sampler() {
|
|
|
|
return ticker_;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-13 11:31:22 +00:00
|
|
|
FILE* Logger::TearDown() {
|
|
|
|
if (!is_initialized_) return NULL;
|
2011-03-18 20:35:07 +00:00
|
|
|
is_initialized_ = false;
|
2009-05-28 13:56:32 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// Stop the profiler before closing the file.
|
|
|
|
if (profiler_ != NULL) {
|
|
|
|
profiler_->Disengage();
|
|
|
|
delete profiler_;
|
|
|
|
profiler_ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete ticker_;
|
2009-05-25 08:25:36 +00:00
|
|
|
ticker_ = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2013-07-15 11:35:39 +00:00
|
|
|
if (ll_logger_) {
|
|
|
|
delete ll_logger_;
|
|
|
|
ll_logger_ = NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-18 13:18:46 +00:00
|
|
|
if (jit_logger_) {
|
|
|
|
delete jit_logger_;
|
|
|
|
jit_logger_ = NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-13 11:31:22 +00:00
|
|
|
return log_->Close();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} } // namespace v8::internal
|