blob: 65ab50a04f2b0d040b01bc617313d73e123d1c64 [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;
22
23namespace i = v8::internal;
24
25static void SetUp() {
26 // Log to memory buffer.
27 i::FLAG_logfile = "*";
28 i::FLAG_log = true;
29 Logger::Setup();
30}
31
32static void TearDown() {
33 Logger::TearDown();
34}
35
36
37TEST(EmptyLog) {
38 SetUp();
39 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
40 CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
41 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
42 CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
43 TearDown();
44}
45
46
47TEST(GetMessages) {
48 SetUp();
49 Logger::StringEvent("aaa", "bbb");
50 Logger::StringEvent("cccc", "dddd");
51 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
52 char log_lines[100];
53 memset(log_lines, 0, sizeof(log_lines));
54 // Requesting data size which is smaller than first log message length.
55 CHECK_EQ(0, Logger::GetLogLines(0, log_lines, 3));
56 // See Logger::StringEvent.
57 const char* line_1 = "aaa,\"bbb\"\n";
58 const int line_1_len = strlen(line_1);
59 // Still smaller than log message length.
60 CHECK_EQ(0, Logger::GetLogLines(0, log_lines, line_1_len - 1));
61 // The exact size.
62 CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len));
63 CHECK_EQ(line_1, log_lines);
64 memset(log_lines, 0, sizeof(log_lines));
65 // A bit more than the first line length.
66 CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len + 3));
67 log_lines[line_1_len] = '\0';
68 CHECK_EQ(line_1, log_lines);
69 memset(log_lines, 0, sizeof(log_lines));
70 const char* line_2 = "cccc,\"dddd\"\n";
71 const int line_2_len = strlen(line_2);
72 // Now start with line_2 beginning.
73 CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 0));
74 CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 3));
75 CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, line_2_len - 1));
76 CHECK_EQ(line_2_len, Logger::GetLogLines(line_1_len, log_lines, line_2_len));
77 CHECK_EQ(line_2, log_lines);
78 memset(log_lines, 0, sizeof(log_lines));
79 CHECK_EQ(line_2_len,
80 Logger::GetLogLines(line_1_len, log_lines, line_2_len + 3));
81 CHECK_EQ(line_2, log_lines);
82 memset(log_lines, 0, sizeof(log_lines));
83 // Now get entire buffer contents.
84 const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
85 const int all_lines_len = strlen(all_lines);
86 CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len));
87 CHECK_EQ(all_lines, log_lines);
88 memset(log_lines, 0, sizeof(log_lines));
89 CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len + 3));
90 CHECK_EQ(all_lines, log_lines);
91 memset(log_lines, 0, sizeof(log_lines));
92 TearDown();
93}
94
95
96static int GetLogLines(int start_pos, i::Vector<char>* buffer) {
97 return Logger::GetLogLines(start_pos, buffer->start(), buffer->length());
98}
99
100
101TEST(BeyondWritePosition) {
102 SetUp();
103 Logger::StringEvent("aaa", "bbb");
104 Logger::StringEvent("cccc", "dddd");
105 // See Logger::StringEvent.
106 const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
107 const int all_lines_len = strlen(all_lines);
108 EmbeddedVector<char, 100> buffer;
109 const int beyond_write_pos = all_lines_len;
110 CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos, buffer.start(), 1));
111 CHECK_EQ(0, GetLogLines(beyond_write_pos, &buffer));
112 CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 1, buffer.start(), 1));
113 CHECK_EQ(0, GetLogLines(beyond_write_pos + 1, &buffer));
114 CHECK_EQ(0, Logger::GetLogLines(beyond_write_pos + 100, buffer.start(), 1));
115 CHECK_EQ(0, GetLogLines(beyond_write_pos + 100, &buffer));
116 CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, buffer.start(), 1));
117 CHECK_EQ(0, GetLogLines(10 * 1024 * 1024, &buffer));
118 TearDown();
119}
120
121
122TEST(MemoryLoggingTurnedOff) {
123 // Log to stdout
124 i::FLAG_logfile = "-";
125 i::FLAG_log = true;
126 Logger::Setup();
127 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
128 CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
129 CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
130 CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
131 Logger::TearDown();
132}
133
134
135static void CompileAndRunScript(const char *src) {
136 v8::Script::Compile(v8::String::New(src))->Run();
137}
138
139
140namespace v8 {
141namespace internal {
142
143class LoggerTestHelper : public AllStatic {
144 public:
145 static bool IsSamplerActive() { return Logger::IsProfilerSamplerActive(); }
146};
147
148} // namespace v8::internal
149} // namespace v8
150
151using v8::internal::LoggerTestHelper;
152
153
154// Under Linux, we need to check if signals were delivered to avoid false
155// positives. Under other platforms profiling is done via a high-priority
156// thread, so this case never happen.
157static bool was_sigprof_received = true;
158#ifdef __linux__
159
160struct sigaction old_sigprof_handler;
161pthread_t our_thread;
162
163static void SigProfSignalHandler(int signal, siginfo_t* info, void* context) {
164 if (signal != SIGPROF || !pthread_equal(pthread_self(), our_thread)) return;
165 was_sigprof_received = true;
166 old_sigprof_handler.sa_sigaction(signal, info, context);
167}
168
169#endif // __linux__
170
171
172static int CheckThatProfilerWorks(int log_pos) {
173 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU);
174 CHECK(LoggerTestHelper::IsSamplerActive());
175
176 // Verify that the current map of compiled functions has been logged.
177 EmbeddedVector<char, 102400> buffer;
178 int map_log_size = GetLogLines(log_pos, &buffer);
179 printf("map_log_size: %d\n", map_log_size);
180 CHECK_GT(map_log_size, 0);
181 CHECK_GT(buffer.length(), map_log_size);
182 log_pos += map_log_size;
183 // Check buffer contents.
184 buffer[map_log_size] = '\0';
185 const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/
186 CHECK_NE(NULL, strstr(buffer.start(), code_creation));
187
188#ifdef __linux__
189 // Intercept SIGPROF handler to make sure that the test process
190 // had received it. Under load, system can defer it causing test failure.
191 // It is important to execute this after 'ResumeProfiler'.
192 our_thread = pthread_self();
193 was_sigprof_received = false;
194 struct sigaction sa;
195 sa.sa_sigaction = SigProfSignalHandler;
196 sigemptyset(&sa.sa_mask);
197 sa.sa_flags = SA_SIGINFO;
198 CHECK_EQ(0, sigaction(SIGPROF, &sa, &old_sigprof_handler));
199#endif // __linux__
200
201 // Force compiler to generate new code by parametrizing source.
202 EmbeddedVector<char, 100> script_src;
203 i::OS::SNPrintF(script_src,
204 "for (var i = 0; i < 1000; ++i) { "
205 "(function(x) { return %d * x; })(i); }",
206 log_pos);
207 // Run code for 200 msecs to get some ticks.
208 const double end_time = i::OS::TimeCurrentMillis() + 200;
209 while (i::OS::TimeCurrentMillis() < end_time) {
210 CompileAndRunScript(script_src.start());
211 // Yield CPU to give Profiler thread a chance to process ticks.
212 i::OS::Sleep(1);
213 }
214
215 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU);
216 CHECK(!LoggerTestHelper::IsSamplerActive());
217
218 // Wait 50 msecs to allow Profiler thread to process the last
219 // tick sample it has got.
220 i::OS::Sleep(50);
221
222 // Now we must have compiler and tick records.
223 int log_size = GetLogLines(log_pos, &buffer);
224 printf("log_size: %d\n", log_size);
225 CHECK_GT(log_size, 0);
226 CHECK_GT(buffer.length(), log_size);
227 log_pos += log_size;
228 // Check buffer contents.
229 buffer[log_size] = '\0';
230 const char* tick = "\ntick,";
231 CHECK_NE(NULL, strstr(buffer.start(), code_creation));
232 const bool ticks_found = strstr(buffer.start(), tick) != NULL;
233 CHECK_EQ(was_sigprof_received, ticks_found);
234
235 return log_pos;
236}
237
238
239TEST(ProfLazyMode) {
240 const bool saved_prof_lazy = i::FLAG_prof_lazy;
241 const bool saved_prof = i::FLAG_prof;
242 const bool saved_prof_auto = i::FLAG_prof_auto;
243 i::FLAG_prof = true;
244 i::FLAG_prof_lazy = true;
245 i::FLAG_prof_auto = false;
246 i::FLAG_logfile = "*";
247
248 // If tests are being run manually, V8 will be already initialized
249 // by the test below.
250 const bool need_to_set_up_logger = i::V8::IsRunning();
251 v8::HandleScope scope;
252 v8::Handle<v8::Context> env = v8::Context::New();
253 if (need_to_set_up_logger) Logger::Setup();
254 env->Enter();
255
256 // No sampling should happen prior to resuming profiler.
257 CHECK(!LoggerTestHelper::IsSamplerActive());
258
259 // Read initial logged data (static libs map).
260 EmbeddedVector<char, 102400> buffer;
261 int log_pos = GetLogLines(0, &buffer);
262 CHECK_GT(log_pos, 0);
263 CHECK_GT(buffer.length(), log_pos);
264
265 CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
266
267 // Nothing must be logged while profiling is suspended.
268 CHECK_EQ(0, GetLogLines(log_pos, &buffer));
269
270 log_pos = CheckThatProfilerWorks(log_pos);
271
272 CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
273
274 // No new data beyond last retrieved position.
275 CHECK_EQ(0, GetLogLines(log_pos, &buffer));
276
277 // Check that profiling can be resumed again.
278 CheckThatProfilerWorks(log_pos);
279
280 env->Exit();
281 Logger::TearDown();
282 i::FLAG_prof_lazy = saved_prof_lazy;
283 i::FLAG_prof = saved_prof;
284 i::FLAG_prof_auto = saved_prof_auto;
285}
286
287
288// Profiling multiple threads that use V8 is currently only available on Linux.
289#ifdef __linux__
290
291namespace {
292
293class LoopingThread : public v8::internal::Thread {
294 public:
295 LoopingThread()
296 : v8::internal::Thread(),
297 semaphore_(v8::internal::OS::CreateSemaphore(0)),
298 run_(true) {
299 }
300
301 virtual ~LoopingThread() { delete semaphore_; }
302
303 void Run() {
304 self_ = pthread_self();
305 RunLoop();
306 }
307
308 void SendSigProf() { pthread_kill(self_, SIGPROF); }
309
310 void Stop() { run_ = false; }
311
312 bool WaitForRunning() { return semaphore_->Wait(1000000); }
313
314 protected:
315 bool IsRunning() { return run_; }
316
317 virtual void RunLoop() = 0;
318
319 void SetV8ThreadId() {
320 v8_thread_id_ = v8::V8::GetCurrentThreadId();
321 }
322
323 void SignalRunning() { semaphore_->Signal(); }
324
325 private:
326 v8::internal::Semaphore* semaphore_;
327 bool run_;
328 pthread_t self_;
329 int v8_thread_id_;
330};
331
332
333class LoopingJsThread : public LoopingThread {
334 public:
335 void RunLoop() {
336 {
337 v8::Locker locker;
338 CHECK(v8::internal::ThreadManager::HasId());
339 SetV8ThreadId();
340 }
341 while (IsRunning()) {
342 v8::Locker locker;
343 v8::HandleScope scope;
344 v8::Persistent<v8::Context> context = v8::Context::New();
345 v8::Context::Scope context_scope(context);
346 SignalRunning();
347 CompileAndRunScript(
348 "var j; for (var i=0; i<10000; ++i) { j = Math.sin(i); }");
349 context.Dispose();
350 i::OS::Sleep(1);
351 }
352 }
353};
354
355
356class LoopingNonJsThread : public LoopingThread {
357 public:
358 void RunLoop() {
359 v8::Locker locker;
360 v8::Unlocker unlocker;
361 // Now thread has V8's id, but will not run VM code.
362 CHECK(v8::internal::ThreadManager::HasId());
363 double i = 10;
364 SignalRunning();
365 while (IsRunning()) {
366 i = sin(i);
367 i::OS::Sleep(1);
368 }
369 }
370};
371
372
373class TestSampler : public v8::internal::Sampler {
374 public:
375 TestSampler()
376 : Sampler(0, true),
377 semaphore_(v8::internal::OS::CreateSemaphore(0)),
378 was_sample_stack_called_(false) {
379 }
380
381 ~TestSampler() { delete semaphore_; }
382
383 void SampleStack(v8::internal::TickSample*) {
384 was_sample_stack_called_ = true;
385 }
386
387 void Tick(v8::internal::TickSample*) { semaphore_->Signal(); }
388
389 bool WaitForTick() { return semaphore_->Wait(1000000); }
390
391 void Reset() { was_sample_stack_called_ = false; }
392
393 bool WasSampleStackCalled() { return was_sample_stack_called_; }
394
395 private:
396 v8::internal::Semaphore* semaphore_;
397 bool was_sample_stack_called_;
398};
399
400
401} // namespace
402
403TEST(ProfMultipleThreads) {
404 LoopingJsThread jsThread;
405 jsThread.Start();
406 LoopingNonJsThread nonJsThread;
407 nonJsThread.Start();
408
409 TestSampler sampler;
410 sampler.Start();
411 CHECK(!sampler.WasSampleStackCalled());
412 jsThread.WaitForRunning();
413 jsThread.SendSigProf();
414 CHECK(sampler.WaitForTick());
415 CHECK(sampler.WasSampleStackCalled());
416 sampler.Reset();
417 CHECK(!sampler.WasSampleStackCalled());
418 nonJsThread.WaitForRunning();
419 nonJsThread.SendSigProf();
420 CHECK(sampler.WaitForTick());
421 CHECK(!sampler.WasSampleStackCalled());
422 sampler.Stop();
423
424 jsThread.Stop();
425 nonJsThread.Stop();
426 jsThread.Join();
427 nonJsThread.Join();
428}
429
430#endif // __linux__
431
432
433static inline bool IsStringEqualTo(const char* r, const char* s) {
434 return strncmp(r, s, strlen(r)) == 0;
435}
436
437
438static bool Consume(const char* str, char** buf) {
439 if (IsStringEqualTo(str, *buf)) {
440 *buf += strlen(str);
441 return true;
442 }
443 return false;
444}
445
446
447namespace {
448
449// A code entity is a pointer to a position of code-creation event in buffer log
450// offset to a point where entity size begins, i.e.: '255,"func"\n'. This makes
451// comparing code entities pretty easy.
452typedef char* CodeEntityInfo;
453
454class Interval {
455 public:
456 Interval()
457 : min_addr_(reinterpret_cast<Address>(-1)),
458 max_addr_(reinterpret_cast<Address>(0)), next_(NULL) {}
459
460 ~Interval() { delete next_; }
461
462 size_t Length() {
463 size_t result = max_addr_ - min_addr_ + 1;
464 if (next_ != NULL) result += next_->Length();
465 return result;
466 }
467
468 void CloneFrom(Interval* src) {
469 while (src != NULL) {
470 RegisterAddress(src->min_addr_);
471 RegisterAddress(src->max_addr_);
472 src = src->next_;
473 }
474 }
475
476 bool Contains(Address addr) {
477 if (min_addr_ <= addr && addr <= max_addr_) {
478 return true;
479 }
480 if (next_ != NULL) {
481 return next_->Contains(addr);
482 } else {
483 return false;
484 }
485 }
486
487 size_t GetIndex(Address addr) {
488 if (min_addr_ <= addr && addr <= max_addr_) {
489 return addr - min_addr_;
490 }
491 CHECK_NE(NULL, next_);
492 return (max_addr_ - min_addr_ + 1) + next_->GetIndex(addr);
493 }
494
495 Address GetMinAddr() {
496 return next_ == NULL ? min_addr_ : i::Min(min_addr_, next_->GetMinAddr());
497 }
498
499 Address GetMaxAddr() {
500 return next_ == NULL ? max_addr_ : i::Max(max_addr_, next_->GetMaxAddr());
501 }
502
503 void RegisterAddress(Address addr) {
504 if (min_addr_ == reinterpret_cast<Address>(-1)
505 || (size_t)(addr > min_addr_ ?
506 addr - min_addr_ : min_addr_ - addr) < MAX_DELTA) {
507 if (addr < min_addr_) min_addr_ = addr;
508 if (addr > max_addr_) max_addr_ = addr;
509 } else {
510 if (next_ == NULL) next_ = new Interval();
511 next_->RegisterAddress(addr);
512 }
513 }
514
515 Address raw_min_addr() { return min_addr_; }
516
517 Address raw_max_addr() { return max_addr_; }
518
519 Interval* get_next() { return next_; }
520
521 private:
522 static const size_t MAX_DELTA = 0x100000;
523 Address min_addr_;
524 Address max_addr_;
525 Interval* next_;
526};
527
528
529// A structure used to return log parsing results.
530class ParseLogResult {
531 public:
532 ParseLogResult()
533 : entities_map(NULL), entities(NULL),
534 max_entities(0) {}
535
536 ~ParseLogResult() {
537 i::DeleteArray(entities_map);
538 i::DeleteArray(entities);
539 }
540
541 void AllocateEntities() {
542 // Make sure that the test doesn't operate on a bogus log.
543 CHECK_GT(max_entities, 0);
544 CHECK_GT(bounds.GetMinAddr(), 0);
545 CHECK_GT(bounds.GetMaxAddr(), bounds.GetMinAddr());
546
547 entities = i::NewArray<CodeEntityInfo>(max_entities);
548 for (int i = 0; i < max_entities; ++i) {
549 entities[i] = NULL;
550 }
551 const size_t map_length = bounds.Length();
552 entities_map = i::NewArray<int>(map_length);
553 for (size_t i = 0; i < map_length; ++i) {
554 entities_map[i] = -1;
555 }
556 }
557
558 bool HasIndexForAddress(Address addr) {
559 return bounds.Contains(addr);
560 }
561
562 size_t GetIndexForAddress(Address addr) {
563 CHECK(HasIndexForAddress(addr));
564 return bounds.GetIndex(addr);
565 }
566
567 CodeEntityInfo GetEntity(Address addr) {
568 if (HasIndexForAddress(addr)) {
569 size_t idx = GetIndexForAddress(addr);
570 int item = entities_map[idx];
571 return item != -1 ? entities[item] : NULL;
572 }
573 return NULL;
574 }
575
576 void ParseAddress(char* start) {
577 Address addr =
578 reinterpret_cast<Address>(strtoul(start, NULL, 16)); // NOLINT
579 bounds.RegisterAddress(addr);
580 }
581
582 Address ConsumeAddress(char** start) {
583 char* end_ptr;
584 Address addr =
585 reinterpret_cast<Address>(strtoul(*start, &end_ptr, 16)); // NOLINT
586 CHECK(HasIndexForAddress(addr));
587 *start = end_ptr;
588 return addr;
589 }
590
591 Interval bounds;
592 // Memory map of entities start addresses.
593 int* entities_map;
594 // An array of code entities.
595 CodeEntityInfo* entities;
596 // Maximal entities count. Actual entities count can be lower,
597 // empty entity slots are pointing to NULL.
598 int max_entities;
599};
600
601} // namespace
602
603
604typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* result);
605
606static void ParserCycle(
607 char* start, char* end, ParseLogResult* result,
608 ParserBlock block_creation, ParserBlock block_delete,
609 ParserBlock block_move) {
610
611 const char* code_creation = "code-creation,";
612 const char* code_delete = "code-delete,";
613 const char* code_move = "code-move,";
614
615 const char* lazy_compile = "LazyCompile,";
616 const char* script = "Script,";
617 const char* function = "Function,";
618
619 while (start < end) {
620 if (Consume(code_creation, &start)) {
621 if (Consume(lazy_compile, &start)
622 || Consume(script, &start)
623 || Consume(function, &start)) {
624 block_creation(start, end, result);
625 }
626 } else if (Consume(code_delete, &start)) {
627 block_delete(start, end, result);
628 } else if (Consume(code_move, &start)) {
629 block_move(start, end, result);
630 }
631 while (start < end && *start != '\n') ++start;
632 ++start;
633 }
634}
635
636
637static void Pass1CodeCreation(char* start, char* end, ParseLogResult* result) {
638 result->ParseAddress(start);
639 ++result->max_entities;
640}
641
642
643static void Pass1CodeDelete(char* start, char* end, ParseLogResult* result) {
644 result->ParseAddress(start);
645}
646
647
648static void Pass1CodeMove(char* start, char* end, ParseLogResult* result) {
649 result->ParseAddress(start);
650 // Skip old address.
651 while (start < end && *start != ',') ++start;
652 CHECK_GT(end, start);
653 ++start; // Skip ','.
654 result->ParseAddress(start);
655}
656
657
658static void Pass2CodeCreation(char* start, char* end, ParseLogResult* result) {
659 Address addr = result->ConsumeAddress(&start);
660 CHECK_GT(end, start);
661 ++start; // Skip ','.
662
663 size_t idx = result->GetIndexForAddress(addr);
664 result->entities_map[idx] = -1;
665 for (int i = 0; i < result->max_entities; ++i) {
666 // Find an empty slot and fill it.
667 if (result->entities[i] == NULL) {
668 result->entities[i] = start;
669 result->entities_map[idx] = i;
670 break;
671 }
672 }
673 // Make sure that a slot was found.
674 CHECK_GE(result->entities_map[idx], 0);
675}
676
677
678static void Pass2CodeDelete(char* start, char* end, ParseLogResult* result) {
679 Address addr = result->ConsumeAddress(&start);
680 size_t idx = result->GetIndexForAddress(addr);
681 // There can be code deletes that are not related to JS code.
682 if (result->entities_map[idx] >= 0) {
683 result->entities[result->entities_map[idx]] = NULL;
684 result->entities_map[idx] = -1;
685 }
686}
687
688
689static void Pass2CodeMove(char* start, char* end, ParseLogResult* result) {
690 Address from_addr = result->ConsumeAddress(&start);
691 CHECK_GT(end, start);
692 ++start; // Skip ','.
693 Address to_addr = result->ConsumeAddress(&start);
694 CHECK_GT(end, start);
695
696 size_t from_idx = result->GetIndexForAddress(from_addr);
697 size_t to_idx = result->GetIndexForAddress(to_addr);
698 // There can be code moves that are not related to JS code.
699 if (from_idx != to_idx && result->entities_map[from_idx] >= 0) {
700 CHECK_EQ(-1, result->entities_map[to_idx]);
701 result->entities_map[to_idx] = result->entities_map[from_idx];
702 result->entities_map[from_idx] = -1;
703 };
704}
705
706
707static void ParseLog(char* start, char* end, ParseLogResult* result) {
708 // Pass 1: Calculate boundaries of addresses and entities count.
709 ParserCycle(start, end, result,
710 Pass1CodeCreation, Pass1CodeDelete, Pass1CodeMove);
711
712 printf("min_addr: %p, max_addr: %p, entities: %d\n",
713 result->bounds.GetMinAddr(), result->bounds.GetMaxAddr(),
714 result->max_entities);
715
716 result->AllocateEntities();
717
718 // Pass 2: Fill in code entries data.
719 ParserCycle(start, end, result,
720 Pass2CodeCreation, Pass2CodeDelete, Pass2CodeMove);
721}
722
723
724static inline void PrintCodeEntityInfo(CodeEntityInfo entity) {
725 const int max_len = 50;
726 if (entity != NULL) {
727 char* eol = strchr(entity, '\n');
728 int len = eol - entity;
729 len = len <= max_len ? len : max_len;
730 printf("%-*.*s ", max_len, len, entity);
731 } else {
732 printf("%*s", max_len + 1, "");
733 }
734}
735
736
737static void PrintCodeEntitiesInfo(
738 bool is_equal, Address addr,
739 CodeEntityInfo l_entity, CodeEntityInfo r_entity) {
740 printf("%c %p ", is_equal ? ' ' : '*', addr);
741 PrintCodeEntityInfo(l_entity);
742 PrintCodeEntityInfo(r_entity);
743 printf("\n");
744}
745
746
747static inline int StrChrLen(const char* s, char c) {
748 return strchr(s, c) - s;
749}
750
751
752static bool AreFuncSizesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
753 int ref_len = StrChrLen(ref_s, ',');
754 int new_len = StrChrLen(new_s, ',');
755 return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
756}
757
758
759static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
760 // Skip size.
761 ref_s = strchr(ref_s, ',') + 1;
762 new_s = strchr(new_s, ',') + 1;
763 int ref_len = StrChrLen(ref_s, '\n');
764 int new_len = StrChrLen(new_s, '\n');
765 // If reference is anonymous (""), it's OK to have anything in new.
766 if (ref_len == 2) return true;
767 // A special case for ErrorPrototype. Haven't yet figured out why they
768 // are different.
769 const char* error_prototype = "\"ErrorPrototype";
770 if (IsStringEqualTo(error_prototype, ref_s)
771 && IsStringEqualTo(error_prototype, new_s)) {
772 return true;
773 }
774 // Built-in objects have problems too.
775 const char* built_ins[] = {
776 "\"Boolean\"", "\"Function\"", "\"Number\"",
777 "\"Object\"", "\"Script\"", "\"String\""
778 };
779 for (size_t i = 0; i < sizeof(built_ins) / sizeof(*built_ins); ++i) {
780 if (IsStringEqualTo(built_ins[i], new_s)) {
781 return true;
782 }
783 }
784 return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
785}
786
787
788static bool AreEntitiesEqual(CodeEntityInfo ref_e, CodeEntityInfo new_e) {
789 if (ref_e == NULL && new_e != NULL) return true;
790 if (ref_e != NULL && new_e != NULL) {
791 return AreFuncSizesEqual(ref_e, new_e) && AreFuncNamesEqual(ref_e, new_e);
792 }
793 if (ref_e != NULL && new_e == NULL) {
794 // args_count entities (argument adapters) are not found by heap traversal,
795 // but they are not needed because they doesn't contain any code.
796 ref_e = strchr(ref_e, ',') + 1;
797 const char* args_count = "\"args_count:";
798 return IsStringEqualTo(args_count, ref_e);
799 }
800 return false;
801}
802
803
804// Test that logging of code create / move / delete events
805// is equivalent to traversal of a resulting heap.
806TEST(EquivalenceOfLoggingAndTraversal) {
807 // This test needs to be run on a "clean" V8 to ensure that snapshot log
808 // is loaded. This is always true when running using tools/test.py because
809 // it launches a new cctest instance for every test. To be sure that launching
810 // cctest manually also works, please be sure that no tests below
811 // are using V8.
812 //
813 // P.S. No, V8 can't be re-initialized after disposal, see include/v8.h.
814 CHECK(!i::V8::IsRunning());
815
816 i::FLAG_logfile = "*";
817 i::FLAG_log = true;
818 i::FLAG_log_code = true;
819
820 // Make sure objects move.
821 bool saved_always_compact = i::FLAG_always_compact;
822 if (!i::FLAG_never_compact) {
823 i::FLAG_always_compact = true;
824 }
825
826 v8::HandleScope scope;
827 v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>();
828 v8::Handle<v8::Context> env = v8::Context::New(
829 0, v8::Handle<v8::ObjectTemplate>(), global_object);
830 env->Enter();
831
832 // Compile and run a function that creates other functions.
833 CompileAndRunScript(
834 "(function f(obj) {\n"
835 " obj.test =\n"
836 " (function a(j) { return function b() { return j; } })(100);\n"
837 "})(this);");
838 i::Heap::CollectAllGarbage(false);
839
840 EmbeddedVector<char, 204800> buffer;
841 int log_size;
842 ParseLogResult ref_result;
843
844 // Retrieve the log.
845 {
846 // Make sure that no GCs occur prior to LogCompiledFunctions call.
847 i::AssertNoAllocation no_alloc;
848
849 log_size = GetLogLines(0, &buffer);
850 CHECK_GT(log_size, 0);
851 CHECK_GT(buffer.length(), log_size);
852
853 // Fill a map of compiled code objects.
854 ParseLog(buffer.start(), buffer.start() + log_size, &ref_result);
855 }
856
857 // Iterate heap to find compiled functions, will write to log.
858 i::Logger::LogCompiledFunctions();
859 char* new_log_start = buffer.start() + log_size;
860 const int new_log_size = Logger::GetLogLines(
861 log_size, new_log_start, buffer.length() - log_size);
862 CHECK_GT(new_log_size, 0);
863 CHECK_GT(buffer.length(), log_size + new_log_size);
864
865 // Fill an equivalent map of compiled code objects.
866 ParseLogResult new_result;
867 ParseLog(new_log_start, new_log_start + new_log_size, &new_result);
868
869 // Test their actual equivalence.
870 Interval combined;
871 combined.CloneFrom(&ref_result.bounds);
872 combined.CloneFrom(&new_result.bounds);
873 Interval* iter = &combined;
874 bool results_equal = true;
875
876 while (iter != NULL) {
877 for (Address addr = iter->raw_min_addr();
878 addr <= iter->raw_max_addr(); ++addr) {
879 CodeEntityInfo ref_entity = ref_result.GetEntity(addr);
880 CodeEntityInfo new_entity = new_result.GetEntity(addr);
881 if (ref_entity != NULL || new_entity != NULL) {
882 const bool equal = AreEntitiesEqual(ref_entity, new_entity);
883 if (!equal) results_equal = false;
884 PrintCodeEntitiesInfo(equal, addr, ref_entity, new_entity);
885 }
886 }
887 iter = iter->get_next();
888 }
889 // Make sure that all log data is written prior crash due to CHECK failure.
890 fflush(stdout);
891 CHECK(results_equal);
892
893 env->Exit();
894 Logger::TearDown();
895 i::FLAG_always_compact = saved_always_compact;
896}
897
898#endif // ENABLE_LOGGING_AND_PROFILING