Chromium Code Reviews| Index: test/cctest/test-log.cc |
| diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc |
| index 10a90bcf1745963d8ee8710817174405268fbca7..5704b07ace5fce9fe2e89027b5a92973477253df 100644 |
| --- a/test/cctest/test-log.cc |
| +++ b/test/cctest/test-log.cc |
| @@ -2,8 +2,6 @@ |
| // |
| // Tests of logging functions from log.h |
| -#ifdef ENABLE_LOGGING_AND_PROFILING |
| - |
| #ifdef __linux__ |
| #include <math.h> |
| #include <pthread.h> |
| @@ -15,6 +13,7 @@ |
| #include "log.h" |
| #include "cpu-profiler.h" |
| #include "v8threads.h" |
| +#include "v8utils.h" |
| #include "cctest.h" |
| #include "vm-state-inl.h" |
| @@ -25,269 +24,75 @@ using v8::internal::StrLength; |
| namespace i = v8::internal; |
| -static void SetUp() { |
| - // Log to memory buffer. |
| - i::FLAG_logfile = "*"; |
| - i::FLAG_log = true; |
| - LOGGER->Setup(); |
| -} |
| - |
| -static void TearDown() { |
| - LOGGER->TearDown(); |
| -} |
| - |
| - |
| -TEST(EmptyLog) { |
| - SetUp(); |
| - CHECK_EQ(0, LOGGER->GetLogLines(0, NULL, 0)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(100, NULL, 0)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(0, NULL, 100)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(100, NULL, 100)); |
| - TearDown(); |
| -} |
| - |
| - |
| -TEST(GetMessages) { |
| - SetUp(); |
| - LOGGER->StringEvent("aaa", "bbb"); |
| - LOGGER->StringEvent("cccc", "dddd"); |
| - CHECK_EQ(0, LOGGER->GetLogLines(0, NULL, 0)); |
| - char log_lines[100]; |
| - memset(log_lines, 0, sizeof(log_lines)); |
| - // See Logger::StringEvent. |
| - const char* line_1 = "aaa,\"bbb\"\n"; |
| - const int line_1_len = StrLength(line_1); |
| - // The exact size. |
| - CHECK_EQ(line_1_len, LOGGER->GetLogLines(0, log_lines, line_1_len)); |
| - CHECK_EQ(line_1, log_lines); |
| - memset(log_lines, 0, sizeof(log_lines)); |
| - // A bit more than the first line length. |
| - CHECK_EQ(line_1_len, LOGGER->GetLogLines(0, log_lines, line_1_len + 3)); |
| - log_lines[line_1_len] = '\0'; |
| - CHECK_EQ(line_1, log_lines); |
| - memset(log_lines, 0, sizeof(log_lines)); |
| - const char* line_2 = "cccc,\"dddd\"\n"; |
| - const int line_2_len = StrLength(line_2); |
| - // Now start with line_2 beginning. |
| - CHECK_EQ(0, LOGGER->GetLogLines(line_1_len, log_lines, 0)); |
| - CHECK_EQ(line_2_len, LOGGER->GetLogLines(line_1_len, log_lines, line_2_len)); |
| - CHECK_EQ(line_2, log_lines); |
| - memset(log_lines, 0, sizeof(log_lines)); |
| - CHECK_EQ(line_2_len, |
| - LOGGER->GetLogLines(line_1_len, log_lines, line_2_len + 3)); |
| - CHECK_EQ(line_2, log_lines); |
| - memset(log_lines, 0, sizeof(log_lines)); |
| - // Now get entire buffer contents. |
| - const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n"; |
| - const int all_lines_len = StrLength(all_lines); |
| - CHECK_EQ(all_lines_len, LOGGER->GetLogLines(0, log_lines, all_lines_len)); |
| - CHECK_EQ(all_lines, log_lines); |
| - memset(log_lines, 0, sizeof(log_lines)); |
| - CHECK_EQ(all_lines_len, LOGGER->GetLogLines(0, log_lines, all_lines_len + 3)); |
| - CHECK_EQ(all_lines, log_lines); |
| - memset(log_lines, 0, sizeof(log_lines)); |
| - TearDown(); |
| -} |
| - |
| - |
| -static int GetLogLines(int start_pos, i::Vector<char>* buffer) { |
| - return LOGGER->GetLogLines(start_pos, buffer->start(), buffer->length()); |
| -} |
| - |
| - |
| -TEST(BeyondWritePosition) { |
| - SetUp(); |
| - LOGGER->StringEvent("aaa", "bbb"); |
| - LOGGER->StringEvent("cccc", "dddd"); |
| - // See Logger::StringEvent. |
| - const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n"; |
| - const int all_lines_len = StrLength(all_lines); |
| - EmbeddedVector<char, 100> buffer; |
| - const int beyond_write_pos = all_lines_len; |
| - CHECK_EQ(0, LOGGER->GetLogLines(beyond_write_pos, buffer.start(), 1)); |
| - CHECK_EQ(0, GetLogLines(beyond_write_pos, &buffer)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(beyond_write_pos + 1, buffer.start(), 1)); |
| - CHECK_EQ(0, GetLogLines(beyond_write_pos + 1, &buffer)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(beyond_write_pos + 100, buffer.start(), 1)); |
| - CHECK_EQ(0, GetLogLines(beyond_write_pos + 100, &buffer)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(10 * 1024 * 1024, buffer.start(), 1)); |
| - CHECK_EQ(0, GetLogLines(10 * 1024 * 1024, &buffer)); |
| - TearDown(); |
| -} |
| - |
| - |
| -TEST(MemoryLoggingTurnedOff) { |
| - // Log to stdout |
| - i::FLAG_logfile = "-"; |
| - i::FLAG_log = true; |
| - LOGGER->Setup(); |
| - CHECK_EQ(0, LOGGER->GetLogLines(0, NULL, 0)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(100, NULL, 0)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(0, NULL, 100)); |
| - CHECK_EQ(0, LOGGER->GetLogLines(100, NULL, 100)); |
| - LOGGER->TearDown(); |
| -} |
| - |
| - |
| -static void CompileAndRunScript(const char *src) { |
| - v8::Script::Compile(v8::String::New(src))->Run(); |
| -} |
| - |
| - |
| -namespace v8 { |
| -namespace internal { |
| - |
| -class LoggerTestHelper : public AllStatic { |
| - public: |
| - static bool IsSamplerActive() { return LOGGER->IsProfilerSamplerActive(); } |
| - static void ResetSamplesTaken() { |
| - reinterpret_cast<Sampler*>(LOGGER->ticker_)->ResetSamplesTaken(); |
| - } |
| - static bool has_samples_taken() { |
| - return reinterpret_cast<Sampler*>(LOGGER->ticker_)->samples_taken() > 0; |
| - } |
| -}; |
| - |
| -} // namespace v8::internal |
| -} // namespace v8 |
| - |
| -using v8::internal::LoggerTestHelper; |
| - |
| - |
| namespace { |
| class ScopedLoggerInitializer { |
| public: |
| explicit ScopedLoggerInitializer(bool prof_lazy) |
| - : saved_prof_lazy_(i::FLAG_prof_lazy), |
| + : saved_log_(i::FLAG_log), |
| + saved_prof_lazy_(i::FLAG_prof_lazy), |
| saved_prof_(i::FLAG_prof), |
| saved_prof_auto_(i::FLAG_prof_auto), |
| + temp_file_(NULL), |
| + // Need to run this prior to creating the scope. |
| trick_to_run_init_flags_(init_flags_(prof_lazy)), |
| - need_to_set_up_logger_(i::V8::IsRunning()), |
| scope_(), |
| env_(v8::Context::New()) { |
| - if (need_to_set_up_logger_) LOGGER->Setup(); |
| env_->Enter(); |
| } |
| ~ScopedLoggerInitializer() { |
| env_->Exit(); |
| LOGGER->TearDown(); |
| + if (temp_file_ != NULL) fclose(temp_file_); |
| i::FLAG_prof_lazy = saved_prof_lazy_; |
| i::FLAG_prof = saved_prof_; |
| i::FLAG_prof_auto = saved_prof_auto_; |
| + i::FLAG_log = saved_log_; |
| } |
| v8::Handle<v8::Context>& env() { return env_; } |
| + FILE* StopLoggingGetTempFile() { |
| + temp_file_ = LOGGER->TearDown(); |
| + CHECK_NE(NULL, temp_file_); |
| + fflush(temp_file_); |
| + rewind(temp_file_); |
| + return temp_file_; |
| + } |
| + |
| private: |
| static bool init_flags_(bool prof_lazy) { |
| + i::FLAG_log = true; |
| i::FLAG_prof = true; |
| i::FLAG_prof_lazy = prof_lazy; |
| i::FLAG_prof_auto = false; |
| - i::FLAG_logfile = "*"; |
| + i::FLAG_logfile = i::Log::kLogToTemporaryFile; |
| return prof_lazy; |
| } |
| + const bool saved_log_; |
| const bool saved_prof_lazy_; |
| const bool saved_prof_; |
| const bool saved_prof_auto_; |
| + FILE* temp_file_; |
| const bool trick_to_run_init_flags_; |
| - const bool need_to_set_up_logger_; |
| v8::HandleScope scope_; |
| v8::Handle<v8::Context> env_; |
| DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer); |
| }; |
| - |
| -class LogBufferMatcher { |
| - public: |
| - LogBufferMatcher() { |
| - // Skip all initially logged stuff. |
| - log_pos_ = GetLogLines(0, &buffer_); |
| - } |
| - |
| - int log_pos() { return log_pos_; } |
| - |
| - int GetNextChunk() { |
| - int chunk_size = GetLogLines(log_pos_, &buffer_); |
| - CHECK_GT(buffer_.length(), chunk_size); |
| - buffer_[chunk_size] = '\0'; |
| - log_pos_ += chunk_size; |
| - return chunk_size; |
| - } |
| - |
| - const char* Find(const char* substr) { |
| - return strstr(buffer_.start(), substr); |
| - } |
| - |
| - const char* Find(const i::Vector<char>& substr) { |
| - return Find(substr.start()); |
| - } |
| - |
| - bool IsInSequence(const char* s1, const char* s2) { |
| - const char* s1_pos = Find(s1); |
| - const char* s2_pos = Find(s2); |
| - CHECK_NE(NULL, s1_pos); |
| - CHECK_NE(NULL, s2_pos); |
| - return s1_pos < s2_pos; |
| - } |
| - |
| - void PrintBuffer() { |
| - puts(buffer_.start()); |
| - } |
| - |
| - private: |
| - EmbeddedVector<char, 102400> buffer_; |
| - int log_pos_; |
| -}; |
| - |
| } // namespace |
| -static void CheckThatProfilerWorks(LogBufferMatcher* matcher) { |
| - CHECK(i::RuntimeProfiler::IsEnabled() || |
| - !LoggerTestHelper::IsSamplerActive()); |
| - LoggerTestHelper::ResetSamplesTaken(); |
| - |
| - LOGGER->ResumeProfiler(); |
| - CHECK(LoggerTestHelper::IsSamplerActive()); |
| - |
| - // Verify that the current map of compiled functions has been logged. |
| - CHECK_GT(matcher->GetNextChunk(), 0); |
| - const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/ |
| - CHECK_NE(NULL, matcher->Find(code_creation)); |
| - |
| - // Force compiler to generate new code by parametrizing source. |
| - EmbeddedVector<char, 100> script_src; |
| - i::OS::SNPrintF(script_src, |
| - "function f%d(x) { return %d * x; }" |
| - "for (var i = 0; i < 10000; ++i) { f%d(i); }", |
| - matcher->log_pos(), matcher->log_pos(), matcher->log_pos()); |
| - // Run code for 200 msecs to get some ticks. |
| - const double end_time = i::OS::TimeCurrentMillis() + 200; |
| - while (i::OS::TimeCurrentMillis() < end_time) { |
| - CompileAndRunScript(script_src.start()); |
| - // Yield CPU to give Profiler thread a chance to process ticks. |
| - i::OS::Sleep(1); |
| - } |
| - |
| - LOGGER->PauseProfiler(); |
| - CHECK(i::RuntimeProfiler::IsEnabled() || |
| - !LoggerTestHelper::IsSamplerActive()); |
| - |
| - // Wait 50 msecs to allow Profiler thread to process the last |
| - // tick sample it has got. |
| - i::OS::Sleep(50); |
| - |
| - // Now we must have compiler and tick records. |
| - CHECK_GT(matcher->GetNextChunk(), 0); |
| - matcher->PrintBuffer(); |
| - CHECK_NE(NULL, matcher->Find(code_creation)); |
| - const char* tick = "\ntick,"; |
| - const bool ticks_found = matcher->Find(tick) != NULL; |
| - CHECK_EQ(LoggerTestHelper::has_samples_taken(), ticks_found); |
| +static const char* StrNStr(const char* s1, const char* s2, int n) { |
| + if (s1[n] == '\0') return strstr(s1, s2); |
| + i::ScopedVector<char> str(n + 1); |
| + i::OS::StrNCpy(str, s1, static_cast<size_t>(n)); |
| + str[n] = '\0'; |
| + char* found = strstr(str.start(), s2); |
| + return found != NULL ? s1 + (found - str.start()) : NULL; |
| } |
| @@ -296,29 +101,61 @@ TEST(ProfLazyMode) { |
| if (!i::V8::UseCrankshaft()) return; |
| - // No sampling should happen prior to resuming profiler unless we |
| - // are runtime profiling. |
| - CHECK(i::RuntimeProfiler::IsEnabled() || |
| - !LoggerTestHelper::IsSamplerActive()); |
| + LOGGER->StringEvent("test-start", ""); |
| + CompileRun("var a = (function(x) { return x + 1; })(10);"); |
| + LOGGER->StringEvent("test-profiler-start", ""); |
| + v8::V8::ResumeProfiler(); |
| + CompileRun( |
| + "var b = (function(x) { return x + 2; })(10);\n" |
| + "var c = (function(x) { return x + 3; })(10);\n" |
| + "var d = (function(x) { return x + 4; })(10);\n" |
| + "var e = (function(x) { return x + 5; })(10);"); |
| + v8::V8::PauseProfiler(); |
| + LOGGER->StringEvent("test-profiler-stop", ""); |
| + CompileRun("var f = (function(x) { return x + 6; })(10);"); |
| + // Check that profiling can be resumed again. |
| + LOGGER->StringEvent("test-profiler-start-2", ""); |
| + v8::V8::ResumeProfiler(); |
| + CompileRun( |
| + "var g = (function(x) { return x + 7; })(10);\n" |
| + "var h = (function(x) { return x + 8; })(10);\n" |
| + "var i = (function(x) { return x + 9; })(10);\n" |
| + "var j = (function(x) { return x + 10; })(10);"); |
| + v8::V8::PauseProfiler(); |
| + LOGGER->StringEvent("test-profiler-stop-2", ""); |
| + LOGGER->StringEvent("test-stop", ""); |
| + |
| + bool exists = false; |
| + i::Vector<const char> log( |
| + i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true)); |
| + CHECK(exists); |
| + |
| + const char* test_start_position = |
| + StrNStr(log.start(), "test-start,", log.length()); |
| + CHECK_NE(NULL, test_start_position); |
| + const char* test_profiler_start_position = |
| + StrNStr(log.start(), "test-profiler-start,", log.length()); |
| + CHECK_NE(NULL, test_profiler_start_position); |
| + CHECK_GT(test_profiler_start_position, test_start_position); |
| + const char* test_profiler_stop_position = |
| + StrNStr(log.start(), "test-profiler-stop,", log.length()); |
| + CHECK_NE(NULL, test_profiler_stop_position); |
| + CHECK_GT(test_profiler_stop_position, test_profiler_start_position); |
| + const char* test_profiler_start_2_position = |
| + StrNStr(log.start(), "test-profiler-start-2,", log.length()); |
| + CHECK_NE(NULL, test_profiler_start_2_position); |
| + CHECK_GT(test_profiler_start_2_position, test_profiler_stop_position); |
| - LogBufferMatcher matcher; |
| // Nothing must be logged until profiling is resumed. |
| - CHECK_EQ(0, matcher.log_pos()); |
| - |
| - CompileAndRunScript("var a = (function(x) { return x + 1; })(10);"); |
| - |
| + CHECK_EQ(NULL, StrNStr(test_start_position, |
| + "code-creation,", |
| + static_cast<int>(test_profiler_start_position - |
| + test_start_position))); |
| // Nothing must be logged while profiling is suspended. |
| - CHECK_EQ(0, matcher.GetNextChunk()); |
| - |
| - CheckThatProfilerWorks(&matcher); |
| - |
| - CompileAndRunScript("var a = (function(x) { return x + 1; })(10);"); |
| - |
| - // No new data beyond last retrieved position. |
| - CHECK_EQ(0, matcher.GetNextChunk()); |
| - |
| - // Check that profiling can be resumed again. |
| - CheckThatProfilerWorks(&matcher); |
| + CHECK_EQ(NULL, StrNStr(test_profiler_stop_position, |
| + "code-creation,", |
| + static_cast<int>(test_profiler_start_2_position - |
| + test_profiler_stop_position))); |
| } |
| @@ -383,7 +220,7 @@ class LoopingJsThread : public LoopingThread { |
| { |
| v8::Context::Scope context_scope(context); |
| SignalRunning(); |
| - CompileAndRunScript( |
| + CompileRun( |
| "var j; for (var i=0; i<10000; ++i) { j = Math.sin(i); }"); |
| } |
| context.Dispose(); |
| @@ -531,34 +368,34 @@ static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) { |
| TEST(LogCallbacks) { |
| ScopedLoggerInitializer initialize_logger(false); |
| - LogBufferMatcher matcher; |
| v8::Persistent<v8::FunctionTemplate> obj = |
| v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); |
| - obj->SetClassName(v8::String::New("Obj")); |
| + obj->SetClassName(v8_str("Obj")); |
| v8::Handle<v8::ObjectTemplate> proto = obj->PrototypeTemplate(); |
| v8::Local<v8::Signature> signature = v8::Signature::New(obj); |
| - proto->Set(v8::String::New("method1"), |
| + proto->Set(v8_str("method1"), |
| v8::FunctionTemplate::New(ObjMethod1, |
| v8::Handle<v8::Value>(), |
| signature), |
| static_cast<v8::PropertyAttribute>(v8::DontDelete)); |
| initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction()); |
| - CompileAndRunScript("Obj.prototype.method1.toString();"); |
| + CompileRun("Obj.prototype.method1.toString();"); |
| LOGGER->LogCompiledFunctions(); |
| - CHECK_GT(matcher.GetNextChunk(), 0); |
| - const char* callback_rec = "code-creation,Callback,"; |
| - char* pos = const_cast<char*>(matcher.Find(callback_rec)); |
| - CHECK_NE(NULL, pos); |
| - pos += strlen(callback_rec); |
| - EmbeddedVector<char, 100> ref_data; |
| + bool exists = false; |
| + i::Vector<const char> log( |
| + i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true)); |
| + CHECK(exists); |
| + |
| + i::EmbeddedVector<char, 100> ref_data; |
| i::OS::SNPrintF(ref_data, |
| - "0x%" V8PRIxPTR ",1,\"method1\"", ObjMethod1); |
| - *(pos + strlen(ref_data.start())) = '\0'; |
| - CHECK_EQ(ref_data.start(), pos); |
| + "code-creation,Callback,0x%" V8PRIxPTR ",1,\"method1\"\0", |
| + ObjMethod1); |
| + |
| + CHECK_NE(NULL, StrNStr(log.start(), ref_data.start(), log.length())); |
| obj.Dispose(); |
| } |
| @@ -581,34 +418,41 @@ static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property, |
| TEST(LogAccessorCallbacks) { |
| ScopedLoggerInitializer initialize_logger(false); |
| - LogBufferMatcher matcher; |
| v8::Persistent<v8::FunctionTemplate> obj = |
| v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); |
| - obj->SetClassName(v8::String::New("Obj")); |
| + obj->SetClassName(v8_str("Obj")); |
| v8::Handle<v8::ObjectTemplate> inst = obj->InstanceTemplate(); |
| - inst->SetAccessor(v8::String::New("prop1"), Prop1Getter, Prop1Setter); |
| - inst->SetAccessor(v8::String::New("prop2"), Prop2Getter); |
| + inst->SetAccessor(v8_str("prop1"), Prop1Getter, Prop1Setter); |
| + inst->SetAccessor(v8_str("prop2"), Prop2Getter); |
| LOGGER->LogAccessorCallbacks(); |
| - CHECK_GT(matcher.GetNextChunk(), 0); |
| - matcher.PrintBuffer(); |
| + |
| + bool exists = false; |
| + i::Vector<const char> log( |
| + i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true)); |
| + CHECK(exists); |
| EmbeddedVector<char, 100> prop1_getter_record; |
| i::OS::SNPrintF(prop1_getter_record, |
| "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"", |
| Prop1Getter); |
| - CHECK_NE(NULL, matcher.Find(prop1_getter_record)); |
| + CHECK_NE(NULL, |
| + StrNStr(log.start(), prop1_getter_record.start(), log.length())); |
| + |
| EmbeddedVector<char, 100> prop1_setter_record; |
| i::OS::SNPrintF(prop1_setter_record, |
| "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"", |
| Prop1Setter); |
| - CHECK_NE(NULL, matcher.Find(prop1_setter_record)); |
| + CHECK_NE(NULL, |
| + StrNStr(log.start(), prop1_setter_record.start(), log.length())); |
| + |
| EmbeddedVector<char, 100> prop2_getter_record; |
| i::OS::SNPrintF(prop2_getter_record, |
| "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"", |
| Prop2Getter); |
| - CHECK_NE(NULL, matcher.Find(prop2_getter_record)); |
| + CHECK_NE(NULL, |
| + StrNStr(log.start(), prop2_getter_record.start(), log.length())); |
| obj.Dispose(); |
| } |
| @@ -625,377 +469,6 @@ TEST(IsLoggingPreserved) { |
| } |
| -static inline bool IsStringEqualTo(const char* r, const char* s) { |
| - return strncmp(r, s, strlen(r)) == 0; |
| -} |
| - |
| - |
| -static bool Consume(const char* str, char** buf) { |
| - if (IsStringEqualTo(str, *buf)) { |
| - *buf += strlen(str); |
| - return true; |
| - } |
| - return false; |
| -} |
| - |
| - |
| -namespace { |
| - |
| -// A code entity is a pointer to a position of code-creation event in buffer log |
| -// offset to a point where entity size begins, i.e.: '255,"func"\n'. This makes |
| -// comparing code entities pretty easy. |
| -typedef char* CodeEntityInfo; |
| - |
| -class Interval { |
| - public: |
| - Interval() |
| - : min_addr_(reinterpret_cast<Address>(-1)), |
| - max_addr_(reinterpret_cast<Address>(0)), next_(NULL) {} |
| - |
| - ~Interval() { delete next_; } |
| - |
| - size_t Length() { |
| - size_t result = max_addr_ - min_addr_ + 1; |
| - if (next_ != NULL) result += next_->Length(); |
| - return result; |
| - } |
| - |
| - void CloneFrom(Interval* src) { |
| - while (src != NULL) { |
| - RegisterAddress(src->min_addr_); |
| - RegisterAddress(src->max_addr_); |
| - src = src->next_; |
| - } |
| - } |
| - |
| - bool Contains(Address addr) { |
| - if (min_addr_ <= addr && addr <= max_addr_) { |
| - return true; |
| - } |
| - if (next_ != NULL) { |
| - return next_->Contains(addr); |
| - } else { |
| - return false; |
| - } |
| - } |
| - |
| - size_t GetIndex(Address addr) { |
| - if (min_addr_ <= addr && addr <= max_addr_) { |
| - return addr - min_addr_; |
| - } |
| - CHECK_NE(NULL, next_); |
| - return (max_addr_ - min_addr_ + 1) + next_->GetIndex(addr); |
| - } |
| - |
| - Address GetMinAddr() { |
| - return next_ == NULL ? min_addr_ : i::Min(min_addr_, next_->GetMinAddr()); |
| - } |
| - |
| - Address GetMaxAddr() { |
| - return next_ == NULL ? max_addr_ : i::Max(max_addr_, next_->GetMaxAddr()); |
| - } |
| - |
| - void RegisterAddress(Address addr) { |
| - if (min_addr_ == reinterpret_cast<Address>(-1) |
| - || (size_t)(addr > min_addr_ ? |
| - addr - min_addr_ : min_addr_ - addr) < MAX_DELTA) { |
| - if (addr < min_addr_) min_addr_ = addr; |
| - if (addr > max_addr_) max_addr_ = addr; |
| - } else { |
| - if (next_ == NULL) next_ = new Interval(); |
| - next_->RegisterAddress(addr); |
| - } |
| - } |
| - |
| - Address raw_min_addr() { return min_addr_; } |
| - |
| - Address raw_max_addr() { return max_addr_; } |
| - |
| - Interval* get_next() { return next_; } |
| - |
| - private: |
| - static const size_t MAX_DELTA = 0x100000; |
| - Address min_addr_; |
| - Address max_addr_; |
| - Interval* next_; |
| -}; |
| - |
| - |
| -// A structure used to return log parsing results. |
| -class ParseLogResult { |
| - public: |
| - ParseLogResult() |
| - : entities_map(NULL), entities(NULL), |
| - max_entities(0) {} |
| - |
| - ~ParseLogResult() { |
| - i::DeleteArray(entities_map); |
| - i::DeleteArray(entities); |
| - } |
| - |
| - void AllocateEntities() { |
| - // Make sure that the test doesn't operate on a bogus log. |
| - CHECK_GT(max_entities, 0); |
| - CHECK_GT(bounds.GetMinAddr(), 0); |
| - CHECK_GT(bounds.GetMaxAddr(), bounds.GetMinAddr()); |
| - |
| - entities = i::NewArray<CodeEntityInfo>(max_entities); |
| - for (int i = 0; i < max_entities; ++i) { |
| - entities[i] = NULL; |
| - } |
| - const size_t map_length = bounds.Length(); |
| - entities_map = i::NewArray<int>(static_cast<int>(map_length)); |
| - for (size_t i = 0; i < map_length; ++i) { |
| - entities_map[i] = -1; |
| - } |
| - } |
| - |
| - bool HasIndexForAddress(Address addr) { |
| - return bounds.Contains(addr); |
| - } |
| - |
| - size_t GetIndexForAddress(Address addr) { |
| - CHECK(HasIndexForAddress(addr)); |
| - return bounds.GetIndex(addr); |
| - } |
| - |
| - CodeEntityInfo GetEntity(Address addr) { |
| - if (HasIndexForAddress(addr)) { |
| - size_t idx = GetIndexForAddress(addr); |
| - int item = entities_map[idx]; |
| - return item != -1 ? entities[item] : NULL; |
| - } |
| - return NULL; |
| - } |
| - |
| - void ParseAddress(char* start) { |
| - Address addr = |
| - reinterpret_cast<Address>(strtoul(start, NULL, 16)); // NOLINT |
| - bounds.RegisterAddress(addr); |
| - } |
| - |
| - Address ConsumeAddress(char** start) { |
| - char* end_ptr; |
| - Address addr = |
| - reinterpret_cast<Address>(strtoul(*start, &end_ptr, 16)); // NOLINT |
| - CHECK(HasIndexForAddress(addr)); |
| - *start = end_ptr; |
| - return addr; |
| - } |
| - |
| - Interval bounds; |
| - // Memory map of entities start addresses. |
| - int* entities_map; |
| - // An array of code entities. |
| - CodeEntityInfo* entities; |
| - // Maximal entities count. Actual entities count can be lower, |
| - // empty entity slots are pointing to NULL. |
| - int max_entities; |
| -}; |
| - |
| -} // namespace |
| - |
| - |
| -typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* result); |
| - |
| -static void ParserCycle( |
| - char* start, char* end, ParseLogResult* result, |
| - ParserBlock block_creation, ParserBlock block_delete, |
| - ParserBlock block_move) { |
| - |
| - const char* code_creation = "code-creation,"; |
| - const char* code_delete = "code-delete,"; |
| - const char* code_move = "code-move,"; |
| - |
| - const char* lazy_compile = "LazyCompile,"; |
| - const char* script = "Script,"; |
| - const char* function = "Function,"; |
| - |
| - while (start < end) { |
| - if (Consume(code_creation, &start)) { |
| - if (Consume(lazy_compile, &start) |
| - || Consume(script, &start) |
| - || Consume(function, &start)) { |
| - block_creation(start, end, result); |
| - } |
| - } else if (Consume(code_delete, &start)) { |
| - block_delete(start, end, result); |
| - } else if (Consume(code_move, &start)) { |
| - block_move(start, end, result); |
| - } |
| - while (start < end && *start != '\n') ++start; |
| - ++start; |
| - } |
| -} |
| - |
| - |
| -static void Pass1CodeCreation(char* start, char* end, ParseLogResult* result) { |
| - result->ParseAddress(start); |
| - ++result->max_entities; |
| -} |
| - |
| - |
| -static void Pass1CodeDelete(char* start, char* end, ParseLogResult* result) { |
| - result->ParseAddress(start); |
| -} |
| - |
| - |
| -static void Pass1CodeMove(char* start, char* end, ParseLogResult* result) { |
| - result->ParseAddress(start); |
| - // Skip old address. |
| - while (start < end && *start != ',') ++start; |
| - CHECK_GT(end, start); |
| - ++start; // Skip ','. |
| - result->ParseAddress(start); |
| -} |
| - |
| - |
| -static void Pass2CodeCreation(char* start, char* end, ParseLogResult* result) { |
| - Address addr = result->ConsumeAddress(&start); |
| - CHECK_GT(end, start); |
| - ++start; // Skip ','. |
| - |
| - size_t idx = result->GetIndexForAddress(addr); |
| - result->entities_map[idx] = -1; |
| - for (int i = 0; i < result->max_entities; ++i) { |
| - // Find an empty slot and fill it. |
| - if (result->entities[i] == NULL) { |
| - result->entities[i] = start; |
| - result->entities_map[idx] = i; |
| - break; |
| - } |
| - } |
| - // Make sure that a slot was found. |
| - CHECK_GE(result->entities_map[idx], 0); |
| -} |
| - |
| - |
| -static void Pass2CodeDelete(char* start, char* end, ParseLogResult* result) { |
| - Address addr = result->ConsumeAddress(&start); |
| - size_t idx = result->GetIndexForAddress(addr); |
| - // There can be code deletes that are not related to JS code. |
| - if (result->entities_map[idx] >= 0) { |
| - result->entities[result->entities_map[idx]] = NULL; |
| - result->entities_map[idx] = -1; |
| - } |
| -} |
| - |
| - |
| -static void Pass2CodeMove(char* start, char* end, ParseLogResult* result) { |
| - Address from_addr = result->ConsumeAddress(&start); |
| - CHECK_GT(end, start); |
| - ++start; // Skip ','. |
| - Address to_addr = result->ConsumeAddress(&start); |
| - CHECK_GT(end, start); |
| - |
| - size_t from_idx = result->GetIndexForAddress(from_addr); |
| - size_t to_idx = result->GetIndexForAddress(to_addr); |
| - // There can be code moves that are not related to JS code. |
| - if (from_idx != to_idx && result->entities_map[from_idx] >= 0) { |
| - CHECK_EQ(-1, result->entities_map[to_idx]); |
| - result->entities_map[to_idx] = result->entities_map[from_idx]; |
| - result->entities_map[from_idx] = -1; |
| - }; |
| -} |
| - |
| - |
| -static void ParseLog(char* start, char* end, ParseLogResult* result) { |
| - // Pass 1: Calculate boundaries of addresses and entities count. |
| - ParserCycle(start, end, result, |
| - Pass1CodeCreation, Pass1CodeDelete, Pass1CodeMove); |
| - |
| - printf("min_addr: %p, max_addr: %p, entities: %d\n", |
| - result->bounds.GetMinAddr(), result->bounds.GetMaxAddr(), |
| - result->max_entities); |
| - |
| - result->AllocateEntities(); |
| - |
| - // Pass 2: Fill in code entries data. |
| - ParserCycle(start, end, result, |
| - Pass2CodeCreation, Pass2CodeDelete, Pass2CodeMove); |
| -} |
| - |
| - |
| -static inline void PrintCodeEntityInfo(CodeEntityInfo entity) { |
| - const int max_len = 50; |
| - if (entity != NULL) { |
| - char* eol = strchr(entity, '\n'); |
| - int len = static_cast<int>(eol - entity); |
| - len = len <= max_len ? len : max_len; |
| - printf("%-*.*s ", max_len, len, entity); |
| - } else { |
| - printf("%*s", max_len + 1, ""); |
| - } |
| -} |
| - |
| - |
| -static void PrintCodeEntitiesInfo( |
| - bool is_equal, Address addr, |
| - CodeEntityInfo l_entity, CodeEntityInfo r_entity) { |
| - printf("%c %p ", is_equal ? ' ' : '*', addr); |
| - PrintCodeEntityInfo(l_entity); |
| - PrintCodeEntityInfo(r_entity); |
| - printf("\n"); |
| -} |
| - |
| - |
| -static inline int StrChrLen(const char* s, char c) { |
| - return static_cast<int>(strchr(s, c) - s); |
| -} |
| - |
| - |
| -static bool AreFuncSizesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) { |
| - int ref_len = StrChrLen(ref_s, ','); |
| - int new_len = StrChrLen(new_s, ','); |
| - return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0; |
| -} |
| - |
| - |
| -static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) { |
| - // Skip size. |
| - ref_s = strchr(ref_s, ',') + 1; |
| - new_s = strchr(new_s, ',') + 1; |
| - CHECK_EQ('"', ref_s[0]); |
| - CHECK_EQ('"', new_s[0]); |
| - int ref_len = StrChrLen(ref_s + 1, '\"'); |
| - int new_len = StrChrLen(new_s + 1, '\"'); |
| - // A special case for ErrorPrototype. Haven't yet figured out why they |
| - // are different. |
| - const char* error_prototype = "\"ErrorPrototype"; |
| - if (IsStringEqualTo(error_prototype, ref_s) |
| - && IsStringEqualTo(error_prototype, new_s)) { |
| - return true; |
| - } |
| - // Built-in objects have problems too. |
| - const char* built_ins[] = { |
| - "\"Boolean\"", "\"Function\"", "\"Number\"", |
| - "\"Object\"", "\"Script\"", "\"String\"" |
| - }; |
| - for (size_t i = 0; i < sizeof(built_ins) / sizeof(*built_ins); ++i) { |
| - if (IsStringEqualTo(built_ins[i], new_s)) { |
| - return true; |
| - } |
| - } |
| - return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0; |
| -} |
| - |
| - |
| -static bool AreEntitiesEqual(CodeEntityInfo ref_e, CodeEntityInfo new_e) { |
| - if (ref_e == NULL && new_e != NULL) return true; |
| - if (ref_e != NULL && new_e != NULL) { |
| - return AreFuncSizesEqual(ref_e, new_e) && AreFuncNamesEqual(ref_e, new_e); |
| - } |
| - if (ref_e != NULL && new_e == NULL) { |
| - // args_count entities (argument adapters) are not found by heap traversal, |
| - // but they are not needed because they doesn't contain any code. |
| - ref_e = strchr(ref_e, ',') + 1; |
| - const char* args_count = "\"args_count:"; |
| - return IsStringEqualTo(args_count, ref_e); |
| - } |
| - return false; |
| -} |
| - |
| - |
| // Test that logging of code create / move / delete events |
| // is equivalent to traversal of a resulting heap. |
| TEST(EquivalenceOfLoggingAndTraversal) { |
| @@ -1008,86 +481,68 @@ TEST(EquivalenceOfLoggingAndTraversal) { |
| // P.S. No, V8 can't be re-initialized after disposal, see include/v8.h. |
| CHECK(!i::V8::IsRunning()); |
| - i::FLAG_logfile = "*"; |
| - i::FLAG_log = true; |
| - i::FLAG_log_code = true; |
| - |
| - // Make sure objects move. |
| - bool saved_always_compact = i::FLAG_always_compact; |
| - if (!i::FLAG_never_compact) { |
| - i::FLAG_always_compact = true; |
| - } |
| - |
| - v8::HandleScope scope; |
| - v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>(); |
| - v8::Handle<v8::Context> env = v8::Context::New( |
| - 0, v8::Handle<v8::ObjectTemplate>(), global_object); |
| - env->Enter(); |
| + // Start with profiling to capture all code events from the beginning. |
| + ScopedLoggerInitializer initialize_logger(false); |
| // Compile and run a function that creates other functions. |
| - CompileAndRunScript( |
| + CompileRun( |
| "(function f(obj) {\n" |
| " obj.test =\n" |
| " (function a(j) { return function b() { return j; } })(100);\n" |
| "})(this);"); |
| - HEAP->CollectAllGarbage(false); |
| - |
| - EmbeddedVector<char, 204800> buffer; |
| - int log_size; |
| - ParseLogResult ref_result; |
| - |
| - // Retrieve the log. |
| - { |
| - // Make sure that no GCs occur prior to LogCompiledFunctions call. |
| - i::AssertNoAllocation no_alloc; |
| - |
| - log_size = GetLogLines(0, &buffer); |
| - CHECK_GT(log_size, 0); |
| - CHECK_GT(buffer.length(), log_size); |
| - |
| - // Fill a map of compiled code objects. |
| - ParseLog(buffer.start(), buffer.start() + log_size, &ref_result); |
| - } |
| + v8::V8::PauseProfiler(); |
| + HEAP->CollectAllGarbage(true); |
| + LOGGER->StringEvent("test-logging-done", ""); |
| // Iterate heap to find compiled functions, will write to log. |
| LOGGER->LogCompiledFunctions(); |
| - char* new_log_start = buffer.start() + log_size; |
| - const int new_log_size = LOGGER->GetLogLines( |
| - log_size, new_log_start, buffer.length() - log_size); |
| - CHECK_GT(new_log_size, 0); |
| - CHECK_GT(buffer.length(), log_size + new_log_size); |
| - |
| - // Fill an equivalent map of compiled code objects. |
| - ParseLogResult new_result; |
| - ParseLog(new_log_start, new_log_start + new_log_size, &new_result); |
| - |
| - // Test their actual equivalence. |
| - Interval combined; |
| - combined.CloneFrom(&ref_result.bounds); |
| - combined.CloneFrom(&new_result.bounds); |
| - Interval* iter = &combined; |
| - bool results_equal = true; |
| - |
| - while (iter != NULL) { |
| - for (Address addr = iter->raw_min_addr(); |
| - addr <= iter->raw_max_addr(); ++addr) { |
| - CodeEntityInfo ref_entity = ref_result.GetEntity(addr); |
| - CodeEntityInfo new_entity = new_result.GetEntity(addr); |
| - if (ref_entity != NULL || new_entity != NULL) { |
| - const bool equal = AreEntitiesEqual(ref_entity, new_entity); |
| - if (!equal) results_equal = false; |
| - PrintCodeEntitiesInfo(equal, addr, ref_entity, new_entity); |
| - } |
| + LOGGER->StringEvent("test-traversal-done", ""); |
| + |
| + bool exists = false; |
| + i::Vector<const char> log( |
| + i::ReadFile(initialize_logger.StopLoggingGetTempFile(), &exists, true)); |
| + CHECK(exists); |
| + v8::Handle<v8::String> log_str = v8::String::New(log.start(), log.length()); |
| + initialize_logger.env()->Global()->Set(v8_str("_log"), log_str); |
| + |
| + const char* scripts[] = { |
| + "tools/splaytree.js", "tools/codemap.js", "tools/csvparser.js", |
| + "tools/consarray.js", "tools/profile.js", "tools/profile_view.js", |
| + "tools/logreader.js", "test/cctest/log-eq-of-logging-and-traversal.js" |
|
Sven Panne
2011/07/13 13:11:51
This change broke testing in an architecture-speci
|
| + }; |
| + int scripts_count = sizeof(scripts) / sizeof(scripts[0]); |
| + v8::Handle<v8::Value> last_result; |
| + for (int i = 0; i < scripts_count; ++i) { |
| + bool exists = true; |
| + i::Vector<const char> source(i::ReadFile(scripts[i], &exists, true)); |
| + CHECK(exists); |
| + CHECK_GT(source.length(), 0); |
| + v8::Handle<v8::String> source_str = |
| + v8::String::New(source.start(), source.length()); |
| + v8::TryCatch try_catch; |
| + v8::Handle<v8::Script> script = |
| + v8::Script::Compile(source_str, v8_str(scripts[i])); |
| + if (script.IsEmpty()) { |
| + v8::String::Utf8Value exception(try_catch.Exception()); |
| + printf("compile %s: %s\n", scripts[i], *exception); |
| + CHECK(false); |
| + } |
| + last_result = script->Run(); |
| + if (last_result.IsEmpty()) { |
| + v8::String::Utf8Value exception(try_catch.Exception()); |
| + printf("run %s: %s\n", scripts[i], *exception); |
| + CHECK(false); |
| } |
| - iter = iter->get_next(); |
| } |
| - // Make sure that all log data is written prior crash due to CHECK failure. |
| - fflush(stdout); |
| - CHECK(results_equal); |
| - |
| - env->Exit(); |
| - LOGGER->TearDown(); |
| - i::FLAG_always_compact = saved_always_compact; |
| + // The result either be a "true" literal or problem description. |
| + if (!last_result->IsTrue()) { |
| + v8::Local<v8::String> s = last_result->ToString(); |
| + i::ScopedVector<char> data(s->Length() + 1); |
| + CHECK_NE(NULL, data.start()); |
| + s->WriteAscii(data.start()); |
| + printf("%s\n", data.start()); |
| + // Make sure that our output is written prior crash due to CHECK failure. |
| + fflush(stdout); |
| + CHECK(false); |
| + } |
| } |
| - |
| -#endif // ENABLE_LOGGING_AND_PROFILING |