log.cc revision a7e24c173cf37484693b9abb38e494fa7bd7baeb
1// 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"
33#include "log.h"
34#include "macro-assembler.h"
35#include "serialize.h"
36#include "string-stream.h"
37
38namespace v8 {
39namespace internal {
40
41#ifdef ENABLE_LOGGING_AND_PROFILING
42
43//
44// Sliding state window.  Updates counters to keep track of the last
45// window of kBufferSize states.  This is useful to track where we
46// spent our time.
47//
48class SlidingStateWindow {
49 public:
50  SlidingStateWindow();
51  ~SlidingStateWindow();
52  void AddState(StateTag state);
53
54 private:
55  static const int kBufferSize = 256;
56  int current_index_;
57  bool is_full_;
58  byte buffer_[kBufferSize];
59
60
61  void IncrementStateCounter(StateTag state) {
62    Counters::state_counters[state].Increment();
63  }
64
65
66  void DecrementStateCounter(StateTag state) {
67    Counters::state_counters[state].Decrement();
68  }
69};
70
71
72//
73// The Profiler samples pc and sp values for the main thread.
74// Each sample is appended to a circular buffer.
75// An independent thread removes data and writes it to the log.
76// This design minimizes the time spent in the sampler.
77//
78class Profiler: public Thread {
79 public:
80  Profiler();
81  void Engage();
82  void Disengage();
83
84  // Inserts collected profiling data into buffer.
85  void Insert(TickSample* sample) {
86    if (paused_)
87      return;
88
89    if (Succ(head_) == tail_) {
90      overflow_ = true;
91    } else {
92      buffer_[head_] = *sample;
93      head_ = Succ(head_);
94      buffer_semaphore_->Signal();  // Tell we have an element.
95    }
96  }
97
98  // Waits for a signal and removes profiling data.
99  bool Remove(TickSample* sample) {
100    buffer_semaphore_->Wait();  // Wait for an element.
101    *sample = buffer_[tail_];
102    bool result = overflow_;
103    tail_ = Succ(tail_);
104    overflow_ = false;
105    return result;
106  }
107
108  void Run();
109
110  // Pause and Resume TickSample data collection.
111  static bool paused() { return paused_; }
112  static void pause() { paused_ = true; }
113  static void resume() { paused_ = false; }
114
115 private:
116  // Returns the next index in the cyclic buffer.
117  int Succ(int index) { return (index + 1) % kBufferSize; }
118
119  // Cyclic buffer for communicating profiling samples
120  // between the signal handler and the worker thread.
121  static const int kBufferSize = 128;
122  TickSample buffer_[kBufferSize];  // Buffer storage.
123  int head_;  // Index to the buffer head.
124  int tail_;  // Index to the buffer tail.
125  bool overflow_;  // Tell whether a buffer overflow has occurred.
126  Semaphore* buffer_semaphore_;  // Sempahore used for buffer synchronization.
127
128  // Tells whether worker thread should continue running.
129  bool running_;
130
131  // Tells whether we are currently recording tick samples.
132  static bool paused_;
133};
134
135bool Profiler::paused_ = false;
136
137
138//
139// StackTracer implementation
140//
141void StackTracer::Trace(TickSample* sample) {
142  if (sample->state == GC) {
143    sample->frames_count = 0;
144    return;
145  }
146
147  const Address js_entry_sp = Top::js_entry_sp(Top::GetCurrentThread());
148  if (js_entry_sp == 0) {
149    // Not executing JS now.
150    sample->frames_count = 0;
151    return;
152  }
153
154  SafeStackTraceFrameIterator it(
155      reinterpret_cast<Address>(sample->fp),
156      reinterpret_cast<Address>(sample->sp),
157      reinterpret_cast<Address>(sample->sp),
158      js_entry_sp);
159  int i = 0;
160  while (!it.done() && i < TickSample::kMaxFramesCount) {
161    sample->stack[i++] = it.frame()->pc();
162    it.Advance();
163  }
164  sample->frames_count = i;
165}
166
167
168//
169// Ticker used to provide ticks to the profiler and the sliding state
170// window.
171//
172class Ticker: public Sampler {
173 public:
174  explicit Ticker(int interval):
175      Sampler(interval, FLAG_prof), window_(NULL), profiler_(NULL) {}
176
177  ~Ticker() { if (IsActive()) Stop(); }
178
179  void SampleStack(TickSample* sample) {
180    StackTracer::Trace(sample);
181  }
182
183  void Tick(TickSample* sample) {
184    if (profiler_) profiler_->Insert(sample);
185    if (window_) window_->AddState(sample->state);
186  }
187
188  void SetWindow(SlidingStateWindow* window) {
189    window_ = window;
190    if (!IsActive()) Start();
191  }
192
193  void ClearWindow() {
194    window_ = NULL;
195    if (!profiler_ && IsActive()) Stop();
196  }
197
198  void SetProfiler(Profiler* profiler) {
199    profiler_ = profiler;
200    if (!FLAG_prof_lazy && !IsActive()) Start();
201  }
202
203  void ClearProfiler() {
204    profiler_ = NULL;
205    if (!window_ && IsActive()) Stop();
206  }
207
208 private:
209  SlidingStateWindow* window_;
210  Profiler* profiler_;
211};
212
213
214//
215// SlidingStateWindow implementation.
216//
217SlidingStateWindow::SlidingStateWindow(): current_index_(0), is_full_(false) {
218  for (int i = 0; i < kBufferSize; i++) {
219    buffer_[i] = static_cast<byte>(OTHER);
220  }
221  Logger::ticker_->SetWindow(this);
222}
223
224
225SlidingStateWindow::~SlidingStateWindow() {
226  Logger::ticker_->ClearWindow();
227}
228
229
230void SlidingStateWindow::AddState(StateTag state) {
231  if (is_full_) {
232    DecrementStateCounter(static_cast<StateTag>(buffer_[current_index_]));
233  } else if (current_index_ == kBufferSize - 1) {
234    is_full_ = true;
235  }
236  buffer_[current_index_] = static_cast<byte>(state);
237  IncrementStateCounter(state);
238  ASSERT(IsPowerOf2(kBufferSize));
239  current_index_ = (current_index_ + 1) & (kBufferSize - 1);
240}
241
242
243//
244// Profiler implementation.
245//
246Profiler::Profiler() {
247  buffer_semaphore_ = OS::CreateSemaphore(0);
248  head_ = 0;
249  tail_ = 0;
250  overflow_ = false;
251  running_ = false;
252}
253
254
255void Profiler::Engage() {
256  OS::LogSharedLibraryAddresses();
257
258  // Start thread processing the profiler buffer.
259  running_ = true;
260  Start();
261
262  // Register to get ticks.
263  Logger::ticker_->SetProfiler(this);
264
265  Logger::ProfilerBeginEvent();
266  Logger::LogAliases();
267}
268
269
270void Profiler::Disengage() {
271  // Stop receiving ticks.
272  Logger::ticker_->ClearProfiler();
273
274  // Terminate the worker thread by setting running_ to false,
275  // inserting a fake element in the queue and then wait for
276  // the thread to terminate.
277  running_ = false;
278  TickSample sample;
279  // Reset 'paused_' flag, otherwise semaphore may not be signalled.
280  resume();
281  Insert(&sample);
282  Join();
283
284  LOG(UncheckedStringEvent("profiler", "end"));
285}
286
287
288void Profiler::Run() {
289  TickSample sample;
290  bool overflow = Logger::profiler_->Remove(&sample);
291  while (running_) {
292    LOG(TickEvent(&sample, overflow));
293    overflow = Logger::profiler_->Remove(&sample);
294  }
295}
296
297
298//
299// Logger class implementation.
300//
301Ticker* Logger::ticker_ = NULL;
302Profiler* Logger::profiler_ = NULL;
303VMState* Logger::current_state_ = NULL;
304VMState Logger::bottom_state_(EXTERNAL);
305SlidingStateWindow* Logger::sliding_state_window_ = NULL;
306const char** Logger::log_events_ = NULL;
307CompressionHelper* Logger::compression_helper_ = NULL;
308bool Logger::is_logging_ = false;
309
310#define DECLARE_LONG_EVENT(ignore1, long_name, ignore2) long_name,
311const char* kLongLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
312  LOG_EVENTS_AND_TAGS_LIST(DECLARE_LONG_EVENT)
313};
314#undef DECLARE_LONG_EVENT
315
316#define DECLARE_SHORT_EVENT(ignore1, ignore2, short_name) short_name,
317const char* kCompressedLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
318  LOG_EVENTS_AND_TAGS_LIST(DECLARE_SHORT_EVENT)
319};
320#undef DECLARE_SHORT_EVENT
321
322
323void Logger::ProfilerBeginEvent() {
324  if (!Log::IsEnabled()) return;
325  LogMessageBuilder msg;
326  msg.Append("profiler,\"begin\",%d\n", kSamplingIntervalMs);
327  if (FLAG_compress_log) {
328    msg.Append("profiler,\"compression\",%d\n", kCompressionWindowSize);
329  }
330  msg.WriteToLogFile();
331}
332
333
334void Logger::LogAliases() {
335  if (!Log::IsEnabled() || !FLAG_compress_log) return;
336  LogMessageBuilder msg;
337  for (int i = 0; i < NUMBER_OF_LOG_EVENTS; ++i) {
338    msg.Append("alias,%s,%s\n",
339               kCompressedLogEventsNames[i], kLongLogEventsNames[i]);
340  }
341  msg.WriteToLogFile();
342}
343
344#endif  // ENABLE_LOGGING_AND_PROFILING
345
346
347void Logger::Preamble(const char* content) {
348#ifdef ENABLE_LOGGING_AND_PROFILING
349  if (!Log::IsEnabled() || !FLAG_log_code) return;
350  LogMessageBuilder msg;
351  msg.WriteCStringToLogFile(content);
352#endif
353}
354
355
356void Logger::StringEvent(const char* name, const char* value) {
357#ifdef ENABLE_LOGGING_AND_PROFILING
358  if (FLAG_log) UncheckedStringEvent(name, value);
359#endif
360}
361
362
363#ifdef ENABLE_LOGGING_AND_PROFILING
364void Logger::UncheckedStringEvent(const char* name, const char* value) {
365  if (!Log::IsEnabled()) return;
366  LogMessageBuilder msg;
367  msg.Append("%s,\"%s\"\n", name, value);
368  msg.WriteToLogFile();
369}
370#endif
371
372
373void Logger::IntEvent(const char* name, int value) {
374#ifdef ENABLE_LOGGING_AND_PROFILING
375  if (!Log::IsEnabled() || !FLAG_log) return;
376  LogMessageBuilder msg;
377  msg.Append("%s,%d\n", name, value);
378  msg.WriteToLogFile();
379#endif
380}
381
382
383void Logger::HandleEvent(const char* name, Object** location) {
384#ifdef ENABLE_LOGGING_AND_PROFILING
385  if (!Log::IsEnabled() || !FLAG_log_handles) return;
386  LogMessageBuilder msg;
387  msg.Append("%s,0x%" V8PRIxPTR "\n", name, location);
388  msg.WriteToLogFile();
389#endif
390}
391
392
393#ifdef ENABLE_LOGGING_AND_PROFILING
394// ApiEvent is private so all the calls come from the Logger class.  It is the
395// caller's responsibility to ensure that log is enabled and that
396// FLAG_log_api is true.
397void Logger::ApiEvent(const char* format, ...) {
398  ASSERT(Log::IsEnabled() && FLAG_log_api);
399  LogMessageBuilder msg;
400  va_list ap;
401  va_start(ap, format);
402  msg.AppendVA(format, ap);
403  va_end(ap);
404  msg.WriteToLogFile();
405}
406#endif
407
408
409void Logger::ApiNamedSecurityCheck(Object* key) {
410#ifdef ENABLE_LOGGING_AND_PROFILING
411  if (!Log::IsEnabled() || !FLAG_log_api) return;
412  if (key->IsString()) {
413    SmartPointer<char> str =
414        String::cast(key)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
415    ApiEvent("api,check-security,\"%s\"\n", *str);
416  } else if (key->IsUndefined()) {
417    ApiEvent("api,check-security,undefined\n");
418  } else {
419    ApiEvent("api,check-security,['no-name']\n");
420  }
421#endif
422}
423
424
425void Logger::SharedLibraryEvent(const char* library_path,
426                                uintptr_t start,
427                                uintptr_t end) {
428#ifdef ENABLE_LOGGING_AND_PROFILING
429  if (!Log::IsEnabled() || !FLAG_prof) return;
430  LogMessageBuilder msg;
431  msg.Append("shared-library,\"%s\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR "\n",
432             library_path,
433             start,
434             end);
435  msg.WriteToLogFile();
436#endif
437}
438
439
440void Logger::SharedLibraryEvent(const wchar_t* library_path,
441                                uintptr_t start,
442                                uintptr_t end) {
443#ifdef ENABLE_LOGGING_AND_PROFILING
444  if (!Log::IsEnabled() || !FLAG_prof) return;
445  LogMessageBuilder msg;
446  msg.Append("shared-library,\"%ls\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR "\n",
447             library_path,
448             start,
449             end);
450  msg.WriteToLogFile();
451#endif
452}
453
454
455#ifdef ENABLE_LOGGING_AND_PROFILING
456void Logger::LogRegExpSource(Handle<JSRegExp> regexp) {
457  // Prints "/" + re.source + "/" +
458  //      (re.global?"g":"") + (re.ignorecase?"i":"") + (re.multiline?"m":"")
459  LogMessageBuilder msg;
460
461  Handle<Object> source = GetProperty(regexp, "source");
462  if (!source->IsString()) {
463    msg.Append("no source");
464    return;
465  }
466
467  switch (regexp->TypeTag()) {
468    case JSRegExp::ATOM:
469      msg.Append('a');
470      break;
471    default:
472      break;
473  }
474  msg.Append('/');
475  msg.AppendDetailed(*Handle<String>::cast(source), false);
476  msg.Append('/');
477
478  // global flag
479  Handle<Object> global = GetProperty(regexp, "global");
480  if (global->IsTrue()) {
481    msg.Append('g');
482  }
483  // ignorecase flag
484  Handle<Object> ignorecase = GetProperty(regexp, "ignoreCase");
485  if (ignorecase->IsTrue()) {
486    msg.Append('i');
487  }
488  // multiline flag
489  Handle<Object> multiline = GetProperty(regexp, "multiline");
490  if (multiline->IsTrue()) {
491    msg.Append('m');
492  }
493
494  msg.WriteToLogFile();
495}
496#endif  // ENABLE_LOGGING_AND_PROFILING
497
498
499void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache) {
500#ifdef ENABLE_LOGGING_AND_PROFILING
501  if (!Log::IsEnabled() || !FLAG_log_regexp) return;
502  LogMessageBuilder msg;
503  msg.Append("regexp-compile,");
504  LogRegExpSource(regexp);
505  msg.Append(in_cache ? ",hit\n" : ",miss\n");
506  msg.WriteToLogFile();
507#endif
508}
509
510
511void Logger::LogRuntime(Vector<const char> format, JSArray* args) {
512#ifdef ENABLE_LOGGING_AND_PROFILING
513  if (!Log::IsEnabled() || !FLAG_log_runtime) return;
514  HandleScope scope;
515  LogMessageBuilder msg;
516  for (int i = 0; i < format.length(); i++) {
517    char c = format[i];
518    if (c == '%' && i <= format.length() - 2) {
519      i++;
520      ASSERT('0' <= format[i] && format[i] <= '9');
521      Object* obj = args->GetElement(format[i] - '0');
522      i++;
523      switch (format[i]) {
524        case 's':
525          msg.AppendDetailed(String::cast(obj), false);
526          break;
527        case 'S':
528          msg.AppendDetailed(String::cast(obj), true);
529          break;
530        case 'r':
531          Logger::LogRegExpSource(Handle<JSRegExp>(JSRegExp::cast(obj)));
532          break;
533        case 'x':
534          msg.Append("0x%x", Smi::cast(obj)->value());
535          break;
536        case 'i':
537          msg.Append("%i", Smi::cast(obj)->value());
538          break;
539        default:
540          UNREACHABLE();
541      }
542    } else {
543      msg.Append(c);
544    }
545  }
546  msg.Append('\n');
547  msg.WriteToLogFile();
548#endif
549}
550
551
552void Logger::ApiIndexedSecurityCheck(uint32_t index) {
553#ifdef ENABLE_LOGGING_AND_PROFILING
554  if (!Log::IsEnabled() || !FLAG_log_api) return;
555  ApiEvent("api,check-security,%u\n", index);
556#endif
557}
558
559
560void Logger::ApiNamedPropertyAccess(const char* tag,
561                                    JSObject* holder,
562                                    Object* name) {
563#ifdef ENABLE_LOGGING_AND_PROFILING
564  ASSERT(name->IsString());
565  if (!Log::IsEnabled() || !FLAG_log_api) return;
566  String* class_name_obj = holder->class_name();
567  SmartPointer<char> class_name =
568      class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
569  SmartPointer<char> property_name =
570      String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
571  Logger::ApiEvent("api,%s,\"%s\",\"%s\"\n", tag, *class_name, *property_name);
572#endif
573}
574
575void Logger::ApiIndexedPropertyAccess(const char* tag,
576                                      JSObject* holder,
577                                      uint32_t index) {
578#ifdef ENABLE_LOGGING_AND_PROFILING
579  if (!Log::IsEnabled() || !FLAG_log_api) return;
580  String* class_name_obj = holder->class_name();
581  SmartPointer<char> class_name =
582      class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
583  Logger::ApiEvent("api,%s,\"%s\",%u\n", tag, *class_name, index);
584#endif
585}
586
587void Logger::ApiObjectAccess(const char* tag, JSObject* object) {
588#ifdef ENABLE_LOGGING_AND_PROFILING
589  if (!Log::IsEnabled() || !FLAG_log_api) return;
590  String* class_name_obj = object->class_name();
591  SmartPointer<char> class_name =
592      class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
593  Logger::ApiEvent("api,%s,\"%s\"\n", tag, *class_name);
594#endif
595}
596
597
598void Logger::ApiEntryCall(const char* name) {
599#ifdef ENABLE_LOGGING_AND_PROFILING
600  if (!Log::IsEnabled() || !FLAG_log_api) return;
601  Logger::ApiEvent("api,%s\n", name);
602#endif
603}
604
605
606void Logger::NewEvent(const char* name, void* object, size_t size) {
607#ifdef ENABLE_LOGGING_AND_PROFILING
608  if (!Log::IsEnabled() || !FLAG_log) return;
609  LogMessageBuilder msg;
610  msg.Append("new,%s,0x%" V8PRIxPTR ",%u\n", name, object,
611             static_cast<unsigned int>(size));
612  msg.WriteToLogFile();
613#endif
614}
615
616
617void Logger::DeleteEvent(const char* name, void* object) {
618#ifdef ENABLE_LOGGING_AND_PROFILING
619  if (!Log::IsEnabled() || !FLAG_log) return;
620  LogMessageBuilder msg;
621  msg.Append("delete,%s,0x%" V8PRIxPTR "\n", name, object);
622  msg.WriteToLogFile();
623#endif
624}
625
626
627#ifdef ENABLE_LOGGING_AND_PROFILING
628
629// A class that contains all common code dealing with record compression.
630class CompressionHelper {
631 public:
632  explicit CompressionHelper(int window_size)
633      : compressor_(window_size), repeat_count_(0) { }
634
635  // Handles storing message in compressor, retrieving the previous one and
636  // prefixing it with repeat count, if needed.
637  // Returns true if message needs to be written to log.
638  bool HandleMessage(LogMessageBuilder* msg) {
639    if (!msg->StoreInCompressor(&compressor_)) {
640      // Current message repeats the previous one, don't write it.
641      ++repeat_count_;
642      return false;
643    }
644    if (repeat_count_ == 0) {
645      return msg->RetrieveCompressedPrevious(&compressor_);
646    }
647    OS::SNPrintF(prefix_, "%s,%d,",
648                 Logger::log_events_[Logger::REPEAT_META_EVENT],
649                 repeat_count_ + 1);
650    repeat_count_ = 0;
651    return msg->RetrieveCompressedPrevious(&compressor_, prefix_.start());
652  }
653
654 private:
655  LogRecordCompressor compressor_;
656  int repeat_count_;
657  EmbeddedVector<char, 20> prefix_;
658};
659
660#endif  // ENABLE_LOGGING_AND_PROFILING
661
662
663void Logger::CodeCreateEvent(LogEventsAndTags tag,
664                             Code* code,
665                             const char* comment) {
666#ifdef ENABLE_LOGGING_AND_PROFILING
667  if (!Log::IsEnabled() || !FLAG_log_code) return;
668  LogMessageBuilder msg;
669  msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]);
670  msg.AppendAddress(code->address());
671  msg.Append(",%d,\"", code->ExecutableSize());
672  for (const char* p = comment; *p != '\0'; p++) {
673    if (*p == '"') {
674      msg.Append('\\');
675    }
676    msg.Append(*p);
677  }
678  msg.Append('"');
679  if (FLAG_compress_log) {
680    ASSERT(compression_helper_ != NULL);
681    if (!compression_helper_->HandleMessage(&msg)) return;
682  }
683  msg.Append('\n');
684  msg.WriteToLogFile();
685#endif
686}
687
688
689void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, String* name) {
690#ifdef ENABLE_LOGGING_AND_PROFILING
691  if (!Log::IsEnabled() || !FLAG_log_code) return;
692  LogMessageBuilder msg;
693  SmartPointer<char> str =
694      name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
695  msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]);
696  msg.AppendAddress(code->address());
697  msg.Append(",%d,\"%s\"", code->ExecutableSize(), *str);
698  if (FLAG_compress_log) {
699    ASSERT(compression_helper_ != NULL);
700    if (!compression_helper_->HandleMessage(&msg)) return;
701  }
702  msg.Append('\n');
703  msg.WriteToLogFile();
704#endif
705}
706
707
708void Logger::CodeCreateEvent(LogEventsAndTags tag,
709                             Code* code, String* name,
710                             String* source, int line) {
711#ifdef ENABLE_LOGGING_AND_PROFILING
712  if (!Log::IsEnabled() || !FLAG_log_code) return;
713  LogMessageBuilder msg;
714  SmartPointer<char> str =
715      name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
716  SmartPointer<char> sourcestr =
717      source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
718  msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]);
719  msg.AppendAddress(code->address());
720  msg.Append(",%d,\"%s %s:%d\"",
721             code->ExecutableSize(), *str, *sourcestr, line);
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#endif
729}
730
731
732void Logger::CodeCreateEvent(LogEventsAndTags tag, Code* code, int args_count) {
733#ifdef ENABLE_LOGGING_AND_PROFILING
734  if (!Log::IsEnabled() || !FLAG_log_code) return;
735  LogMessageBuilder msg;
736  msg.Append("%s,%s,", log_events_[CODE_CREATION_EVENT], log_events_[tag]);
737  msg.AppendAddress(code->address());
738  msg.Append(",%d,\"args_count: %d\"", code->ExecutableSize(), args_count);
739  if (FLAG_compress_log) {
740    ASSERT(compression_helper_ != NULL);
741    if (!compression_helper_->HandleMessage(&msg)) return;
742  }
743  msg.Append('\n');
744  msg.WriteToLogFile();
745#endif
746}
747
748
749void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
750#ifdef ENABLE_LOGGING_AND_PROFILING
751  if (!Log::IsEnabled() || !FLAG_log_code) return;
752  LogMessageBuilder msg;
753  msg.Append("%s,%s,",
754             log_events_[CODE_CREATION_EVENT], log_events_[REG_EXP_TAG]);
755  msg.AppendAddress(code->address());
756  msg.Append(",%d,\"", code->ExecutableSize());
757  msg.AppendDetailed(source, false);
758  msg.Append('\"');
759  if (FLAG_compress_log) {
760    ASSERT(compression_helper_ != NULL);
761    if (!compression_helper_->HandleMessage(&msg)) return;
762  }
763  msg.Append('\n');
764  msg.WriteToLogFile();
765#endif
766}
767
768
769void Logger::CodeMoveEvent(Address from, Address to) {
770#ifdef ENABLE_LOGGING_AND_PROFILING
771  static Address prev_to_ = NULL;
772  if (!Log::IsEnabled() || !FLAG_log_code) return;
773  LogMessageBuilder msg;
774  msg.Append("%s,", log_events_[CODE_MOVE_EVENT]);
775  msg.AppendAddress(from);
776  msg.Append(',');
777  msg.AppendAddress(to, prev_to_);
778  prev_to_ = to;
779  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::CodeDeleteEvent(Address from) {
790#ifdef ENABLE_LOGGING_AND_PROFILING
791  if (!Log::IsEnabled() || !FLAG_log_code) return;
792  LogMessageBuilder msg;
793  msg.Append("%s,", log_events_[CODE_DELETE_EVENT]);
794  msg.AppendAddress(from);
795  if (FLAG_compress_log) {
796    ASSERT(compression_helper_ != NULL);
797    if (!compression_helper_->HandleMessage(&msg)) return;
798  }
799  msg.Append('\n');
800  msg.WriteToLogFile();
801#endif
802}
803
804
805void Logger::ResourceEvent(const char* name, const char* tag) {
806#ifdef ENABLE_LOGGING_AND_PROFILING
807  if (!Log::IsEnabled() || !FLAG_log) return;
808  LogMessageBuilder msg;
809  msg.Append("%s,%s,", name, tag);
810
811  uint32_t sec, usec;
812  if (OS::GetUserTime(&sec, &usec) != -1) {
813    msg.Append("%d,%d,", sec, usec);
814  }
815  msg.Append("%.0f", OS::TimeCurrentMillis());
816
817  msg.Append('\n');
818  msg.WriteToLogFile();
819#endif
820}
821
822
823void Logger::SuspectReadEvent(String* name, Object* obj) {
824#ifdef ENABLE_LOGGING_AND_PROFILING
825  if (!Log::IsEnabled() || !FLAG_log_suspect) return;
826  LogMessageBuilder msg;
827  String* class_name = obj->IsJSObject()
828                       ? JSObject::cast(obj)->class_name()
829                       : Heap::empty_string();
830  msg.Append("suspect-read,");
831  msg.Append(class_name);
832  msg.Append(',');
833  msg.Append('"');
834  msg.Append(name);
835  msg.Append('"');
836  msg.Append('\n');
837  msg.WriteToLogFile();
838#endif
839}
840
841
842void Logger::HeapSampleBeginEvent(const char* space, const char* kind) {
843#ifdef ENABLE_LOGGING_AND_PROFILING
844  if (!Log::IsEnabled() || !FLAG_log_gc) return;
845  LogMessageBuilder msg;
846  // Using non-relative system time in order to be able to synchronize with
847  // external memory profiling events (e.g. DOM memory size).
848  msg.Append("heap-sample-begin,\"%s\",\"%s\",%.0f\n",
849             space, kind, OS::TimeCurrentMillis());
850  msg.WriteToLogFile();
851#endif
852}
853
854
855void Logger::HeapSampleStats(const char* space, const char* kind,
856                             int capacity, int used) {
857#ifdef ENABLE_LOGGING_AND_PROFILING
858  if (!Log::IsEnabled() || !FLAG_log_gc) return;
859  LogMessageBuilder msg;
860  msg.Append("heap-sample-stats,\"%s\",\"%s\",%d,%d\n",
861             space, kind, capacity, used);
862  msg.WriteToLogFile();
863#endif
864}
865
866
867void Logger::HeapSampleEndEvent(const char* space, const char* kind) {
868#ifdef ENABLE_LOGGING_AND_PROFILING
869  if (!Log::IsEnabled() || !FLAG_log_gc) return;
870  LogMessageBuilder msg;
871  msg.Append("heap-sample-end,\"%s\",\"%s\"\n", space, kind);
872  msg.WriteToLogFile();
873#endif
874}
875
876
877void Logger::HeapSampleItemEvent(const char* type, int number, int bytes) {
878#ifdef ENABLE_LOGGING_AND_PROFILING
879  if (!Log::IsEnabled() || !FLAG_log_gc) return;
880  LogMessageBuilder msg;
881  msg.Append("heap-sample-item,%s,%d,%d\n", type, number, bytes);
882  msg.WriteToLogFile();
883#endif
884}
885
886
887void Logger::HeapSampleJSConstructorEvent(const char* constructor,
888                                          int number, int bytes) {
889#ifdef ENABLE_LOGGING_AND_PROFILING
890  if (!Log::IsEnabled() || !FLAG_log_gc) return;
891  LogMessageBuilder msg;
892  msg.Append("heap-js-cons-item,%s,%d,%d\n", constructor, number, bytes);
893  msg.WriteToLogFile();
894#endif
895}
896
897
898void Logger::HeapSampleJSRetainersEvent(
899    const char* constructor, const char* event) {
900#ifdef ENABLE_LOGGING_AND_PROFILING
901  if (!Log::IsEnabled() || !FLAG_log_gc) return;
902  // Event starts with comma, so we don't have it in the format string.
903  static const char* event_text = "heap-js-ret-item,%s";
904  // We take placeholder strings into account, but it's OK to be conservative.
905  static const int event_text_len = strlen(event_text);
906  const int cons_len = strlen(constructor), event_len = strlen(event);
907  int pos = 0;
908  // Retainer lists can be long. We may need to split them into multiple events.
909  do {
910    LogMessageBuilder msg;
911    msg.Append(event_text, constructor);
912    int to_write = event_len - pos;
913    if (to_write > Log::kMessageBufferSize - (cons_len + event_text_len)) {
914      int cut_pos = pos + Log::kMessageBufferSize - (cons_len + event_text_len);
915      ASSERT(cut_pos < event_len);
916      while (cut_pos > pos && event[cut_pos] != ',') --cut_pos;
917      if (event[cut_pos] != ',') {
918        // Crash in debug mode, skip in release mode.
919        ASSERT(false);
920        return;
921      }
922      // Append a piece of event that fits, without trailing comma.
923      msg.AppendStringPart(event + pos, cut_pos - pos);
924      // Start next piece with comma.
925      pos = cut_pos;
926    } else {
927      msg.Append("%s", event + pos);
928      pos += event_len;
929    }
930    msg.Append('\n');
931    msg.WriteToLogFile();
932  } while (pos < event_len);
933#endif
934}
935
936
937void Logger::DebugTag(const char* call_site_tag) {
938#ifdef ENABLE_LOGGING_AND_PROFILING
939  if (!Log::IsEnabled() || !FLAG_log) return;
940  LogMessageBuilder msg;
941  msg.Append("debug-tag,%s\n", call_site_tag);
942  msg.WriteToLogFile();
943#endif
944}
945
946
947void Logger::DebugEvent(const char* event_type, Vector<uint16_t> parameter) {
948#ifdef ENABLE_LOGGING_AND_PROFILING
949  if (!Log::IsEnabled() || !FLAG_log) return;
950  StringBuilder s(parameter.length() + 1);
951  for (int i = 0; i < parameter.length(); ++i) {
952    s.AddCharacter(static_cast<char>(parameter[i]));
953  }
954  char* parameter_string = s.Finalize();
955  LogMessageBuilder msg;
956  msg.Append("debug-queue-event,%s,%15.3f,%s\n",
957             event_type,
958             OS::TimeCurrentMillis(),
959             parameter_string);
960  DeleteArray(parameter_string);
961  msg.WriteToLogFile();
962#endif
963}
964
965
966#ifdef ENABLE_LOGGING_AND_PROFILING
967void Logger::TickEvent(TickSample* sample, bool overflow) {
968  if (!Log::IsEnabled() || !FLAG_prof) return;
969  static Address prev_sp = NULL;
970  LogMessageBuilder msg;
971  msg.Append("%s,", log_events_[TICK_EVENT]);
972  Address prev_addr = reinterpret_cast<Address>(sample->pc);
973  msg.AppendAddress(prev_addr);
974  msg.Append(',');
975  msg.AppendAddress(reinterpret_cast<Address>(sample->sp), prev_sp);
976  prev_sp = reinterpret_cast<Address>(sample->sp);
977  msg.Append(",%d", static_cast<int>(sample->state));
978  if (overflow) {
979    msg.Append(",overflow");
980  }
981  for (int i = 0; i < sample->frames_count; ++i) {
982    msg.Append(',');
983    msg.AppendAddress(sample->stack[i], prev_addr);
984    prev_addr = sample->stack[i];
985  }
986  if (FLAG_compress_log) {
987    ASSERT(compression_helper_ != NULL);
988    if (!compression_helper_->HandleMessage(&msg)) return;
989  }
990  msg.Append('\n');
991  msg.WriteToLogFile();
992}
993
994
995int Logger::GetActiveProfilerModules() {
996  int result = PROFILER_MODULE_NONE;
997  if (!profiler_->paused()) {
998    result |= PROFILER_MODULE_CPU;
999  }
1000  if (FLAG_log_gc) {
1001    result |= PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS;
1002  }
1003  return result;
1004}
1005
1006
1007void Logger::PauseProfiler(int flags) {
1008  if (!Log::IsEnabled()) return;
1009  const int active_modules = GetActiveProfilerModules();
1010  const int modules_to_disable = active_modules & flags;
1011  if (modules_to_disable == PROFILER_MODULE_NONE) return;
1012
1013  if (modules_to_disable & PROFILER_MODULE_CPU) {
1014    profiler_->pause();
1015    if (FLAG_prof_lazy) {
1016      if (!FLAG_sliding_state_window) ticker_->Stop();
1017      FLAG_log_code = false;
1018      // Must be the same message as Log::kDynamicBufferSeal.
1019      LOG(UncheckedStringEvent("profiler", "pause"));
1020    }
1021  }
1022  if (modules_to_disable &
1023      (PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
1024    FLAG_log_gc = false;
1025  }
1026  // Turn off logging if no active modules remain.
1027  if ((active_modules & ~flags) == PROFILER_MODULE_NONE) {
1028    is_logging_ = false;
1029  }
1030}
1031
1032
1033void Logger::ResumeProfiler(int flags) {
1034  if (!Log::IsEnabled()) return;
1035  const int modules_to_enable = ~GetActiveProfilerModules() & flags;
1036  if (modules_to_enable != PROFILER_MODULE_NONE) {
1037    is_logging_ = true;
1038  }
1039  if (modules_to_enable & PROFILER_MODULE_CPU) {
1040    if (FLAG_prof_lazy) {
1041      LOG(UncheckedStringEvent("profiler", "resume"));
1042      FLAG_log_code = true;
1043      LogCompiledFunctions();
1044      if (!FLAG_sliding_state_window) ticker_->Start();
1045    }
1046    profiler_->resume();
1047  }
1048  if (modules_to_enable &
1049      (PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
1050    FLAG_log_gc = true;
1051  }
1052}
1053
1054
1055// This function can be called when Log's mutex is acquired,
1056// either from main or Profiler's thread.
1057void Logger::StopLoggingAndProfiling() {
1058  Log::stop();
1059  PauseProfiler(PROFILER_MODULE_CPU);
1060}
1061
1062
1063bool Logger::IsProfilerSamplerActive() {
1064  return ticker_->IsActive();
1065}
1066
1067
1068int Logger::GetLogLines(int from_pos, char* dest_buf, int max_size) {
1069  return Log::GetLogLines(from_pos, dest_buf, max_size);
1070}
1071
1072
1073void Logger::LogCompiledFunctions() {
1074  HandleScope scope;
1075  Handle<SharedFunctionInfo>* sfis = NULL;
1076  int compiled_funcs_count = 0;
1077
1078  {
1079    AssertNoAllocation no_alloc;
1080
1081    HeapIterator iterator;
1082    while (iterator.has_next()) {
1083      HeapObject* obj = iterator.next();
1084      ASSERT(obj != NULL);
1085      if (obj->IsSharedFunctionInfo()
1086          && SharedFunctionInfo::cast(obj)->is_compiled()) {
1087        ++compiled_funcs_count;
1088      }
1089    }
1090
1091    sfis = NewArray< Handle<SharedFunctionInfo> >(compiled_funcs_count);
1092    iterator.reset();
1093
1094    int i = 0;
1095    while (iterator.has_next()) {
1096      HeapObject* obj = iterator.next();
1097      ASSERT(obj != NULL);
1098      if (obj->IsSharedFunctionInfo()
1099          && SharedFunctionInfo::cast(obj)->is_compiled()) {
1100        sfis[i++] = Handle<SharedFunctionInfo>(SharedFunctionInfo::cast(obj));
1101      }
1102    }
1103  }
1104
1105  // During iteration, there can be heap allocation due to
1106  // GetScriptLineNumber call.
1107  for (int i = 0; i < compiled_funcs_count; ++i) {
1108    Handle<SharedFunctionInfo> shared = sfis[i];
1109    Handle<String> name(String::cast(shared->name()));
1110    Handle<String> func_name(name->length() > 0 ?
1111                             *name : shared->inferred_name());
1112    if (shared->script()->IsScript()) {
1113      Handle<Script> script(Script::cast(shared->script()));
1114      if (script->name()->IsString()) {
1115        Handle<String> script_name(String::cast(script->name()));
1116        int line_num = GetScriptLineNumber(script, shared->start_position());
1117        if (line_num > 0) {
1118          LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG,
1119                              shared->code(), *func_name,
1120                              *script_name, line_num + 1));
1121        } else {
1122          // Can't distinguish enum and script here, so always use Script.
1123          LOG(CodeCreateEvent(Logger::SCRIPT_TAG,
1124                              shared->code(), *script_name));
1125        }
1126        continue;
1127      }
1128    }
1129    // If no script or script has no name.
1130    LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG, shared->code(), *func_name));
1131  }
1132
1133  DeleteArray(sfis);
1134}
1135
1136#endif
1137
1138
1139bool Logger::Setup() {
1140#ifdef ENABLE_LOGGING_AND_PROFILING
1141  // --log-all enables all the log flags.
1142  if (FLAG_log_all) {
1143    FLAG_log_runtime = true;
1144    FLAG_log_api = true;
1145    FLAG_log_code = true;
1146    FLAG_log_gc = true;
1147    FLAG_log_suspect = true;
1148    FLAG_log_handles = true;
1149    FLAG_log_regexp = true;
1150  }
1151
1152  // --prof implies --log-code.
1153  if (FLAG_prof) FLAG_log_code = true;
1154
1155  // --prof_lazy controls --log-code, implies --noprof_auto.
1156  if (FLAG_prof_lazy) {
1157    FLAG_log_code = false;
1158    FLAG_prof_auto = false;
1159  }
1160
1161  bool start_logging = FLAG_log || FLAG_log_runtime || FLAG_log_api
1162      || FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
1163      || FLAG_log_regexp || FLAG_log_state_changes;
1164
1165  bool open_log_file = start_logging || FLAG_prof_lazy;
1166
1167  // If we're logging anything, we need to open the log file.
1168  if (open_log_file) {
1169    if (strcmp(FLAG_logfile, "-") == 0) {
1170      Log::OpenStdout();
1171    } else if (strcmp(FLAG_logfile, "*") == 0) {
1172      Log::OpenMemoryBuffer();
1173    } else if (strchr(FLAG_logfile, '%') != NULL) {
1174      // If there's a '%' in the log file name we have to expand
1175      // placeholders.
1176      HeapStringAllocator allocator;
1177      StringStream stream(&allocator);
1178      for (const char* p = FLAG_logfile; *p; p++) {
1179        if (*p == '%') {
1180          p++;
1181          switch (*p) {
1182            case '\0':
1183              // If there's a % at the end of the string we back up
1184              // one character so we can escape the loop properly.
1185              p--;
1186              break;
1187            case 't': {
1188              // %t expands to the current time in milliseconds.
1189              double time = OS::TimeCurrentMillis();
1190              stream.Add("%.0f", FmtElm(time));
1191              break;
1192            }
1193            case '%':
1194              // %% expands (contracts really) to %.
1195              stream.Put('%');
1196              break;
1197            default:
1198              // All other %'s expand to themselves.
1199              stream.Put('%');
1200              stream.Put(*p);
1201              break;
1202          }
1203        } else {
1204          stream.Put(*p);
1205        }
1206      }
1207      SmartPointer<const char> expanded = stream.ToCString();
1208      Log::OpenFile(*expanded);
1209    } else {
1210      Log::OpenFile(FLAG_logfile);
1211    }
1212  }
1213
1214  current_state_ = &bottom_state_;
1215
1216  ticker_ = new Ticker(kSamplingIntervalMs);
1217
1218  if (FLAG_sliding_state_window && sliding_state_window_ == NULL) {
1219    sliding_state_window_ = new SlidingStateWindow();
1220  }
1221
1222  log_events_ = FLAG_compress_log ?
1223      kCompressedLogEventsNames : kLongLogEventsNames;
1224  if (FLAG_compress_log) {
1225    compression_helper_ = new CompressionHelper(kCompressionWindowSize);
1226  }
1227
1228  is_logging_ = start_logging;
1229
1230  if (FLAG_prof) {
1231    profiler_ = new Profiler();
1232    if (!FLAG_prof_auto) {
1233      profiler_->pause();
1234    } else {
1235      is_logging_ = true;
1236    }
1237    profiler_->Engage();
1238  }
1239
1240  LogMessageBuilder::set_write_failure_handler(StopLoggingAndProfiling);
1241
1242  return true;
1243
1244#else
1245  return false;
1246#endif
1247}
1248
1249
1250void Logger::TearDown() {
1251#ifdef ENABLE_LOGGING_AND_PROFILING
1252  LogMessageBuilder::set_write_failure_handler(NULL);
1253
1254  // Stop the profiler before closing the file.
1255  if (profiler_ != NULL) {
1256    profiler_->Disengage();
1257    delete profiler_;
1258    profiler_ = NULL;
1259  }
1260
1261  delete compression_helper_;
1262  compression_helper_ = NULL;
1263
1264  delete sliding_state_window_;
1265  sliding_state_window_ = NULL;
1266
1267  delete ticker_;
1268  ticker_ = NULL;
1269
1270  Log::Close();
1271#endif
1272}
1273
1274
1275void Logger::EnableSlidingStateWindow() {
1276#ifdef ENABLE_LOGGING_AND_PROFILING
1277  // If the ticker is NULL, Logger::Setup has not been called yet.  In
1278  // that case, we set the sliding_state_window flag so that the
1279  // sliding window computation will be started when Logger::Setup is
1280  // called.
1281  if (ticker_ == NULL) {
1282    FLAG_sliding_state_window = true;
1283    return;
1284  }
1285  // Otherwise, if the sliding state window computation has not been
1286  // started we do it now.
1287  if (sliding_state_window_ == NULL) {
1288    sliding_state_window_ = new SlidingStateWindow();
1289  }
1290#endif
1291}
1292
1293
1294} }  // namespace v8::internal
1295