• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2006-2009 the V8 project authors. All rights reserved.
2 //
3 // Tests of logging functions from log.h
4 
5 #ifdef ENABLE_LOGGING_AND_PROFILING
6 
7 #ifdef __linux__
8 #include <math.h>
9 #include <pthread.h>
10 #include <signal.h>
11 #include <unistd.h>
12 #endif  // __linux__
13 
14 #include "v8.h"
15 #include "log.h"
16 #include "v8threads.h"
17 #include "cctest.h"
18 
19 using v8::internal::Address;
20 using v8::internal::EmbeddedVector;
21 using v8::internal::Logger;
22 using v8::internal::StrLength;
23 
24 namespace i = v8::internal;
25 
SetUp()26 static void SetUp() {
27   // Log to memory buffer.
28   i::FLAG_logfile = "*";
29   i::FLAG_log = true;
30   Logger::Setup();
31 }
32 
TearDown()33 static void TearDown() {
34   Logger::TearDown();
35 }
36 
37 
TEST(EmptyLog)38 TEST(EmptyLog) {
39   SetUp();
40   CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
41   CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
42   CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
43   CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
44   TearDown();
45 }
46 
47 
TEST(GetMessages)48 TEST(GetMessages) {
49   SetUp();
50   Logger::StringEvent("aaa", "bbb");
51   Logger::StringEvent("cccc", "dddd");
52   CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
53   char log_lines[100];
54   memset(log_lines, 0, sizeof(log_lines));
55   // Requesting data size which is smaller than first log message length.
56   CHECK_EQ(0, Logger::GetLogLines(0, log_lines, 3));
57   // See Logger::StringEvent.
58   const char* line_1 = "aaa,\"bbb\"\n";
59   const int line_1_len = StrLength(line_1);
60   // Still smaller than log message length.
61   CHECK_EQ(0, Logger::GetLogLines(0, log_lines, line_1_len - 1));
62   // The exact size.
63   CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len));
64   CHECK_EQ(line_1, log_lines);
65   memset(log_lines, 0, sizeof(log_lines));
66   // A bit more than the first line length.
67   CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len + 3));
68   log_lines[line_1_len] = '\0';
69   CHECK_EQ(line_1, log_lines);
70   memset(log_lines, 0, sizeof(log_lines));
71   const char* line_2 = "cccc,\"dddd\"\n";
72   const int line_2_len = StrLength(line_2);
73   // Now start with line_2 beginning.
74   CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 0));
75   CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 3));
76   CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, line_2_len - 1));
77   CHECK_EQ(line_2_len, Logger::GetLogLines(line_1_len, log_lines, line_2_len));
78   CHECK_EQ(line_2, log_lines);
79   memset(log_lines, 0, sizeof(log_lines));
80   CHECK_EQ(line_2_len,
81            Logger::GetLogLines(line_1_len, log_lines, line_2_len + 3));
82   CHECK_EQ(line_2, log_lines);
83   memset(log_lines, 0, sizeof(log_lines));
84   // Now get entire buffer contents.
85   const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
86   const int all_lines_len = StrLength(all_lines);
87   CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len));
88   CHECK_EQ(all_lines, log_lines);
89   memset(log_lines, 0, sizeof(log_lines));
90   CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len + 3));
91   CHECK_EQ(all_lines, log_lines);
92   memset(log_lines, 0, sizeof(log_lines));
93   TearDown();
94 }
95 
96 
GetLogLines(int start_pos,i::Vector<char> * buffer)97 static int GetLogLines(int start_pos, i::Vector<char>* buffer) {
98   return Logger::GetLogLines(start_pos, buffer->start(), buffer->length());
99 }
100 
101 
TEST(BeyondWritePosition)102 TEST(BeyondWritePosition) {
103   SetUp();
104   Logger::StringEvent("aaa", "bbb");
105   Logger::StringEvent("cccc", "dddd");
106   // See Logger::StringEvent.
107   const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
108   const int all_lines_len = StrLength(all_lines);
109   EmbeddedVector<char, 100> buffer;
110   const int beyond_write_pos = all_lines_len;
111   CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos, buffer.start(), 1));
112   CHECK_EQ(0, GetLogLines(beyond_write_pos, &buffer));
113   CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 1, buffer.start(), 1));
114   CHECK_EQ(0, GetLogLines(beyond_write_pos + 1, &buffer));
115   CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 100, buffer.start(), 1));
116   CHECK_EQ(0, GetLogLines(beyond_write_pos + 100, &buffer));
117   CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, buffer.start(), 1));
118   CHECK_EQ(0, GetLogLines(10 * 1024 * 1024, &buffer));
119   TearDown();
120 }
121 
122 
TEST(MemoryLoggingTurnedOff)123 TEST(MemoryLoggingTurnedOff) {
124   // Log to stdout
125   i::FLAG_logfile = "-";
126   i::FLAG_log = true;
127   Logger::Setup();
128   CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
129   CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
130   CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
131   CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
132   Logger::TearDown();
133 }
134 
135 
CompileAndRunScript(const char * src)136 static void CompileAndRunScript(const char *src) {
137   v8::Script::Compile(v8::String::New(src))->Run();
138 }
139 
140 
141 namespace v8 {
142 namespace internal {
143 
144 class LoggerTestHelper : public AllStatic {
145  public:
IsSamplerActive()146   static bool IsSamplerActive() { return Logger::IsProfilerSamplerActive(); }
147 };
148 
149 }  // namespace v8::internal
150 }  // namespace v8
151 
152 using v8::internal::LoggerTestHelper;
153 
154 
155 // Under Linux, we need to check if signals were delivered to avoid false
156 // positives.  Under other platforms profiling is done via a high-priority
157 // thread, so this case never happen.
158 static bool was_sigprof_received = true;
159 #ifdef __linux__
160 
161 struct sigaction old_sigprof_handler;
162 pthread_t our_thread;
163 
SigProfSignalHandler(int signal,siginfo_t * info,void * context)164 static void SigProfSignalHandler(int signal, siginfo_t* info, void* context) {
165   if (signal != SIGPROF || !pthread_equal(pthread_self(), our_thread)) return;
166   was_sigprof_received = true;
167   old_sigprof_handler.sa_sigaction(signal, info, context);
168 }
169 
170 #endif  // __linux__
171 
172 
173 namespace {
174 
175 class ScopedLoggerInitializer {
176  public:
ScopedLoggerInitializer(bool log,bool prof_lazy)177   explicit ScopedLoggerInitializer(bool log, bool prof_lazy)
178       : saved_log_(i::FLAG_log),
179         saved_prof_lazy_(i::FLAG_prof_lazy),
180         saved_prof_(i::FLAG_prof),
181         saved_prof_auto_(i::FLAG_prof_auto),
182         trick_to_run_init_flags_(init_flags_(log, prof_lazy)),
183         need_to_set_up_logger_(i::V8::IsRunning()),
184         scope_(),
185         env_(v8::Context::New()) {
186     if (need_to_set_up_logger_) Logger::Setup();
187     env_->Enter();
188   }
189 
~ScopedLoggerInitializer()190   ~ScopedLoggerInitializer() {
191     env_->Exit();
192     Logger::TearDown();
193     i::FLAG_prof_lazy = saved_prof_lazy_;
194     i::FLAG_prof = saved_prof_;
195     i::FLAG_prof_auto = saved_prof_auto_;
196     i::FLAG_log = saved_log_;
197   }
198 
env()199   v8::Handle<v8::Context>& env() { return env_; }
200 
201  private:
init_flags_(bool log,bool prof_lazy)202   static bool init_flags_(bool log, bool prof_lazy) {
203     i::FLAG_log = log;
204     i::FLAG_prof = true;
205     i::FLAG_prof_lazy = prof_lazy;
206     i::FLAG_prof_auto = false;
207     i::FLAG_logfile = "*";
208     return prof_lazy;
209   }
210 
211   const bool saved_log_;
212   const bool saved_prof_lazy_;
213   const bool saved_prof_;
214   const bool saved_prof_auto_;
215   const bool trick_to_run_init_flags_;
216   const bool need_to_set_up_logger_;
217   v8::HandleScope scope_;
218   v8::Handle<v8::Context> env_;
219 
220   DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer);
221 };
222 
223 
224 class LogBufferMatcher {
225  public:
LogBufferMatcher()226   LogBufferMatcher() {
227     // Skip all initially logged stuff.
228     log_pos_ = GetLogLines(0, &buffer_);
229   }
230 
log_pos()231   int log_pos() { return log_pos_; }
232 
GetNextChunk()233   int GetNextChunk() {
234     int chunk_size = GetLogLines(log_pos_, &buffer_);
235     CHECK_GT(buffer_.length(), chunk_size);
236     buffer_[chunk_size] = '\0';
237     log_pos_ += chunk_size;
238     return chunk_size;
239   }
240 
Find(const char * substr)241   const char* Find(const char* substr) {
242     return strstr(buffer_.start(), substr);
243   }
244 
Find(const i::Vector<char> & substr)245   const char* Find(const i::Vector<char>& substr) {
246     return Find(substr.start());
247   }
248 
IsInSequence(const char * s1,const char * s2)249   bool IsInSequence(const char* s1, const char* s2) {
250     const char* s1_pos = Find(s1);
251     const char* s2_pos = Find(s2);
252     CHECK_NE(NULL, s1_pos);
253     CHECK_NE(NULL, s2_pos);
254     return s1_pos < s2_pos;
255   }
256 
PrintBuffer()257   void PrintBuffer() {
258     puts(buffer_.start());
259   }
260 
261  private:
262   EmbeddedVector<char, 102400> buffer_;
263   int log_pos_;
264 };
265 
266 }  // namespace
267 
268 
CheckThatProfilerWorks(LogBufferMatcher * matcher)269 static void CheckThatProfilerWorks(LogBufferMatcher* matcher) {
270   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
271   CHECK(LoggerTestHelper::IsSamplerActive());
272 
273   // Verify that the current map of compiled functions has been logged.
274   CHECK_GT(matcher->GetNextChunk(), 0);
275   const char* code_creation = "\ncode-creation,";  // eq. to /^code-creation,/
276   CHECK_NE(NULL, matcher->Find(code_creation));
277 
278 #ifdef __linux__
279   // Intercept SIGPROF handler to make sure that the test process
280   // had received it. Under load, system can defer it causing test failure.
281   // It is important to execute this after 'ResumeProfiler'.
282   our_thread = pthread_self();
283   was_sigprof_received = false;
284   struct sigaction sa;
285   sa.sa_sigaction = SigProfSignalHandler;
286   sigemptyset(&sa.sa_mask);
287   sa.sa_flags = SA_SIGINFO;
288   CHECK_EQ(0, sigaction(SIGPROF, &sa, &old_sigprof_handler));
289 #endif  // __linux__
290 
291   // Force compiler to generate new code by parametrizing source.
292   EmbeddedVector<char, 100> script_src;
293   i::OS::SNPrintF(script_src,
294                   "function f%d(x) { return %d * x; }"
295                   "for (var i = 0; i < 10000; ++i) { f%d(i); }",
296                   matcher->log_pos(), matcher->log_pos(), matcher->log_pos());
297   // Run code for 200 msecs to get some ticks.
298   const double end_time = i::OS::TimeCurrentMillis() + 200;
299   while (i::OS::TimeCurrentMillis() < end_time) {
300     CompileAndRunScript(script_src.start());
301     // Yield CPU to give Profiler thread a chance to process ticks.
302     i::OS::Sleep(1);
303   }
304 
305   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
306   CHECK(!LoggerTestHelper::IsSamplerActive());
307 
308   // Wait 50 msecs to allow Profiler thread to process the last
309   // tick sample it has got.
310   i::OS::Sleep(50);
311 
312   // Now we must have compiler and tick records.
313   CHECK_GT(matcher->GetNextChunk(), 0);
314   matcher->PrintBuffer();
315   CHECK_NE(NULL, matcher->Find(code_creation));
316   const char* tick = "\ntick,";
317   const bool ticks_found = matcher->Find(tick) != NULL;
318   CHECK_EQ(was_sigprof_received, ticks_found);
319 }
320 
321 
TEST(ProfLazyMode)322 TEST(ProfLazyMode) {
323   ScopedLoggerInitializer initialize_logger(false, true);
324 
325   // No sampling should happen prior to resuming profiler.
326   CHECK(!LoggerTestHelper::IsSamplerActive());
327 
328   LogBufferMatcher matcher;
329   // Nothing must be logged until profiling is resumed.
330   CHECK_EQ(0, matcher.log_pos());
331 
332   CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
333 
334   // Nothing must be logged while profiling is suspended.
335   CHECK_EQ(0, matcher.GetNextChunk());
336 
337   CheckThatProfilerWorks(&matcher);
338 
339   CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
340 
341   // No new data beyond last retrieved position.
342   CHECK_EQ(0, matcher.GetNextChunk());
343 
344   // Check that profiling can be resumed again.
345   CheckThatProfilerWorks(&matcher);
346 }
347 
348 
349 // Profiling multiple threads that use V8 is currently only available on Linux.
350 #ifdef __linux__
351 
352 namespace {
353 
354 class LoopingThread : public v8::internal::Thread {
355  public:
LoopingThread()356   LoopingThread()
357       : v8::internal::Thread(),
358         semaphore_(v8::internal::OS::CreateSemaphore(0)),
359         run_(true) {
360   }
361 
~LoopingThread()362   virtual ~LoopingThread() { delete semaphore_; }
363 
Run()364   void Run() {
365     self_ = pthread_self();
366     RunLoop();
367   }
368 
SendSigProf()369   void SendSigProf() { pthread_kill(self_, SIGPROF); }
370 
Stop()371   void Stop() { run_ = false; }
372 
WaitForRunning()373   bool WaitForRunning() { return semaphore_->Wait(1000000); }
374 
375  protected:
IsRunning()376   bool IsRunning() { return run_; }
377 
378   virtual void RunLoop() = 0;
379 
SetV8ThreadId()380   void SetV8ThreadId() {
381     v8_thread_id_ = v8::V8::GetCurrentThreadId();
382   }
383 
SignalRunning()384   void SignalRunning() { semaphore_->Signal(); }
385 
386  private:
387   v8::internal::Semaphore* semaphore_;
388   bool run_;
389   pthread_t self_;
390   int v8_thread_id_;
391 };
392 
393 
394 class LoopingJsThread : public LoopingThread {
395  public:
RunLoop()396   void RunLoop() {
397     {
398       v8::Locker locker;
399       CHECK(v8::internal::ThreadManager::HasId());
400       SetV8ThreadId();
401     }
402     while (IsRunning()) {
403       v8::Locker locker;
404       v8::HandleScope scope;
405       v8::Persistent<v8::Context> context = v8::Context::New();
406       v8::Context::Scope context_scope(context);
407       SignalRunning();
408       CompileAndRunScript(
409           "var j; for (var i=0; i<10000; ++i) { j = Math.sin(i); }");
410       context.Dispose();
411       i::OS::Sleep(1);
412     }
413   }
414 };
415 
416 
417 class LoopingNonJsThread : public LoopingThread {
418  public:
RunLoop()419   void RunLoop() {
420     v8::Locker locker;
421     v8::Unlocker unlocker;
422     // Now thread has V8's id, but will not run VM code.
423     CHECK(v8::internal::ThreadManager::HasId());
424     double i = 10;
425     SignalRunning();
426     while (IsRunning()) {
427       i = sin(i);
428       i::OS::Sleep(1);
429     }
430   }
431 };
432 
433 
434 class TestSampler : public v8::internal::Sampler {
435  public:
TestSampler()436   TestSampler()
437       : Sampler(0, true),
438         semaphore_(v8::internal::OS::CreateSemaphore(0)),
439         was_sample_stack_called_(false) {
440   }
441 
~TestSampler()442   ~TestSampler() { delete semaphore_; }
443 
SampleStack(v8::internal::TickSample *)444   void SampleStack(v8::internal::TickSample*) {
445     was_sample_stack_called_ = true;
446   }
447 
Tick(v8::internal::TickSample *)448   void Tick(v8::internal::TickSample*) { semaphore_->Signal(); }
449 
WaitForTick()450   bool WaitForTick() { return semaphore_->Wait(1000000); }
451 
Reset()452   void Reset() { was_sample_stack_called_ = false; }
453 
WasSampleStackCalled()454   bool WasSampleStackCalled() { return was_sample_stack_called_; }
455 
456  private:
457   v8::internal::Semaphore* semaphore_;
458   bool was_sample_stack_called_;
459 };
460 
461 
462 }  // namespace
463 
TEST(ProfMultipleThreads)464 TEST(ProfMultipleThreads) {
465   LoopingJsThread jsThread;
466   jsThread.Start();
467   LoopingNonJsThread nonJsThread;
468   nonJsThread.Start();
469 
470   TestSampler sampler;
471   sampler.Start();
472   CHECK(!sampler.WasSampleStackCalled());
473   jsThread.WaitForRunning();
474   jsThread.SendSigProf();
475   CHECK(sampler.WaitForTick());
476   CHECK(sampler.WasSampleStackCalled());
477   sampler.Reset();
478   CHECK(!sampler.WasSampleStackCalled());
479   nonJsThread.WaitForRunning();
480   nonJsThread.SendSigProf();
481   CHECK(sampler.WaitForTick());
482   CHECK(!sampler.WasSampleStackCalled());
483   sampler.Stop();
484 
485   jsThread.Stop();
486   nonJsThread.Stop();
487   jsThread.Join();
488   nonJsThread.Join();
489 }
490 
491 #endif  // __linux__
492 
493 
494 // Test for issue http://crbug.com/23768 in Chromium.
495 // Heap can contain scripts with already disposed external sources.
496 // We need to verify that LogCompiledFunctions doesn't crash on them.
497 namespace {
498 
499 class SimpleExternalString : public v8::String::ExternalStringResource {
500  public:
SimpleExternalString(const char * source)501   explicit SimpleExternalString(const char* source)
502       : utf_source_(StrLength(source)) {
503     for (int i = 0; i < utf_source_.length(); ++i)
504       utf_source_[i] = source[i];
505   }
~SimpleExternalString()506   virtual ~SimpleExternalString() {}
length() const507   virtual size_t length() const { return utf_source_.length(); }
data() const508   virtual const uint16_t* data() const { return utf_source_.start(); }
509  private:
510   i::ScopedVector<uint16_t> utf_source_;
511 };
512 
513 }  // namespace
514 
TEST(Issue23768)515 TEST(Issue23768) {
516   v8::HandleScope scope;
517   v8::Handle<v8::Context> env = v8::Context::New();
518   env->Enter();
519 
520   SimpleExternalString source_ext_str("(function ext() {})();");
521   v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str);
522   // Script needs to have a name in order to trigger InitLineEnds execution.
523   v8::Handle<v8::String> origin = v8::String::New("issue-23768-test");
524   v8::Handle<v8::Script> evil_script = v8::Script::Compile(source, origin);
525   CHECK(!evil_script.IsEmpty());
526   CHECK(!evil_script->Run().IsEmpty());
527   i::Handle<i::ExternalTwoByteString> i_source(
528       i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source)));
529   // This situation can happen if source was an external string disposed
530   // by its owner.
531   i_source->set_resource(NULL);
532 
533   // Must not crash.
534   i::Logger::LogCompiledFunctions();
535 }
536 
537 
ObjMethod1(const v8::Arguments & args)538 static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) {
539   return v8::Handle<v8::Value>();
540 }
541 
TEST(LogCallbacks)542 TEST(LogCallbacks) {
543   ScopedLoggerInitializer initialize_logger(false, false);
544   LogBufferMatcher matcher;
545 
546   v8::Persistent<v8::FunctionTemplate> obj =
547       v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
548   obj->SetClassName(v8::String::New("Obj"));
549   v8::Handle<v8::ObjectTemplate> proto = obj->PrototypeTemplate();
550   v8::Local<v8::Signature> signature = v8::Signature::New(obj);
551   proto->Set(v8::String::New("method1"),
552              v8::FunctionTemplate::New(ObjMethod1,
553                                        v8::Handle<v8::Value>(),
554                                        signature),
555              static_cast<v8::PropertyAttribute>(v8::DontDelete));
556 
557   initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction());
558   CompileAndRunScript("Obj.prototype.method1.toString();");
559 
560   i::Logger::LogCompiledFunctions();
561   CHECK_GT(matcher.GetNextChunk(), 0);
562 
563   const char* callback_rec = "code-creation,Callback,";
564   char* pos = const_cast<char*>(matcher.Find(callback_rec));
565   CHECK_NE(NULL, pos);
566   pos += strlen(callback_rec);
567   EmbeddedVector<char, 100> ref_data;
568   i::OS::SNPrintF(ref_data,
569                   "0x%" V8PRIxPTR ",1,\"method1\"", ObjMethod1);
570   *(pos + strlen(ref_data.start())) = '\0';
571   CHECK_EQ(ref_data.start(), pos);
572 
573   obj.Dispose();
574 }
575 
576 
Prop1Getter(v8::Local<v8::String> property,const v8::AccessorInfo & info)577 static v8::Handle<v8::Value> Prop1Getter(v8::Local<v8::String> property,
578                                          const v8::AccessorInfo& info) {
579   return v8::Handle<v8::Value>();
580 }
581 
Prop1Setter(v8::Local<v8::String> property,v8::Local<v8::Value> value,const v8::AccessorInfo & info)582 static void Prop1Setter(v8::Local<v8::String> property,
583                                          v8::Local<v8::Value> value,
584                                          const v8::AccessorInfo& info) {
585 }
586 
Prop2Getter(v8::Local<v8::String> property,const v8::AccessorInfo & info)587 static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property,
588                                          const v8::AccessorInfo& info) {
589   return v8::Handle<v8::Value>();
590 }
591 
TEST(LogAccessorCallbacks)592 TEST(LogAccessorCallbacks) {
593   ScopedLoggerInitializer initialize_logger(false, false);
594   LogBufferMatcher matcher;
595 
596   v8::Persistent<v8::FunctionTemplate> obj =
597       v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
598   obj->SetClassName(v8::String::New("Obj"));
599   v8::Handle<v8::ObjectTemplate> inst = obj->InstanceTemplate();
600   inst->SetAccessor(v8::String::New("prop1"), Prop1Getter, Prop1Setter);
601   inst->SetAccessor(v8::String::New("prop2"), Prop2Getter);
602 
603   i::Logger::LogAccessorCallbacks();
604   CHECK_GT(matcher.GetNextChunk(), 0);
605   matcher.PrintBuffer();
606 
607   EmbeddedVector<char, 100> prop1_getter_record;
608   i::OS::SNPrintF(prop1_getter_record,
609                   "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"",
610                   Prop1Getter);
611   CHECK_NE(NULL, matcher.Find(prop1_getter_record));
612   EmbeddedVector<char, 100> prop1_setter_record;
613   i::OS::SNPrintF(prop1_setter_record,
614                   "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"",
615                   Prop1Setter);
616   CHECK_NE(NULL, matcher.Find(prop1_setter_record));
617   EmbeddedVector<char, 100> prop2_getter_record;
618   i::OS::SNPrintF(prop2_getter_record,
619                   "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"",
620                   Prop2Getter);
621   CHECK_NE(NULL, matcher.Find(prop2_getter_record));
622 
623   obj.Dispose();
624 }
625 
626 
TEST(LogTags)627 TEST(LogTags) {
628   ScopedLoggerInitializer initialize_logger(true, false);
629   LogBufferMatcher matcher;
630 
631   const char* open_tag = "open-tag,";
632   const char* close_tag = "close-tag,";
633 
634   // Check compatibility with the old style behavior.
635   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
636   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
637   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
638   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
639   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
640   CHECK_EQ(NULL, matcher.Find(open_tag));
641   CHECK_EQ(NULL, matcher.Find(close_tag));
642 
643   const char* open_tag1 = "open-tag,1\n";
644   const char* close_tag1 = "close-tag,1\n";
645 
646   // Check non-nested tag case.
647   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
648   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
649   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
650   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
651   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
652   CHECK_GT(matcher.GetNextChunk(), 0);
653   CHECK(matcher.IsInSequence(open_tag1, close_tag1));
654 
655   const char* open_tag2 = "open-tag,2\n";
656   const char* close_tag2 = "close-tag,2\n";
657 
658   // Check nested tags case.
659   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
660   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
661   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
662   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
663   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
664   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
665   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
666   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
667   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
668   CHECK_GT(matcher.GetNextChunk(), 0);
669   // open_tag1 < open_tag2 < close_tag2 < close_tag1
670   CHECK(matcher.IsInSequence(open_tag1, open_tag2));
671   CHECK(matcher.IsInSequence(open_tag2, close_tag2));
672   CHECK(matcher.IsInSequence(close_tag2, close_tag1));
673 
674   // Check overlapped tags case.
675   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
676   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
677   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
678   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
679   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
680   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
681   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
682   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
683   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
684   CHECK_GT(matcher.GetNextChunk(), 0);
685   // open_tag1 < open_tag2 < close_tag1 < close_tag2
686   CHECK(matcher.IsInSequence(open_tag1, open_tag2));
687   CHECK(matcher.IsInSequence(open_tag2, close_tag1));
688   CHECK(matcher.IsInSequence(close_tag1, close_tag2));
689 
690   const char* open_tag3 = "open-tag,3\n";
691   const char* close_tag3 = "close-tag,3\n";
692 
693   // Check pausing overflow case.
694   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
695   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
696   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
697   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
698   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
699   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
700   CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
701   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
702   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
703   Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 3);
704   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
705   Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 3);
706   CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
707   // Must be no tags, because logging must be disabled.
708   CHECK_EQ(NULL, matcher.Find(open_tag3));
709   CHECK_EQ(NULL, matcher.Find(close_tag3));
710 }
711 
712 
IsStringEqualTo(const char * r,const char * s)713 static inline bool IsStringEqualTo(const char* r, const char* s) {
714   return strncmp(r, s, strlen(r)) == 0;
715 }
716 
717 
Consume(const char * str,char ** buf)718 static bool Consume(const char* str, char** buf) {
719   if (IsStringEqualTo(str, *buf)) {
720     *buf += strlen(str);
721     return true;
722   }
723   return false;
724 }
725 
726 
727 namespace {
728 
729 // A code entity is a pointer to a position of code-creation event in buffer log
730 // offset to a point where entity size begins, i.e.: '255,"func"\n'. This makes
731 // comparing code entities pretty easy.
732 typedef char* CodeEntityInfo;
733 
734 class Interval {
735  public:
Interval()736   Interval()
737       : min_addr_(reinterpret_cast<Address>(-1)),
738         max_addr_(reinterpret_cast<Address>(0)), next_(NULL) {}
739 
~Interval()740   ~Interval() { delete next_; }
741 
Length()742   size_t Length() {
743     size_t result = max_addr_ - min_addr_ + 1;
744     if (next_ != NULL) result += next_->Length();
745     return result;
746   }
747 
CloneFrom(Interval * src)748   void CloneFrom(Interval* src) {
749     while (src != NULL) {
750       RegisterAddress(src->min_addr_);
751       RegisterAddress(src->max_addr_);
752       src = src->next_;
753     }
754   }
755 
Contains(Address addr)756   bool Contains(Address addr) {
757     if (min_addr_ <= addr && addr <= max_addr_) {
758       return true;
759     }
760     if (next_ != NULL) {
761       return next_->Contains(addr);
762     } else {
763       return false;
764     }
765   }
766 
GetIndex(Address addr)767   size_t GetIndex(Address addr) {
768     if (min_addr_ <= addr && addr <= max_addr_) {
769       return addr - min_addr_;
770     }
771     CHECK_NE(NULL, next_);
772     return (max_addr_ - min_addr_ + 1) + next_->GetIndex(addr);
773   }
774 
GetMinAddr()775   Address GetMinAddr() {
776     return next_ == NULL ? min_addr_ : i::Min(min_addr_, next_->GetMinAddr());
777   }
778 
GetMaxAddr()779   Address GetMaxAddr() {
780     return next_ == NULL ? max_addr_ : i::Max(max_addr_, next_->GetMaxAddr());
781   }
782 
RegisterAddress(Address addr)783   void RegisterAddress(Address addr) {
784     if (min_addr_ == reinterpret_cast<Address>(-1)
785         || (size_t)(addr > min_addr_ ?
786            addr - min_addr_ : min_addr_ - addr) < MAX_DELTA) {
787       if (addr < min_addr_) min_addr_ = addr;
788       if (addr > max_addr_) max_addr_ = addr;
789     } else {
790       if (next_ == NULL) next_ = new Interval();
791       next_->RegisterAddress(addr);
792     }
793   }
794 
raw_min_addr()795   Address raw_min_addr() { return min_addr_; }
796 
raw_max_addr()797   Address raw_max_addr() { return max_addr_; }
798 
get_next()799   Interval* get_next() { return next_; }
800 
801  private:
802   static const size_t MAX_DELTA = 0x100000;
803   Address min_addr_;
804   Address max_addr_;
805   Interval* next_;
806 };
807 
808 
809 // A structure used to return log parsing results.
810 class ParseLogResult {
811  public:
ParseLogResult()812   ParseLogResult()
813       : entities_map(NULL), entities(NULL),
814         max_entities(0) {}
815 
~ParseLogResult()816   ~ParseLogResult() {
817     i::DeleteArray(entities_map);
818     i::DeleteArray(entities);
819   }
820 
AllocateEntities()821   void AllocateEntities() {
822     // Make sure that the test doesn't operate on a bogus log.
823     CHECK_GT(max_entities, 0);
824     CHECK_GT(bounds.GetMinAddr(), 0);
825     CHECK_GT(bounds.GetMaxAddr(), bounds.GetMinAddr());
826 
827     entities = i::NewArray<CodeEntityInfo>(max_entities);
828     for (int i = 0; i < max_entities; ++i) {
829       entities[i] = NULL;
830     }
831     const size_t map_length = bounds.Length();
832     entities_map = i::NewArray<int>(static_cast<int>(map_length));
833     for (size_t i = 0; i < map_length; ++i) {
834       entities_map[i] = -1;
835     }
836   }
837 
HasIndexForAddress(Address addr)838   bool HasIndexForAddress(Address addr) {
839     return bounds.Contains(addr);
840   }
841 
GetIndexForAddress(Address addr)842   size_t GetIndexForAddress(Address addr) {
843     CHECK(HasIndexForAddress(addr));
844     return bounds.GetIndex(addr);
845   }
846 
GetEntity(Address addr)847   CodeEntityInfo GetEntity(Address addr) {
848     if (HasIndexForAddress(addr)) {
849       size_t idx = GetIndexForAddress(addr);
850       int item = entities_map[idx];
851       return item != -1 ? entities[item] : NULL;
852     }
853     return NULL;
854   }
855 
ParseAddress(char * start)856   void ParseAddress(char* start) {
857     Address addr =
858         reinterpret_cast<Address>(strtoul(start, NULL, 16));  // NOLINT
859     bounds.RegisterAddress(addr);
860   }
861 
ConsumeAddress(char ** start)862   Address ConsumeAddress(char** start) {
863     char* end_ptr;
864     Address addr =
865         reinterpret_cast<Address>(strtoul(*start, &end_ptr, 16));  // NOLINT
866     CHECK(HasIndexForAddress(addr));
867     *start = end_ptr;
868     return addr;
869   }
870 
871   Interval bounds;
872   // Memory map of entities start addresses.
873   int* entities_map;
874   // An array of code entities.
875   CodeEntityInfo* entities;
876   // Maximal entities count. Actual entities count can be lower,
877   // empty entity slots are pointing to NULL.
878   int max_entities;
879 };
880 
881 }  // namespace
882 
883 
884 typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* result);
885 
ParserCycle(char * start,char * end,ParseLogResult * result,ParserBlock block_creation,ParserBlock block_delete,ParserBlock block_move)886 static void ParserCycle(
887     char* start, char* end, ParseLogResult* result,
888     ParserBlock block_creation, ParserBlock block_delete,
889     ParserBlock block_move) {
890 
891   const char* code_creation = "code-creation,";
892   const char* code_delete = "code-delete,";
893   const char* code_move = "code-move,";
894 
895   const char* lazy_compile = "LazyCompile,";
896   const char* script = "Script,";
897   const char* function = "Function,";
898 
899   while (start < end) {
900     if (Consume(code_creation, &start)) {
901       if (Consume(lazy_compile, &start)
902           || Consume(script, &start)
903           || Consume(function, &start)) {
904         block_creation(start, end, result);
905       }
906     } else if (Consume(code_delete, &start)) {
907       block_delete(start, end, result);
908     } else if (Consume(code_move, &start)) {
909       block_move(start, end, result);
910     }
911     while (start < end && *start != '\n') ++start;
912     ++start;
913   }
914 }
915 
916 
Pass1CodeCreation(char * start,char * end,ParseLogResult * result)917 static void Pass1CodeCreation(char* start, char* end, ParseLogResult* result) {
918   result->ParseAddress(start);
919   ++result->max_entities;
920 }
921 
922 
Pass1CodeDelete(char * start,char * end,ParseLogResult * result)923 static void Pass1CodeDelete(char* start, char* end, ParseLogResult* result) {
924   result->ParseAddress(start);
925 }
926 
927 
Pass1CodeMove(char * start,char * end,ParseLogResult * result)928 static void Pass1CodeMove(char* start, char* end, ParseLogResult* result) {
929   result->ParseAddress(start);
930   // Skip old address.
931   while (start < end && *start != ',') ++start;
932   CHECK_GT(end, start);
933   ++start;  // Skip ','.
934   result->ParseAddress(start);
935 }
936 
937 
Pass2CodeCreation(char * start,char * end,ParseLogResult * result)938 static void Pass2CodeCreation(char* start, char* end, ParseLogResult* result) {
939   Address addr = result->ConsumeAddress(&start);
940   CHECK_GT(end, start);
941   ++start;  // Skip ','.
942 
943   size_t idx = result->GetIndexForAddress(addr);
944   result->entities_map[idx] = -1;
945   for (int i = 0; i < result->max_entities; ++i) {
946     // Find an empty slot and fill it.
947     if (result->entities[i] == NULL) {
948       result->entities[i] = start;
949       result->entities_map[idx] = i;
950       break;
951     }
952   }
953   // Make sure that a slot was found.
954   CHECK_GE(result->entities_map[idx], 0);
955 }
956 
957 
Pass2CodeDelete(char * start,char * end,ParseLogResult * result)958 static void Pass2CodeDelete(char* start, char* end, ParseLogResult* result) {
959   Address addr = result->ConsumeAddress(&start);
960   size_t idx = result->GetIndexForAddress(addr);
961   // There can be code deletes that are not related to JS code.
962   if (result->entities_map[idx] >= 0) {
963     result->entities[result->entities_map[idx]] = NULL;
964     result->entities_map[idx] = -1;
965   }
966 }
967 
968 
Pass2CodeMove(char * start,char * end,ParseLogResult * result)969 static void Pass2CodeMove(char* start, char* end, ParseLogResult* result) {
970   Address from_addr = result->ConsumeAddress(&start);
971   CHECK_GT(end, start);
972   ++start;  // Skip ','.
973   Address to_addr = result->ConsumeAddress(&start);
974   CHECK_GT(end, start);
975 
976   size_t from_idx = result->GetIndexForAddress(from_addr);
977   size_t to_idx = result->GetIndexForAddress(to_addr);
978   // There can be code moves that are not related to JS code.
979   if (from_idx != to_idx && result->entities_map[from_idx] >= 0) {
980     CHECK_EQ(-1, result->entities_map[to_idx]);
981     result->entities_map[to_idx] = result->entities_map[from_idx];
982     result->entities_map[from_idx] = -1;
983   };
984 }
985 
986 
ParseLog(char * start,char * end,ParseLogResult * result)987 static void ParseLog(char* start, char* end, ParseLogResult* result) {
988   // Pass 1: Calculate boundaries of addresses and entities count.
989   ParserCycle(start, end, result,
990               Pass1CodeCreation, Pass1CodeDelete, Pass1CodeMove);
991 
992   printf("min_addr: %p, max_addr: %p, entities: %d\n",
993          result->bounds.GetMinAddr(), result->bounds.GetMaxAddr(),
994          result->max_entities);
995 
996   result->AllocateEntities();
997 
998   // Pass 2: Fill in code entries data.
999   ParserCycle(start, end, result,
1000               Pass2CodeCreation, Pass2CodeDelete, Pass2CodeMove);
1001 }
1002 
1003 
PrintCodeEntityInfo(CodeEntityInfo entity)1004 static inline void PrintCodeEntityInfo(CodeEntityInfo entity) {
1005   const int max_len = 50;
1006   if (entity != NULL) {
1007     char* eol = strchr(entity, '\n');
1008     int len = static_cast<int>(eol - entity);
1009     len = len <= max_len ? len : max_len;
1010     printf("%-*.*s ", max_len, len, entity);
1011   } else {
1012     printf("%*s", max_len + 1, "");
1013   }
1014 }
1015 
1016 
PrintCodeEntitiesInfo(bool is_equal,Address addr,CodeEntityInfo l_entity,CodeEntityInfo r_entity)1017 static void PrintCodeEntitiesInfo(
1018     bool is_equal, Address addr,
1019     CodeEntityInfo l_entity, CodeEntityInfo r_entity) {
1020   printf("%c %p ", is_equal ? ' ' : '*', addr);
1021   PrintCodeEntityInfo(l_entity);
1022   PrintCodeEntityInfo(r_entity);
1023   printf("\n");
1024 }
1025 
1026 
StrChrLen(const char * s,char c)1027 static inline int StrChrLen(const char* s, char c) {
1028   return static_cast<int>(strchr(s, c) - s);
1029 }
1030 
1031 
AreFuncSizesEqual(CodeEntityInfo ref_s,CodeEntityInfo new_s)1032 static bool AreFuncSizesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
1033   int ref_len = StrChrLen(ref_s, ',');
1034   int new_len = StrChrLen(new_s, ',');
1035   return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
1036 }
1037 
1038 
AreFuncNamesEqual(CodeEntityInfo ref_s,CodeEntityInfo new_s)1039 static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
1040   // Skip size.
1041   ref_s = strchr(ref_s, ',') + 1;
1042   new_s = strchr(new_s, ',') + 1;
1043   int ref_len = StrChrLen(ref_s, '\n');
1044   int new_len = StrChrLen(new_s, '\n');
1045   // If reference is anonymous (""), it's OK to have anything in new.
1046   if (ref_len == 2) return true;
1047   // A special case for ErrorPrototype. Haven't yet figured out why they
1048   // are different.
1049   const char* error_prototype = "\"ErrorPrototype";
1050   if (IsStringEqualTo(error_prototype, ref_s)
1051       && IsStringEqualTo(error_prototype, new_s)) {
1052     return true;
1053   }
1054   // Built-in objects have problems too.
1055   const char* built_ins[] = {
1056       "\"Boolean\"", "\"Function\"", "\"Number\"",
1057       "\"Object\"", "\"Script\"", "\"String\""
1058   };
1059   for (size_t i = 0; i < sizeof(built_ins) / sizeof(*built_ins); ++i) {
1060     if (IsStringEqualTo(built_ins[i], new_s)) {
1061       return true;
1062     }
1063   }
1064   return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
1065 }
1066 
1067 
AreEntitiesEqual(CodeEntityInfo ref_e,CodeEntityInfo new_e)1068 static bool AreEntitiesEqual(CodeEntityInfo ref_e, CodeEntityInfo new_e) {
1069   if (ref_e == NULL && new_e != NULL) return true;
1070   if (ref_e != NULL && new_e != NULL) {
1071     return AreFuncSizesEqual(ref_e, new_e) && AreFuncNamesEqual(ref_e, new_e);
1072   }
1073   if (ref_e != NULL && new_e == NULL) {
1074     // args_count entities (argument adapters) are not found by heap traversal,
1075     // but they are not needed because they doesn't contain any code.
1076     ref_e = strchr(ref_e, ',') + 1;
1077     const char* args_count = "\"args_count:";
1078     return IsStringEqualTo(args_count, ref_e);
1079   }
1080   return false;
1081 }
1082 
1083 
1084 // Test that logging of code create / move / delete events
1085 // is equivalent to traversal of a resulting heap.
TEST(EquivalenceOfLoggingAndTraversal)1086 TEST(EquivalenceOfLoggingAndTraversal) {
1087   // This test needs to be run on a "clean" V8 to ensure that snapshot log
1088   // is loaded. This is always true when running using tools/test.py because
1089   // it launches a new cctest instance for every test. To be sure that launching
1090   // cctest manually also works, please be sure that no tests below
1091   // are using V8.
1092   //
1093   // P.S. No, V8 can't be re-initialized after disposal, see include/v8.h.
1094   CHECK(!i::V8::IsRunning());
1095 
1096   i::FLAG_logfile = "*";
1097   i::FLAG_log = true;
1098   i::FLAG_log_code = true;
1099 
1100   // Make sure objects move.
1101   bool saved_always_compact = i::FLAG_always_compact;
1102   if (!i::FLAG_never_compact) {
1103     i::FLAG_always_compact = true;
1104   }
1105 
1106   v8::HandleScope scope;
1107   v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>();
1108   v8::Handle<v8::Context> env = v8::Context::New(
1109       0, v8::Handle<v8::ObjectTemplate>(), global_object);
1110   env->Enter();
1111 
1112   // Compile and run a function that creates other functions.
1113   CompileAndRunScript(
1114       "(function f(obj) {\n"
1115       "  obj.test =\n"
1116       "    (function a(j) { return function b() { return j; } })(100);\n"
1117       "})(this);");
1118   i::Heap::CollectAllGarbage(false);
1119 
1120   EmbeddedVector<char, 204800> buffer;
1121   int log_size;
1122   ParseLogResult ref_result;
1123 
1124   // Retrieve the log.
1125   {
1126     // Make sure that no GCs occur prior to LogCompiledFunctions call.
1127     i::AssertNoAllocation no_alloc;
1128 
1129     log_size = GetLogLines(0, &buffer);
1130     CHECK_GT(log_size, 0);
1131     CHECK_GT(buffer.length(), log_size);
1132 
1133     // Fill a map of compiled code objects.
1134     ParseLog(buffer.start(), buffer.start() + log_size, &ref_result);
1135   }
1136 
1137   // Iterate heap to find compiled functions, will write to log.
1138   i::Logger::LogCompiledFunctions();
1139   char* new_log_start = buffer.start() + log_size;
1140   const int new_log_size = Logger::GetLogLines(
1141       log_size, new_log_start, buffer.length() - log_size);
1142   CHECK_GT(new_log_size, 0);
1143   CHECK_GT(buffer.length(), log_size + new_log_size);
1144 
1145   // Fill an equivalent map of compiled code objects.
1146   ParseLogResult new_result;
1147   ParseLog(new_log_start, new_log_start + new_log_size, &new_result);
1148 
1149   // Test their actual equivalence.
1150   Interval combined;
1151   combined.CloneFrom(&ref_result.bounds);
1152   combined.CloneFrom(&new_result.bounds);
1153   Interval* iter = &combined;
1154   bool results_equal = true;
1155 
1156   while (iter != NULL) {
1157     for (Address addr = iter->raw_min_addr();
1158          addr <= iter->raw_max_addr(); ++addr) {
1159       CodeEntityInfo ref_entity = ref_result.GetEntity(addr);
1160       CodeEntityInfo new_entity = new_result.GetEntity(addr);
1161       if (ref_entity != NULL || new_entity != NULL) {
1162         const bool equal = AreEntitiesEqual(ref_entity, new_entity);
1163         if (!equal) results_equal = false;
1164         PrintCodeEntitiesInfo(equal, addr, ref_entity, new_entity);
1165       }
1166     }
1167     iter = iter->get_next();
1168   }
1169   // Make sure that all log data is written prior crash due to CHECK failure.
1170   fflush(stdout);
1171   CHECK(results_equal);
1172 
1173   env->Exit();
1174   Logger::TearDown();
1175   i::FLAG_always_compact = saved_always_compact;
1176 }
1177 
1178 #endif  // ENABLE_LOGGING_AND_PROFILING
1179