Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: src/v8-tracing-controller.cc

Issue 1708573003: [WIP]Create a V8 sampler library and tracing controller. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/v8-sampler.cc ('k') | test/cctest/test-cpu-profiler.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "include/v8-tracing-controller.h"
6 #include "src/base/platform/platform.h"
7 #include "src/log.h"
8 #include "src/version.h"
9 #include <iostream>
10
11 namespace v8 {
12
13 // Functions and data for performing output of log messages.
14 class TracingLogger {
15 public:
16 // Performs process-wide initialization.
17 void Initialize(const char* log_file_name);
18
19 // Disables logging, but preserves acquired resources.
20 void stop() { is_stopped_ = true; }
21
22 // Frees all resources acquired in Initialize and Open... functions.
23 // When a temporary file is used for the log, returns its stream descriptor,
24 // leaving the file open.
25 FILE* Close();
26
27 // Returns whether logging is enabled.
28 bool IsEnabled() {
29 return !is_stopped_ && output_handle_ != NULL;
30 }
31
32 // Size of buffer used for formatting log messages.
33 static const int kMessageBufferSize = 8192;
34
35 void SharedLibraryEvent(const std::string& library_path,
36 uintptr_t start,
37 uintptr_t end);
38 void BeginEvent(const char* event_name);
39 void EndEvent(const char* event_name);
40
41 // Utility class for formatting log messages. It fills the message into the
42 // static buffer in TracingLogger.
43 class MessageBuilder {
44 public:
45 // Create a message builder starting from position 0.
46 // This acquires the mutex in the log as well.
47 explicit MessageBuilder(TracingLogger* logger);
48 ~MessageBuilder() { }
49
50 // Append string data to the log message.
51 void Append(const char* format, ...);
52
53 // Append string data to the log message.
54 void AppendVA(const char* format, va_list args);
55
56 // Append a character to the log message.
57 void Append(const char c);
58
59 // Append double quoted string to the log message.
60 void AppendDoubleQuotedString(const char* string);
61
62 // Append a portion of a string.
63 void AppendStringPart(const char* str, int len);
64
65 // Write the log message to the log file currently opened.
66 void WriteToLogFile();
67
68 private:
69 TracingLogger* logger_;
70 base::LockGuard<base::Mutex> lock_guard_;
71 int pos_;
72 };
73
74 private:
75 friend class TracingController;
76
77 explicit TracingLogger();
78 // Opens file for logging.
79 void OpenFile(const char* name);
80
81 // Implementation of writing to a log file.
82 int WriteToFile(const char* msg, int length) {
83 DCHECK(output_handle_ != NULL);
84 size_t rv = fwrite(msg, 1, length, output_handle_);
85 DCHECK(static_cast<size_t>(length) == rv);
86 USE(rv);
87 fflush(output_handle_);
88 return length;
89 }
90
91 // Whether logging is stopped (e.g. due to insufficient resources).
92 bool is_stopped_;
93
94 // When logging is active output_handle_ is used to store a pointer to log
95 // destination. mutex_ should be acquired before using output_handle_.
96 FILE* output_handle_;
97
98 // mutex_ is a Mutex used for enforcing exclusive
99 // access to the formatting buffer and the log file or log memory buffer.
100 base::Mutex mutex_;
101
102 // Buffer used for formatting log messages. This is a singleton buffer and
103 // mutex_ should be acquired before using it.
104 char* message_buffer_;
105 };
106
107 TracingLogger::TracingLogger()
108 : is_stopped_(false),
109 output_handle_(NULL),
110 message_buffer_(NULL) {}
111
112 void TracingLogger::Initialize(const char* log_file_name) {
113 message_buffer_ = i::NewArray<char>(kMessageBufferSize);
114 // If we're logging anything, we need to open the log file.
115 OpenFile(log_file_name);
116 if (output_handle_ != nullptr) {
117 TracingLogger::MessageBuilder msg(this);
118 msg.Append("v8-version,%d,%d,%d,%d,%d", i::Version::GetMajor(),
119 i::Version::GetMinor(), i::Version::GetBuild(),
120 i::Version::GetPatch(), i::Version::IsCandidate());
121 msg.WriteToLogFile();
122 }
123 }
124
125 void TracingLogger::OpenFile(const char* name) {
126 DCHECK(!IsEnabled());
127 output_handle_ = base::OS::FOpen(name, base::OS::LogFileOpenMode);
128 }
129
130 FILE* TracingLogger::Close() {
131 FILE* result = NULL;
132 if (output_handle_ != NULL) {
133 fclose(output_handle_);
134 }
135 output_handle_ = NULL;
136 i::DeleteArray(message_buffer_);
137 message_buffer_ = NULL;
138 is_stopped_ = false;
139 return result;
140 }
141
142 void TracingLogger::SharedLibraryEvent(const std::string& library_path,
143 uintptr_t start,
144 uintptr_t end) {
145 TracingLogger::MessageBuilder msg(this);
146 msg.Append("shared-library,\"%s\",0x%08" V8PRIxPTR ",0x%08" V8PRIxPTR,
147 library_path.c_str(), start, end);
148 msg.WriteToLogFile();
149 }
150
151 void TracingLogger::BeginEvent(const char* event_name) {
152 TracingLogger::MessageBuilder msg(this);
153 msg.Append("%s,\"begin\"", event_name);
154 msg.WriteToLogFile();
155 }
156
157 void TracingLogger::EndEvent(const char* event_name) {
158 TracingLogger::MessageBuilder msg(this);
159 msg.Append("%s,\"end\"", event_name);
160 msg.WriteToLogFile();
161 }
162
163
164 TracingLogger::MessageBuilder::MessageBuilder(TracingLogger* logger)
165 : logger_(logger),
166 lock_guard_(&logger_->mutex_),
167 pos_(0) {
168 DCHECK(logger_->message_buffer_ != NULL);
169 }
170
171 void TracingLogger::MessageBuilder::Append(const char* format, ...) {
172 i::Vector<char> buf(logger_->message_buffer_ + pos_,
173 TracingLogger::kMessageBufferSize - pos_);
174 va_list args;
175 va_start(args, format);
176 AppendVA(format, args);
177 va_end(args);
178 DCHECK(pos_ <= TracingLogger::kMessageBufferSize);
179 }
180
181 void TracingLogger::MessageBuilder::AppendVA(const char* format, va_list args) {
182 i::Vector<char> buf(logger_->message_buffer_ + pos_,
183 TracingLogger::kMessageBufferSize - pos_);
184 int result = v8::internal::VSNPrintF(buf, format, args);
185
186 // Result is -1 if output was truncated.
187 if (result >= 0) {
188 pos_ += result;
189 } else {
190 pos_ = TracingLogger::kMessageBufferSize;
191 }
192 DCHECK(pos_ <= TracingLogger::kMessageBufferSize);
193 }
194
195 void TracingLogger::MessageBuilder::Append(const char c) {
196 if (pos_ < TracingLogger::kMessageBufferSize) {
197 logger_->message_buffer_[pos_++] = c;
198 }
199 DCHECK(pos_ <= TracingLogger::kMessageBufferSize);
200 }
201
202 void TracingLogger::MessageBuilder::AppendDoubleQuotedString(const char* string) {
203 Append('"');
204 for (const char* p = string; *p != '\0'; p++) {
205 if (*p == '"') {
206 Append('\\');
207 }
208 Append(*p);
209 }
210 Append('"');
211 }
212
213 void TracingLogger::MessageBuilder::AppendStringPart(const char* str, int len) {
214 if (pos_ + len > TracingLogger::kMessageBufferSize) {
215 len = TracingLogger::kMessageBufferSize - pos_;
216 DCHECK(len >= 0);
217 if (len == 0) return;
218 }
219 i::Vector<char> buf(logger_->message_buffer_ + pos_,
220 TracingLogger::kMessageBufferSize - pos_);
221 StrNCpy(buf, str, len);
222 pos_ += len;
223 DCHECK(pos_ <= TracingLogger::kMessageBufferSize);
224 }
225
226 void TracingLogger::MessageBuilder::WriteToLogFile() {
227 DCHECK(pos_ <= TracingLogger::kMessageBufferSize);
228 // Assert that we do not already have a new line at the end.
229 DCHECK(pos_ == 0 || logger_->message_buffer_[pos_ - 1] != '\n');
230 if (pos_ == TracingLogger::kMessageBufferSize) pos_--;
231 logger_->message_buffer_[pos_++] = '\n';
232 const int written = logger_->WriteToFile(logger_->message_buffer_, pos_);
233 if (written != pos_) {
234 logger_->stop();
235 }
236 }
237
238
239 // The Profiler samples pc and sp values for the main thread.
240 // Each sample is appended to a circular buffer.
241 // An independent thread removes data and writes it to the log.
242 // This design minimizes the time spent in the sampler.
243 //
244 class Profiler: public base::Thread {
245 public:
246 explicit Profiler();
247 void Engage();
248 void Disengage();
249
250 // Inserts collected profiling data into buffer.
251 void Insert(i::TickSample* sample) {
252 if (paused_)
253 return;
254
255 if (Succ(head_) == static_cast<int>(base::NoBarrier_Load(&tail_))) {
256 overflow_ = true;
257 } else {
258 buffer_[head_] = *sample;
259 head_ = Succ(head_);
260 buffer_semaphore_.Signal(); // Tell we have an element.
261 }
262 }
263
264 virtual void Run();
265
266 // Pause and Resume TickSample data collection.
267 void pause() { paused_ = true; }
268 void resume() { paused_ = false; }
269
270 private:
271 // Waits for a signal and removes profiling data.
272 bool Remove(i::TickSample* sample) {
273 buffer_semaphore_.Wait(); // Wait for an element.
274 *sample = buffer_[base::NoBarrier_Load(&tail_)];
275 bool result = overflow_;
276 base::NoBarrier_Store(&tail_, static_cast<base::Atomic32>(
277 Succ(base::NoBarrier_Load(&tail_))));
278 overflow_ = false;
279 return result;
280 }
281
282 // Returns the next index in the cyclic buffer.
283 int Succ(int index) { return (index + 1) % kBufferSize; }
284
285 // Cyclic buffer for communicating profiling samples
286 // between the signal handler and the worker thread.
287 static const int kBufferSize = 128;
288 i::TickSample buffer_[kBufferSize]; // Buffer storage.
289 int head_; // Index to the buffer head.
290 base::Atomic32 tail_; // Index to the buffer tail.
291 bool overflow_; // Tell whether a buffer overflow has occurred.
292 // Sempahore used for buffer synchronization.
293 base::Semaphore buffer_semaphore_;
294
295 // Tells whether profiler is engaged, that is, processing thread is stated.
296 bool engaged_;
297
298 // Tells whether worker thread should continue running.
299 base::Atomic32 running_;
300
301 // Tells whether we are currently recording tick samples.
302 bool paused_;
303 };
304
305 //
306 // Ticker used to provide samples to the tracing controller.
307 //
308 class Ticker: public V8Sampler {
309 public:
310 Ticker(Isolate* isolate):
311 V8Sampler(isolate),
312 profiler_(NULL) {}
313
314 ~Ticker() { if (IsActive()) Stop(); }
315
316 void SetProfiler(Profiler* profiler) {
317 DCHECK(profiler_ == NULL);
318 profiler_ = profiler;
319 IncreaseProfilingDepth();
320 if (!IsActive())
321 Start();
322 }
323
324 void ClearProfiler() {
325 profiler_ = NULL;
326 if (IsActive()) Stop();
327 DecreaseProfilingDepth();
328 }
329
330 void Tick(i::TickSample* sample) override {
331 if (profiler_) profiler_->Insert(sample);
332 }
333
334 private:
335 Profiler* profiler_;
336 };
337
338
339 //
340 // Profiler implementation.
341 //
342 Profiler::Profiler()
343 : base::Thread(Options("v8:Profiler")),
344 head_(0),
345 overflow_(false),
346 buffer_semaphore_(0),
347 engaged_(false),
348 paused_(false) {
349 base::NoBarrier_Store(&tail_, 0);
350 base::NoBarrier_Store(&running_, 0);
351 }
352
353 void Profiler::Engage() {
354 if (engaged_) return;
355 engaged_ = true;
356
357 std::vector<base::OS::SharedLibraryAddress> addresses =
358 base::OS::GetSharedLibraryAddresses();
359 for (size_t i = 0; i < addresses.size(); ++i) {
360 TracingController::tracing_controller_->logger_->SharedLibraryEvent(
361 addresses[i].library_path, addresses[i].start, addresses[i].end);
362 }
363
364 // Start thread processing the profiler buffer.
365 base::NoBarrier_Store(&running_, 1);
366 Start();
367
368 TracingLogger::MessageBuilder msg(
369 TracingController::tracing_controller_->logger_);
370 }
371
372 void Profiler::Disengage() {
373 if (!engaged_) return;
374
375 // Stop receiving ticks.
376 TracingController::tracing_controller_->ticker_->ClearProfiler();
377
378 // Terminate the worker thread by setting running_ to false,
379 // inserting a fake element in the queue and then wait for
380 // the thread to terminate.
381 base::NoBarrier_Store(&running_, 0);
382 i::TickSample sample;
383 // Reset 'paused_' flag, otherwise semaphore may not be signalled.
384 resume();
385 Insert(&sample);
386 Join();
387
388 TracingLogger::MessageBuilder msg(TracingController::tracing_controller_->logg er_);
389 }
390
391 void Profiler::Run() {
392 i::TickSample sample;
393 bool overflow = Remove(&sample);
394 while (base::NoBarrier_Load(&running_)) {
395 TracingController::tracing_controller_->LogSample(&sample, overflow);
396 overflow = Remove(&sample);
397 }
398 }
399
400
401 base::Mutex* TracingController::mutex_ = NULL;
402 TracingController* TracingController::tracing_controller_ = NULL;
403
404 void TracingController::StartTracing(Isolate* isolate) {
405 base::LockGuard<base::Mutex> lock_guard(mutex_);
406 if (TracingController::tracing_controller_ == NULL) {
407 TracingController::tracing_controller_ = new TracingController(isolate);
408 }
409 TracingController::tracing_controller_->Start();
410 }
411
412 void TracingController::StopTracing() {
413 base::LockGuard<base::Mutex> lock_guard(mutex_);
414 if (TracingController::tracing_controller_ != NULL) {
415 TracingController::tracing_controller_->Stop();
416 }
417 }
418
419 void TracingController::InstallJitCodeEventHandler(Isolate* isolate, void* data) {
420 TracingController::tracing_controller_->ticker_->SetJitCodeEventHandler(
421 v8::JitCodeEventOptions::kJitCodeEventEnumExisting, data);
422 }
423
424 void TracingController::HandleJitCodeEvent(const JitCodeEvent* event) {
425 }
426
427 void TracingController::SetInterval(int interval) {
428 V8Sampler::SetInterval(interval);
429 }
430
431 TracingController::TracingController(Isolate* isolate)
432 : is_tracing_(false),
433 profiler_(NULL),
434 ticker_(NULL),
435 logger_(new TracingLogger()) {
436 isolate_ = isolate;
437 }
438
439 void TracingController::Start() {
440 if (is_tracing_)
441 return;
442 is_tracing_ = true;
443 logger_->Initialize("d8.log");
444 timer_.Start();
445 profiler_ = new Profiler();
446 profiler_->Engage();
447 ticker_ = new Ticker(isolate_);
448 v8::JitCodeEventHandler handler = &HandleJitCodeEvent;
449 isolate_->RequestInterrupt(&InstallJitCodeEventHandler,
450 reinterpret_cast<void*>(handler));
451 ticker_->SetProfiler(profiler_);
452 }
453
454 void TracingController::Stop() {
455 if (!is_tracing_)
456 return;
457 is_tracing_ = false;
458 // Stop the profiler before closing the file.
459 if (profiler_ != NULL) {
460 profiler_->Disengage();
461 delete profiler_;
462 profiler_ = NULL;
463 }
464
465 ticker_->ClearProfiler();
466 delete ticker_;
467 ticker_ = NULL;
468
469 logger_->Close();
470 logger_ = NULL;
471
472 delete tracing_controller_;
473 tracing_controller_ = NULL;
474 }
475
476 void TracingController::LogSample(i::TickSample* sample, bool overflow) {
477 /*
478 TracingLogger::MessageBuilder msg(logger_);
479 msg.Append("%s,", "tick");
480 msg.Append("0x%x", sample->pc);
481 msg.Append(",%ld", static_cast<int>(timer_.Elapsed().InMicroseconds()));
482 if (sample->has_external_callback) {
483 msg.Append(",1,");
484 #if USES_FUNCTION_DESCRIPTORS
485 msg.Append("0x%x", *FUNCTION_ENTRYPOINT_ADDRESS(sample->external_callback));
486 #else
487 msg.Append("0x%x", sample->external_callback);
488 #endif
489 } else {
490 msg.Append(",0,");
491 msg.Append("0x%x", sample->tos);
492 }
493 msg.Append(",%d", static_cast<int>(sample->state));
494 if (overflow) {
495 msg.Append(",overflow");
496 }
497 for (unsigned i = 0; i < sample->frames_count; ++i) {
498 msg.Append(',');
499 msg.Append("0x%x", sample->stack[i]);
500 }
501 msg.WriteToLogFile();
502 */
503 }
504
505 }
OLDNEW
« no previous file with comments | « src/v8-sampler.cc ('k') | test/cctest/test-cpu-profiler.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698