ll_prof: Reduce profiling hooks overhead from >400% to 25%.

Switched to using binary low-level log instead of the textual log used
by the ticks processor. The binary log contains code-related events,
code object names, and their bodies. When writing to the log we ask
glibc to use a larger buffer. To avoid complex processing of the
snapshot log (which is still textual) the serializer emits final
snapshot position to code name mappings that can be quickly be read
without replaying the snapshot log. (This might be useful for the
ticks processor.)

Review URL: http://codereview.chromium.org/6904127

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7729 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
vitalyr@chromium.org 2011-04-29 16:06:25 +00:00
parent 796ac25a4f
commit a9ea3f98b0
7 changed files with 553 additions and 164 deletions

View File

@ -818,7 +818,7 @@ bool Isolate::Init(Deserializer* des) {
// If we are deserializing, log non-function code objects and compiled
// functions found in the snapshot.
if (des != NULL && FLAG_log_code) {
if (des != NULL && (FLAG_log_code || FLAG_ll_prof)) {
HandleScope scope;
LOG(this, LogCodeObjects());
LOG(this, LogCompiledFunctions());

View File

@ -126,7 +126,7 @@ Log::Log(Logger* logger)
: write_to_file_(false),
is_stopped_(false),
output_handle_(NULL),
output_code_handle_(NULL),
ll_output_handle_(NULL),
output_buffer_(NULL),
mutex_(NULL),
message_buffer_(NULL),
@ -168,7 +168,7 @@ void Log::Initialize() {
bool start_logging = FLAG_log || FLAG_log_runtime || FLAG_log_api
|| FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
|| FLAG_log_regexp || FLAG_log_state_changes;
|| FLAG_log_regexp || FLAG_log_state_changes || FLAG_ll_prof;
bool open_log_file = start_logging || FLAG_prof_lazy;
@ -233,7 +233,12 @@ void Log::OpenStdout() {
}
static const char kCodeLogExt[] = ".code";
// Extension added to V8 log file name to get the low-level log name.
static const char kLowLevelLogExt[] = ".ll";
// File buffer size of the low-level log. We don't use the default to
// minimize the associated overhead.
static const int kLowLevelLogBufferSize = 2 * MB;
void Log::OpenFile(const char* name) {
@ -241,14 +246,13 @@ void Log::OpenFile(const char* name) {
output_handle_ = OS::FOpen(name, OS::LogFileOpenMode);
write_to_file_ = true;
if (FLAG_ll_prof) {
// Open a file for logging the contents of code objects so that
// they can be disassembled later.
size_t name_len = strlen(name);
ScopedVector<char> code_name(
static_cast<int>(name_len + sizeof(kCodeLogExt)));
memcpy(code_name.start(), name, name_len);
memcpy(code_name.start() + name_len, kCodeLogExt, sizeof(kCodeLogExt));
output_code_handle_ = OS::FOpen(code_name.start(), OS::LogFileOpenMode);
// Open the low-level log file.
size_t len = strlen(name);
ScopedVector<char> ll_name(static_cast<int>(len + sizeof(kLowLevelLogExt)));
memcpy(ll_name.start(), name, len);
memcpy(ll_name.start() + len, kLowLevelLogExt, sizeof(kLowLevelLogExt));
ll_output_handle_ = OS::FOpen(ll_name.start(), OS::LogFileOpenMode);
setvbuf(ll_output_handle_, NULL, _IOFBF, kLowLevelLogBufferSize);
}
}
@ -266,8 +270,8 @@ void Log::Close() {
if (write_to_file_) {
if (output_handle_ != NULL) fclose(output_handle_);
output_handle_ = NULL;
if (output_code_handle_ != NULL) fclose(output_code_handle_);
output_code_handle_ = NULL;
if (ll_output_handle_ != NULL) fclose(ll_output_handle_);
ll_output_handle_ = NULL;
} else {
delete output_buffer_;
output_buffer_ = NULL;
@ -361,6 +365,7 @@ void LogMessageBuilder::AppendAddress(Address addr) {
void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
if (str == NULL) return;
AssertNoAllocation no_heap_allocation; // Ensure string stay valid.
int len = str->length();
if (len > 0x1000)

View File

@ -154,8 +154,8 @@ class Log {
// mutex_ should be acquired before using output_handle_ or output_buffer_.
FILE* output_handle_;
// Used when low-level profiling is active to save code object contents.
FILE* output_code_handle_;
// Used when low-level profiling is active.
FILE* ll_output_handle_;
LogDynamicBuffer* output_buffer_;

View File

@ -334,10 +334,187 @@ void Profiler::Run() {
}
// Low-level profiling event structures.
struct LowLevelCodeCreateStruct {
static const char kTag = 'C';
int32_t name_size;
Address code_address;
int32_t code_size;
};
struct LowLevelCodeMoveStruct {
static const char kTag = 'M';
Address from_address;
Address to_address;
};
struct LowLevelCodeDeleteStruct {
static const char kTag = 'D';
Address address;
};
struct LowLevelSnapshotPositionStruct {
static const char kTag = 'P';
Address address;
int32_t position;
};
static const char kCodeMovingGCTag = 'G';
//
// Logger class implementation.
//
class Logger::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<const 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);
};
class Logger::NameBuffer {
public:
NameBuffer() { Reset(); }
void Reset() {
utf8_pos_ = 0;
}
void AppendString(String* str) {
if (str == NULL) return;
if (str->HasOnlyAsciiChars()) {
int utf8_length = Min(str->length(), kUtf8BufferSize - utf8_pos_);
String::WriteToFlat(str, utf8_buffer_ + utf8_pos_, 0, utf8_length);
utf8_pos_ += utf8_length;
return;
}
int uc16_length = Min(str->length(), kUc16BufferSize);
String::WriteToFlat(str, uc16_buffer_, 0, uc16_length);
for (int i = 0; i < uc16_length && utf8_pos_ < kUtf8BufferSize; ++i) {
uc16 c = uc16_buffer_[i];
if (c <= String::kMaxAsciiCharCodeU) {
utf8_buffer_[utf8_pos_++] = c;
} else {
int char_length = unibrow::Utf8::Length(c);
if (utf8_pos_ + char_length > kUtf8BufferSize) break;
unibrow::Utf8::Encode(utf8_buffer_ + utf8_pos_, c);
utf8_pos_ += char_length;
}
}
}
void AppendBytes(const char* bytes, int size) {
size = Min(size, kUtf8BufferSize - utf8_pos_);
memcpy(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;
}
}
const char* get() { return utf8_buffer_; }
int size() const { return utf8_pos_; }
private:
static const int kUtf8BufferSize = 512;
static const int kUc16BufferSize = 128;
int utf8_pos_;
char utf8_buffer_[kUtf8BufferSize];
uc16 uc16_buffer_[kUc16BufferSize];
};
Logger::Logger()
: ticker_(NULL),
profiler_(NULL),
@ -347,6 +524,8 @@ Logger::Logger()
cpu_profiler_nesting_(0),
heap_profiler_nesting_(0),
log_(new Log(this)),
name_buffer_(new NameBuffer),
address_to_name_map_(NULL),
is_initialized_(false),
last_address_(NULL),
prev_sp_(NULL),
@ -355,10 +534,14 @@ Logger::Logger()
prev_code_(NULL) {
}
Logger::~Logger() {
delete address_to_name_map_;
delete name_buffer_;
delete log_;
}
#define DECLARE_EVENT(ignore1, name) name,
static const char* const kLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
LOG_EVENTS_AND_TAGS_LIST(DECLARE_EVENT)
@ -735,7 +918,20 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
const char* comment) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled() || !FLAG_log_code) return;
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof || Serializer::enabled()) {
name_buffer_->Reset();
name_buffer_->AppendBytes(kLogEventsNames[tag]);
name_buffer_->AppendByte(':');
name_buffer_->AppendBytes(comment);
}
if (FLAG_ll_prof) {
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
}
if (Serializer::enabled()) {
RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
}
if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT],
@ -749,7 +945,6 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
msg.Append(*p);
}
msg.Append('"');
LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@ -760,13 +955,30 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
Code* code,
String* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (name != NULL) {
SmartPointer<char> str =
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
CodeCreateEvent(tag, code, *str);
} else {
CodeCreateEvent(tag, code, "");
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof || Serializer::enabled()) {
name_buffer_->Reset();
name_buffer_->AppendBytes(kLogEventsNames[tag]);
name_buffer_->AppendByte(':');
name_buffer_->AppendString(name);
}
if (FLAG_ll_prof) {
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
}
if (Serializer::enabled()) {
RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
}
if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]);
msg.AppendAddress(code->address());
msg.Append(",%d,\"", code->ExecutableSize());
msg.AppendDetailed(name, false);
msg.Append('"');
msg.Append('\n');
msg.WriteToLogFile();
#endif
}
@ -788,7 +1000,20 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
SharedFunctionInfo* shared,
String* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled() || !FLAG_log_code) return;
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof || Serializer::enabled()) {
name_buffer_->Reset();
name_buffer_->AppendBytes(kLogEventsNames[tag]);
name_buffer_->AppendByte(':');
name_buffer_->AppendString(name);
}
if (FLAG_ll_prof) {
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
}
if (Serializer::enabled()) {
RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
}
if (!FLAG_log_code) return;
if (code == Isolate::Current()->builtins()->builtin(
Builtins::kLazyCompile))
return;
@ -803,7 +1028,6 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
msg.Append(",%d,\"%s\",", code->ExecutableSize(), *str);
msg.AppendAddress(shared->address());
msg.Append(",%s", ComputeMarker(code));
LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@ -818,7 +1042,24 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
SharedFunctionInfo* shared,
String* source, int line) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled() || !FLAG_log_code) return;
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof || Serializer::enabled()) {
name_buffer_->Reset();
name_buffer_->AppendBytes(kLogEventsNames[tag]);
name_buffer_->AppendByte(':');
name_buffer_->AppendString(shared->DebugName());
name_buffer_->AppendByte(' ');
name_buffer_->AppendString(source);
name_buffer_->AppendByte(':');
name_buffer_->AppendInt(line);
}
if (FLAG_ll_prof) {
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
}
if (Serializer::enabled()) {
RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
}
if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
SmartPointer<char> name =
shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
@ -835,7 +1076,6 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
line);
msg.AppendAddress(shared->address());
msg.Append(",%s", ComputeMarker(code));
LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@ -844,14 +1084,26 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag,
void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled() || !FLAG_log_code) return;
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof || Serializer::enabled()) {
name_buffer_->Reset();
name_buffer_->AppendBytes(kLogEventsNames[tag]);
name_buffer_->AppendByte(':');
name_buffer_->AppendInt(args_count);
}
if (FLAG_ll_prof) {
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
}
if (Serializer::enabled()) {
RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
}
if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT],
kLogEventsNames[tag]);
msg.AppendAddress(code->address());
msg.Append(",%d,\"args_count: %d\"", code->ExecutableSize(), args_count);
LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@ -860,10 +1112,8 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) {
void Logger::CodeMovingGCEvent() {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled() || !FLAG_log_code || !FLAG_ll_prof) return;
LogMessageBuilder msg(this);
msg.Append("%s\n", kLogEventsNames[CODE_MOVING_GC]);
msg.WriteToLogFile();
if (!log_->IsEnabled() || !FLAG_ll_prof) return;
LowLevelLogWriteBytes(&kCodeMovingGCTag, sizeof(kCodeMovingGCTag));
OS::SignalCodeMovingGC();
#endif
}
@ -871,7 +1121,20 @@ void Logger::CodeMovingGCEvent() {
void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled() || !FLAG_log_code) return;
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof || Serializer::enabled()) {
name_buffer_->Reset();
name_buffer_->AppendBytes(kLogEventsNames[REG_EXP_TAG]);
name_buffer_->AppendByte(':');
name_buffer_->AppendString(source);
}
if (FLAG_ll_prof) {
LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size());
}
if (Serializer::enabled()) {
RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size());
}
if (!FLAG_log_code) return;
LogMessageBuilder msg(this);
msg.Append("%s,%s,",
kLogEventsNames[CODE_CREATION_EVENT],
@ -880,7 +1143,6 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
msg.Append(",%d,\"", code->ExecutableSize());
msg.AppendDetailed(source, false);
msg.Append('\"');
LowLevelCodeCreateEvent(code, &msg);
msg.Append('\n');
msg.WriteToLogFile();
#endif
@ -889,6 +1151,11 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
void Logger::CodeMoveEvent(Address from, Address to) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof) LowLevelCodeMoveEvent(from, to);
if (Serializer::enabled() && address_to_name_map_ != NULL) {
address_to_name_map_->Move(from, to);
}
MoveEventInternal(CODE_MOVE_EVENT, from, to);
#endif
}
@ -896,6 +1163,11 @@ void Logger::CodeMoveEvent(Address from, Address to) {
void Logger::CodeDeleteEvent(Address from) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof) LowLevelCodeDeleteEvent(from);
if (Serializer::enabled() && address_to_name_map_ != NULL) {
address_to_name_map_->Remove(from);
}
DeleteEventInternal(CODE_DELETE_EVENT, from);
#endif
}
@ -903,7 +1175,21 @@ void Logger::CodeDeleteEvent(Address from) {
void Logger::SnapshotPositionEvent(Address addr, int pos) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled() || !FLAG_log_snapshot_positions) return;
if (!log_->IsEnabled()) return;
if (FLAG_ll_prof) LowLevelSnapshotPositionEvent(addr, pos);
if (Serializer::enabled() && address_to_name_map_ != NULL) {
const char* code_name = address_to_name_map_->Lookup(addr);
if (code_name == NULL) return; // Not a code object.
LogMessageBuilder msg(this);
msg.Append("%s,%d,\"", kLogEventsNames[SNAPSHOT_CODE_NAME_EVENT], pos);
for (const char* p = code_name; *p != '\0'; ++p) {
if (*p == '"') msg.Append('\\');
msg.Append(*p);
}
msg.Append("\"\n");
msg.WriteToLogFile();
}
if (!FLAG_log_snapshot_positions) return;
LogMessageBuilder msg(this);
msg.Append("%s,", kLogEventsNames[SNAPSHOT_POSITION_EVENT]);
msg.AppendAddress(addr);
@ -1308,7 +1594,7 @@ static int EnumerateCompiledFunctions(Handle<SharedFunctionInfo>* sfis,
void Logger::LogCodeObject(Object* object) {
if (FLAG_log_code) {
if (FLAG_log_code || FLAG_ll_prof) {
Code* code_object = Code::cast(object);
LogEventsAndTags tag = Logger::STUB_TAG;
const char* description = "Unknown code from the snapshot";
@ -1370,7 +1656,7 @@ void Logger::LogCodeObject(Object* object) {
void Logger::LogCodeInfo() {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!log_->IsEnabled() || !FLAG_log_code || !FLAG_ll_prof) return;
if (!log_->IsEnabled() || !FLAG_ll_prof) return;
#if V8_TARGET_ARCH_IA32
const char arch[] = "ia32";
#elif V8_TARGET_ARCH_X64
@ -1380,21 +1666,69 @@ void Logger::LogCodeInfo() {
#else
const char arch[] = "unknown";
#endif
LogMessageBuilder msg(this);
msg.Append("code-info,%s,%d\n", arch, Code::kHeaderSize);
msg.WriteToLogFile();
LowLevelLogWriteBytes(arch, sizeof(arch));
#endif // ENABLE_LOGGING_AND_PROFILING
}
void Logger::LowLevelCodeCreateEvent(Code* code, LogMessageBuilder* msg) {
if (!FLAG_ll_prof || log_->output_code_handle_ == NULL) return;
int pos = static_cast<int>(ftell(log_->output_code_handle_));
size_t rv = fwrite(code->instruction_start(), 1, code->instruction_size(),
log_->output_code_handle_);
ASSERT(static_cast<size_t>(code->instruction_size()) == rv);
void Logger::RegisterSnapshotCodeName(Code* code,
const char* name,
int name_size) {
ASSERT(Serializer::enabled());
if (address_to_name_map_ == NULL) {
address_to_name_map_ = new NameMap;
}
address_to_name_map_->Insert(code->address(), name, name_size);
}
void Logger::LowLevelCodeCreateEvent(Code* code,
const char* name,
int name_size) {
if (log_->ll_output_handle_ == NULL) return;
LowLevelCodeCreateStruct event;
event.name_size = name_size;
event.code_address = code->instruction_start();
ASSERT(event.code_address == code->address() + Code::kHeaderSize);
event.code_size = code->instruction_size();
LowLevelLogWriteStruct(event);
LowLevelLogWriteBytes(name, name_size);
LowLevelLogWriteBytes(
reinterpret_cast<const char*>(code->instruction_start()),
code->instruction_size());
}
void Logger::LowLevelCodeMoveEvent(Address from, Address to) {
if (log_->ll_output_handle_ == NULL) return;
LowLevelCodeMoveStruct event;
event.from_address = from + Code::kHeaderSize;
event.to_address = to + Code::kHeaderSize;
LowLevelLogWriteStruct(event);
}
void Logger::LowLevelCodeDeleteEvent(Address from) {
if (log_->ll_output_handle_ == NULL) return;
LowLevelCodeDeleteStruct event;
event.address = from + Code::kHeaderSize;
LowLevelLogWriteStruct(event);
}
void Logger::LowLevelSnapshotPositionEvent(Address addr, int pos) {
if (log_->ll_output_handle_ == NULL) return;
LowLevelSnapshotPositionStruct event;
event.address = addr + Code::kHeaderSize;
event.position = pos;
LowLevelLogWriteStruct(event);
}
void Logger::LowLevelLogWriteBytes(const char* bytes, int size) {
size_t rv = fwrite(bytes, 1, size, log_->ll_output_handle_);
ASSERT(static_cast<size_t>(size) == rv);
USE(rv);
msg->Append(",%d", pos);
}
@ -1497,7 +1831,6 @@ bool Logger::Setup() {
// --ll-prof implies --log-code and --log-snapshot-positions.
if (FLAG_ll_prof) {
FLAG_log_code = true;
FLAG_log_snapshot_positions = true;
}
@ -1524,7 +1857,7 @@ bool Logger::Setup() {
bool start_logging = FLAG_log || FLAG_log_runtime || FLAG_log_api
|| FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
|| FLAG_log_regexp || FLAG_log_state_changes;
|| FLAG_log_regexp || FLAG_log_state_changes || FLAG_ll_prof;
if (start_logging) {
logging_nesting_ = 1;

View File

@ -69,11 +69,12 @@ namespace internal {
// tick profiler requires code events, so --prof implies --log-code.
// Forward declarations.
class Ticker;
class HashMap;
class LogMessageBuilder;
class Profiler;
class Semaphore;
class SlidingStateWindow;
class LogMessageBuilder;
class Ticker;
#undef LOG
#ifdef ENABLE_LOGGING_AND_PROFILING
@ -95,6 +96,7 @@ class LogMessageBuilder;
V(CODE_MOVING_GC, "code-moving-gc") \
V(SHARED_FUNC_MOVE_EVENT, "sfi-move") \
V(SNAPSHOT_POSITION_EVENT, "snapshot-pos") \
V(SNAPSHOT_CODE_NAME_EVENT, "snapshot-code-name") \
V(TICK_EVENT, "tick") \
V(REPEAT_META_EVENT, "repeat") \
V(BUILTIN_TAG, "Builtin") \
@ -300,6 +302,9 @@ class Logger {
void LogFailure();
private:
class NameBuffer;
class NameMap;
Logger();
~Logger();
@ -326,8 +331,26 @@ class Logger {
// Emits general information about generated code.
void LogCodeInfo();
// Handles code creation when low-level profiling is active.
void LowLevelCodeCreateEvent(Code* code, LogMessageBuilder* msg);
void RegisterSnapshotCodeName(Code* code, const char* name, int name_size);
// Low-level logging support.
void LowLevelCodeCreateEvent(Code* code, const char* name, int name_size);
void LowLevelCodeMoveEvent(Address from, Address to);
void LowLevelCodeDeleteEvent(Address from);
void LowLevelSnapshotPositionEvent(Address addr, int pos);
void LowLevelLogWriteBytes(const char* bytes, int size);
template <typename T>
void LowLevelLogWriteStruct(const T& s) {
char tag = T::kTag;
LowLevelLogWriteBytes(reinterpret_cast<const char*>(&tag), sizeof(tag));
LowLevelLogWriteBytes(reinterpret_cast<const char*>(&s), sizeof(s));
}
// Emits a profiler tick event. Used by the profiler thread.
void TickEvent(TickSample* sample, bool overflow);
@ -379,6 +402,10 @@ class Logger {
Log* log_;
NameBuffer* name_buffer_;
NameMap* address_to_name_map_;
// Guards against multiple calls to TearDown() that can happen in some tests.
// 'true' between Setup() and TearDown().
bool is_initialized_;

View File

@ -251,6 +251,12 @@ static inline uint32_t ComputeIntegerHash(uint32_t key) {
}
static inline uint32_t ComputePointerHash(void* ptr) {
return ComputeIntegerHash(
static_cast<uint32_t>(reinterpret_cast<intptr_t>(ptr)));
}
// ----------------------------------------------------------------------------
// Miscellaneous

View File

@ -124,7 +124,7 @@ class Code(object):
self.callee_ticks = collections.defaultdict(lambda: 0)
self.callee_ticks[callee] += 1
def PrintAnnotated(self, code_info, options):
def PrintAnnotated(self, arch, options):
if self.self_ticks_map is None:
ticks_map = []
else:
@ -135,7 +135,7 @@ class Code(object):
ticks_offsets = [t[0] for t in ticks_map]
ticks_counts = [t[1] for t in ticks_map]
# Get a list of disassembled lines and their addresses.
lines = self._GetDisasmLines(code_info, options)
lines = self._GetDisasmLines(arch, options)
if len(lines) == 0:
return
# Print annotated lines.
@ -174,17 +174,17 @@ class Code(object):
self.end_address - self.start_address,
self.origin)
def _GetDisasmLines(self, code_info, options):
def _GetDisasmLines(self, arch, options):
if self.origin == JS_ORIGIN or self.origin == JS_SNAPSHOT_ORIGIN:
inplace = False
filename = options.log + ".code"
filename = options.log + ".ll"
else:
inplace = True
filename = self.origin
return disasm.GetDisasmLines(filename,
self.origin_offset,
self.end_address - self.start_address,
code_info.arch,
arch,
inplace)
@ -304,76 +304,102 @@ class CodeInfo(object):
self.header_size = header_size
class CodeLogReader(object):
"""V8 code event log reader."""
class SnapshotLogReader(object):
"""V8 snapshot log reader."""
_CODE_INFO_RE = re.compile(
r"code-info,([^,]+),(\d+)")
_SNAPSHOT_CODE_NAME_RE = re.compile(
r"snapshot-code-name,(\d+),\"(.*)\"")
_CODE_CREATE_RE = re.compile(
r"code-creation,([^,]+),(0x[a-f0-9]+),(\d+),\"(.*)\"(?:,(0x[a-f0-9]+),([~*])?)?(?:,(\d+))?")
def __init__(self, log_name):
self.log_name = log_name
_CODE_MOVE_RE = re.compile(
r"code-move,(0x[a-f0-9]+),(0x[a-f0-9]+)")
def ReadNameMap(self):
log = open(self.log_name, "r")
try:
snapshot_pos_to_name = {}
for line in log:
match = SnapshotLogReader._SNAPSHOT_CODE_NAME_RE.match(line)
if match:
pos = int(match.group(1))
name = match.group(2)
snapshot_pos_to_name[pos] = name
finally:
log.close()
return snapshot_pos_to_name
_CODE_DELETE_RE = re.compile(
r"code-delete,(0x[a-f0-9]+)")
_SNAPSHOT_POS_RE = re.compile(
r"snapshot-pos,(0x[a-f0-9]+),(\d+)")
class LogReader(object):
"""V8 low-level (binary) log reader."""
_CODE_MOVING_GC = "code-moving-gc"
_ARCH_TO_POINTER_TYPE_MAP = {
"ia32": ctypes.c_uint32,
"arm": ctypes.c_uint32,
"x64": ctypes.c_uint64
}
def __init__(self, log_name, code_map, is_snapshot, snapshot_pos_to_name):
self.log = open(log_name, "r")
_CODE_CREATE_TAG = "C"
_CODE_MOVE_TAG = "M"
_CODE_DELETE_TAG = "D"
_SNAPSHOT_POSITION_TAG = "P"
_CODE_MOVING_GC_TAG = "G"
def __init__(self, log_name, code_map, snapshot_pos_to_name):
self.log_file = open(log_name, "r")
self.log = mmap.mmap(self.log_file.fileno(), 0, mmap.MAP_PRIVATE)
self.log_pos = 0
self.code_map = code_map
self.is_snapshot = is_snapshot
self.snapshot_pos_to_name = snapshot_pos_to_name
self.address_to_snapshot_name = {}
def ReadCodeInfo(self):
line = self.log.readline() or ""
match = CodeLogReader._CODE_INFO_RE.match(line)
assert match, "No code info in log"
return CodeInfo(arch=match.group(1), header_size=int(match.group(2)))
self.arch = self.log[:self.log.find("\0")]
self.log_pos += len(self.arch) + 1
assert self.arch in LogReader._ARCH_TO_POINTER_TYPE_MAP, \
"Unsupported architecture %s" % self.arch
pointer_type = LogReader._ARCH_TO_POINTER_TYPE_MAP[self.arch]
def ReadUpToGC(self, code_info):
made_progress = False
code_header_size = code_info.header_size
while True:
line = self.log.readline()
if not line:
return made_progress
made_progress = True
self.code_create_struct = LogReader._DefineStruct([
("name_size", ctypes.c_int32),
("code_address", pointer_type),
("code_size", ctypes.c_int32)])
if line.startswith(CodeLogReader._CODE_MOVING_GC):
self.code_move_struct = LogReader._DefineStruct([
("from_address", pointer_type),
("to_address", pointer_type)])
self.code_delete_struct = LogReader._DefineStruct([
("address", pointer_type)])
self.snapshot_position_struct = LogReader._DefineStruct([
("address", pointer_type),
("position", ctypes.c_int32)])
def ReadUpToGC(self):
while self.log_pos < self.log.size():
tag = self.log[self.log_pos]
self.log_pos += 1
if tag == LogReader._CODE_MOVING_GC_TAG:
self.address_to_snapshot_name.clear()
return made_progress
return
match = CodeLogReader._CODE_CREATE_RE.match(line)
if match:
start_address = int(match.group(2), 16) + code_header_size
end_address = start_address + int(match.group(3)) - code_header_size
if tag == LogReader._CODE_CREATE_TAG:
event = self.code_create_struct.from_buffer(self.log, self.log_pos)
self.log_pos += ctypes.sizeof(event)
start_address = event.code_address
end_address = start_address + event.code_size
if start_address in self.address_to_snapshot_name:
name = self.address_to_snapshot_name[start_address]
origin = JS_SNAPSHOT_ORIGIN
else:
tag = match.group(1)
optimization_status = match.group(6)
func_name = match.group(4)
if optimization_status:
name = "%s:%s%s" % (tag, optimization_status, func_name)
else:
name = "%s:%s" % (tag, func_name)
name = self.log[self.log_pos:self.log_pos + event.name_size]
origin = JS_ORIGIN
if self.is_snapshot:
origin_offset = 0
else:
origin_offset = int(match.group(7))
self.log_pos += event.name_size
origin_offset = self.log_pos
self.log_pos += event.code_size
code = Code(name, start_address, end_address, origin, origin_offset)
conficting_code = self.code_map.Find(start_address)
if conficting_code:
CodeLogReader._HandleCodeConflict(conficting_code, code)
LogReader._HandleCodeConflict(conficting_code, code)
# TODO(vitalyr): this warning is too noisy because of our
# attempts to reconstruct code log from the snapshot.
# print >>sys.stderr, \
@ -382,10 +408,11 @@ class CodeLogReader(object):
self.code_map.Add(code)
continue
match = CodeLogReader._CODE_MOVE_RE.match(line)
if match:
old_start_address = int(match.group(1), 16) + code_header_size
new_start_address = int(match.group(2), 16) + code_header_size
if tag == LogReader._CODE_MOVE_TAG:
event = self.code_move_struct.from_buffer(self.log, self.log_pos)
self.log_pos += ctypes.sizeof(event)
old_start_address = event.from_address
new_start_address = event.to_address
if old_start_address == new_start_address:
# Skip useless code move entries.
continue
@ -402,9 +429,10 @@ class CodeLogReader(object):
self.code_map.Add(code)
continue
match = CodeLogReader._CODE_DELETE_RE.match(line)
if match:
old_start_address = int(match.group(1), 16) + code_header_size
if tag == LogReader._CODE_DELETE_TAG:
event = self.code_delete_struct.from_buffer(self.log, self.log_pos)
self.log_pos += ctypes.sizeof(event)
old_start_address = event.address
code = self.code_map.Find(old_start_address)
if not code:
print >>sys.stderr, "Warning: Not found %x" % old_start_address
@ -414,40 +442,36 @@ class CodeLogReader(object):
self.code_map.Remove(code)
continue
match = CodeLogReader._SNAPSHOT_POS_RE.match(line)
if match:
start_address = int(match.group(1), 16) + code_header_size
snapshot_pos = int(match.group(2))
if self.is_snapshot:
code = self.code_map.Find(start_address)
if code:
assert code.start_address == start_address, \
"Inexact snapshot address %x for %s" % (start_address, code)
self.snapshot_pos_to_name[snapshot_pos] = code.name
else:
if snapshot_pos in self.snapshot_pos_to_name:
self.address_to_snapshot_name[start_address] = \
self.snapshot_pos_to_name[snapshot_pos]
if tag == LogReader._SNAPSHOT_POSITION_TAG:
event = self.snapshot_position_struct.from_buffer(self.log,
self.log_pos)
self.log_pos += ctypes.sizeof(event)
start_address = event.address
snapshot_pos = event.position
if snapshot_pos in self.snapshot_pos_to_name:
self.address_to_snapshot_name[start_address] = \
self.snapshot_pos_to_name[snapshot_pos]
continue
assert False, "Unknown tag %s" % tag
def Dispose(self):
self.log.close()
self.log_file.close()
@staticmethod
def _DefineStruct(fields):
class Struct(ctypes.Structure):
_fields_ = fields
return Struct
@staticmethod
def _HandleCodeConflict(old_code, new_code):
assert (old_code.start_address == new_code.start_address and
old_code.end_address == new_code.end_address), \
"Conficting code log entries %s and %s" % (old_code, new_code)
CodeLogReader._UpdateNames(old_code, new_code)
@staticmethod
def _UpdateNames(old_code, new_code):
if old_code.name == new_code.name:
return
# Kludge: there are code objects with custom names that don't
# match their flags.
misnamed_code = set(["Builtin:CpuFeatures::Probe"])
if old_code.name in misnamed_code:
return
# Code object may be shared by a few functions. Collect the full
# set of names.
old_code.AddName(new_code.name)
@ -756,14 +780,14 @@ class LibraryRepo(object):
return True
def PrintReport(code_map, library_repo, code_info, options):
def PrintReport(code_map, library_repo, arch, options):
print "Ticks per symbol:"
used_code = [code for code in code_map.UsedCode()]
used_code.sort(key=lambda x: x.self_ticks, reverse=True)
for i, code in enumerate(used_code):
print "%10d %s [%s]" % (code.self_ticks, code.FullName(), code.origin)
if options.disasm_all or i < options.disasm_top:
code.PrintAnnotated(code_info, options)
code.PrintAnnotated(arch, options)
print
print "Ticks per library:"
mmap_infos = [m for m in library_repo.infos]
@ -825,11 +849,11 @@ if __name__ == "__main__":
if not options.quiet:
if options.snapshot:
print "V8 logs: %s, %s, %s.code" % (options.snapshot_log,
options.log,
options.log)
print "V8 logs: %s, %s, %s.ll" % (options.snapshot_log,
options.log,
options.log)
else:
print "V8 log: %s, %s.code (no snapshot)" % (options.log, options.log)
print "V8 log: %s, %s.ll (no snapshot)" % (options.log, options.log)
print "Perf trace file: %s" % options.trace
# Stats.
@ -840,30 +864,24 @@ if __name__ == "__main__":
mmap_time = 0
sample_time = 0
# Initialize the log reader and get the code info.
code_map = CodeMap()
snapshot_name_map = {}
log_reader = CodeLogReader(log_name=options.log,
code_map=code_map,
is_snapshot=False,
snapshot_pos_to_name=snapshot_name_map)
code_info = log_reader.ReadCodeInfo()
if not options.quiet:
print "Generated code architecture: %s" % code_info.arch
print
# Process the snapshot log to fill the snapshot name map.
snapshot_name_map = {}
if options.snapshot:
snapshot_log_reader = CodeLogReader(log_name=options.snapshot_log,
code_map=CodeMap(),
is_snapshot=True,
snapshot_pos_to_name=snapshot_name_map)
while snapshot_log_reader.ReadUpToGC(code_info):
pass
snapshot_log_reader = SnapshotLogReader(log_name=options.snapshot_log)
snapshot_name_map = snapshot_log_reader.ReadNameMap()
# Initialize the log reader.
code_map = CodeMap()
log_reader = LogReader(log_name=options.log + ".ll",
code_map=code_map,
snapshot_pos_to_name=snapshot_name_map)
if not options.quiet:
print "Generated code architecture: %s" % log_reader.arch
print
# Process the code and trace logs.
library_repo = LibraryRepo()
log_reader.ReadUpToGC(code_info)
log_reader.ReadUpToGC()
trace_reader = TraceReader(options.trace)
while True:
header, offset = trace_reader.ReadEventHeader()
@ -874,7 +892,7 @@ if __name__ == "__main__":
start = time.time()
mmap_info = trace_reader.ReadMmap(header, offset)
if mmap_info.filename == V8_GC_FAKE_MMAP:
log_reader.ReadUpToGC(code_info)
log_reader.ReadUpToGC()
else:
library_repo.Load(mmap_info, code_map, options)
mmap_time += time.time() - start
@ -901,7 +919,7 @@ if __name__ == "__main__":
if options.dot:
PrintDot(code_map, options)
else:
PrintReport(code_map, library_repo, code_info, options)
PrintReport(code_map, library_repo, log_reader.arch, options)
if not options.quiet:
print