diff options
-rw-r--r-- | chrome/browser/gpu_process_host_ui_shim.cc | 3 | ||||
-rw-r--r-- | chrome/browser/resources/gpu_internals.html | 11 | ||||
-rw-r--r-- | chrome/browser/resources/gpu_internals/raw_events_view.css | 14 | ||||
-rw-r--r-- | chrome/browser/resources/gpu_internals/raw_events_view.html | 9 | ||||
-rw-r--r-- | chrome/browser/resources/gpu_internals/raw_events_view.js | 76 | ||||
-rw-r--r-- | chrome/browser/resources/gpu_internals/tracing_controller.css | 7 | ||||
-rw-r--r-- | chrome/browser/resources/gpu_internals/tracing_controller.html | 14 | ||||
-rw-r--r-- | chrome/browser/resources/gpu_internals/tracing_controller.js | 36 | ||||
-rw-r--r-- | chrome/browser/ui/webui/gpu_internals_ui.cc | 55 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 1 | ||||
-rw-r--r-- | chrome/renderer/ggl/ggl.cc | 3 | ||||
-rw-r--r-- | chrome/renderer/render_widget.cc | 3 | ||||
-rw-r--r-- | content/gpu/gpu_command_buffer_stub.cc | 2 | ||||
-rw-r--r-- | gpu/command_buffer/service/gpu_processor.cc | 2 | ||||
-rw-r--r-- | gpu/common/gpu_trace_event.cc | 216 | ||||
-rw-r--r-- | gpu/common/gpu_trace_event.h | 291 | ||||
-rw-r--r-- | gpu/gpu.gyp | 16 |
17 files changed, 723 insertions, 36 deletions
diff --git a/chrome/browser/gpu_process_host_ui_shim.cc b/chrome/browser/gpu_process_host_ui_shim.cc index 380a37c..bc5b9da 100644 --- a/chrome/browser/gpu_process_host_ui_shim.cc +++ b/chrome/browser/gpu_process_host_ui_shim.cc @@ -15,6 +15,7 @@ #include "content/browser/renderer_host/render_view_host.h" #include "content/browser/renderer_host/render_widget_host_view.h" #include "content/common/gpu_messages.h" +#include "gpu/common/gpu_trace_event.h" #if defined(OS_LINUX) // These two #includes need to come after gpu_messages.h. @@ -269,6 +270,7 @@ void GpuProcessHostUIShim::EstablishGpuChannel( int renderer_id, EstablishChannelCallback *callback) { DCHECK(CalledOnValidThread()); + GPU_TRACE_EVENT0("gpu", "GpuProcessHostUIShim::EstablishGpuChannel"); linked_ptr<EstablishChannelCallback> wrapped_callback(callback); // If GPU features are already blacklisted, no need to establish the channel. @@ -539,4 +541,3 @@ void GpuProcessHostUIShim::OnScheduleComposite(int renderer_id, } #endif - diff --git a/chrome/browser/resources/gpu_internals.html b/chrome/browser/resources/gpu_internals.html index 8763249..5c17de7 100644 --- a/chrome/browser/resources/gpu_internals.html +++ b/chrome/browser/resources/gpu_internals.html @@ -29,6 +29,7 @@ body { <link rel="stylesheet" href="gpu_internals/timeline_view.css"> <link rel="stylesheet" href="gpu_internals/timeline.css"> <link rel="stylesheet" href="gpu_internals/tracing_controller.css"> +<link rel="stylesheet" href="gpu_internals/raw_events_view.css"> <script src="chrome://resources/js/cr.js"></script> <script src="chrome://resources/js/cr/event_target.js"></script> <script src="chrome://resources/js/cr/ui.js"></script> @@ -44,6 +45,7 @@ body { <script src="gpu_internals/timeline_track.js"></script> <script src="gpu_internals/fast_rect_renderer.js"></script> <script src="gpu_internals/timeline_view.js"></script> +<script src="gpu_internals/raw_events_view.js"></script> <script src="gpu_internals/simulated_trace_data.js"></script> <script> @@ -62,6 +64,12 @@ function onLoad() { // Create the views. cr.ui.decorate('#info-view', gpu.InfoView); + var rawEventsView = $('raw-events-view'); + if (browserBridge.debugMode) + cr.ui.decorate('#raw-events-view', gpu.RawEventsView); + else + rawEventsView.parentNode.removeChild(rawEventsView); + timelineView = $('timeline-view'); if (browserBridge.debugMode) cr.ui.decorate(timelineView, gpu.TimelineView); @@ -100,9 +108,8 @@ document.addEventListener('DOMContentLoaded', onLoad); <!-- Tabs --> <div id="main-tabs"> <include src="gpu_internals/info_view.html"> + <include src="gpu_internals/raw_events_view.html"> <include src="gpu_internals/timeline_view.html"> </div> - - <include src="gpu_internals/tracing_controller.html"> </body> </html> diff --git a/chrome/browser/resources/gpu_internals/raw_events_view.css b/chrome/browser/resources/gpu_internals/raw_events_view.css new file mode 100644 index 0000000..c282960 --- /dev/null +++ b/chrome/browser/resources/gpu_internals/raw_events_view.css @@ -0,0 +1,14 @@ +/* +Copyright (c) 2011 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. +*/ +#raw-events-view { + padding: 4px; +} + +#raw-events-view-data { + cursor: text; + font-family: monospace; + -webkit-user-select: text; +}
\ No newline at end of file diff --git a/chrome/browser/resources/gpu_internals/raw_events_view.html b/chrome/browser/resources/gpu_internals/raw_events_view.html new file mode 100644 index 0000000..464bba8 --- /dev/null +++ b/chrome/browser/resources/gpu_internals/raw_events_view.html @@ -0,0 +1,9 @@ +<!-- +Copyright (c) 2011 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. +--> +<div id=raw-events-view label="Raw Events"> + <div class="raw-events-view-data"> + </div> +</div> diff --git a/chrome/browser/resources/gpu_internals/raw_events_view.js b/chrome/browser/resources/gpu_internals/raw_events_view.js new file mode 100644 index 0000000..5ff981a --- /dev/null +++ b/chrome/browser/resources/gpu_internals/raw_events_view.js @@ -0,0 +1,76 @@ +// Copyright (c) 2011 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. + +/** + * + * @fileoverview Displays the traced data in raw format. Its primarily + * usefulness is to allow users to copy-paste their data in an easy to + * read format for bug reports. + * + */ +cr.define('gpu', function() { + /** + * Provides information on the GPU process and underlying graphics hardware. + * @constructor + * @extends {gpu.Tab} + */ + var RawEventsView = cr.ui.define(gpu.Tab); + + RawEventsView.prototype = { + __proto__: gpu.Tab.prototype, + + decorate: function() { + tracingController.addEventListener('traceBegun', this.refresh.bind(this)); + tracingController.addEventListener('traceEnded', this.refresh.bind(this)); + this.addEventListener('selectedChange', this.onSelectedChange_); + this.refresh(); + }, + + onSelectedChange_: function() { + if (this.selected) { + if (!tracingController.traceEvents.length) { + tracingController.beginTracing(); + } + if (this.needsRefreshOnShow_) { + this.needsRefreshOnShow_ = false; + this.refresh(); + } + } + }, + + /** + * Updates the view based on its currently known data + */ + refresh: function() { + if (this.parentNode.selectedTab != this) { + this.needsRefreshOnShow_ = true; + } + + var dataElement = this.querySelector('.raw-events-view-data'); + if (tracingController.isTracingEnabled) { + var tmp = 'Still tracing. ' + + 'Uncheck the enable tracing button to see traced data.'; + dataElement.textContent = tmp; + } else if (!tracingController.traceEvents.length) { + dataElement.textContent = + 'No trace data collected. Collect data first.'; + } else { + var events = tracingController.traceEvents; + var text = JSON.stringify(events); + dataElement.textContent = text; + + var selection = window.getSelection(); + selection.removeAllRanges(); + var range = document.createRange(); + range.selectNodeContents(dataElement); + selection.addRange(range); + } + } + + }; + + return { + RawEventsView: RawEventsView + }; +}); diff --git a/chrome/browser/resources/gpu_internals/tracing_controller.css b/chrome/browser/resources/gpu_internals/tracing_controller.css index 331fe85..895d695 100644 --- a/chrome/browser/resources/gpu_internals/tracing_controller.css +++ b/chrome/browser/resources/gpu_internals/tracing_controller.css @@ -26,8 +26,7 @@ found in the LICENSE file. border-top-right-radius: 8px; border-top-left-radius: 8px; background-clip: border-box; - background: rgb(255,0,0); - border-left: 1px solid black; - border-top: 1px solid black; - border-right: 1px solid black; + background: rgb(255, 0, 0); + border: 1px solid black; + border-bottom: 0; } diff --git a/chrome/browser/resources/gpu_internals/tracing_controller.html b/chrome/browser/resources/gpu_internals/tracing_controller.html deleted file mode 100644 index 7db2635..0000000 --- a/chrome/browser/resources/gpu_internals/tracing_controller.html +++ /dev/null @@ -1,14 +0,0 @@ -<!-- -Copyright (c) 2011 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. ---> -<div hidden> - <div id="gpu-tracing-start-button-template" class="gpu-tracing-start-button"> - Record new data - </div> - <div id="gpu-tracing-overlay-template" class="gpu-tracing-overlay"> - <div class="gpu-tracing-status">Tracing active.</div> - <button class="gpu-tracing-stop-button">Stop tracing</button> - </div> -</div> diff --git a/chrome/browser/resources/gpu_internals/tracing_controller.js b/chrome/browser/resources/gpu_internals/tracing_controller.js index f46e417..8d7471b 100644 --- a/chrome/browser/resources/gpu_internals/tracing_controller.js +++ b/chrome/browser/resources/gpu_internals/tracing_controller.js @@ -7,20 +7,29 @@ * @fileoverview State and UI for trace data collection. */ cr.define('gpu', function() { + function TracingController() { - this.overlay_ = $('gpu-tracing-overlay-template').cloneNode(true); - this.overlay_.removeAttribute('id'); - cr.ui.decorate(this.overlay_, gpu.Overlay); + this.startButton_ = document.createElement('div'); + this.startButton_.className = 'gpu-tracing-start-button'; + this.startButton_.textContent = 'Start tracing'; + this.startButton_.onclick = this.beginTracing.bind(this); + document.body.appendChild(this.startButton_); - this.traceEvents_ = []; + this.overlay_ = document.createElement('div'); + this.overlay_.className = 'gpu-tracing-overlay'; + + cr.ui.decorate(this.overlay_, gpu.Overlay); - var startButton = $('gpu-tracing-start-button-template').cloneNode(true); - startButton.removeAttribute('id'); - document.body.appendChild(startButton); - startButton.onclick = this.beginTracing.bind(this); + var statusDiv = document.createElement('div'); + statusDiv.textContent = 'Tracing active.'; + this.overlay_.appendChild(statusDiv); - var stopButton = this.overlay_.querySelector('.gpu-tracing-stop-button'); + var stopButton = document.createElement('button'); stopButton.onclick = this.endTracing.bind(this); + stopButton.innerText = 'Stop tracing'; + this.overlay_.appendChild(stopButton); + + this.traceEvents_ = []; } TracingController.prototype = { @@ -93,17 +102,18 @@ cr.define('gpu', function() { console.log('Finishing trace'); if (!browserBridge.debugMode) { - chrome.send('beginToEndTracing'); + chrome.send('endTracingAsync'); } else { - var events = getTimelineTestData1(); + var events = window.getTimelineTestData1 ? + getTimelineTestData1() : []; this.onTraceDataCollected(events); window.setTimeout(this.onEndTracingComplete.bind(this), 250); } }, + /** - * Called by the browser when all processes ack tracing - * having completed. + * Called by the browser when all processes complete tracing. */ onEndTracingComplete: function() { this.overlay_.visible = false; diff --git a/chrome/browser/ui/webui/gpu_internals_ui.cc b/chrome/browser/ui/webui/gpu_internals_ui.cc index 8e8bf2c..0729ef1 100644 --- a/chrome/browser/ui/webui/gpu_internals_ui.cc +++ b/chrome/browser/ui/webui/gpu_internals_ui.cc @@ -35,6 +35,7 @@ #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" #include "content/browser/gpu_process_host.h" +#include "content/browser/renderer_host/render_view_host.h" #include "content/browser/tab_contents/tab_contents.h" #include "grit/browser_resources.h" #include "grit/generated_resources.h" @@ -75,6 +76,8 @@ class GpuMessageHandler virtual void RegisterMessages(); // Mesages + void OnBeginTracing(const ListValue* list); + void OnEndTracingAsync(const ListValue* list); void OnBrowserBridgeInitialized(const ListValue* list); void OnCallAsync(const ListValue* list); @@ -82,6 +85,8 @@ class GpuMessageHandler Value* OnRequestClientInfo(const ListValue* list); Value* OnRequestLogMessages(const ListValue* list); + // Callbacks. + void OnTraceDataCollected(const std::string& json_events); void OnGpuInfoUpdate(); // Executes the javascript function |function_name| in the renderer, passing @@ -95,7 +100,11 @@ class GpuMessageHandler // Cache the Singleton for efficiency. GpuDataManager* gpu_data_manager_; + void OnEndTracingComplete(); + Callback0::Type* gpu_info_update_callback_; + + bool trace_enabled_; }; //////////////////////////////////////////////////////////////////////////////// @@ -141,7 +150,9 @@ std::string GpuHTMLSource::GetMimeType(const std::string&) const { // //////////////////////////////////////////////////////////////////////////////// -GpuMessageHandler::GpuMessageHandler() : gpu_info_update_callback_(NULL) { +GpuMessageHandler::GpuMessageHandler() + : gpu_info_update_callback_(NULL) + , trace_enabled_(false) { gpu_data_manager_ = GpuDataManager::GetInstance(); DCHECK(gpu_data_manager_); } @@ -151,6 +162,9 @@ GpuMessageHandler::~GpuMessageHandler() { gpu_data_manager_->RemoveGpuInfoUpdateCallback(gpu_info_update_callback_); delete gpu_info_update_callback_; } + + if (trace_enabled_) + OnEndTracingAsync(NULL); } WebUIMessageHandler* GpuMessageHandler::Attach(WebUI* web_ui) { @@ -164,6 +178,12 @@ void GpuMessageHandler::RegisterMessages() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); web_ui_->RegisterMessageCallback( + "beginTracing", + NewCallback(this, &GpuMessageHandler::OnBeginTracing)); + web_ui_->RegisterMessageCallback( + "endTracingAsync", + NewCallback(this, &GpuMessageHandler::OnEndTracingAsync)); + web_ui_->RegisterMessageCallback( "browserBridgeInitialized", NewCallback(this, &GpuMessageHandler::OnBrowserBridgeInitialized)); web_ui_->RegisterMessageCallback( @@ -368,6 +388,38 @@ void GpuMessageHandler::OnGpuInfoUpdate() { delete gpu_info_val; } +void GpuMessageHandler::OnBeginTracing(const ListValue* args) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + trace_enabled_ = true; + // TODO(jbates): TracingController::BeginTracing() +} + +void GpuMessageHandler::OnEndTracingAsync(const ListValue* list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(trace_enabled_); + + // TODO(jbates): TracingController::OnEndTracingAsync(new + // Callback(this, GpuMessageHandler::OnEndTracingComplete)) +} + +void GpuMessageHandler::OnEndTracingComplete() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + trace_enabled_ = false; + web_ui_->CallJavascriptFunction("tracingController.onEndTracingComplete"); +} + +void GpuMessageHandler::OnTraceDataCollected(const std::string& json_events) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + std::wstring javascript; + javascript += L"tracingController.onTraceDataCollected("; + javascript += UTF8ToWide(json_events); + javascript += L");"; + + web_ui_->GetRenderViewHost()->ExecuteJavascriptInWebFrame(string16(), + WideToUTF16Hack(javascript)); +} + } // namespace @@ -385,4 +437,3 @@ GpuInternalsUI::GpuInternalsUI(TabContents* contents) : WebUI(contents) { // Set up the chrome://gpu/ source. contents->profile()->GetChromeURLDataManager()->AddDataSource(html_source); } - diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index c235e4e..16898a7 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -28,6 +28,7 @@ 'userfeedback_proto', '../app/app.gyp:app_resources', '../content/content.gyp:content_browser', + '../gpu/gpu.gyp:gpu_common', '../media/media.gyp:media', '../ppapi/ppapi.gyp:ppapi_proxy', # For PpapiMsg_LoadPlugin '../printing/printing.gyp:printing', diff --git a/chrome/renderer/ggl/ggl.cc b/chrome/renderer/ggl/ggl.cc index 9fb4bb0..c13b02c 100644 --- a/chrome/renderer/ggl/ggl.cc +++ b/chrome/renderer/ggl/ggl.cc @@ -15,11 +15,13 @@ #include "chrome/renderer/render_widget.h" #include "ipc/ipc_channel_handle.h" + #if defined(ENABLE_GPU) #include "gpu/command_buffer/client/gles2_cmd_helper.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "gpu/command_buffer/client/gles2_lib.h" #include "gpu/command_buffer/common/constants.h" +#include "gpu/common/gpu_trace_event.h" #include "gpu/GLES2/gles2_command_buffer.h" #endif // ENABLE_GPU @@ -387,6 +389,7 @@ bool Context::MakeCurrent(Context* context) { } bool Context::SwapBuffers() { + GPU_TRACE_EVENT0("gpu", "Context::SwapBuffers"); // Don't request latest error status from service. Just use the locally cached // information from the last flush. if (command_buffer_->GetLastState().error != gpu::error::kNoError) diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index b19a87c..8081288 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -17,6 +17,7 @@ #include "chrome/renderer/render_process.h" #include "chrome/renderer/render_thread.h" #include "chrome/renderer/renderer_webkitclient_impl.h" +#include "gpu/common/gpu_trace_event.h" #include "ipc/ipc_sync_message.h" #include "skia/ext/platform_canvas.h" #include "third_party/skia/include/core/SkShader.h" @@ -546,6 +547,8 @@ void RenderWidget::CallDoDeferredUpdate() { } void RenderWidget::DoDeferredUpdate() { + GPU_TRACE_EVENT0("render_widget", "DoDeferredUpdate"); + if (!webwidget_ || update_reply_pending()) return; diff --git a/content/gpu/gpu_command_buffer_stub.cc b/content/gpu/gpu_command_buffer_stub.cc index c1e9b1e9..d838c9f 100644 --- a/content/gpu/gpu_command_buffer_stub.cc +++ b/content/gpu/gpu_command_buffer_stub.cc @@ -12,6 +12,7 @@ #include "content/gpu/gpu_channel.h" #include "content/gpu/gpu_command_buffer_stub.h" #include "content/gpu/gpu_thread.h" +#include "gpu/common/gpu_trace_event.h" using gpu::Buffer; @@ -351,6 +352,7 @@ void GpuCommandBufferStub::OnResizeOffscreenFrameBuffer(const gfx::Size& size) { } void GpuCommandBufferStub::OnSwapBuffers() { + GPU_TRACE_EVENT0("gpu", "GpuCommandBufferStub::OnSwapBuffers"); Send(new GpuCommandBufferMsg_SwapBuffers(route_id_)); } diff --git a/gpu/command_buffer/service/gpu_processor.cc b/gpu/command_buffer/service/gpu_processor.cc index 45d21d7..c7d19d3 100644 --- a/gpu/command_buffer/service/gpu_processor.cc +++ b/gpu/command_buffer/service/gpu_processor.cc @@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "base/message_loop.h" #include "app/gfx/gl/gl_context.h" +#include "gpu/common/gpu_trace_event.h" using ::base::SharedMemory; @@ -124,6 +125,7 @@ const unsigned int kMaxOutstandingSwapBuffersCallsPerOnscreenContext = 1; #endif void GPUProcessor::ProcessCommands() { + GPU_TRACE_EVENT0("gpu", "GPUProcessor:ProcessCommands"); CommandBuffer::State state = command_buffer_->GetState(); if (state.error != error::kNoError) return; diff --git a/gpu/common/gpu_trace_event.cc b/gpu/common/gpu_trace_event.cc new file mode 100644 index 0000000..d9caec3 --- /dev/null +++ b/gpu/common/gpu_trace_event.cc @@ -0,0 +1,216 @@ +// Copyright (c) 2010 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 "gpu/common/gpu_trace_event.h" + +#include "base/format_macros.h" +#include "base/process_util.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "base/time.h" + +#define USE_UNRELIABLE_NOW + +using namespace base; + +namespace gpu { + +// Controls the number of trace events we will buffer in-memory +// before flushing them. +#define TRACE_EVENT_BUFFER_SIZE 16384 + +//////////////////////////////////////////////////////////////////////////////// +// +// TraceLog::Category +// +//////////////////////////////////////////////////////////////////////////////// +TraceCategory::TraceCategory(const char* name, bool enabled) + : name_(name) { + base::subtle::NoBarrier_Store(&enabled_, + static_cast<base::subtle::Atomic32>(enabled)); +} + +TraceCategory::~TraceCategory() { + base::subtle::NoBarrier_Store(&enabled_, + static_cast<base::subtle::Atomic32>(0)); +} + +//////////////////////////////////////////////////////////////////////////////// +// +// TraceEvent +// +//////////////////////////////////////////////////////////////////////////////// + +namespace { +const char* GetPhaseStr(TraceEventPhase phase) { + if (phase == GPU_TRACE_EVENT_PHASE_BEGIN) { + return "B"; + } else if (phase == GPU_TRACE_EVENT_PHASE_INSTANT) { + return "I"; + } else if (phase == GPU_TRACE_EVENT_PHASE_END) { + return "E"; + } else { + DCHECK(false); + return "?"; + } +} +} + +void TraceEvent::AppendAsJSON(std::string* out, + const std::vector<TraceEvent>& events) { + *out += "["; + for (size_t i = 0; i < events.size(); ++i) { + if (i > 0) + *out += ","; + events[i].AppendAsJSON(out); + } + *out += "]"; +} + +void TraceEvent::AppendAsJSON(std::string* out) const { + int nargs = 0; + for (int i = 0; i < TRACE_MAX_NUM_ARGS; ++i) { + if (argNames[i] == NULL) + break; + nargs += 1; + } + + const char* phaseStr = GetPhaseStr(phase); + int64 time_int64 = timestamp.ToInternalValue(); + long long unsigned int time_llui = + static_cast<long long unsigned int>(time_int64); + StringAppendF(out, + "{cat:'%s',pid:%i,tid:%i,ts:0x%llx,ph:'%s',name:'%s',args:{", + category->name(), + static_cast<int>(processId), + static_cast<int>(threadId), + time_llui, + phaseStr, + name); + for (int i = 0; i < nargs; ++i) { + if (i > 0) + *out += ","; + *out += argNames[i]; + *out += ":'"; + *out += argValues[i]; + *out += "'"; + } + *out += "}}"; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// TraceLog +// +//////////////////////////////////////////////////////////////////////////////// + +// static +TraceLog* TraceLog::GetInstance() { + return Singleton<TraceLog, StaticMemorySingletonTraits<TraceLog> >::get(); +} + +TraceLog::TraceLog() + : enabled_(false) +{ + logged_events_.reserve(1024); +} + +TraceLog::~TraceLog() { +} + +TraceCategory* TraceLog::GetCategory(const char* name) { + AutoLock lock(lock_); + // TODO(nduca): replace with a hash_map. + for (int i = categories_.size() - 1; i >= 0; i-- ) { + if (strcmp(categories_[i]->name(), name) == 0) + return categories_[i]; + } + TraceCategory* category = new TraceCategory(name, enabled_); + categories_.push_back(category); + return category; +} + +void TraceLog::SetEnabled(bool enabled) { + AutoLock lock(lock_); + if (enabled == enabled_) + return; + if (enabled) { + // Enable all categories. + enabled_ = true; + for (size_t i = 0; i < categories_.size(); i++) { + base::subtle::NoBarrier_Store(&categories_[i]->enabled_, + static_cast<base::subtle::Atomic32>(1)); + } + } else { + // Disable all categories. + for (size_t i = 0; i < categories_.size(); i++) { + base::subtle::NoBarrier_Store(&categories_[i]->enabled_, + static_cast<base::subtle::Atomic32>(0)); + } + enabled_ = false; + FlushWithLockAlreadyHeld(); + } +} + +void TraceLog::SetOutputCallback(TraceLog::OutputCallback* cb) { + AutoLock lock(lock_); + if (enabled_) { + FlushWithLockAlreadyHeld(); + } + output_callback_.reset(cb); +} + +void TraceLog::AddRemotelyCollectedData(const std::string& json_events) { + AutoLock lock(lock_); + if (output_callback_.get()) + output_callback_->Run(json_events); +} + +void TraceLog::Flush() { + AutoLock lock(lock_); + FlushWithLockAlreadyHeld(); +} + +void TraceLog::FlushWithLockAlreadyHeld() { + if (output_callback_.get() && logged_events_.size()) { + std::string json_events; + TraceEvent::AppendAsJSON(&json_events, logged_events_); + output_callback_->Run(json_events); + } + logged_events_.erase(logged_events_.begin(), logged_events_.end()); +} + +void TraceLog::AddTraceEvent(TraceEventPhase phase, + const char* file, int line, + TraceCategory* category, + const char* name, + const char* arg1name, const char* arg1val, + const char* arg2name, const char* arg2val) { + DCHECK(file && name); +#ifdef USE_UNRELIABLE_NOW + TimeTicks now = TimeTicks::HighResNow(); +#else + TimeTicks now = TimeTicks::Now(); +#endif + //static_cast<unsigned long>(base::GetCurrentProcId()), + AutoLock lock(lock_); + logged_events_.push_back(TraceEvent()); + TraceEvent& event = logged_events_.back(); + event.processId = static_cast<unsigned long>(base::GetCurrentProcId()); + event.threadId = PlatformThread::CurrentId(); + event.timestamp = now; + event.phase = phase; + event.category = category; + event.name = name; + event.argNames[0] = arg1name; + event.argValues[0] = arg1name ? arg1val : ""; + event.argNames[1] = arg2name; + event.argValues[1] = arg2name ? arg2val : ""; + COMPILE_ASSERT(TRACE_MAX_NUM_ARGS == 2, TraceEvent_arc_count_out_of_sync); + + if (logged_events_.size() > TRACE_EVENT_BUFFER_SIZE) + FlushWithLockAlreadyHeld(); +} + +} // namespace gpu diff --git a/gpu/common/gpu_trace_event.h b/gpu/common/gpu_trace_event.h new file mode 100644 index 0000000..1c8e46a --- /dev/null +++ b/gpu/common/gpu_trace_event.h @@ -0,0 +1,291 @@ +// Copyright (c) 2010 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. + +// Trace events are for tracking application performance. +// +// Events are issued against categories. Whereas LOG's +// categories are statically defined, TRACE categories are created +// implicitly with a string. For example: +// GPU_TRACE_EVENT_INSTANT0("MY_SUBSYSTEM", "SomeImportantEvent") +// +// Events can be INSTANT, or can be pairs of BEGIN and END: +// GPU_TRACE_EVENT_BEGIN0("MY_SUBSYSTEM", "SomethingCostly") +// doSomethingCostly() +// GPU_TRACE_EVENT_END0("MY_SUBSYSTEM", "SomethingCostly") +// +// A common use case is to trace entire function scopes. This +// issues a trace BEGIN and END automatically: +// void doSomethingCostly() { +// GPU_TRACE_EVENT0("MY_SUBSYSTEM", "doSomethingCostly"); +// ... +// } +// +// Additional parameters can be associated with an event: +// void doSomethingCostly2(int howMuch) { +// GPU_TRACE_EVENT1("MY_SUBSYSTEM", "doSomethingCostly", +// "howMuch", StringPrintf("%i", howMuch).c_str()); +// ... +// } +// +// The trace system will automatically add to this information the +// current process id, thread id, a timestamp down to the +// microsecond, as well as the file and line number of the calling location. +// +// By default, trace collection is compiled in, but turned off at runtime. +// Collecting trace data is the responsibility of the embedding +// application. In Chrome's case, navigating to about:gpu will turn on +// tracing and display data collected across all active processes. +// + +#ifndef GPU_TRACE_EVENT_H_ +#define GPU_TRACE_EVENT_H_ +#pragma once + +#include "build/build_config.h" + +#include <string> + +#include "base/scoped_ptr.h" +#include "base/scoped_vector.h" +#include "base/atomicops.h" +#include "base/singleton.h" +#include "base/time.h" +#include "base/timer.h" +#include "base/callback.h" +#include <vector> + + +// Implementation detail: trace event macros create temporary variables +// to keep instrumentation overhead low. These macros give each temporary +// variable a unique name based on the line number to prevent name collissions. +#define GPU_TRACE_EVENT_UNIQUE_IDENTIFIER3(a,b) a##b +#define GPU_TRACE_EVENT_UNIQUE_IDENTIFIER2(a,b) \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER3(a,b) +#define GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(name_prefix) \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER2(name_prefix, __LINE__) + +// Records a pair of begin and end events called "name" for the current +// scope, with 0, 1 or 2 associated arguments. If the category is not +// enabled, then this does nothing. +#define GPU_TRACE_EVENT0(category, name) \ + GPU_TRACE_EVENT1(category, name, NULL, NULL) +#define GPU_TRACE_EVENT1(category, name, arg1name, arg1val) \ + GPU_TRACE_EVENT2(category, name, arg1name, arg1val, NULL, NULL) +#define GPU_TRACE_EVENT2(category, name, arg1name, arg1val, arg2name, arg2val) \ + static gpu::TraceCategory* \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic) = \ + gpu::TraceLog::GetInstance()->GetCategory(category); \ + if (base::subtle::Acquire_Load(\ + &(GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic))->enabled_)) { \ + gpu::TraceLog::GetInstance()->AddTraceEvent( \ + gpu::GPU_TRACE_EVENT_PHASE_BEGIN, \ + __FILE__, __LINE__, \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic), \ + name, \ + arg1name, arg1val, \ + arg2name, arg2val); \ + } \ + gpu::internal::TraceEndOnScopeClose __profileScope ## __LINE ( \ + __FILE__, __LINE__, \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic), name); + +// Records a single event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +#define GPU_TRACE_EVENT_INSTANT0(category, name) \ + GPU_TRACE_EVENT_INSTANT1(category, name, NULL, NULL) +#define GPU_TRACE_EVENT_INSTANT1(category, name, arg1name, arg1val) \ + GPU_TRACE_EVENT_INSTANT2(category, name, arg1name, arg1val, NULL, NULL) +#define GPU_TRACE_EVENT_INSTANT2(category, name, arg1name, arg1val, \ + arg2name, arg2val) \ + static gpu::TraceCategory* \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic) = \ + gpu::TraceLog::GetInstance()->GetCategory(category); \ + if (base::subtle::Acquire_Load( \ + &(GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic))->enabled_)) { \ + gpu::TraceLog::GetInstance()->AddTraceEvent( \ + gpu::GPU_TRACE_EVENT_PHASE_INSTANT, \ + __FILE__, __LINE__, \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic), \ + name, \ + arg1name, arg1val, \ + arg2name, arg2val); \ + } + +// Records a single BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +#define GPU_TRACE_EVENT_BEGIN0(category, name) \ + GPU_TRACE_EVENT_BEGIN1(category, name, NULL, NULL) +#define GPU_TRACE_EVENT_BEGIN1(category, name, arg1name, arg1val) \ + GPU_TRACE_EVENT_BEGIN2(category, name, arg1name, arg1val, NULL, NULL) +#define GPU_TRACE_EVENT_BEGIN2(category, name, arg1name, arg1val, \ + arg2name, arg2val) \ + static gpu::TraceCategory* \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic) = \ + gpu::TraceLog::GetInstance()->GetCategory(category); \ + if (base::subtle::Acquire_Load( \ + &(GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic))->enabled_)) { \ + gpu::TraceLog::GetInstance()->AddTraceEvent( \ + gpu::GPU_TRACE_EVENT_PHASE_BEGIN, \ + __FILE__, __LINE__, \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic), \ + name, \ + arg1name, arg1val, \ + arg2name, arg2val); \ + } + +// Records a single END event for "name" immediately. If the category +// is not enabled, then this does nothing. +#define GPU_TRACE_EVENT_END0(category, name) \ + static gpu::TraceCategory* \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic) = \ + gpu::TraceLog::GetInstance()->GetCategory(category); \ + if (base::subtle::Acquire_Load( \ + &(GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic))->enabled_)) { \ + gpu::TraceLog::GetInstance()->AddTraceEvent( \ + gpu::GPU_TRACE_EVENT_PHASE_END, \ + __FILE__, __LINE__, \ + GPU_TRACE_EVENT_UNIQUE_IDENTIFIER(catstatic), \ + name, \ + arg1name, arg1val, \ + arg2name, arg2val); \ + } + + +namespace gpu { + +// Categories allow enabling/disabling of streams of trace events +// Don't manipulate the category object directly, as this may lead +// to threading issues. Use the TraceLog methods instead. +class TraceCategory { + public: + TraceCategory(const char* name, bool enabled); + ~TraceCategory(); + + const char* name() const { return name_; } + + // NEVER read these directly, let the macros do it for you + volatile base::subtle::Atomic32 enabled_; + protected: + const char* name_; +}; + +#define TRACE_MAX_NUM_ARGS 2 + +enum TraceEventPhase { + GPU_TRACE_EVENT_PHASE_BEGIN, + GPU_TRACE_EVENT_PHASE_END, + GPU_TRACE_EVENT_PHASE_INSTANT +}; + +// Output records are "Events" and can be obtained via the +// OutputCallback whenever the logging system decides to flush. This +// can happen at any time, on any thread, or you can programatically +// force it to happen. +struct TraceEvent { + static void AppendAsJSON(std::string* out, + const std::vector<TraceEvent>& events); + void AppendAsJSON(std::string* out) const; + + + unsigned long processId; + unsigned long threadId; + base::TimeTicks timestamp; + TraceEventPhase phase; + TraceCategory* category; + const char* name; + const char* argNames[TRACE_MAX_NUM_ARGS]; + std::string argValues[TRACE_MAX_NUM_ARGS]; +}; + + +class TraceLog { + public: + static TraceLog* GetInstance(); + + // Global enable of tracing. Currently enables all categories or not. + // TODO(nduca) Replaced with an Enable/DisableCategory() that + // implicitly controls the global logging state. + void SetEnabled(bool enabled); + + // When enough events are collected, they are handed (in bulk) to + // the output callback. If no callback is set, the output will be + // silently dropped. + typedef Callback1<const std::string& /* json_events */>::Type OutputCallback; + void SetOutputCallback(OutputCallback* cb); + + // Forwards data collected by a child process to the registered + // output callback. + void AddRemotelyCollectedData(const std::string& json_events); + + // Flushes all logged data to the callback. + void Flush(); + + // Called by GPU_TRACE_EVENT* macros, don't call this directly. + TraceCategory* GetCategory(const char* name); + + // Called by GPU_TRACE_EVENT* macros, don't call this directly. + void AddTraceEvent(TraceEventPhase phase, + const char* file, int line, + TraceCategory* category, + const char* name, + const char* arg1name, const char* arg1val, + const char* arg2name, const char* arg2val); + + private: + // This allows constructor and destructor to be private and usable only + // by the Singleton class. + friend struct StaticMemorySingletonTraits<TraceLog>; + + TraceLog(); + ~TraceLog(); + void FlushWithLockAlreadyHeld(); + + // TODO(nduca): switch to per-thread trace buffers to reduce thread + // synchronization. + base::Lock lock_; + bool enabled_; + ScopedVector<TraceCategory> categories_; + scoped_ptr<OutputCallback> output_callback_; + std::vector<TraceEvent> logged_events_; + + DISALLOW_COPY_AND_ASSIGN(TraceLog); +}; + +namespace internal { + +// Used by GPU_TRACE_EVENTx macro. Do not use directly. +class TraceEndOnScopeClose { + public: + TraceEndOnScopeClose(const char* file, int line, + TraceCategory* category, + const char* name) + : file_(file) + , line_(line) + , category_(category) + , name_(name) { } + + ~TraceEndOnScopeClose() { + if (base::subtle::Acquire_Load(&category_->enabled_)) + gpu::TraceLog::GetInstance()->AddTraceEvent( + gpu::GPU_TRACE_EVENT_PHASE_END, + file_, line_, + category_, + name_, + NULL, NULL, NULL, NULL); + } + + private: + const char* file_; + int line_; + TraceCategory* category_; + const char* name_; +}; + +} // namespace internal + +} // namespace gpu + +#endif // GPU_TRACE_EVENT_H_ diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp index 0fb7581..d53badd 100644 --- a/gpu/gpu.gyp +++ b/gpu/gpu.gyp @@ -25,6 +25,20 @@ }, 'targets': [ { + 'target_name': 'gpu_common', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'common/gpu_trace_event.cc', + 'common/gpu_trace_event.h', + ], + }, + { 'target_name': 'command_buffer_common', 'type': 'static_library', 'include_dirs': [ @@ -163,6 +177,7 @@ }, 'dependencies': [ 'command_buffer_common', + 'gpu_common', '../app/app.gyp:app_base', '../base/base.gyp:base', '../ui/gfx/gfx.gyp:gfx', @@ -231,6 +246,7 @@ 'command_buffer_client', 'command_buffer_common', 'command_buffer_service', + 'gpu_common', 'gpu_unittest_utils', 'gles2_implementation_client_side_arrays', 'gles2_cmd_helper', |