blob: 9853af32444fa4c40365b4c7110ced67dc3bcd46 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// 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
19using v8::internal::Address;
20using v8::internal::EmbeddedVector;
21using v8::internal::Logger;
Steve Blockd0582a62009-12-15 09:54:21 +000022using v8::internal::StrLength;
Steve Blocka7e24c12009-10-30 11:49:00 +000023
24namespace i = v8::internal;
25
26static void SetUp() {
27 // Log to memory buffer.
28 i::FLAG_logfile = "*";
29 i::FLAG_log = true;
30 Logger::Setup();
31}
32
33static void TearDown() {
34 Logger::TearDown();
35}
36
37
38TEST(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
48TEST(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";
Steve Blockd0582a62009-12-15 09:54:21 +000059 const int line_1_len = StrLength(line_1);
Steve Blocka7e24c12009-10-30 11:49:00 +000060 // 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";
Steve Blockd0582a62009-12-15 09:54:21 +000072 const int line_2_len = StrLength(line_2);
Steve Blocka7e24c12009-10-30 11:49:00 +000073 // 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";
Steve Blockd0582a62009-12-15 09:54:21 +000086 const int all_lines_len = StrLength(all_lines);
Steve Blocka7e24c12009-10-30 11:49:00 +000087 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
97static int GetLogLines(int start_pos, i::Vector<char>* buffer) {
98 return Logger::GetLogLines(start_pos, buffer->start(), buffer->length());
99}
100
101
102TEST(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";
Steve Blockd0582a62009-12-15 09:54:21 +0000108 const int all_lines_len = StrLength(all_lines);
Steve Blocka7e24c12009-10-30 11:49:00 +0000109 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
123TEST(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
136static void CompileAndRunScript(const char *src) {
137 v8::Script::Compile(v8::String::New(src))->Run();
138}
139
140
141namespace v8 {
142namespace internal {
143
144class LoggerTestHelper : public AllStatic {
145 public:
146 static bool IsSamplerActive() { return Logger::IsProfilerSamplerActive(); }
147};
148
149} // namespace v8::internal
150} // namespace v8
151
152using 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.
158static bool was_sigprof_received = true;
159#ifdef __linux__
160
161struct sigaction old_sigprof_handler;
162pthread_t our_thread;
163
164static 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
Andrei Popescu402d9372010-02-26 13:31:12 +0000173namespace {
174
175class ScopedLoggerInitializer {
176 public:
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
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
199 v8::Handle<v8::Context>& env() { return env_; }
200
201 private:
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
224class LogBufferMatcher {
225 public:
226 LogBufferMatcher() {
227 // Skip all initially logged stuff.
228 log_pos_ = GetLogLines(0, &buffer_);
229 }
230
231 int log_pos() { return log_pos_; }
232
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
241 const char* Find(const char* substr) {
242 return strstr(buffer_.start(), substr);
243 }
244
245 const char* Find(const i::Vector<char>& substr) {
246 return Find(substr.start());
247 }
248
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
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
269static void CheckThatProfilerWorks(LogBufferMatcher* matcher) {
270 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000271 CHECK(LoggerTestHelper::IsSamplerActive());
272
273 // Verify that the current map of compiled functions has been logged.
Andrei Popescu402d9372010-02-26 13:31:12 +0000274 CHECK_GT(matcher->GetNextChunk(), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000275 const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/
Andrei Popescu402d9372010-02-26 13:31:12 +0000276 CHECK_NE(NULL, matcher->Find(code_creation));
Steve Blocka7e24c12009-10-30 11:49:00 +0000277
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,
Leon Clarked91b9f72010-01-27 17:25:45 +0000294 "function f%d(x) { return %d * x; }"
295 "for (var i = 0; i < 10000; ++i) { f%d(i); }",
Andrei Popescu402d9372010-02-26 13:31:12 +0000296 matcher->log_pos(), matcher->log_pos(), matcher->log_pos());
Steve Blocka7e24c12009-10-30 11:49:00 +0000297 // 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
Andrei Popescu402d9372010-02-26 13:31:12 +0000305 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000306 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.
Andrei Popescu402d9372010-02-26 13:31:12 +0000313 CHECK_GT(matcher->GetNextChunk(), 0);
314 matcher->PrintBuffer();
315 CHECK_NE(NULL, matcher->Find(code_creation));
Steve Blocka7e24c12009-10-30 11:49:00 +0000316 const char* tick = "\ntick,";
Andrei Popescu402d9372010-02-26 13:31:12 +0000317 const bool ticks_found = matcher->Find(tick) != NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000318 CHECK_EQ(was_sigprof_received, ticks_found);
Steve Blocka7e24c12009-10-30 11:49:00 +0000319}
320
321
322TEST(ProfLazyMode) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000323 ScopedLoggerInitializer initialize_logger(false, true);
Steve Blocka7e24c12009-10-30 11:49:00 +0000324
325 // No sampling should happen prior to resuming profiler.
326 CHECK(!LoggerTestHelper::IsSamplerActive());
327
Andrei Popescu402d9372010-02-26 13:31:12 +0000328 LogBufferMatcher matcher;
Steve Blockd0582a62009-12-15 09:54:21 +0000329 // Nothing must be logged until profiling is resumed.
Andrei Popescu402d9372010-02-26 13:31:12 +0000330 CHECK_EQ(0, matcher.log_pos());
Steve Blocka7e24c12009-10-30 11:49:00 +0000331
332 CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
333
334 // Nothing must be logged while profiling is suspended.
Andrei Popescu402d9372010-02-26 13:31:12 +0000335 CHECK_EQ(0, matcher.GetNextChunk());
Steve Blocka7e24c12009-10-30 11:49:00 +0000336
Andrei Popescu402d9372010-02-26 13:31:12 +0000337 CheckThatProfilerWorks(&matcher);
Steve Blocka7e24c12009-10-30 11:49:00 +0000338
339 CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
340
341 // No new data beyond last retrieved position.
Andrei Popescu402d9372010-02-26 13:31:12 +0000342 CHECK_EQ(0, matcher.GetNextChunk());
Steve Blocka7e24c12009-10-30 11:49:00 +0000343
344 // Check that profiling can be resumed again.
Andrei Popescu402d9372010-02-26 13:31:12 +0000345 CheckThatProfilerWorks(&matcher);
Steve Blocka7e24c12009-10-30 11:49:00 +0000346}
347
348
349// Profiling multiple threads that use V8 is currently only available on Linux.
350#ifdef __linux__
351
352namespace {
353
354class LoopingThread : public v8::internal::Thread {
355 public:
356 LoopingThread()
357 : v8::internal::Thread(),
358 semaphore_(v8::internal::OS::CreateSemaphore(0)),
359 run_(true) {
360 }
361
362 virtual ~LoopingThread() { delete semaphore_; }
363
364 void Run() {
365 self_ = pthread_self();
366 RunLoop();
367 }
368
369 void SendSigProf() { pthread_kill(self_, SIGPROF); }
370
371 void Stop() { run_ = false; }
372
373 bool WaitForRunning() { return semaphore_->Wait(1000000); }
374
375 protected:
376 bool IsRunning() { return run_; }
377
378 virtual void RunLoop() = 0;
379
380 void SetV8ThreadId() {
381 v8_thread_id_ = v8::V8::GetCurrentThreadId();
382 }
383
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
394class LoopingJsThread : public LoopingThread {
395 public:
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
417class LoopingNonJsThread : public LoopingThread {
418 public:
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
434class TestSampler : public v8::internal::Sampler {
435 public:
436 TestSampler()
437 : Sampler(0, true),
438 semaphore_(v8::internal::OS::CreateSemaphore(0)),
439 was_sample_stack_called_(false) {
440 }
441
442 ~TestSampler() { delete semaphore_; }
443
444 void SampleStack(v8::internal::TickSample*) {
445 was_sample_stack_called_ = true;
446 }
447
448 void Tick(v8::internal::TickSample*) { semaphore_->Signal(); }
449
450 bool WaitForTick() { return semaphore_->Wait(1000000); }
451
452 void Reset() { was_sample_stack_called_ = false; }
453
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
464TEST(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
Steve Block3ce2e202009-11-05 08:53:23 +0000494// 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.
497namespace {
498
499class SimpleExternalString : public v8::String::ExternalStringResource {
500 public:
501 explicit SimpleExternalString(const char* source)
Steve Blockd0582a62009-12-15 09:54:21 +0000502 : utf_source_(StrLength(source)) {
Steve Block3ce2e202009-11-05 08:53:23 +0000503 for (int i = 0; i < utf_source_.length(); ++i)
504 utf_source_[i] = source[i];
505 }
506 virtual ~SimpleExternalString() {}
507 virtual size_t length() const { return utf_source_.length(); }
508 virtual const uint16_t* data() const { return utf_source_.start(); }
509 private:
510 i::ScopedVector<uint16_t> utf_source_;
511};
512
513} // namespace
514
515TEST(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
Steve Blockd0582a62009-12-15 09:54:21 +0000538static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) {
539 return v8::Handle<v8::Value>();
540}
541
542TEST(LogCallbacks) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000543 ScopedLoggerInitializer initialize_logger(false, false);
544 LogBufferMatcher matcher;
Steve Blockd0582a62009-12-15 09:54:21 +0000545
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
Andrei Popescu402d9372010-02-26 13:31:12 +0000557 initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction());
Steve Blockd0582a62009-12-15 09:54:21 +0000558 CompileAndRunScript("Obj.prototype.method1.toString();");
559
560 i::Logger::LogCompiledFunctions();
Andrei Popescu402d9372010-02-26 13:31:12 +0000561 CHECK_GT(matcher.GetNextChunk(), 0);
Steve Blockd0582a62009-12-15 09:54:21 +0000562
563 const char* callback_rec = "code-creation,Callback,";
Andrei Popescu402d9372010-02-26 13:31:12 +0000564 char* pos = const_cast<char*>(matcher.Find(callback_rec));
Steve Blockd0582a62009-12-15 09:54:21 +0000565 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();
Steve Blockd0582a62009-12-15 09:54:21 +0000574}
575
576
577static v8::Handle<v8::Value> Prop1Getter(v8::Local<v8::String> property,
578 const v8::AccessorInfo& info) {
579 return v8::Handle<v8::Value>();
580}
581
582static void Prop1Setter(v8::Local<v8::String> property,
583 v8::Local<v8::Value> value,
584 const v8::AccessorInfo& info) {
585}
586
587static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property,
588 const v8::AccessorInfo& info) {
589 return v8::Handle<v8::Value>();
590}
591
592TEST(LogAccessorCallbacks) {
Andrei Popescu402d9372010-02-26 13:31:12 +0000593 ScopedLoggerInitializer initialize_logger(false, false);
594 LogBufferMatcher matcher;
Steve Blockd0582a62009-12-15 09:54:21 +0000595
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();
Andrei Popescu402d9372010-02-26 13:31:12 +0000604 CHECK_GT(matcher.GetNextChunk(), 0);
605 matcher.PrintBuffer();
Steve Blockd0582a62009-12-15 09:54:21 +0000606
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);
Andrei Popescu402d9372010-02-26 13:31:12 +0000611 CHECK_NE(NULL, matcher.Find(prop1_getter_record));
Steve Blockd0582a62009-12-15 09:54:21 +0000612 EmbeddedVector<char, 100> prop1_setter_record;
613 i::OS::SNPrintF(prop1_setter_record,
614 "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"",
615 Prop1Setter);
Andrei Popescu402d9372010-02-26 13:31:12 +0000616 CHECK_NE(NULL, matcher.Find(prop1_setter_record));
Steve Blockd0582a62009-12-15 09:54:21 +0000617 EmbeddedVector<char, 100> prop2_getter_record;
618 i::OS::SNPrintF(prop2_getter_record,
619 "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"",
620 Prop2Getter);
Andrei Popescu402d9372010-02-26 13:31:12 +0000621 CHECK_NE(NULL, matcher.Find(prop2_getter_record));
Steve Blockd0582a62009-12-15 09:54:21 +0000622
623 obj.Dispose();
Andrei Popescu402d9372010-02-26 13:31:12 +0000624}
Steve Blockd0582a62009-12-15 09:54:21 +0000625
Andrei Popescu402d9372010-02-26 13:31:12 +0000626
627TEST(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));
Steve Blockd0582a62009-12-15 09:54:21 +0000710}
711
712
Steve Blocka7e24c12009-10-30 11:49:00 +0000713static inline bool IsStringEqualTo(const char* r, const char* s) {
714 return strncmp(r, s, strlen(r)) == 0;
715}
716
717
718static 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
727namespace {
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.
732typedef char* CodeEntityInfo;
733
734class Interval {
735 public:
736 Interval()
737 : min_addr_(reinterpret_cast<Address>(-1)),
738 max_addr_(reinterpret_cast<Address>(0)), next_(NULL) {}
739
740 ~Interval() { delete next_; }
741
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
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
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
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
775 Address GetMinAddr() {
776 return next_ == NULL ? min_addr_ : i::Min(min_addr_, next_->GetMinAddr());
777 }
778
779 Address GetMaxAddr() {
780 return next_ == NULL ? max_addr_ : i::Max(max_addr_, next_->GetMaxAddr());
781 }
782
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
795 Address raw_min_addr() { return min_addr_; }
796
797 Address raw_max_addr() { return max_addr_; }
798
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.
810class ParseLogResult {
811 public:
812 ParseLogResult()
813 : entities_map(NULL), entities(NULL),
814 max_entities(0) {}
815
816 ~ParseLogResult() {
817 i::DeleteArray(entities_map);
818 i::DeleteArray(entities);
819 }
820
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();
Steve Blockd0582a62009-12-15 09:54:21 +0000832 entities_map = i::NewArray<int>(static_cast<int>(map_length));
Steve Blocka7e24c12009-10-30 11:49:00 +0000833 for (size_t i = 0; i < map_length; ++i) {
834 entities_map[i] = -1;
835 }
836 }
837
838 bool HasIndexForAddress(Address addr) {
839 return bounds.Contains(addr);
840 }
841
842 size_t GetIndexForAddress(Address addr) {
843 CHECK(HasIndexForAddress(addr));
844 return bounds.GetIndex(addr);
845 }
846
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
856 void ParseAddress(char* start) {
857 Address addr =
858 reinterpret_cast<Address>(strtoul(start, NULL, 16)); // NOLINT
859 bounds.RegisterAddress(addr);
860 }
861
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
884typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* result);
885
886static 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
917static void Pass1CodeCreation(char* start, char* end, ParseLogResult* result) {
918 result->ParseAddress(start);
919 ++result->max_entities;
920}
921
922
923static void Pass1CodeDelete(char* start, char* end, ParseLogResult* result) {
924 result->ParseAddress(start);
925}
926
927
928static 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
938static 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
958static 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
969static 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
987static 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
1004static inline void PrintCodeEntityInfo(CodeEntityInfo entity) {
1005 const int max_len = 50;
1006 if (entity != NULL) {
1007 char* eol = strchr(entity, '\n');
Steve Blockd0582a62009-12-15 09:54:21 +00001008 int len = static_cast<int>(eol - entity);
Steve Blocka7e24c12009-10-30 11:49:00 +00001009 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
1017static 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
1027static inline int StrChrLen(const char* s, char c) {
Steve Blockd0582a62009-12-15 09:54:21 +00001028 return static_cast<int>(strchr(s, c) - s);
Steve Blocka7e24c12009-10-30 11:49:00 +00001029}
1030
1031
1032static 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
1039static 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
1068static 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.
1086TEST(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