| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/trace_event/trace_event_impl.h" | 
 |  | 
 | #include <fcntl.h> | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 |  | 
 | #include "base/format_macros.h" | 
 | #include "base/logging.h" | 
 | #include "base/posix/eintr_wrapper.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/synchronization/waitable_event.h" | 
 | #include "base/threading/thread.h" | 
 | #include "base/trace_event/trace_event.h" | 
 |  | 
 | namespace base { | 
 | namespace trace_event { | 
 |  | 
 | namespace { | 
 |  | 
 | int g_atrace_fd = -1; | 
 | const char kATraceMarkerFile[] = "/sys/kernel/debug/tracing/trace_marker"; | 
 |  | 
 | void WriteToATrace(int fd, const char* buffer, size_t size) { | 
 |   size_t total_written = 0; | 
 |   while (total_written < size) { | 
 |     ssize_t written = HANDLE_EINTR(write( | 
 |         fd, buffer + total_written, size - total_written)); | 
 |     if (written <= 0) | 
 |       break; | 
 |     total_written += written; | 
 |   } | 
 |   if (total_written < size) { | 
 |     PLOG(WARNING) << "Failed to write buffer '" << std::string(buffer, size) | 
 |                   << "' to " << kATraceMarkerFile; | 
 |   } | 
 | } | 
 |  | 
 | void WriteEvent( | 
 |     char phase, | 
 |     const char* category_group, | 
 |     const char* name, | 
 |     unsigned long long id, | 
 |     const char** arg_names, | 
 |     const unsigned char* arg_types, | 
 |     const TraceEvent::TraceValue* arg_values, | 
 |     const std::unique_ptr<ConvertableToTraceFormat>* convertable_values, | 
 |     unsigned int flags) { | 
 |   std::string out = StringPrintf("%c|%d|%s", phase, getpid(), name); | 
 |   if (flags & TRACE_EVENT_FLAG_HAS_ID) | 
 |     StringAppendF(&out, "-%" PRIx64, static_cast<uint64_t>(id)); | 
 |   out += '|'; | 
 |  | 
 |   for (int i = 0; i < kTraceMaxNumArgs && arg_names[i]; | 
 |        ++i) { | 
 |     if (i) | 
 |       out += ';'; | 
 |     out += arg_names[i]; | 
 |     out += '='; | 
 |     std::string::size_type value_start = out.length(); | 
 |     if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) | 
 |       convertable_values[i]->AppendAsTraceFormat(&out); | 
 |     else | 
 |       TraceEvent::AppendValueAsJSON(arg_types[i], arg_values[i], &out); | 
 |  | 
 |     // Remove the quotes which may confuse the atrace script. | 
 |     ReplaceSubstringsAfterOffset(&out, value_start, "\\\"", "'"); | 
 |     ReplaceSubstringsAfterOffset(&out, value_start, "\"", ""); | 
 |     // Replace chars used for separators with similar chars in the value. | 
 |     std::replace(out.begin() + value_start, out.end(), ';', ','); | 
 |     std::replace(out.begin() + value_start, out.end(), '|', '!'); | 
 |   } | 
 |  | 
 |   out += '|'; | 
 |   out += category_group; | 
 |   WriteToATrace(g_atrace_fd, out.c_str(), out.size()); | 
 | } | 
 |  | 
 | void NoOpOutputCallback(WaitableEvent* complete_event, | 
 |                         const scoped_refptr<RefCountedString>&, | 
 |                         bool has_more_events) { | 
 |   if (!has_more_events) | 
 |     complete_event->Signal(); | 
 | } | 
 |  | 
 | void EndChromeTracing(TraceLog* trace_log, | 
 |                       WaitableEvent* complete_event) { | 
 |   trace_log->SetDisabled(); | 
 |   // Delete the buffered trace events as they have been sent to atrace. | 
 |   trace_log->Flush(Bind(&NoOpOutputCallback, complete_event)); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // These functions support Android systrace.py when 'webview' category is | 
 | // traced. With the new adb_profile_chrome, we may have two phases: | 
 | // - before WebView is ready for combined tracing, we can use adb_profile_chrome | 
 | //   to trace android categories other than 'webview' and chromium categories. | 
 | //   In this way we can avoid the conflict between StartATrace/StopATrace and | 
 | //   the intents. | 
 | // - TODO(wangxianzhu): after WebView is ready for combined tracing, remove | 
 | //   StartATrace, StopATrace and SendToATrace, and perhaps send Java traces | 
 | //   directly to atrace in trace_event_binding.cc. | 
 |  | 
 | void TraceLog::StartATrace() { | 
 |   if (g_atrace_fd != -1) | 
 |     return; | 
 |  | 
 |   g_atrace_fd = HANDLE_EINTR(open(kATraceMarkerFile, O_WRONLY)); | 
 |   if (g_atrace_fd == -1) { | 
 |     PLOG(WARNING) << "Couldn't open " << kATraceMarkerFile; | 
 |     return; | 
 |   } | 
 |   TraceConfig trace_config; | 
 |   trace_config.SetTraceRecordMode(RECORD_CONTINUOUSLY); | 
 |   SetEnabled(trace_config, TraceLog::RECORDING_MODE); | 
 | } | 
 |  | 
 | void TraceLog::StopATrace() { | 
 |   if (g_atrace_fd == -1) | 
 |     return; | 
 |  | 
 |   close(g_atrace_fd); | 
 |   g_atrace_fd = -1; | 
 |  | 
 |   // TraceLog::Flush() requires the current thread to have a message loop, but | 
 |   // this thread called from Java may not have one, so flush in another thread. | 
 |   Thread end_chrome_tracing_thread("end_chrome_tracing"); | 
 |   WaitableEvent complete_event(WaitableEvent::ResetPolicy::AUTOMATIC, | 
 |                                WaitableEvent::InitialState::NOT_SIGNALED); | 
 |   end_chrome_tracing_thread.Start(); | 
 |   end_chrome_tracing_thread.task_runner()->PostTask( | 
 |       FROM_HERE, base::Bind(&EndChromeTracing, Unretained(this), | 
 |                             Unretained(&complete_event))); | 
 |   complete_event.Wait(); | 
 | } | 
 |  | 
 | void TraceEvent::SendToATrace() { | 
 |   if (g_atrace_fd == -1) | 
 |     return; | 
 |  | 
 |   const char* category_group = | 
 |       TraceLog::GetCategoryGroupName(category_group_enabled_); | 
 |  | 
 |   switch (phase_) { | 
 |     case TRACE_EVENT_PHASE_BEGIN: | 
 |       WriteEvent('B', category_group, name_, id_, | 
 |                  arg_names_, arg_types_, arg_values_, convertable_values_, | 
 |                  flags_); | 
 |       break; | 
 |  | 
 |     case TRACE_EVENT_PHASE_COMPLETE: | 
 |       WriteEvent(duration_.ToInternalValue() == -1 ? 'B' : 'E', | 
 |                  category_group, name_, id_, | 
 |                  arg_names_, arg_types_, arg_values_, convertable_values_, | 
 |                  flags_); | 
 |       break; | 
 |  | 
 |     case TRACE_EVENT_PHASE_END: | 
 |       // Though a single 'E' is enough, here append pid, name and | 
 |       // category_group etc. So that unpaired events can be found easily. | 
 |       WriteEvent('E', category_group, name_, id_, | 
 |                  arg_names_, arg_types_, arg_values_, convertable_values_, | 
 |                  flags_); | 
 |       break; | 
 |  | 
 |     case TRACE_EVENT_PHASE_INSTANT: | 
 |       // Simulate an instance event with a pair of begin/end events. | 
 |       WriteEvent('B', category_group, name_, id_, | 
 |                  arg_names_, arg_types_, arg_values_, convertable_values_, | 
 |                  flags_); | 
 |       WriteToATrace(g_atrace_fd, "E", 1); | 
 |       break; | 
 |  | 
 |     case TRACE_EVENT_PHASE_COUNTER: | 
 |       for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) { | 
 |         DCHECK(arg_types_[i] == TRACE_VALUE_TYPE_INT); | 
 |         std::string out = base::StringPrintf( | 
 |             "C|%d|%s-%s", getpid(), name_, arg_names_[i]); | 
 |         if (flags_ & TRACE_EVENT_FLAG_HAS_ID) | 
 |           StringAppendF(&out, "-%" PRIx64, static_cast<uint64_t>(id_)); | 
 |         StringAppendF(&out, "|%d|%s", | 
 |                       static_cast<int>(arg_values_[i].as_int), category_group); | 
 |         WriteToATrace(g_atrace_fd, out.c_str(), out.size()); | 
 |       } | 
 |       break; | 
 |  | 
 |     default: | 
 |       // Do nothing. | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | void TraceLog::AddClockSyncMetadataEvent() { | 
 |   int atrace_fd = HANDLE_EINTR(open(kATraceMarkerFile, O_WRONLY | O_APPEND)); | 
 |   if (atrace_fd == -1) { | 
 |     PLOG(WARNING) << "Couldn't open " << kATraceMarkerFile; | 
 |     return; | 
 |   } | 
 |  | 
 |   // Android's kernel trace system has a trace_marker feature: this is a file on | 
 |   // debugfs that takes the written data and pushes it onto the trace | 
 |   // buffer. So, to establish clock sync, we write our monotonic clock into that | 
 |   // trace buffer. | 
 |   double now_in_seconds = (TRACE_TIME_TICKS_NOW() - TimeTicks()).InSecondsF(); | 
 |   std::string marker = StringPrintf( | 
 |       "trace_event_clock_sync: parent_ts=%f\n", now_in_seconds); | 
 |   WriteToATrace(atrace_fd, marker.c_str(), marker.size()); | 
 |   close(atrace_fd); | 
 | } | 
 |  | 
 | }  // namespace trace_event | 
 | }  // namespace base |