Index: src/log.cc |
diff --git a/src/log.cc b/src/log.cc |
index a44add6cb928f8fe59fc6d8ffff6d595318a6055..843d128679649c920112bd702defd15276ed92db 100644 |
--- a/src/log.cc |
+++ b/src/log.cc |
@@ -45,6 +45,82 @@ |
namespace v8 { |
namespace internal { |
+ |
+// Low-level logging support. |
+class LowLevelLogger { |
+ public: |
+ explicit LowLevelLogger(const char* file_name); |
+ ~LowLevelLogger(); |
+ |
+ void CodeCreateEvent(Code* code, const char* name, int name_size); |
+ void CodeMoveEvent(Address from, Address to); |
+ void CodeDeleteEvent(Address from); |
+ void SnapshotPositionEvent(Address addr, int pos); |
+ void CodeMovingGCEvent(); |
+ |
+ private: |
+ // Low-level profiling event structures. |
+ |
+ 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"; |
+ |
+#define LL_LOG(Call) if (ll_logger_) ll_logger_->Call; |
+ |
+ |
// 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. |
@@ -210,43 +286,6 @@ 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. |
// |
@@ -405,6 +444,7 @@ Logger::Logger(Isolate* isolate) |
logging_nesting_(0), |
cpu_profiler_nesting_(0), |
log_(new Log(this)), |
+ ll_logger_(NULL), |
name_buffer_(new NameBuffer), |
address_to_name_map_(NULL), |
is_initialized_(false), |
@@ -952,9 +992,7 @@ void Logger::LogRecordedBuffer(Code* code, SharedFunctionInfo* shared) { |
name_buffer_->size()); |
} |
if (!log_->IsEnabled()) return; |
- if (FLAG_ll_prof) { |
- LowLevelCodeCreateEvent(code, name_buffer_->get(), name_buffer_->size()); |
- } |
+ LL_LOG(CodeCreateEvent(code, name_buffer_->get(), name_buffer_->size())); |
if (Serializer::enabled()) { |
RegisterSnapshotCodeName(code, name_buffer_->get(), name_buffer_->size()); |
} |
@@ -1142,7 +1180,7 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) { |
void Logger::CodeMovingGCEvent() { |
if (!log_->IsEnabled() || !FLAG_ll_prof) return; |
- LowLevelLogWriteBytes(&kCodeMovingGCTag, sizeof(kCodeMovingGCTag)); |
+ LL_LOG(CodeMovingGCEvent()); |
OS::SignalCodeMovingGC(); |
} |
@@ -1169,7 +1207,7 @@ void Logger::RegExpCodeCreateEvent(Code* code, String* source) { |
void Logger::CodeMoveEvent(Address from, Address to) { |
if (code_event_handler_ != NULL) IssueCodeMovedEvent(from, to); |
if (!log_->IsEnabled()) return; |
- if (FLAG_ll_prof) LowLevelCodeMoveEvent(from, to); |
+ LL_LOG(CodeMoveEvent(from, to)); |
if (Serializer::enabled() && address_to_name_map_ != NULL) { |
address_to_name_map_->Move(from, to); |
} |
@@ -1180,7 +1218,7 @@ void Logger::CodeMoveEvent(Address from, Address to) { |
void Logger::CodeDeleteEvent(Address from) { |
if (code_event_handler_ != NULL) IssueCodeRemovedEvent(from); |
if (!log_->IsEnabled()) return; |
- if (FLAG_ll_prof) LowLevelCodeDeleteEvent(from); |
+ LL_LOG(CodeDeleteEvent(from)); |
if (Serializer::enabled() && address_to_name_map_ != NULL) { |
address_to_name_map_->Remove(from); |
} |
@@ -1226,7 +1264,7 @@ void Logger::CodeEndLinePosInfoRecordEvent(Code* code, |
void Logger::SnapshotPositionEvent(Address addr, int pos) { |
if (!log_->IsEnabled()) return; |
- if (FLAG_ll_prof) LowLevelSnapshotPositionEvent(addr, pos); |
+ LL_LOG(SnapshotPositionEvent(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. |
@@ -1564,8 +1602,38 @@ void Logger::LogCodeObject(Object* object) { |
} |
-void Logger::LogCodeInfo() { |
- if (!log_->IsEnabled() || !FLAG_ll_prof) return; |
+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); |
+} |
+ |
+ |
+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 |
@@ -1577,71 +1645,62 @@ void Logger::LogCodeInfo() { |
#else |
const char arch[] = "unknown"; |
#endif |
- LowLevelLogWriteBytes(arch, sizeof(arch)); |
-} |
- |
- |
-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); |
+ LogWriteBytes(arch, sizeof(arch)); |
} |
- |
-void Logger::LowLevelCodeCreateEvent(Code* code, |
+void LowLevelLogger::CodeCreateEvent(Code* code, |
const char* name, |
int name_size) { |
- if (log_->ll_output_handle_ == NULL) return; |
- LowLevelCodeCreateStruct event; |
+ CodeCreateStruct 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( |
+ LogWriteStruct(event); |
+ LogWriteBytes(name, name_size); |
+ LogWriteBytes( |
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; |
+void LowLevelLogger::CodeMoveEvent(Address from, Address to) { |
+ CodeMoveStruct event; |
event.from_address = from + Code::kHeaderSize; |
event.to_address = to + Code::kHeaderSize; |
- LowLevelLogWriteStruct(event); |
+ LogWriteStruct(event); |
} |
-void Logger::LowLevelCodeDeleteEvent(Address from) { |
- if (log_->ll_output_handle_ == NULL) return; |
- LowLevelCodeDeleteStruct event; |
+void LowLevelLogger::CodeDeleteEvent(Address from) { |
+ CodeDeleteStruct event; |
event.address = from + Code::kHeaderSize; |
- LowLevelLogWriteStruct(event); |
+ LogWriteStruct(event); |
} |
-void Logger::LowLevelSnapshotPositionEvent(Address addr, int pos) { |
- if (log_->ll_output_handle_ == NULL) return; |
- LowLevelSnapshotPositionStruct event; |
+void LowLevelLogger::SnapshotPositionEvent(Address addr, int pos) { |
+ SnapshotPositionStruct event; |
event.address = addr + Code::kHeaderSize; |
event.position = pos; |
- LowLevelLogWriteStruct(event); |
+ LogWriteStruct(event); |
} |
-void Logger::LowLevelLogWriteBytes(const char* bytes, int size) { |
- size_t rv = fwrite(bytes, 1, size, log_->ll_output_handle_); |
+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)); |
+} |
+ |
+ |
void Logger::LogCodeObjects() { |
Heap* heap = isolate_->heap(); |
heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, |
@@ -1744,6 +1803,63 @@ void Logger::LogAccessorCallbacks() { |
} |
+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); |
+} |
+ |
+ |
bool Logger::SetUp(Isolate* isolate) { |
// Tests and EnsureInitialize() can call this twice in a row. It's harmless. |
if (is_initialized_) return true; |
@@ -1760,9 +1876,13 @@ bool Logger::SetUp(Isolate* isolate) { |
FLAG_prof_auto = false; |
} |
- log_->Initialize(); |
+ SmartArrayPointer<const char> log_file_name = |
+ PrepareLogFileName(FLAG_logfile); |
+ log_->Initialize(*log_file_name); |
- if (FLAG_ll_prof) LogCodeInfo(); |
+ if (FLAG_ll_prof) { |
+ ll_logger_ = new LowLevelLogger(*log_file_name); |
+ } |
ticker_ = new Ticker(isolate, kSamplingIntervalMs); |
@@ -1819,6 +1939,11 @@ FILE* Logger::TearDown() { |
delete ticker_; |
ticker_ = NULL; |
+ if (ll_logger_) { |
+ delete ll_logger_; |
+ ll_logger_ = NULL; |
+ } |
+ |
return log_->Close(); |
} |