blob: 85ff331a622f41d82b29a0fb3faaed3dff237825 [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
173static int CheckThatProfilerWorks(int log_pos) {
174 Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU);
175 CHECK(LoggerTestHelper::IsSamplerActive());
176
177 // Verify that the current map of compiled functions has been logged.
178 EmbeddedVector<char, 102400> buffer;
179 int map_log_size = GetLogLines(log_pos, &buffer);
180 printf("map_log_size: %d\n", map_log_size);
181 CHECK_GT(map_log_size, 0);
182 CHECK_GT(buffer.length(), map_log_size);
183 log_pos += map_log_size;
184 // Check buffer contents.
185 buffer[map_log_size] = '\0';
186 const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/
187 CHECK_NE(NULL, strstr(buffer.start(), code_creation));
188
189#ifdef __linux__
190 // Intercept SIGPROF handler to make sure that the test process
191 // had received it. Under load, system can defer it causing test failure.
192 // It is important to execute this after 'ResumeProfiler'.
193 our_thread = pthread_self();
194 was_sigprof_received = false;
195 struct sigaction sa;
196 sa.sa_sigaction = SigProfSignalHandler;
197 sigemptyset(&sa.sa_mask);
198 sa.sa_flags = SA_SIGINFO;
199 CHECK_EQ(0, sigaction(SIGPROF, &sa, &old_sigprof_handler));
200#endif // __linux__
201
202 // Force compiler to generate new code by parametrizing source.
203 EmbeddedVector<char, 100> script_src;
204 i::OS::SNPrintF(script_src,
205 "for (var i = 0; i < 1000; ++i) { "
206 "(function(x) { return %d * x; })(i); }",
207 log_pos);
208 // Run code for 200 msecs to get some ticks.
209 const double end_time = i::OS::TimeCurrentMillis() + 200;
210 while (i::OS::TimeCurrentMillis() < end_time) {
211 CompileAndRunScript(script_src.start());
212 // Yield CPU to give Profiler thread a chance to process ticks.
213 i::OS::Sleep(1);
214 }
215
216 Logger::PauseProfiler(v8::PROFILER_MODULE_CPU);
217 CHECK(!LoggerTestHelper::IsSamplerActive());
218
219 // Wait 50 msecs to allow Profiler thread to process the last
220 // tick sample it has got.
221 i::OS::Sleep(50);
222
223 // Now we must have compiler and tick records.
224 int log_size = GetLogLines(log_pos, &buffer);
225 printf("log_size: %d\n", log_size);
226 CHECK_GT(log_size, 0);
227 CHECK_GT(buffer.length(), log_size);
228 log_pos += log_size;
229 // Check buffer contents.
230 buffer[log_size] = '\0';
231 const char* tick = "\ntick,";
232 CHECK_NE(NULL, strstr(buffer.start(), code_creation));
233 const bool ticks_found = strstr(buffer.start(), tick) != NULL;
234 CHECK_EQ(was_sigprof_received, ticks_found);
235
236 return log_pos;
237}
238
239
240TEST(ProfLazyMode) {
241 const bool saved_prof_lazy = i::FLAG_prof_lazy;
242 const bool saved_prof = i::FLAG_prof;
243 const bool saved_prof_auto = i::FLAG_prof_auto;
244 i::FLAG_prof = true;
245 i::FLAG_prof_lazy = true;
246 i::FLAG_prof_auto = false;
247 i::FLAG_logfile = "*";
248
249 // If tests are being run manually, V8 will be already initialized
Steve Blockd0582a62009-12-15 09:54:21 +0000250 // by the bottom test.
Steve Blocka7e24c12009-10-30 11:49:00 +0000251 const bool need_to_set_up_logger = i::V8::IsRunning();
252 v8::HandleScope scope;
253 v8::Handle<v8::Context> env = v8::Context::New();
254 if (need_to_set_up_logger) Logger::Setup();
255 env->Enter();
256
257 // No sampling should happen prior to resuming profiler.
258 CHECK(!LoggerTestHelper::IsSamplerActive());
259
Steve Blocka7e24c12009-10-30 11:49:00 +0000260 EmbeddedVector<char, 102400> buffer;
Steve Blockd0582a62009-12-15 09:54:21 +0000261 // Nothing must be logged until profiling is resumed.
Steve Blocka7e24c12009-10-30 11:49:00 +0000262 int log_pos = GetLogLines(0, &buffer);
Steve Blockd0582a62009-12-15 09:54:21 +0000263 CHECK_EQ(0, log_pos);
Steve Blocka7e24c12009-10-30 11:49:00 +0000264
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
Steve Block3ce2e202009-11-05 08:53:23 +0000433// Test for issue http://crbug.com/23768 in Chromium.
434// Heap can contain scripts with already disposed external sources.
435// We need to verify that LogCompiledFunctions doesn't crash on them.
436namespace {
437
438class SimpleExternalString : public v8::String::ExternalStringResource {
439 public:
440 explicit SimpleExternalString(const char* source)
Steve Blockd0582a62009-12-15 09:54:21 +0000441 : utf_source_(StrLength(source)) {
Steve Block3ce2e202009-11-05 08:53:23 +0000442 for (int i = 0; i < utf_source_.length(); ++i)
443 utf_source_[i] = source[i];
444 }
445 virtual ~SimpleExternalString() {}
446 virtual size_t length() const { return utf_source_.length(); }
447 virtual const uint16_t* data() const { return utf_source_.start(); }
448 private:
449 i::ScopedVector<uint16_t> utf_source_;
450};
451
452} // namespace
453
454TEST(Issue23768) {
455 v8::HandleScope scope;
456 v8::Handle<v8::Context> env = v8::Context::New();
457 env->Enter();
458
459 SimpleExternalString source_ext_str("(function ext() {})();");
460 v8::Local<v8::String> source = v8::String::NewExternal(&source_ext_str);
461 // Script needs to have a name in order to trigger InitLineEnds execution.
462 v8::Handle<v8::String> origin = v8::String::New("issue-23768-test");
463 v8::Handle<v8::Script> evil_script = v8::Script::Compile(source, origin);
464 CHECK(!evil_script.IsEmpty());
465 CHECK(!evil_script->Run().IsEmpty());
466 i::Handle<i::ExternalTwoByteString> i_source(
467 i::ExternalTwoByteString::cast(*v8::Utils::OpenHandle(*source)));
468 // This situation can happen if source was an external string disposed
469 // by its owner.
470 i_source->set_resource(NULL);
471
472 // Must not crash.
473 i::Logger::LogCompiledFunctions();
474}
475
476
Steve Blockd0582a62009-12-15 09:54:21 +0000477static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) {
478 return v8::Handle<v8::Value>();
479}
480
481TEST(LogCallbacks) {
482 const bool saved_prof_lazy = i::FLAG_prof_lazy;
483 const bool saved_prof = i::FLAG_prof;
484 const bool saved_prof_auto = i::FLAG_prof_auto;
485 i::FLAG_prof = true;
486 i::FLAG_prof_lazy = false;
487 i::FLAG_prof_auto = false;
488 i::FLAG_logfile = "*";
489
490 // If tests are being run manually, V8 will be already initialized
491 // by the bottom test.
492 const bool need_to_set_up_logger = i::V8::IsRunning();
493 v8::HandleScope scope;
494 v8::Handle<v8::Context> env = v8::Context::New();
495 if (need_to_set_up_logger) Logger::Setup();
496 env->Enter();
497
498 // Skip all initially logged stuff.
499 EmbeddedVector<char, 102400> buffer;
500 int log_pos = GetLogLines(0, &buffer);
501
502 v8::Persistent<v8::FunctionTemplate> obj =
503 v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
504 obj->SetClassName(v8::String::New("Obj"));
505 v8::Handle<v8::ObjectTemplate> proto = obj->PrototypeTemplate();
506 v8::Local<v8::Signature> signature = v8::Signature::New(obj);
507 proto->Set(v8::String::New("method1"),
508 v8::FunctionTemplate::New(ObjMethod1,
509 v8::Handle<v8::Value>(),
510 signature),
511 static_cast<v8::PropertyAttribute>(v8::DontDelete));
512
513 env->Global()->Set(v8_str("Obj"), obj->GetFunction());
514 CompileAndRunScript("Obj.prototype.method1.toString();");
515
516 i::Logger::LogCompiledFunctions();
517 log_pos = GetLogLines(log_pos, &buffer);
518 CHECK_GT(log_pos, 0);
519 buffer[log_pos] = 0;
520
521 const char* callback_rec = "code-creation,Callback,";
522 char* pos = strstr(buffer.start(), callback_rec);
523 CHECK_NE(NULL, pos);
524 pos += strlen(callback_rec);
525 EmbeddedVector<char, 100> ref_data;
526 i::OS::SNPrintF(ref_data,
527 "0x%" V8PRIxPTR ",1,\"method1\"", ObjMethod1);
528 *(pos + strlen(ref_data.start())) = '\0';
529 CHECK_EQ(ref_data.start(), pos);
530
531 obj.Dispose();
532
533 env->Exit();
534 Logger::TearDown();
535 i::FLAG_prof_lazy = saved_prof_lazy;
536 i::FLAG_prof = saved_prof;
537 i::FLAG_prof_auto = saved_prof_auto;
538}
539
540
541static v8::Handle<v8::Value> Prop1Getter(v8::Local<v8::String> property,
542 const v8::AccessorInfo& info) {
543 return v8::Handle<v8::Value>();
544}
545
546static void Prop1Setter(v8::Local<v8::String> property,
547 v8::Local<v8::Value> value,
548 const v8::AccessorInfo& info) {
549}
550
551static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property,
552 const v8::AccessorInfo& info) {
553 return v8::Handle<v8::Value>();
554}
555
556TEST(LogAccessorCallbacks) {
557 const bool saved_prof_lazy = i::FLAG_prof_lazy;
558 const bool saved_prof = i::FLAG_prof;
559 const bool saved_prof_auto = i::FLAG_prof_auto;
560 i::FLAG_prof = true;
561 i::FLAG_prof_lazy = false;
562 i::FLAG_prof_auto = false;
563 i::FLAG_logfile = "*";
564
565 // If tests are being run manually, V8 will be already initialized
566 // by the bottom test.
567 const bool need_to_set_up_logger = i::V8::IsRunning();
568 v8::HandleScope scope;
569 v8::Handle<v8::Context> env = v8::Context::New();
570 if (need_to_set_up_logger) Logger::Setup();
571 env->Enter();
572
573 // Skip all initially logged stuff.
574 EmbeddedVector<char, 102400> buffer;
575 int log_pos = GetLogLines(0, &buffer);
576
577 v8::Persistent<v8::FunctionTemplate> obj =
578 v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
579 obj->SetClassName(v8::String::New("Obj"));
580 v8::Handle<v8::ObjectTemplate> inst = obj->InstanceTemplate();
581 inst->SetAccessor(v8::String::New("prop1"), Prop1Getter, Prop1Setter);
582 inst->SetAccessor(v8::String::New("prop2"), Prop2Getter);
583
584 i::Logger::LogAccessorCallbacks();
585 log_pos = GetLogLines(log_pos, &buffer);
586 CHECK_GT(log_pos, 0);
587 buffer[log_pos] = 0;
588 printf("%s", buffer.start());
589
590 EmbeddedVector<char, 100> prop1_getter_record;
591 i::OS::SNPrintF(prop1_getter_record,
592 "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"",
593 Prop1Getter);
594 CHECK_NE(NULL, strstr(buffer.start(), prop1_getter_record.start()));
595 EmbeddedVector<char, 100> prop1_setter_record;
596 i::OS::SNPrintF(prop1_setter_record,
597 "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"",
598 Prop1Setter);
599 CHECK_NE(NULL, strstr(buffer.start(), prop1_setter_record.start()));
600 EmbeddedVector<char, 100> prop2_getter_record;
601 i::OS::SNPrintF(prop2_getter_record,
602 "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"",
603 Prop2Getter);
604 CHECK_NE(NULL, strstr(buffer.start(), prop2_getter_record.start()));
605
606 obj.Dispose();
607
608 env->Exit();
609 Logger::TearDown();
610 i::FLAG_prof_lazy = saved_prof_lazy;
611 i::FLAG_prof = saved_prof;
612 i::FLAG_prof_auto = saved_prof_auto;
613}
614
615
Steve Blocka7e24c12009-10-30 11:49:00 +0000616static inline bool IsStringEqualTo(const char* r, const char* s) {
617 return strncmp(r, s, strlen(r)) == 0;
618}
619
620
621static bool Consume(const char* str, char** buf) {
622 if (IsStringEqualTo(str, *buf)) {
623 *buf += strlen(str);
624 return true;
625 }
626 return false;
627}
628
629
630namespace {
631
632// A code entity is a pointer to a position of code-creation event in buffer log
633// offset to a point where entity size begins, i.e.: '255,"func"\n'. This makes
634// comparing code entities pretty easy.
635typedef char* CodeEntityInfo;
636
637class Interval {
638 public:
639 Interval()
640 : min_addr_(reinterpret_cast<Address>(-1)),
641 max_addr_(reinterpret_cast<Address>(0)), next_(NULL) {}
642
643 ~Interval() { delete next_; }
644
645 size_t Length() {
646 size_t result = max_addr_ - min_addr_ + 1;
647 if (next_ != NULL) result += next_->Length();
648 return result;
649 }
650
651 void CloneFrom(Interval* src) {
652 while (src != NULL) {
653 RegisterAddress(src->min_addr_);
654 RegisterAddress(src->max_addr_);
655 src = src->next_;
656 }
657 }
658
659 bool Contains(Address addr) {
660 if (min_addr_ <= addr && addr <= max_addr_) {
661 return true;
662 }
663 if (next_ != NULL) {
664 return next_->Contains(addr);
665 } else {
666 return false;
667 }
668 }
669
670 size_t GetIndex(Address addr) {
671 if (min_addr_ <= addr && addr <= max_addr_) {
672 return addr - min_addr_;
673 }
674 CHECK_NE(NULL, next_);
675 return (max_addr_ - min_addr_ + 1) + next_->GetIndex(addr);
676 }
677
678 Address GetMinAddr() {
679 return next_ == NULL ? min_addr_ : i::Min(min_addr_, next_->GetMinAddr());
680 }
681
682 Address GetMaxAddr() {
683 return next_ == NULL ? max_addr_ : i::Max(max_addr_, next_->GetMaxAddr());
684 }
685
686 void RegisterAddress(Address addr) {
687 if (min_addr_ == reinterpret_cast<Address>(-1)
688 || (size_t)(addr > min_addr_ ?
689 addr - min_addr_ : min_addr_ - addr) < MAX_DELTA) {
690 if (addr < min_addr_) min_addr_ = addr;
691 if (addr > max_addr_) max_addr_ = addr;
692 } else {
693 if (next_ == NULL) next_ = new Interval();
694 next_->RegisterAddress(addr);
695 }
696 }
697
698 Address raw_min_addr() { return min_addr_; }
699
700 Address raw_max_addr() { return max_addr_; }
701
702 Interval* get_next() { return next_; }
703
704 private:
705 static const size_t MAX_DELTA = 0x100000;
706 Address min_addr_;
707 Address max_addr_;
708 Interval* next_;
709};
710
711
712// A structure used to return log parsing results.
713class ParseLogResult {
714 public:
715 ParseLogResult()
716 : entities_map(NULL), entities(NULL),
717 max_entities(0) {}
718
719 ~ParseLogResult() {
720 i::DeleteArray(entities_map);
721 i::DeleteArray(entities);
722 }
723
724 void AllocateEntities() {
725 // Make sure that the test doesn't operate on a bogus log.
726 CHECK_GT(max_entities, 0);
727 CHECK_GT(bounds.GetMinAddr(), 0);
728 CHECK_GT(bounds.GetMaxAddr(), bounds.GetMinAddr());
729
730 entities = i::NewArray<CodeEntityInfo>(max_entities);
731 for (int i = 0; i < max_entities; ++i) {
732 entities[i] = NULL;
733 }
734 const size_t map_length = bounds.Length();
Steve Blockd0582a62009-12-15 09:54:21 +0000735 entities_map = i::NewArray<int>(static_cast<int>(map_length));
Steve Blocka7e24c12009-10-30 11:49:00 +0000736 for (size_t i = 0; i < map_length; ++i) {
737 entities_map[i] = -1;
738 }
739 }
740
741 bool HasIndexForAddress(Address addr) {
742 return bounds.Contains(addr);
743 }
744
745 size_t GetIndexForAddress(Address addr) {
746 CHECK(HasIndexForAddress(addr));
747 return bounds.GetIndex(addr);
748 }
749
750 CodeEntityInfo GetEntity(Address addr) {
751 if (HasIndexForAddress(addr)) {
752 size_t idx = GetIndexForAddress(addr);
753 int item = entities_map[idx];
754 return item != -1 ? entities[item] : NULL;
755 }
756 return NULL;
757 }
758
759 void ParseAddress(char* start) {
760 Address addr =
761 reinterpret_cast<Address>(strtoul(start, NULL, 16)); // NOLINT
762 bounds.RegisterAddress(addr);
763 }
764
765 Address ConsumeAddress(char** start) {
766 char* end_ptr;
767 Address addr =
768 reinterpret_cast<Address>(strtoul(*start, &end_ptr, 16)); // NOLINT
769 CHECK(HasIndexForAddress(addr));
770 *start = end_ptr;
771 return addr;
772 }
773
774 Interval bounds;
775 // Memory map of entities start addresses.
776 int* entities_map;
777 // An array of code entities.
778 CodeEntityInfo* entities;
779 // Maximal entities count. Actual entities count can be lower,
780 // empty entity slots are pointing to NULL.
781 int max_entities;
782};
783
784} // namespace
785
786
787typedef void (*ParserBlock)(char* start, char* end, ParseLogResult* result);
788
789static void ParserCycle(
790 char* start, char* end, ParseLogResult* result,
791 ParserBlock block_creation, ParserBlock block_delete,
792 ParserBlock block_move) {
793
794 const char* code_creation = "code-creation,";
795 const char* code_delete = "code-delete,";
796 const char* code_move = "code-move,";
797
798 const char* lazy_compile = "LazyCompile,";
799 const char* script = "Script,";
800 const char* function = "Function,";
801
802 while (start < end) {
803 if (Consume(code_creation, &start)) {
804 if (Consume(lazy_compile, &start)
805 || Consume(script, &start)
806 || Consume(function, &start)) {
807 block_creation(start, end, result);
808 }
809 } else if (Consume(code_delete, &start)) {
810 block_delete(start, end, result);
811 } else if (Consume(code_move, &start)) {
812 block_move(start, end, result);
813 }
814 while (start < end && *start != '\n') ++start;
815 ++start;
816 }
817}
818
819
820static void Pass1CodeCreation(char* start, char* end, ParseLogResult* result) {
821 result->ParseAddress(start);
822 ++result->max_entities;
823}
824
825
826static void Pass1CodeDelete(char* start, char* end, ParseLogResult* result) {
827 result->ParseAddress(start);
828}
829
830
831static void Pass1CodeMove(char* start, char* end, ParseLogResult* result) {
832 result->ParseAddress(start);
833 // Skip old address.
834 while (start < end && *start != ',') ++start;
835 CHECK_GT(end, start);
836 ++start; // Skip ','.
837 result->ParseAddress(start);
838}
839
840
841static void Pass2CodeCreation(char* start, char* end, ParseLogResult* result) {
842 Address addr = result->ConsumeAddress(&start);
843 CHECK_GT(end, start);
844 ++start; // Skip ','.
845
846 size_t idx = result->GetIndexForAddress(addr);
847 result->entities_map[idx] = -1;
848 for (int i = 0; i < result->max_entities; ++i) {
849 // Find an empty slot and fill it.
850 if (result->entities[i] == NULL) {
851 result->entities[i] = start;
852 result->entities_map[idx] = i;
853 break;
854 }
855 }
856 // Make sure that a slot was found.
857 CHECK_GE(result->entities_map[idx], 0);
858}
859
860
861static void Pass2CodeDelete(char* start, char* end, ParseLogResult* result) {
862 Address addr = result->ConsumeAddress(&start);
863 size_t idx = result->GetIndexForAddress(addr);
864 // There can be code deletes that are not related to JS code.
865 if (result->entities_map[idx] >= 0) {
866 result->entities[result->entities_map[idx]] = NULL;
867 result->entities_map[idx] = -1;
868 }
869}
870
871
872static void Pass2CodeMove(char* start, char* end, ParseLogResult* result) {
873 Address from_addr = result->ConsumeAddress(&start);
874 CHECK_GT(end, start);
875 ++start; // Skip ','.
876 Address to_addr = result->ConsumeAddress(&start);
877 CHECK_GT(end, start);
878
879 size_t from_idx = result->GetIndexForAddress(from_addr);
880 size_t to_idx = result->GetIndexForAddress(to_addr);
881 // There can be code moves that are not related to JS code.
882 if (from_idx != to_idx && result->entities_map[from_idx] >= 0) {
883 CHECK_EQ(-1, result->entities_map[to_idx]);
884 result->entities_map[to_idx] = result->entities_map[from_idx];
885 result->entities_map[from_idx] = -1;
886 };
887}
888
889
890static void ParseLog(char* start, char* end, ParseLogResult* result) {
891 // Pass 1: Calculate boundaries of addresses and entities count.
892 ParserCycle(start, end, result,
893 Pass1CodeCreation, Pass1CodeDelete, Pass1CodeMove);
894
895 printf("min_addr: %p, max_addr: %p, entities: %d\n",
896 result->bounds.GetMinAddr(), result->bounds.GetMaxAddr(),
897 result->max_entities);
898
899 result->AllocateEntities();
900
901 // Pass 2: Fill in code entries data.
902 ParserCycle(start, end, result,
903 Pass2CodeCreation, Pass2CodeDelete, Pass2CodeMove);
904}
905
906
907static inline void PrintCodeEntityInfo(CodeEntityInfo entity) {
908 const int max_len = 50;
909 if (entity != NULL) {
910 char* eol = strchr(entity, '\n');
Steve Blockd0582a62009-12-15 09:54:21 +0000911 int len = static_cast<int>(eol - entity);
Steve Blocka7e24c12009-10-30 11:49:00 +0000912 len = len <= max_len ? len : max_len;
913 printf("%-*.*s ", max_len, len, entity);
914 } else {
915 printf("%*s", max_len + 1, "");
916 }
917}
918
919
920static void PrintCodeEntitiesInfo(
921 bool is_equal, Address addr,
922 CodeEntityInfo l_entity, CodeEntityInfo r_entity) {
923 printf("%c %p ", is_equal ? ' ' : '*', addr);
924 PrintCodeEntityInfo(l_entity);
925 PrintCodeEntityInfo(r_entity);
926 printf("\n");
927}
928
929
930static inline int StrChrLen(const char* s, char c) {
Steve Blockd0582a62009-12-15 09:54:21 +0000931 return static_cast<int>(strchr(s, c) - s);
Steve Blocka7e24c12009-10-30 11:49:00 +0000932}
933
934
935static bool AreFuncSizesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
936 int ref_len = StrChrLen(ref_s, ',');
937 int new_len = StrChrLen(new_s, ',');
938 return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
939}
940
941
942static bool AreFuncNamesEqual(CodeEntityInfo ref_s, CodeEntityInfo new_s) {
943 // Skip size.
944 ref_s = strchr(ref_s, ',') + 1;
945 new_s = strchr(new_s, ',') + 1;
946 int ref_len = StrChrLen(ref_s, '\n');
947 int new_len = StrChrLen(new_s, '\n');
948 // If reference is anonymous (""), it's OK to have anything in new.
949 if (ref_len == 2) return true;
950 // A special case for ErrorPrototype. Haven't yet figured out why they
951 // are different.
952 const char* error_prototype = "\"ErrorPrototype";
953 if (IsStringEqualTo(error_prototype, ref_s)
954 && IsStringEqualTo(error_prototype, new_s)) {
955 return true;
956 }
957 // Built-in objects have problems too.
958 const char* built_ins[] = {
959 "\"Boolean\"", "\"Function\"", "\"Number\"",
960 "\"Object\"", "\"Script\"", "\"String\""
961 };
962 for (size_t i = 0; i < sizeof(built_ins) / sizeof(*built_ins); ++i) {
963 if (IsStringEqualTo(built_ins[i], new_s)) {
964 return true;
965 }
966 }
967 return ref_len == new_len && strncmp(ref_s, new_s, ref_len) == 0;
968}
969
970
971static bool AreEntitiesEqual(CodeEntityInfo ref_e, CodeEntityInfo new_e) {
972 if (ref_e == NULL && new_e != NULL) return true;
973 if (ref_e != NULL && new_e != NULL) {
974 return AreFuncSizesEqual(ref_e, new_e) && AreFuncNamesEqual(ref_e, new_e);
975 }
976 if (ref_e != NULL && new_e == NULL) {
977 // args_count entities (argument adapters) are not found by heap traversal,
978 // but they are not needed because they doesn't contain any code.
979 ref_e = strchr(ref_e, ',') + 1;
980 const char* args_count = "\"args_count:";
981 return IsStringEqualTo(args_count, ref_e);
982 }
983 return false;
984}
985
986
987// Test that logging of code create / move / delete events
988// is equivalent to traversal of a resulting heap.
989TEST(EquivalenceOfLoggingAndTraversal) {
990 // This test needs to be run on a "clean" V8 to ensure that snapshot log
991 // is loaded. This is always true when running using tools/test.py because
992 // it launches a new cctest instance for every test. To be sure that launching
993 // cctest manually also works, please be sure that no tests below
994 // are using V8.
995 //
996 // P.S. No, V8 can't be re-initialized after disposal, see include/v8.h.
997 CHECK(!i::V8::IsRunning());
998
999 i::FLAG_logfile = "*";
1000 i::FLAG_log = true;
1001 i::FLAG_log_code = true;
1002
1003 // Make sure objects move.
1004 bool saved_always_compact = i::FLAG_always_compact;
1005 if (!i::FLAG_never_compact) {
1006 i::FLAG_always_compact = true;
1007 }
1008
1009 v8::HandleScope scope;
1010 v8::Handle<v8::Value> global_object = v8::Handle<v8::Value>();
1011 v8::Handle<v8::Context> env = v8::Context::New(
1012 0, v8::Handle<v8::ObjectTemplate>(), global_object);
1013 env->Enter();
1014
1015 // Compile and run a function that creates other functions.
1016 CompileAndRunScript(
1017 "(function f(obj) {\n"
1018 " obj.test =\n"
1019 " (function a(j) { return function b() { return j; } })(100);\n"
1020 "})(this);");
1021 i::Heap::CollectAllGarbage(false);
1022
1023 EmbeddedVector<char, 204800> buffer;
1024 int log_size;
1025 ParseLogResult ref_result;
1026
1027 // Retrieve the log.
1028 {
1029 // Make sure that no GCs occur prior to LogCompiledFunctions call.
1030 i::AssertNoAllocation no_alloc;
1031
1032 log_size = GetLogLines(0, &buffer);
1033 CHECK_GT(log_size, 0);
1034 CHECK_GT(buffer.length(), log_size);
1035
1036 // Fill a map of compiled code objects.
1037 ParseLog(buffer.start(), buffer.start() + log_size, &ref_result);
1038 }
1039
1040 // Iterate heap to find compiled functions, will write to log.
1041 i::Logger::LogCompiledFunctions();
1042 char* new_log_start = buffer.start() + log_size;
1043 const int new_log_size = Logger::GetLogLines(
1044 log_size, new_log_start, buffer.length() - log_size);
1045 CHECK_GT(new_log_size, 0);
1046 CHECK_GT(buffer.length(), log_size + new_log_size);
1047
1048 // Fill an equivalent map of compiled code objects.
1049 ParseLogResult new_result;
1050 ParseLog(new_log_start, new_log_start + new_log_size, &new_result);
1051
1052 // Test their actual equivalence.
1053 Interval combined;
1054 combined.CloneFrom(&ref_result.bounds);
1055 combined.CloneFrom(&new_result.bounds);
1056 Interval* iter = &combined;
1057 bool results_equal = true;
1058
1059 while (iter != NULL) {
1060 for (Address addr = iter->raw_min_addr();
1061 addr <= iter->raw_max_addr(); ++addr) {
1062 CodeEntityInfo ref_entity = ref_result.GetEntity(addr);
1063 CodeEntityInfo new_entity = new_result.GetEntity(addr);
1064 if (ref_entity != NULL || new_entity != NULL) {
1065 const bool equal = AreEntitiesEqual(ref_entity, new_entity);
1066 if (!equal) results_equal = false;
1067 PrintCodeEntitiesInfo(equal, addr, ref_entity, new_entity);
1068 }
1069 }
1070 iter = iter->get_next();
1071 }
1072 // Make sure that all log data is written prior crash due to CHECK failure.
1073 fflush(stdout);
1074 CHECK(results_equal);
1075
1076 env->Exit();
1077 Logger::TearDown();
1078 i::FLAG_always_compact = saved_always_compact;
1079}
1080
1081#endif // ENABLE_LOGGING_AND_PROFILING