blob: 55f15debd04e145c34762271854150faef99f121 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdarg.h>
29
30#include "v8.h"
31
32#include "bootstrapper.h"
Kristian Monsen80d68ea2010-09-08 11:05:35 +010033#include "code-stubs.h"
Steve Blockd0582a62009-12-15 09:54:21 +000034#include "global-handles.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000035#include "log.h"
36#include "macro-assembler.h"
37#include "serialize.h"
38#include "string-stream.h"
39
40namespace v8 {
41namespace internal {
42
43#ifdef ENABLE_LOGGING_AND_PROFILING
44
45//
46// Sliding state window. Updates counters to keep track of the last
47// window of kBufferSize states. This is useful to track where we
48// spent our time.
49//
50class SlidingStateWindow {
51 public:
52 SlidingStateWindow();
53 ~SlidingStateWindow();
54 void AddState(StateTag state);
55
56 private:
57 static const int kBufferSize = 256;
58 int current_index_;
59 bool is_full_;
60 byte buffer_[kBufferSize];
61
62
63 void IncrementStateCounter(StateTag state) {
64 Counters::state_counters[state].Increment();
65 }
66
67
68 void DecrementStateCounter(StateTag state) {
69 Counters::state_counters[state].Decrement();
70 }
71};
72
73
74//
75// The Profiler samples pc and sp values for the main thread.
76// Each sample is appended to a circular buffer.
77// An independent thread removes data and writes it to the log.
78// This design minimizes the time spent in the sampler.
79//
80class Profiler: public Thread {
81 public:
82 Profiler();
83 void Engage();
84 void Disengage();
85
86 // Inserts collected profiling data into buffer.
87 void Insert(TickSample* sample) {
88 if (paused_)
89 return;
90
91 if (Succ(head_) == tail_) {
92 overflow_ = true;
93 } else {
94 buffer_[head_] = *sample;
95 head_ = Succ(head_);
96 buffer_semaphore_->Signal(); // Tell we have an element.
97 }
98 }
99
100 // Waits for a signal and removes profiling data.
101 bool Remove(TickSample* sample) {
102 buffer_semaphore_->Wait(); // Wait for an element.
103 *sample = buffer_[tail_];
104 bool result = overflow_;
105 tail_ = Succ(tail_);
106 overflow_ = false;
107 return result;
108 }
109
110 void Run();
111
112 // Pause and Resume TickSample data collection.
113 static bool paused() { return paused_; }
114 static void pause() { paused_ = true; }
115 static void resume() { paused_ = false; }
116
117 private:
118 // Returns the next index in the cyclic buffer.
119 int Succ(int index) { return (index + 1) % kBufferSize; }
120
121 // Cyclic buffer for communicating profiling samples
122 // between the signal handler and the worker thread.
123 static const int kBufferSize = 128;
124 TickSample buffer_[kBufferSize]; // Buffer storage.
125 int head_; // Index to the buffer head.
126 int tail_; // Index to the buffer tail.
127 bool overflow_; // Tell whether a buffer overflow has occurred.
128 Semaphore* buffer_semaphore_; // Sempahore used for buffer synchronization.
129
Steve Blockd0582a62009-12-15 09:54:21 +0000130 // Tells whether profiler is engaged, that is, processing thread is stated.
131 bool engaged_;
132
Steve Blocka7e24c12009-10-30 11:49:00 +0000133 // Tells whether worker thread should continue running.
134 bool running_;
135
136 // Tells whether we are currently recording tick samples.
137 static bool paused_;
138};
139
140bool Profiler::paused_ = false;
141
142
143//
144// StackTracer implementation
145//
146void StackTracer::Trace(TickSample* sample) {
Steve Block6ded16b2010-05-10 14:33:55 +0100147 sample->function = NULL;
148 sample->frames_count = 0;
149
150 if (sample->state == GC) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000151
152 const Address js_entry_sp = Top::js_entry_sp(Top::GetCurrentThread());
153 if (js_entry_sp == 0) {
154 // Not executing JS now.
Steve Blocka7e24c12009-10-30 11:49:00 +0000155 return;
156 }
157
Leon Clarked91b9f72010-01-27 17:25:45 +0000158 const Address functionAddr =
159 sample->fp + JavaScriptFrameConstants::kFunctionOffset;
160 if (SafeStackFrameIterator::IsWithinBounds(sample->sp, js_entry_sp,
161 functionAddr)) {
162 sample->function = Memory::Address_at(functionAddr) - kHeapObjectTag;
163 }
164
Steve Blockd0582a62009-12-15 09:54:21 +0000165 int i = 0;
Steve Block6ded16b2010-05-10 14:33:55 +0100166 const Address callback = VMState::external_callback();
Teng-Hui Zhu3e5fa292010-11-09 16:16:48 -0800167 // Surprisingly, PC can point _exactly_ to callback start, with good
168 // probability, and this will result in reporting fake nested
169 // callback call.
170 if (callback != NULL && callback != sample->pc) {
Steve Blockd0582a62009-12-15 09:54:21 +0000171 sample->stack[i++] = callback;
172 }
173
Leon Clarked91b9f72010-01-27 17:25:45 +0000174 SafeStackTraceFrameIterator it(sample->fp, sample->sp,
175 sample->sp, js_entry_sp);
Steve Blocka7e24c12009-10-30 11:49:00 +0000176 while (!it.done() && i < TickSample::kMaxFramesCount) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100177 sample->stack[i++] =
178 reinterpret_cast<Address>(it.frame()->function_slot_object()) -
179 kHeapObjectTag;
Steve Blocka7e24c12009-10-30 11:49:00 +0000180 it.Advance();
181 }
182 sample->frames_count = i;
183}
184
185
186//
187// Ticker used to provide ticks to the profiler and the sliding state
188// window.
189//
190class Ticker: public Sampler {
191 public:
192 explicit Ticker(int interval):
193 Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL) {}
194
195 ~Ticker() { if (IsActive()) Stop(); }
196
Ben Murdochf87a2032010-10-22 12:50:53 +0100197 virtual void Tick(TickSample* sample) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000198 if (profiler_) profiler_->Insert(sample);
199 if (window_) window_->AddState(sample->state);
200 }
201
202 void SetWindow(SlidingStateWindow* window) {
203 window_ = window;
204 if (!IsActive()) Start();
205 }
206
207 void ClearWindow() {
208 window_ = NULL;
209 if (!profiler_ && IsActive()) Stop();
210 }
211
212 void SetProfiler(Profiler* profiler) {
213 profiler_ = profiler;
214 if (!FLAG_prof_lazy && !IsActive()) Start();
215 }
216
217 void ClearProfiler() {
218 profiler_ = NULL;
219 if (!window_ && IsActive()) Stop();
220 }
221
Shimeng (Simon) Wang8a31eba2010-12-06 19:01:33 -0800222 protected:
223 virtual void DoSampleStack(TickSample* sample) {
224 ASSERT(IsSynchronous());
225 StackTracer::Trace(sample);
226 }
227
Steve Blocka7e24c12009-10-30 11:49:00 +0000228 private:
229 SlidingStateWindow* window_;
230 Profiler* profiler_;
231};
232
233
234//
235// SlidingStateWindow implementation.
236//
237SlidingStateWindow::SlidingStateWindow(): current_index_(0), is_full_(false) {
238 for (int i = 0; i < kBufferSize; i++) {
239 buffer_[i] = static_cast<byte>(OTHER);
240 }
241 Logger::ticker_->SetWindow(this);
242}
243
244
245SlidingStateWindow::~SlidingStateWindow() {
246 Logger::ticker_->ClearWindow();
247}
248
249
250void SlidingStateWindow::AddState(StateTag state) {
251 if (is_full_) {
252 DecrementStateCounter(static_cast<StateTag>(buffer_[current_index_]));
253 } else if (current_index_ == kBufferSize - 1) {
254 is_full_ = true;
255 }
256 buffer_[current_index_] = static_cast<byte>(state);
257 IncrementStateCounter(state);
258 ASSERT(IsPowerOf2(kBufferSize));
259 current_index_ = (current_index_ + 1) & (kBufferSize - 1);
260}
261
262
263//
264// Profiler implementation.
265//
Steve Blockd0582a62009-12-15 09:54:21 +0000266Profiler::Profiler()
267 : head_(0),
268 tail_(0),
269 overflow_(false),
270 buffer_semaphore_(OS::CreateSemaphore(0)),
271 engaged_(false),
272 running_(false) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000273}
274
275
276void Profiler::Engage() {
Steve Blockd0582a62009-12-15 09:54:21 +0000277 if (engaged_) return;
278 engaged_ = true;
279
280 // TODO(mnaganov): This is actually "Chromium" mode. Flags need to be revised.
281 // http://code.google.com/p/v8/issues/detail?id=487
282 if (!FLAG_prof_lazy) {
283 OS::LogSharedLibraryAddresses();
284 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000285
286 // Start thread processing the profiler buffer.
287 running_ = true;
288 Start();
289
290 // Register to get ticks.
291 Logger::ticker_->SetProfiler(this);
292
293 Logger::ProfilerBeginEvent();
294 Logger::LogAliases();
295}
296
297
298void Profiler::Disengage() {
Steve Blockd0582a62009-12-15 09:54:21 +0000299 if (!engaged_) return;
300
Steve Blocka7e24c12009-10-30 11:49:00 +0000301 // Stop receiving ticks.
302 Logger::ticker_->ClearProfiler();
303
304 // Terminate the worker thread by setting running_ to false,
305 // inserting a fake element in the queue and then wait for
306 // the thread to terminate.
307 running_ = false;
308 TickSample sample;
309 // Reset 'paused_' flag, otherwise semaphore may not be signalled.
310 resume();
311 Insert(&sample);
312 Join();
313
314 LOG(UncheckedStringEvent("profiler", "end"));
315}
316
317
318void Profiler::Run() {
319 TickSample sample;
Steve Block8defd9f2010-07-08 12:39:36 +0100320 bool overflow = Remove(&sample);
Steve Blocka7e24c12009-10-30 11:49:00 +0000321 while (running_) {
322 LOG(TickEvent(&sample, overflow));
Steve Block8defd9f2010-07-08 12:39:36 +0100323 overflow = Remove(&sample);
Steve Blocka7e24c12009-10-30 11:49:00 +0000324 }
325}
326
327
328//
329// Logger class implementation.
330//
331Ticker* Logger::ticker_ = NULL;
332Profiler* Logger::profiler_ = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000333SlidingStateWindow* Logger::sliding_state_window_ = NULL;
334const char** Logger::log_events_ = NULL;
335CompressionHelper* Logger::compression_helper_ = NULL;
Steve Block6ded16b2010-05-10 14:33:55 +0100336int Logger::logging_nesting_ = 0;
Andrei Popescu402d9372010-02-26 13:31:12 +0000337int Logger::cpu_profiler_nesting_ = 0;
338int Logger::heap_profiler_nesting_ = 0;
Steve Blocka7e24c12009-10-30 11:49:00 +0000339
340#define DECLARE_LONG_EVENT(ignore1, long_name, ignore2) long_name,
341const char* kLongLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
342 LOG_EVENTS_AND_TAGS_LIST(DECLARE_LONG_EVENT)
343};
344#undef DECLARE_LONG_EVENT
345
346#define DECLARE_SHORT_EVENT(ignore1, ignore2, short_name) short_name,
347const char* kCompressedLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
348 LOG_EVENTS_AND_TAGS_LIST(DECLARE_SHORT_EVENT)
349};
350#undef DECLARE_SHORT_EVENT
351
352
353void Logger::ProfilerBeginEvent() {
354 if (!Log::IsEnabled()) return;
355 LogMessageBuilder msg;
356 msg.Append("profiler,\"begin\",%d\n", kSamplingIntervalMs);
357 if (FLAG_compress_log) {
358 msg.Append("profiler,\"compression\",%d\n", kCompressionWindowSize);
359 }
360 msg.WriteToLogFile();
361}
362
363
364void Logger::LogAliases() {
365 if (!Log::IsEnabled() || !FLAG_compress_log) return;
366 LogMessageBuilder msg;
367 for (int i = 0; i < NUMBER_OF_LOG_EVENTS; ++i) {
368 msg.Append("alias,%s,%s\n",
369 kCompressedLogEventsNames[i], kLongLogEventsNames[i]);
370 }
371 msg.WriteToLogFile();
372}
373
374#endif // ENABLE_LOGGING_AND_PROFILING
375
376
Steve Blocka7e24c12009-10-30 11:49:00 +0000377void Logger::StringEvent(const char* name, const char* value) {
378#ifdef ENABLE_LOGGING_AND_PROFILING
379 if (FLAG_log) UncheckedStringEvent(name, value);
380#endif
381}
382
383
384#ifdef ENABLE_LOGGING_AND_PROFILING
385void Logger::UncheckedStringEvent(const char* name, const char* value) {
386 if (!Log::IsEnabled()) return;
387 LogMessageBuilder msg;
388 msg.Append("%s,\"%s\"\n", name, value);
389 msg.WriteToLogFile();
390}
391#endif
392
393
394void Logger::IntEvent(const char* name, int value) {
395#ifdef ENABLE_LOGGING_AND_PROFILING
Steve Block6ded16b2010-05-10 14:33:55 +0100396 if (FLAG_log) UncheckedIntEvent(name, value);
397#endif
398}
399
400
Ben Murdochf87a2032010-10-22 12:50:53 +0100401void Logger::IntPtrTEvent(const char* name, intptr_t value) {
402#ifdef ENABLE_LOGGING_AND_PROFILING
403 if (FLAG_log) UncheckedIntPtrTEvent(name, value);
404#endif
405}
406
407
Steve Block6ded16b2010-05-10 14:33:55 +0100408#ifdef ENABLE_LOGGING_AND_PROFILING
409void Logger::UncheckedIntEvent(const char* name, int value) {
410 if (!Log::IsEnabled()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000411 LogMessageBuilder msg;
412 msg.Append("%s,%d\n", name, value);
413 msg.WriteToLogFile();
Steve Blocka7e24c12009-10-30 11:49:00 +0000414}
Steve Block6ded16b2010-05-10 14:33:55 +0100415#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000416
417
Ben Murdochf87a2032010-10-22 12:50:53 +0100418#ifdef ENABLE_LOGGING_AND_PROFILING
419void Logger::UncheckedIntPtrTEvent(const char* name, intptr_t value) {
420 if (!Log::IsEnabled()) return;
421 LogMessageBuilder msg;
422 msg.Append("%s,%" V8_PTR_PREFIX "d\n", name, value);
423 msg.WriteToLogFile();
424}
425#endif
426
427
Steve Blocka7e24c12009-10-30 11:49:00 +0000428void Logger::HandleEvent(const char* name, Object** location) {
429#ifdef ENABLE_LOGGING_AND_PROFILING
430 if (!Log::IsEnabled() || !FLAG_log_handles) return;
431 LogMessageBuilder msg;
432 msg.Append("%s,0x%" V8PRIxPTR "\n", name, location);
433 msg.WriteToLogFile();
434#endif
435}
436
437
438#ifdef ENABLE_LOGGING_AND_PROFILING
439// ApiEvent is private so all the calls come from the Logger class. It is the
440// caller's responsibility to ensure that log is enabled and that
441// FLAG_log_api is true.
442void Logger::ApiEvent(const char* format, ...) {
443 ASSERT(Log::IsEnabled() && FLAG_log_api);
444 LogMessageBuilder msg;
445 va_list ap;
446 va_start(ap, format);
447 msg.AppendVA(format, ap);
448 va_end(ap);
449 msg.WriteToLogFile();
450}
451#endif
452
453
454void Logger::ApiNamedSecurityCheck(Object* key) {
455#ifdef ENABLE_LOGGING_AND_PROFILING
456 if (!Log::IsEnabled() || !FLAG_log_api) return;
457 if (key->IsString()) {
458 SmartPointer<char> str =
459 String::cast(key)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
460 ApiEvent("api,check-security,\"%s\"\n", *str);
461 } else if (key->IsUndefined()) {
462 ApiEvent("api,check-security,undefined\n");
463 } else {
464 ApiEvent("api,check-security,['no-name']\n");
465 }
466#endif
467}
468
469
470void Logger::SharedLibraryEvent(const char* library_path,
471 uintptr_t start,
472 uintptr_t end) {
473#ifdef ENABLE_LOGGING_AND_PROFILING
474 if (!Log::IsEnabled() || !FLAG_prof) return;
475 LogMessageBuilder msg;
476 msg.Append("shared-library,\"%s\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR "\n",
477 library_path,
478 start,
479 end);
480 msg.WriteToLogFile();
481#endif
482}
483
484
485void Logger::SharedLibraryEvent(const wchar_t* library_path,
486 uintptr_t start,
487 uintptr_t end) {
488#ifdef ENABLE_LOGGING_AND_PROFILING
489 if (!Log::IsEnabled() || !FLAG_prof) return;
490 LogMessageBuilder msg;
491 msg.Append("shared-library,\"%ls\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR "\n",
492 library_path,
493 start,
494 end);
495 msg.WriteToLogFile();
496#endif
497}
498
499
500#ifdef ENABLE_LOGGING_AND_PROFILING
501void Logger::LogRegExpSource(Handle<JSRegExp> regexp) {
502 // Prints "/" + re.source + "/" +
503 // (re.global?"g":"") + (re.ignorecase?"i":"") + (re.multiline?"m":"")
504 LogMessageBuilder msg;
505
506 Handle<Object> source = GetProperty(regexp, "source");
507 if (!source->IsString()) {
508 msg.Append("no source");
509 return;
510 }
511
512 switch (regexp->TypeTag()) {
513 case JSRegExp::ATOM:
514 msg.Append('a');
515 break;
516 default:
517 break;
518 }
519 msg.Append('/');
520 msg.AppendDetailed(*Handle<String>::cast(source), false);
521 msg.Append('/');
522
523 // global flag
524 Handle<Object> global = GetProperty(regexp, "global");
525 if (global->IsTrue()) {
526 msg.Append('g');
527 }
528 // ignorecase flag
529 Handle<Object> ignorecase = GetProperty(regexp, "ignoreCase");
530 if (ignorecase->IsTrue()) {
531 msg.Append('i');
532 }
533 // multiline flag
534 Handle<Object> multiline = GetProperty(regexp, "multiline");
535 if (multiline->IsTrue()) {
536 msg.Append('m');
537 }
538
539 msg.WriteToLogFile();
540}
541#endif // ENABLE_LOGGING_AND_PROFILING
542
543
544void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache) {
545#ifdef ENABLE_LOGGING_AND_PROFILING
546 if (!Log::IsEnabled() || !FLAG_log_regexp) return;
547 LogMessageBuilder msg;
548 msg.Append("regexp-compile,");
549 LogRegExpSource(regexp);
550 msg.Append(in_cache ? ",hit\n" : ",miss\n");
551 msg.WriteToLogFile();
552#endif
553}
554
555
556void Logger::LogRuntime(Vector<const char> format, JSArray* args) {
557#ifdef ENABLE_LOGGING_AND_PROFILING
558 if (!Log::IsEnabled() || !FLAG_log_runtime) return;
559 HandleScope scope;
560 LogMessageBuilder msg;
561 for (int i = 0; i < format.length(); i++) {
562 char c = format[i];
563 if (c == '%' && i <= format.length() - 2) {
564 i++;
565 ASSERT('0' <= format[i] && format[i] <= '9');
John Reck59135872010-11-02 12:39:01 -0700566 MaybeObject* maybe = args->GetElement(format[i] - '0');
567 Object* obj;
568 if (!maybe->ToObject(&obj)) {
569 msg.Append("<exception>");
570 continue;
571 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000572 i++;
573 switch (format[i]) {
574 case 's':
575 msg.AppendDetailed(String::cast(obj), false);
576 break;
577 case 'S':
578 msg.AppendDetailed(String::cast(obj), true);
579 break;
580 case 'r':
581 Logger::LogRegExpSource(Handle<JSRegExp>(JSRegExp::cast(obj)));
582 break;
583 case 'x':
584 msg.Append("0x%x", Smi::cast(obj)->value());
585 break;
586 case 'i':
587 msg.Append("%i", Smi::cast(obj)->value());
588 break;
589 default:
590 UNREACHABLE();
591 }
592 } else {
593 msg.Append(c);
594 }
595 }
596 msg.Append('\n');
597 msg.WriteToLogFile();
598#endif
599}
600
601
602void Logger::ApiIndexedSecurityCheck(uint32_t index) {
603#ifdef ENABLE_LOGGING_AND_PROFILING
604 if (!Log::IsEnabled() || !FLAG_log_api) return;
605 ApiEvent("api,check-security,%u\n", index);
606#endif
607}
608
609
610void Logger::ApiNamedPropertyAccess(const char* tag,
611 JSObject* holder,
612 Object* name) {
613#ifdef ENABLE_LOGGING_AND_PROFILING
614 ASSERT(name->IsString());
615 if (!Log::IsEnabled() || !FLAG_log_api) return;
616 String* class_name_obj = holder->class_name();
617 SmartPointer<char> class_name =
618 class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
619 SmartPointer<char> property_name =
620 String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
621 Logger::ApiEvent("api,%s,\"%s\",\"%s\"\n", tag, *class_name, *property_name);
622#endif
623}
624
625void Logger::ApiIndexedPropertyAccess(const char* tag,
626 JSObject* holder,
627 uint32_t index) {
628#ifdef ENABLE_LOGGING_AND_PROFILING
629 if (!Log::IsEnabled() || !FLAG_log_api) return;
630 String* class_name_obj = holder->class_name();
631 SmartPointer<char> class_name =
632 class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
633 Logger::ApiEvent("api,%s,\"%s\",%u\n", tag, *class_name, index);
634#endif
635}
636
637void Logger::ApiObjectAccess(const char* tag, JSObject* object) {
638#ifdef ENABLE_LOGGING_AND_PROFILING
639 if (!Log::IsEnabled() || !FLAG_log_api) return;
640 String* class_name_obj = object->class_name();
641 SmartPointer<char> class_name =
642 class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
643 Logger::ApiEvent("api,%s,\"%s\"\n", tag, *class_name);
644#endif
645}
646
647
648void Logger::ApiEntryCall(const char* name) {
649#ifdef ENABLE_LOGGING_AND_PROFILING
650 if (!Log::IsEnabled() || !FLAG_log_api) return;
651 Logger::ApiEvent("api,%s\n", name);
652#endif
653}
654
655
656void Logger::NewEvent(const char* name, void* object, size_t size) {
657#ifdef ENABLE_LOGGING_AND_PROFILING
658 if (!Log::IsEnabled() || !FLAG_log) return;
659 LogMessageBuilder msg;
660 msg.Append("new,%s,0x%" V8PRIxPTR ",%u\n", name, object,
661 static_cast<unsigned int>(size));
662 msg.WriteToLogFile();
663#endif
664}
665
666
667void Logger::DeleteEvent(const char* name, void* object) {
668#ifdef ENABLE_LOGGING_AND_PROFILING
669 if (!Log::IsEnabled() || !FLAG_log) return;
670 LogMessageBuilder msg;
671 msg.Append("delete,%s,0x%" V8PRIxPTR "\n", name, object);
672 msg.WriteToLogFile();
673#endif
674}
675
676
677#ifdef ENABLE_LOGGING_AND_PROFILING
678
679// A class that contains all common code dealing with record compression.
680class CompressionHelper {
681 public:
682 explicit CompressionHelper(int window_size)
683 : compressor_(window_size), repeat_count_(0) { }
684
685 // Handles storing message in compressor, retrieving the previous one and
686 // prefixing it with repeat count, if needed.
687 // Returns true if message needs to be written to log.
688 bool HandleMessage(LogMessageBuilder* msg) {
689 if (!msg->StoreInCompressor(&compressor_)) {
690 // Current message repeats the previous one, don't write it.
691 ++repeat_count_;
692 return false;
693 }
694 if (repeat_count_ == 0) {
695 return msg->RetrieveCompressedPrevious(&compressor_);
696 }
697 OS::SNPrintF(prefix_, "%s,%d,",
698 Logger::log_events_[Logger::REPEAT_META_EVENT],
699 repeat_count_ + 1);
700 repeat_count_ = 0;
701 return msg->RetrieveCompressedPrevious(&compressor_, prefix_.start());
702 }
703
704 private:
705 LogRecordCompressor compressor_;
706 int repeat_count_;
707 EmbeddedVector<char, 20> prefix_;
708};
709
710#endif // ENABLE_LOGGING_AND_PROFILING
711
712
Steve Blockd0582a62009-12-15 09:54:21 +0000713#ifdef ENABLE_LOGGING_AND_PROFILING
714void Logger::CallbackEventInternal(const char* prefix, const char* name,
715 Address entry_point) {
716 if (!Log::IsEnabled() || !FLAG_log_code) return;
717 LogMessageBuilder msg;
718 msg.Append("%s,%s,",
719 log_events_[CODE_CREATION_EVENT], log_events_[CALLBACK_TAG]);
720 msg.AppendAddress(entry_point);
721 msg.Append(",1,\"%s%s\"", prefix, name);
722 if (FLAG_compress_log) {
723 ASSERT(compression_helper_ != NULL);
724 if (!compression_helper_->HandleMessage(&msg)) return;
725 }
726 msg.Append('\n');
727 msg.WriteToLogFile();
728}
729#endif
730
731
732void Logger::CallbackEvent(String* name, Address entry_point) {
733#ifdef ENABLE_LOGGING_AND_PROFILING
734 if (!Log::IsEnabled() || !FLAG_log_code) return;
735 SmartPointer<char> str =
736 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
737 CallbackEventInternal("", *str, entry_point);
738#endif
739}
740
741
742void Logger::GetterCallbackEvent(String* name, Address entry_point) {
743#ifdef ENABLE_LOGGING_AND_PROFILING
744 if (!Log::IsEnabled() || !FLAG_log_code) return;
745 SmartPointer<char> str =
746 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
747 CallbackEventInternal("get ", *str, entry_point);
748#endif
749}
750
751
752void Logger::SetterCallbackEvent(String* name, Address entry_point) {
753#ifdef ENABLE_LOGGING_AND_PROFILING
754 if (!Log::IsEnabled() || !FLAG_log_code) return;
755 SmartPointer<char> str =
756 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
757 CallbackEventInternal("set ", *str, entry_point);
758#endif
759}
760
761
Steve Blocka7e24c12009-10-30 11:49:00 +0000762void Logger::CodeCreateEvent(LogEventsAndTags tag,
763 Code* code,
764 const char* comment) {
765#ifdef ENABLE_LOGGING_AND_PROFILING
766 if (!Log::IsEnabled() || !FLAG_log_code) return;
767 LogMessageBuilder msg;
768 msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]);
769 msg.AppendAddress(code->address());
770 msg.Append(",%d,\"", code->ExecutableSize());
771 for (const char* p = comment; *p != '\0'; p++) {
772 if (*p == '"') {
773 msg.Append('\\');
774 }
775 msg.Append(*p);
776 }
777 msg.Append('"');
Ben Murdochf87a2032010-10-22 12:50:53 +0100778 LowLevelCodeCreateEvent(code, &msg);
Steve Blocka7e24c12009-10-30 11:49:00 +0000779 if (FLAG_compress_log) {
780 ASSERT(compression_helper_ != NULL);
781 if (!compression_helper_->HandleMessage(&msg)) return;
782 }
783 msg.Append('\n');
784 msg.WriteToLogFile();
785#endif
786}
787
788
789void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name) {
790#ifdef ENABLE_LOGGING_AND_PROFILING
791 if (!Log::IsEnabled() || !FLAG_log_code) return;
792 LogMessageBuilder msg;
793 SmartPointer<char> str =
794 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
795 msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]);
796 msg.AppendAddress(code->address());
797 msg.Append(",%d,\"%s\"", code->ExecutableSize(), *str);
Ben Murdochf87a2032010-10-22 12:50:53 +0100798 LowLevelCodeCreateEvent(code, &msg);
Steve Blocka7e24c12009-10-30 11:49:00 +0000799 if (FLAG_compress_log) {
800 ASSERT(compression_helper_ != NULL);
801 if (!compression_helper_->HandleMessage(&msg)) return;
802 }
803 msg.Append('\n');
804 msg.WriteToLogFile();
805#endif
806}
807
808
809void Logger::CodeCreateEvent(LogEventsAndTags tag,
810 Code* code, String* name,
811 String* source, int line) {
812#ifdef ENABLE_LOGGING_AND_PROFILING
813 if (!Log::IsEnabled() || !FLAG_log_code) return;
814 LogMessageBuilder msg;
815 SmartPointer<char> str =
816 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
817 SmartPointer<char> sourcestr =
818 source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
819 msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]);
820 msg.AppendAddress(code->address());
821 msg.Append(",%d,\"%s %s:%d\"",
822 code->ExecutableSize(), *str, *sourcestr, line);
Ben Murdochf87a2032010-10-22 12:50:53 +0100823 LowLevelCodeCreateEvent(code, &msg);
Steve Blocka7e24c12009-10-30 11:49:00 +0000824 if (FLAG_compress_log) {
825 ASSERT(compression_helper_ != NULL);
826 if (!compression_helper_->HandleMessage(&msg)) return;
827 }
828 msg.Append('\n');
829 msg.WriteToLogFile();
830#endif
831}
832
833
834void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) {
835#ifdef ENABLE_LOGGING_AND_PROFILING
836 if (!Log::IsEnabled() || !FLAG_log_code) return;
837 LogMessageBuilder msg;
838 msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]);
839 msg.AppendAddress(code->address());
840 msg.Append(",%d,\"args_count: %d\"", code->ExecutableSize(), args_count);
Ben Murdochf87a2032010-10-22 12:50:53 +0100841 LowLevelCodeCreateEvent(code, &msg);
Steve Blocka7e24c12009-10-30 11:49:00 +0000842 if (FLAG_compress_log) {
843 ASSERT(compression_helper_ != NULL);
844 if (!compression_helper_->HandleMessage(&msg)) return;
845 }
846 msg.Append('\n');
847 msg.WriteToLogFile();
848#endif
849}
850
851
Ben Murdochf87a2032010-10-22 12:50:53 +0100852void Logger::CodeMovingGCEvent() {
853#ifdef ENABLE_LOGGING_AND_PROFILING
854 if (!Log::IsEnabled() || !FLAG_log_code || !FLAG_ll_prof) return;
855 LogMessageBuilder msg;
856 msg.Append("%s\n", log_events_[CODE_MOVING_GC]);
857 msg.WriteToLogFile();
858 OS::SignalCodeMovingGC();
859#endif
860}
861
862
Steve Blocka7e24c12009-10-30 11:49:00 +0000863void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
864#ifdef ENABLE_LOGGING_AND_PROFILING
865 if (!Log::IsEnabled() || !FLAG_log_code) return;
866 LogMessageBuilder msg;
867 msg.Append("%s,%s,",
868 log_events_[CODE_CREATION_EVENT], log_events_[REG_EXP_TAG]);
869 msg.AppendAddress(code->address());
870 msg.Append(",%d,\"", code->ExecutableSize());
871 msg.AppendDetailed(source, false);
872 msg.Append('\"');
Ben Murdochf87a2032010-10-22 12:50:53 +0100873 LowLevelCodeCreateEvent(code, &msg);
Steve Blocka7e24c12009-10-30 11:49:00 +0000874 if (FLAG_compress_log) {
875 ASSERT(compression_helper_ != NULL);
876 if (!compression_helper_->HandleMessage(&msg)) return;
877 }
878 msg.Append('\n');
879 msg.WriteToLogFile();
880#endif
881}
882
883
884void Logger::CodeMoveEvent(Address from, Address to) {
885#ifdef ENABLE_LOGGING_AND_PROFILING
Leon Clarked91b9f72010-01-27 17:25:45 +0000886 MoveEventInternal(CODE_MOVE_EVENT, from, to);
Steve Blocka7e24c12009-10-30 11:49:00 +0000887#endif
888}
889
890
891void Logger::CodeDeleteEvent(Address from) {
892#ifdef ENABLE_LOGGING_AND_PROFILING
Leon Clarked91b9f72010-01-27 17:25:45 +0000893 DeleteEventInternal(CODE_DELETE_EVENT, from);
Steve Blocka7e24c12009-10-30 11:49:00 +0000894#endif
895}
896
897
Leon Clarkee46be812010-01-19 14:06:41 +0000898void Logger::SnapshotPositionEvent(Address addr, int pos) {
899#ifdef ENABLE_LOGGING_AND_PROFILING
900 if (!Log::IsEnabled() || !FLAG_log_snapshot_positions) return;
901 LogMessageBuilder msg;
902 msg.Append("%s,", log_events_[SNAPSHOT_POSITION_EVENT]);
903 msg.AppendAddress(addr);
904 msg.Append(",%d", pos);
905 if (FLAG_compress_log) {
906 ASSERT(compression_helper_ != NULL);
907 if (!compression_helper_->HandleMessage(&msg)) return;
908 }
909 msg.Append('\n');
910 msg.WriteToLogFile();
911#endif
912}
913
914
Leon Clarked91b9f72010-01-27 17:25:45 +0000915void Logger::FunctionCreateEvent(JSFunction* function) {
916#ifdef ENABLE_LOGGING_AND_PROFILING
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100917 // This function can be called from GC iterators (during Scavenge,
918 // MC, and MS), so marking bits can be set on objects. That's
919 // why unchecked accessors are used here.
Leon Clarked91b9f72010-01-27 17:25:45 +0000920 static Address prev_code = NULL;
921 if (!Log::IsEnabled() || !FLAG_log_code) return;
922 LogMessageBuilder msg;
923 msg.Append("%s,", log_events_[FUNCTION_CREATION_EVENT]);
924 msg.AppendAddress(function->address());
925 msg.Append(',');
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100926 msg.AppendAddress(function->unchecked_code()->address(), prev_code);
927 prev_code = function->unchecked_code()->address();
Leon Clarked91b9f72010-01-27 17:25:45 +0000928 if (FLAG_compress_log) {
929 ASSERT(compression_helper_ != NULL);
930 if (!compression_helper_->HandleMessage(&msg)) return;
931 }
932 msg.Append('\n');
933 msg.WriteToLogFile();
934#endif
935}
936
937
Ben Murdochf87a2032010-10-22 12:50:53 +0100938void Logger::FunctionCreateEventFromMove(JSFunction* function) {
Kristian Monsen0d5e1162010-09-30 15:31:59 +0100939#ifdef ENABLE_LOGGING_AND_PROFILING
940 if (function->unchecked_code() != Builtins::builtin(Builtins::LazyCompile)) {
941 FunctionCreateEvent(function);
942 }
943#endif
944}
945
946
Leon Clarked91b9f72010-01-27 17:25:45 +0000947void Logger::FunctionMoveEvent(Address from, Address to) {
948#ifdef ENABLE_LOGGING_AND_PROFILING
949 MoveEventInternal(FUNCTION_MOVE_EVENT, from, to);
950#endif
951}
952
953
954void Logger::FunctionDeleteEvent(Address from) {
955#ifdef ENABLE_LOGGING_AND_PROFILING
956 DeleteEventInternal(FUNCTION_DELETE_EVENT, from);
957#endif
958}
959
960
961#ifdef ENABLE_LOGGING_AND_PROFILING
962void Logger::MoveEventInternal(LogEventsAndTags event,
963 Address from,
964 Address to) {
965 static Address prev_to_ = NULL;
966 if (!Log::IsEnabled() || !FLAG_log_code) return;
967 LogMessageBuilder msg;
968 msg.Append("%s,", log_events_[event]);
969 msg.AppendAddress(from);
970 msg.Append(',');
971 msg.AppendAddress(to, prev_to_);
972 prev_to_ = to;
973 if (FLAG_compress_log) {
974 ASSERT(compression_helper_ != NULL);
975 if (!compression_helper_->HandleMessage(&msg)) return;
976 }
977 msg.Append('\n');
978 msg.WriteToLogFile();
979}
980#endif
981
982
983#ifdef ENABLE_LOGGING_AND_PROFILING
984void Logger::DeleteEventInternal(LogEventsAndTags event, Address from) {
985 if (!Log::IsEnabled() || !FLAG_log_code) return;
986 LogMessageBuilder msg;
987 msg.Append("%s,", log_events_[event]);
988 msg.AppendAddress(from);
989 if (FLAG_compress_log) {
990 ASSERT(compression_helper_ != NULL);
991 if (!compression_helper_->HandleMessage(&msg)) return;
992 }
993 msg.Append('\n');
994 msg.WriteToLogFile();
995}
996#endif
997
998
Steve Blocka7e24c12009-10-30 11:49:00 +0000999void Logger::ResourceEvent(const char* name, const char* tag) {
1000#ifdef ENABLE_LOGGING_AND_PROFILING
1001 if (!Log::IsEnabled() || !FLAG_log) return;
1002 LogMessageBuilder msg;
1003 msg.Append("%s,%s,", name, tag);
1004
1005 uint32_t sec, usec;
1006 if (OS::GetUserTime(&sec, &usec) != -1) {
1007 msg.Append("%d,%d,", sec, usec);
1008 }
1009 msg.Append("%.0f", OS::TimeCurrentMillis());
1010
1011 msg.Append('\n');
1012 msg.WriteToLogFile();
1013#endif
1014}
1015
1016
1017void Logger::SuspectReadEvent(String* name, Object* obj) {
1018#ifdef ENABLE_LOGGING_AND_PROFILING
1019 if (!Log::IsEnabled() || !FLAG_log_suspect) return;
1020 LogMessageBuilder msg;
1021 String* class_name = obj->IsJSObject()
1022 ? JSObject::cast(obj)->class_name()
1023 : Heap::empty_string();
1024 msg.Append("suspect-read,");
1025 msg.Append(class_name);
1026 msg.Append(',');
1027 msg.Append('"');
1028 msg.Append(name);
1029 msg.Append('"');
1030 msg.Append('\n');
1031 msg.WriteToLogFile();
1032#endif
1033}
1034
1035
1036void Logger::HeapSampleBeginEvent(const char* space, const char* kind) {
1037#ifdef ENABLE_LOGGING_AND_PROFILING
1038 if (!Log::IsEnabled() || !FLAG_log_gc) return;
1039 LogMessageBuilder msg;
1040 // Using non-relative system time in order to be able to synchronize with
1041 // external memory profiling events (e.g. DOM memory size).
1042 msg.Append("heap-sample-begin,\"%s\",\"%s\",%.0f\n",
1043 space, kind, OS::TimeCurrentMillis());
1044 msg.WriteToLogFile();
1045#endif
1046}
1047
1048
1049void Logger::HeapSampleStats(const char* space, const char* kind,
Ben Murdochf87a2032010-10-22 12:50:53 +01001050 intptr_t capacity, intptr_t used) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001051#ifdef ENABLE_LOGGING_AND_PROFILING
1052 if (!Log::IsEnabled() || !FLAG_log_gc) return;
1053 LogMessageBuilder msg;
Ben Murdochf87a2032010-10-22 12:50:53 +01001054 msg.Append("heap-sample-stats,\"%s\",\"%s\","
1055 "%" V8_PTR_PREFIX "d,%" V8_PTR_PREFIX "d\n",
Steve Blocka7e24c12009-10-30 11:49:00 +00001056 space, kind, capacity, used);
1057 msg.WriteToLogFile();
1058#endif
1059}
1060
1061
1062void Logger::HeapSampleEndEvent(const char* space, const char* kind) {
1063#ifdef ENABLE_LOGGING_AND_PROFILING
1064 if (!Log::IsEnabled() || !FLAG_log_gc) return;
1065 LogMessageBuilder msg;
1066 msg.Append("heap-sample-end,\"%s\",\"%s\"\n", space, kind);
1067 msg.WriteToLogFile();
1068#endif
1069}
1070
1071
1072void Logger::HeapSampleItemEvent(const char* type, int number, int bytes) {
1073#ifdef ENABLE_LOGGING_AND_PROFILING
1074 if (!Log::IsEnabled() || !FLAG_log_gc) return;
1075 LogMessageBuilder msg;
1076 msg.Append("heap-sample-item,%s,%d,%d\n", type, number, bytes);
1077 msg.WriteToLogFile();
1078#endif
1079}
1080
1081
1082void Logger::HeapSampleJSConstructorEvent(const char* constructor,
1083 int number, int bytes) {
1084#ifdef ENABLE_LOGGING_AND_PROFILING
1085 if (!Log::IsEnabled() || !FLAG_log_gc) return;
1086 LogMessageBuilder msg;
1087 msg.Append("heap-js-cons-item,%s,%d,%d\n", constructor, number, bytes);
1088 msg.WriteToLogFile();
1089#endif
1090}
1091
1092
1093void Logger::HeapSampleJSRetainersEvent(
1094 const char* constructor, const char* event) {
1095#ifdef ENABLE_LOGGING_AND_PROFILING
1096 if (!Log::IsEnabled() || !FLAG_log_gc) return;
1097 // Event starts with comma, so we don't have it in the format string.
1098 static const char* event_text = "heap-js-ret-item,%s";
1099 // We take placeholder strings into account, but it's OK to be conservative.
Steve Blockd0582a62009-12-15 09:54:21 +00001100 static const int event_text_len = StrLength(event_text);
1101 const int cons_len = StrLength(constructor);
1102 const int event_len = StrLength(event);
Steve Blocka7e24c12009-10-30 11:49:00 +00001103 int pos = 0;
1104 // Retainer lists can be long. We may need to split them into multiple events.
1105 do {
1106 LogMessageBuilder msg;
1107 msg.Append(event_text, constructor);
1108 int to_write = event_len - pos;
1109 if (to_write > Log::kMessageBufferSize - (cons_len + event_text_len)) {
1110 int cut_pos = pos + Log::kMessageBufferSize - (cons_len + event_text_len);
1111 ASSERT(cut_pos < event_len);
1112 while (cut_pos > pos && event[cut_pos] != ',') --cut_pos;
1113 if (event[cut_pos] != ',') {
1114 // Crash in debug mode, skip in release mode.
1115 ASSERT(false);
1116 return;
1117 }
1118 // Append a piece of event that fits, without trailing comma.
1119 msg.AppendStringPart(event + pos, cut_pos - pos);
1120 // Start next piece with comma.
1121 pos = cut_pos;
1122 } else {
1123 msg.Append("%s", event + pos);
1124 pos += event_len;
1125 }
1126 msg.Append('\n');
1127 msg.WriteToLogFile();
1128 } while (pos < event_len);
1129#endif
1130}
1131
1132
Steve Block3ce2e202009-11-05 08:53:23 +00001133void Logger::HeapSampleJSProducerEvent(const char* constructor,
1134 Address* stack) {
1135#ifdef ENABLE_LOGGING_AND_PROFILING
1136 if (!Log::IsEnabled() || !FLAG_log_gc) return;
1137 LogMessageBuilder msg;
1138 msg.Append("heap-js-prod-item,%s", constructor);
1139 while (*stack != NULL) {
1140 msg.Append(",0x%" V8PRIxPTR, *stack++);
1141 }
1142 msg.Append("\n");
1143 msg.WriteToLogFile();
1144#endif
1145}
1146
1147
Steve Blocka7e24c12009-10-30 11:49:00 +00001148void Logger::DebugTag(const char* call_site_tag) {
1149#ifdef ENABLE_LOGGING_AND_PROFILING
1150 if (!Log::IsEnabled() || !FLAG_log) return;
1151 LogMessageBuilder msg;
1152 msg.Append("debug-tag,%s\n", call_site_tag);
1153 msg.WriteToLogFile();
1154#endif
1155}
1156
1157
1158void Logger::DebugEvent(const char* event_type, Vector<uint16_t> parameter) {
1159#ifdef ENABLE_LOGGING_AND_PROFILING
1160 if (!Log::IsEnabled() || !FLAG_log) return;
1161 StringBuilder s(parameter.length() + 1);
1162 for (int i = 0; i < parameter.length(); ++i) {
1163 s.AddCharacter(static_cast<char>(parameter[i]));
1164 }
1165 char* parameter_string = s.Finalize();
1166 LogMessageBuilder msg;
1167 msg.Append("debug-queue-event,%s,%15.3f,%s\n",
1168 event_type,
1169 OS::TimeCurrentMillis(),
1170 parameter_string);
1171 DeleteArray(parameter_string);
1172 msg.WriteToLogFile();
1173#endif
1174}
1175
1176
1177#ifdef ENABLE_LOGGING_AND_PROFILING
1178void Logger::TickEvent(TickSample* sample, bool overflow) {
1179 if (!Log::IsEnabled() || !FLAG_prof) return;
1180 static Address prev_sp = NULL;
Leon Clarked91b9f72010-01-27 17:25:45 +00001181 static Address prev_function = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +00001182 LogMessageBuilder msg;
1183 msg.Append("%s,", log_events_[TICK_EVENT]);
Leon Clarked91b9f72010-01-27 17:25:45 +00001184 Address prev_addr = sample->pc;
Steve Blocka7e24c12009-10-30 11:49:00 +00001185 msg.AppendAddress(prev_addr);
1186 msg.Append(',');
Leon Clarked91b9f72010-01-27 17:25:45 +00001187 msg.AppendAddress(sample->sp, prev_sp);
1188 prev_sp = sample->sp;
1189 msg.Append(',');
1190 msg.AppendAddress(sample->function, prev_function);
1191 prev_function = sample->function;
Steve Blocka7e24c12009-10-30 11:49:00 +00001192 msg.Append(",%d", static_cast<int>(sample->state));
1193 if (overflow) {
1194 msg.Append(",overflow");
1195 }
1196 for (int i = 0; i < sample->frames_count; ++i) {
1197 msg.Append(',');
1198 msg.AppendAddress(sample->stack[i], prev_addr);
1199 prev_addr = sample->stack[i];
1200 }
1201 if (FLAG_compress_log) {
1202 ASSERT(compression_helper_ != NULL);
1203 if (!compression_helper_->HandleMessage(&msg)) return;
1204 }
1205 msg.Append('\n');
1206 msg.WriteToLogFile();
1207}
1208
1209
1210int Logger::GetActiveProfilerModules() {
1211 int result = PROFILER_MODULE_NONE;
Steve Block8defd9f2010-07-08 12:39:36 +01001212 if (profiler_ != NULL && !profiler_->paused()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001213 result |= PROFILER_MODULE_CPU;
1214 }
1215 if (FLAG_log_gc) {
1216 result |= PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS;
1217 }
1218 return result;
1219}
1220
1221
Andrei Popescu402d9372010-02-26 13:31:12 +00001222void Logger::PauseProfiler(int flags, int tag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001223 if (!Log::IsEnabled()) return;
Steve Block8defd9f2010-07-08 12:39:36 +01001224 if (profiler_ != NULL && (flags & PROFILER_MODULE_CPU)) {
Andrei Popescu402d9372010-02-26 13:31:12 +00001225 // It is OK to have negative nesting.
1226 if (--cpu_profiler_nesting_ == 0) {
1227 profiler_->pause();
1228 if (FLAG_prof_lazy) {
1229 if (!FLAG_sliding_state_window) ticker_->Stop();
1230 FLAG_log_code = false;
1231 // Must be the same message as Log::kDynamicBufferSeal.
1232 LOG(UncheckedStringEvent("profiler", "pause"));
1233 }
Steve Block6ded16b2010-05-10 14:33:55 +01001234 --logging_nesting_;
Steve Blocka7e24c12009-10-30 11:49:00 +00001235 }
1236 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001237 if (flags &
Steve Blocka7e24c12009-10-30 11:49:00 +00001238 (PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
Andrei Popescu402d9372010-02-26 13:31:12 +00001239 if (--heap_profiler_nesting_ == 0) {
1240 FLAG_log_gc = false;
Steve Block6ded16b2010-05-10 14:33:55 +01001241 --logging_nesting_;
Andrei Popescu402d9372010-02-26 13:31:12 +00001242 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001243 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001244 if (tag != 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01001245 UncheckedIntEvent("close-tag", tag);
Steve Blocka7e24c12009-10-30 11:49:00 +00001246 }
1247}
1248
1249
Andrei Popescu402d9372010-02-26 13:31:12 +00001250void Logger::ResumeProfiler(int flags, int tag) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001251 if (!Log::IsEnabled()) return;
Andrei Popescu402d9372010-02-26 13:31:12 +00001252 if (tag != 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01001253 UncheckedIntEvent("open-tag", tag);
Steve Blocka7e24c12009-10-30 11:49:00 +00001254 }
Steve Block8defd9f2010-07-08 12:39:36 +01001255 if (profiler_ != NULL && (flags & PROFILER_MODULE_CPU)) {
Andrei Popescu402d9372010-02-26 13:31:12 +00001256 if (cpu_profiler_nesting_++ == 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01001257 ++logging_nesting_;
Andrei Popescu402d9372010-02-26 13:31:12 +00001258 if (FLAG_prof_lazy) {
1259 profiler_->Engage();
1260 LOG(UncheckedStringEvent("profiler", "resume"));
1261 FLAG_log_code = true;
1262 LogCompiledFunctions();
1263 LogFunctionObjects();
1264 LogAccessorCallbacks();
1265 if (!FLAG_sliding_state_window) ticker_->Start();
1266 }
1267 profiler_->resume();
Steve Blocka7e24c12009-10-30 11:49:00 +00001268 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001269 }
Andrei Popescu402d9372010-02-26 13:31:12 +00001270 if (flags &
Steve Blocka7e24c12009-10-30 11:49:00 +00001271 (PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
Andrei Popescu402d9372010-02-26 13:31:12 +00001272 if (heap_profiler_nesting_++ == 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01001273 ++logging_nesting_;
Andrei Popescu402d9372010-02-26 13:31:12 +00001274 FLAG_log_gc = true;
1275 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001276 }
1277}
1278
1279
1280// This function can be called when Log's mutex is acquired,
1281// either from main or Profiler's thread.
1282void Logger::StopLoggingAndProfiling() {
1283 Log::stop();
Andrei Popescu402d9372010-02-26 13:31:12 +00001284 PauseProfiler(PROFILER_MODULE_CPU, 0);
Steve Blocka7e24c12009-10-30 11:49:00 +00001285}
1286
1287
1288bool Logger::IsProfilerSamplerActive() {
1289 return ticker_->IsActive();
1290}
1291
1292
1293int Logger::GetLogLines(int from_pos, char* dest_buf, int max_size) {
1294 return Log::GetLogLines(from_pos, dest_buf, max_size);
1295}
1296
1297
Steve Block3ce2e202009-11-05 08:53:23 +00001298static int EnumerateCompiledFunctions(Handle<SharedFunctionInfo>* sfis) {
1299 AssertNoAllocation no_alloc;
Steve Blocka7e24c12009-10-30 11:49:00 +00001300 int compiled_funcs_count = 0;
Steve Block3ce2e202009-11-05 08:53:23 +00001301 HeapIterator iterator;
Leon Clarked91b9f72010-01-27 17:25:45 +00001302 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
Steve Block3ce2e202009-11-05 08:53:23 +00001303 if (!obj->IsSharedFunctionInfo()) continue;
1304 SharedFunctionInfo* sfi = SharedFunctionInfo::cast(obj);
1305 if (sfi->is_compiled()
1306 && (!sfi->script()->IsScript()
1307 || Script::cast(sfi->script())->HasValidSource())) {
1308 if (sfis != NULL)
1309 sfis[compiled_funcs_count] = Handle<SharedFunctionInfo>(sfi);
1310 ++compiled_funcs_count;
Steve Blocka7e24c12009-10-30 11:49:00 +00001311 }
1312 }
Steve Block3ce2e202009-11-05 08:53:23 +00001313 return compiled_funcs_count;
1314}
1315
1316
Steve Blockd0582a62009-12-15 09:54:21 +00001317void Logger::LogCodeObject(Object* object) {
1318 if (FLAG_log_code) {
1319 Code* code_object = Code::cast(object);
1320 LogEventsAndTags tag = Logger::STUB_TAG;
1321 const char* description = "Unknown code from the snapshot";
1322 switch (code_object->kind()) {
1323 case Code::FUNCTION:
1324 return; // We log this later using LogCompiledFunctions.
Steve Block6ded16b2010-05-10 14:33:55 +01001325 case Code::BINARY_OP_IC:
1326 // fall through
Steve Blockd0582a62009-12-15 09:54:21 +00001327 case Code::STUB:
Kristian Monsen80d68ea2010-09-08 11:05:35 +01001328 description =
1329 CodeStub::MajorName(CodeStub::GetMajorKey(code_object), true);
Andrei Popescu31002712010-02-23 13:46:05 +00001330 if (description == NULL)
1331 description = "A stub from the snapshot";
Steve Blockd0582a62009-12-15 09:54:21 +00001332 tag = Logger::STUB_TAG;
1333 break;
1334 case Code::BUILTIN:
1335 description = "A builtin from the snapshot";
1336 tag = Logger::BUILTIN_TAG;
1337 break;
1338 case Code::KEYED_LOAD_IC:
1339 description = "A keyed load IC from the snapshot";
1340 tag = Logger::KEYED_LOAD_IC_TAG;
1341 break;
1342 case Code::LOAD_IC:
1343 description = "A load IC from the snapshot";
1344 tag = Logger::LOAD_IC_TAG;
1345 break;
1346 case Code::STORE_IC:
1347 description = "A store IC from the snapshot";
1348 tag = Logger::STORE_IC_TAG;
1349 break;
1350 case Code::KEYED_STORE_IC:
1351 description = "A keyed store IC from the snapshot";
1352 tag = Logger::KEYED_STORE_IC_TAG;
1353 break;
1354 case Code::CALL_IC:
1355 description = "A call IC from the snapshot";
1356 tag = Logger::CALL_IC_TAG;
1357 break;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001358 case Code::KEYED_CALL_IC:
1359 description = "A keyed call IC from the snapshot";
1360 tag = Logger::KEYED_CALL_IC_TAG;
1361 break;
Steve Blockd0582a62009-12-15 09:54:21 +00001362 }
Steve Block6ded16b2010-05-10 14:33:55 +01001363 PROFILE(CodeCreateEvent(tag, code_object, description));
Steve Blockd0582a62009-12-15 09:54:21 +00001364 }
1365}
1366
1367
Ben Murdochf87a2032010-10-22 12:50:53 +01001368void Logger::LogCodeInfo() {
1369#ifdef ENABLE_LOGGING_AND_PROFILING
1370 if (!Log::IsEnabled() || !FLAG_log_code || !FLAG_ll_prof) return;
1371#if V8_TARGET_ARCH_IA32
1372 const char arch[] = "ia32";
1373#elif V8_TARGET_ARCH_X64
1374 const char arch[] = "x64";
1375#elif V8_TARGET_ARCH_ARM
1376 const char arch[] = "arm";
1377#else
1378 const char arch[] = "unknown";
1379#endif
1380 LogMessageBuilder msg;
1381 msg.Append("code-info,%s,%d\n", arch, Code::kHeaderSize);
1382 msg.WriteToLogFile();
1383#endif // ENABLE_LOGGING_AND_PROFILING
1384}
1385
1386
1387void Logger::LowLevelCodeCreateEvent(Code* code, LogMessageBuilder* msg) {
1388 if (!FLAG_ll_prof || Log::output_code_handle_ == NULL) return;
1389 int pos = static_cast<int>(ftell(Log::output_code_handle_));
John Reck59135872010-11-02 12:39:01 -07001390 size_t rv = fwrite(code->instruction_start(), 1, code->instruction_size(),
1391 Log::output_code_handle_);
1392 ASSERT(static_cast<size_t>(code->instruction_size()) == rv);
1393 USE(rv);
Ben Murdochf87a2032010-10-22 12:50:53 +01001394 msg->Append(",%d", pos);
1395}
1396
1397
Andrei Popescu31002712010-02-23 13:46:05 +00001398void Logger::LogCodeObjects() {
1399 AssertNoAllocation no_alloc;
1400 HeapIterator iterator;
1401 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
1402 if (obj->IsCode()) LogCodeObject(obj);
1403 }
1404}
1405
1406
Steve Block3ce2e202009-11-05 08:53:23 +00001407void Logger::LogCompiledFunctions() {
1408 HandleScope scope;
1409 const int compiled_funcs_count = EnumerateCompiledFunctions(NULL);
Kristian Monsen25f61362010-05-21 11:50:48 +01001410 ScopedVector< Handle<SharedFunctionInfo> > sfis(compiled_funcs_count);
1411 EnumerateCompiledFunctions(sfis.start());
Steve Blocka7e24c12009-10-30 11:49:00 +00001412
1413 // During iteration, there can be heap allocation due to
1414 // GetScriptLineNumber call.
1415 for (int i = 0; i < compiled_funcs_count; ++i) {
1416 Handle<SharedFunctionInfo> shared = sfis[i];
1417 Handle<String> name(String::cast(shared->name()));
1418 Handle<String> func_name(name->length() > 0 ?
1419 *name : shared->inferred_name());
1420 if (shared->script()->IsScript()) {
1421 Handle<Script> script(Script::cast(shared->script()));
1422 if (script->name()->IsString()) {
1423 Handle<String> script_name(String::cast(script->name()));
1424 int line_num = GetScriptLineNumber(script, shared->start_position());
1425 if (line_num > 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01001426 PROFILE(CodeCreateEvent(
1427 Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
1428 shared->code(), *func_name,
1429 *script_name, line_num + 1));
Steve Blocka7e24c12009-10-30 11:49:00 +00001430 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001431 // Can't distinguish eval and script here, so always use Script.
1432 PROFILE(CodeCreateEvent(
1433 Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
1434 shared->code(), *script_name));
Steve Blocka7e24c12009-10-30 11:49:00 +00001435 }
Steve Blockd0582a62009-12-15 09:54:21 +00001436 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001437 PROFILE(CodeCreateEvent(
1438 Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
1439 shared->code(), *func_name));
Steve Blocka7e24c12009-10-30 11:49:00 +00001440 }
Steve Block6ded16b2010-05-10 14:33:55 +01001441 } else if (shared->IsApiFunction()) {
Steve Blockd0582a62009-12-15 09:54:21 +00001442 // API function.
Steve Block6ded16b2010-05-10 14:33:55 +01001443 FunctionTemplateInfo* fun_data = shared->get_api_func_data();
Steve Blockd0582a62009-12-15 09:54:21 +00001444 Object* raw_call_data = fun_data->call_code();
1445 if (!raw_call_data->IsUndefined()) {
1446 CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
1447 Object* callback_obj = call_data->callback();
1448 Address entry_point = v8::ToCData<Address>(callback_obj);
Steve Block6ded16b2010-05-10 14:33:55 +01001449 PROFILE(CallbackEvent(*func_name, entry_point));
Steve Blockd0582a62009-12-15 09:54:21 +00001450 }
1451 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001452 PROFILE(CodeCreateEvent(
Steve Blockd0582a62009-12-15 09:54:21 +00001453 Logger::LAZY_COMPILE_TAG, shared->code(), *func_name));
Steve Blocka7e24c12009-10-30 11:49:00 +00001454 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001455 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001456}
1457
Steve Blockd0582a62009-12-15 09:54:21 +00001458
Leon Clarked91b9f72010-01-27 17:25:45 +00001459void Logger::LogFunctionObjects() {
1460 AssertNoAllocation no_alloc;
1461 HeapIterator iterator;
1462 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
1463 if (!obj->IsJSFunction()) continue;
1464 JSFunction* jsf = JSFunction::cast(obj);
1465 if (!jsf->is_compiled()) continue;
Steve Block6ded16b2010-05-10 14:33:55 +01001466 PROFILE(FunctionCreateEvent(jsf));
Leon Clarked91b9f72010-01-27 17:25:45 +00001467 }
1468}
1469
1470
Steve Blockd0582a62009-12-15 09:54:21 +00001471void Logger::LogAccessorCallbacks() {
1472 AssertNoAllocation no_alloc;
1473 HeapIterator iterator;
Leon Clarked91b9f72010-01-27 17:25:45 +00001474 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
Steve Blockd0582a62009-12-15 09:54:21 +00001475 if (!obj->IsAccessorInfo()) continue;
1476 AccessorInfo* ai = AccessorInfo::cast(obj);
1477 if (!ai->name()->IsString()) continue;
1478 String* name = String::cast(ai->name());
1479 Address getter_entry = v8::ToCData<Address>(ai->getter());
1480 if (getter_entry != 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01001481 PROFILE(GetterCallbackEvent(name, getter_entry));
Steve Blockd0582a62009-12-15 09:54:21 +00001482 }
1483 Address setter_entry = v8::ToCData<Address>(ai->setter());
1484 if (setter_entry != 0) {
Steve Block6ded16b2010-05-10 14:33:55 +01001485 PROFILE(SetterCallbackEvent(name, setter_entry));
Steve Blockd0582a62009-12-15 09:54:21 +00001486 }
1487 }
1488}
1489
Steve Blocka7e24c12009-10-30 11:49:00 +00001490#endif
1491
1492
1493bool Logger::Setup() {
1494#ifdef ENABLE_LOGGING_AND_PROFILING
1495 // --log-all enables all the log flags.
1496 if (FLAG_log_all) {
1497 FLAG_log_runtime = true;
1498 FLAG_log_api = true;
1499 FLAG_log_code = true;
1500 FLAG_log_gc = true;
1501 FLAG_log_suspect = true;
1502 FLAG_log_handles = true;
1503 FLAG_log_regexp = true;
1504 }
1505
1506 // --prof implies --log-code.
1507 if (FLAG_prof) FLAG_log_code = true;
1508
Ben Murdochf87a2032010-10-22 12:50:53 +01001509 // --ll-prof implies --log-code and --log-snapshot-positions.
1510 if (FLAG_ll_prof) {
1511 FLAG_log_code = true;
1512 FLAG_log_snapshot_positions = true;
1513 }
1514
Steve Blocka7e24c12009-10-30 11:49:00 +00001515 // --prof_lazy controls --log-code, implies --noprof_auto.
1516 if (FLAG_prof_lazy) {
1517 FLAG_log_code = false;
1518 FLAG_prof_auto = false;
1519 }
1520
1521 bool start_logging = FLAG_log || FLAG_log_runtime || FLAG_log_api
1522 || FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
1523 || FLAG_log_regexp || FLAG_log_state_changes;
1524
1525 bool open_log_file = start_logging || FLAG_prof_lazy;
1526
1527 // If we're logging anything, we need to open the log file.
1528 if (open_log_file) {
1529 if (strcmp(FLAG_logfile, "-") == 0) {
1530 Log::OpenStdout();
1531 } else if (strcmp(FLAG_logfile, "*") == 0) {
1532 Log::OpenMemoryBuffer();
1533 } else if (strchr(FLAG_logfile, '%') != NULL) {
1534 // If there's a '%' in the log file name we have to expand
1535 // placeholders.
1536 HeapStringAllocator allocator;
1537 StringStream stream(&allocator);
1538 for (const char* p = FLAG_logfile; *p; p++) {
1539 if (*p == '%') {
1540 p++;
1541 switch (*p) {
1542 case '\0':
1543 // If there's a % at the end of the string we back up
1544 // one character so we can escape the loop properly.
1545 p--;
1546 break;
1547 case 't': {
1548 // %t expands to the current time in milliseconds.
1549 double time = OS::TimeCurrentMillis();
1550 stream.Add("%.0f", FmtElm(time));
1551 break;
1552 }
1553 case '%':
1554 // %% expands (contracts really) to %.
1555 stream.Put('%');
1556 break;
1557 default:
1558 // All other %'s expand to themselves.
1559 stream.Put('%');
1560 stream.Put(*p);
1561 break;
1562 }
1563 } else {
1564 stream.Put(*p);
1565 }
1566 }
1567 SmartPointer<const char> expanded = stream.ToCString();
1568 Log::OpenFile(*expanded);
1569 } else {
1570 Log::OpenFile(FLAG_logfile);
1571 }
1572 }
1573
Steve Block6ded16b2010-05-10 14:33:55 +01001574 ASSERT(VMState::is_outermost_external());
Steve Blocka7e24c12009-10-30 11:49:00 +00001575
Ben Murdochf87a2032010-10-22 12:50:53 +01001576 if (FLAG_ll_prof) LogCodeInfo();
1577
Steve Blocka7e24c12009-10-30 11:49:00 +00001578 ticker_ = new Ticker(kSamplingIntervalMs);
1579
1580 if (FLAG_sliding_state_window && sliding_state_window_ == NULL) {
1581 sliding_state_window_ = new SlidingStateWindow();
1582 }
1583
1584 log_events_ = FLAG_compress_log ?
1585 kCompressedLogEventsNames : kLongLogEventsNames;
1586 if (FLAG_compress_log) {
1587 compression_helper_ = new CompressionHelper(kCompressionWindowSize);
1588 }
1589
Steve Block6ded16b2010-05-10 14:33:55 +01001590 if (start_logging) {
1591 logging_nesting_ = 1;
1592 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001593
1594 if (FLAG_prof) {
1595 profiler_ = new Profiler();
1596 if (!FLAG_prof_auto) {
1597 profiler_->pause();
1598 } else {
Steve Block6ded16b2010-05-10 14:33:55 +01001599 logging_nesting_ = 1;
Steve Blocka7e24c12009-10-30 11:49:00 +00001600 }
Steve Blockd0582a62009-12-15 09:54:21 +00001601 if (!FLAG_prof_lazy) {
1602 profiler_->Engage();
1603 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001604 }
1605
1606 LogMessageBuilder::set_write_failure_handler(StopLoggingAndProfiling);
1607
1608 return true;
1609
1610#else
1611 return false;
1612#endif
1613}
1614
1615
1616void Logger::TearDown() {
1617#ifdef ENABLE_LOGGING_AND_PROFILING
1618 LogMessageBuilder::set_write_failure_handler(NULL);
1619
1620 // Stop the profiler before closing the file.
1621 if (profiler_ != NULL) {
1622 profiler_->Disengage();
1623 delete profiler_;
1624 profiler_ = NULL;
1625 }
1626
1627 delete compression_helper_;
1628 compression_helper_ = NULL;
1629
1630 delete sliding_state_window_;
1631 sliding_state_window_ = NULL;
1632
1633 delete ticker_;
1634 ticker_ = NULL;
1635
1636 Log::Close();
1637#endif
1638}
1639
1640
1641void Logger::EnableSlidingStateWindow() {
1642#ifdef ENABLE_LOGGING_AND_PROFILING
1643 // If the ticker is NULL, Logger::Setup has not been called yet. In
1644 // that case, we set the sliding_state_window flag so that the
1645 // sliding window computation will be started when Logger::Setup is
1646 // called.
1647 if (ticker_ == NULL) {
1648 FLAG_sliding_state_window = true;
1649 return;
1650 }
1651 // Otherwise, if the sliding state window computation has not been
1652 // started we do it now.
1653 if (sliding_state_window_ == NULL) {
1654 sliding_state_window_ = new SlidingStateWindow();
1655 }
1656#endif
1657}
1658
Steve Blocka7e24c12009-10-30 11:49:00 +00001659} } // namespace v8::internal