Commit 5b4926e1 authored by nduca@chromium.org's avatar nduca@chromium.org

Implement backend for thread names and process names.

Added a new trace event phase 'M'/TRACE_EVENT_PHASE_METADATA,
which can become a general mechanism for adding metadata to traces.

The two M-type events that we then add are:
	{ph=M pid=<pid> name="process_name" args={ name="name of pid" }}
	{ph=M pid=<pid> tid=<tid> name="thread_name" args={ name="name of tid" }}

base::thread is instrumented to set names automatically. I will do a followon
changelist to add instrumentation to Chrome for its various processes and
threads.

Review URL: http://codereview.chromium.org/7495031

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@95997 0039d316-1c4b-4281-b951-d872f2087c98
parent 151648c6
......@@ -4,6 +4,8 @@
#include "base/debug/trace_event.h"
#include <algorithm>
#if defined(OS_WIN)
#include "base/debug/trace_event_win.h"
#endif
......@@ -11,6 +13,7 @@
#include "base/memory/ref_counted_memory.h"
#include "base/process_util.h"
#include "base/stringprintf.h"
#include "base/threading/thread_local.h"
#include "base/utf_string_conversions.h"
#include "base/stl_util.h"
#include "base/time.h"
......@@ -30,13 +33,20 @@ const size_t kTraceEventBatchSize = 1000;
static TraceCategory g_categories[TRACE_EVENT_MAX_CATEGORIES] = {
{ "tracing already shutdown", false },
{ "tracing categories exhausted; must increase TRACE_EVENT_MAX_CATEGORIES",
false },
{ "__metadata",
false }
};
static const TraceCategory* const g_category_already_shutdown =
&g_categories[0];
static const TraceCategory* const g_category_categories_exhausted =
&g_categories[1];
static int g_category_index = 2; // skip initial 2 error categories
static const TraceCategory* const g_category_metadata =
&g_categories[2];
static int g_category_index = 3; // skip initial 3 categories
// Flag to indicate whether we captured the current thread name
static ThreadLocalBoolean g_current_thread_name_captured;
////////////////////////////////////////////////////////////////////////////////
//
......@@ -104,6 +114,8 @@ const char* GetPhaseStr(TraceEventPhase phase) {
return "I";
case TRACE_EVENT_PHASE_END:
return "E";
case TRACE_EVENT_PHASE_METADATA:
return "M";
default:
NOTREACHED() << "Invalid phase argument";
return "?";
......@@ -294,9 +306,13 @@ void TraceLog::SetEnabled(bool enabled) {
// check GetCategoryInternal creation code that users TraceLog::enabled_
g_categories[i].enabled = enabled;
}
if (!enabled)
AddCurrentMetadataEvents();
} // release lock
if (!enabled)
if (!enabled) {
Flush();
}
}
float TraceLog::GetBufferPercentFull() const {
......@@ -361,6 +377,33 @@ int TraceLog::AddTraceEvent(TraceEventPhase phase,
return -1;
if (logged_events_.size() >= kTraceEventBufferSize)
return -1;
PlatformThreadId thread_id = PlatformThread::CurrentId();
// Record the name of the calling thread, if not done already.
if (!g_current_thread_name_captured.Get()) {
const char* cur_name = PlatformThread::GetName();
base::hash_map<PlatformThreadId, std::string>::iterator existing_name =
thread_names_.find(thread_id);
if (existing_name == thread_names_.end()) {
// This is a new thread id, and a new name.
thread_names_[thread_id] = cur_name ? cur_name : "";
} else if(cur_name != NULL) {
// This is a thread id that we've seen before, but potentially with a
// new name.
std::vector<std::string> existing_names;
Tokenize(existing_name->second, std::string(","), &existing_names);
bool found = std::find(existing_names.begin(),
existing_names.end(),
cur_name) != existing_names.end();
if (!found) {
existing_names.push_back(cur_name);
thread_names_[thread_id] =
JoinString(existing_names, ',');
}
}
}
if (threshold_begin_id > -1) {
DCHECK(phase == base::debug::TRACE_EVENT_PHASE_END);
size_t begin_i = static_cast<size_t>(threshold_begin_id);
......@@ -380,7 +423,7 @@ int TraceLog::AddTraceEvent(TraceEventPhase phase,
ret_begin_id = static_cast<int>(logged_events_.size());
logged_events_.push_back(
TraceEvent(static_cast<unsigned long>(base::GetCurrentProcId()),
PlatformThread::CurrentId(),
thread_id,
now, phase, category, name,
arg1_name, arg1_val,
arg2_name, arg2_val,
......@@ -419,6 +462,24 @@ void TraceLog::AddTraceEventEtw(TraceEventPhase phase,
}
}
void TraceLog::AddCurrentMetadataEvents() {
lock_.AssertAcquired();
for(base::hash_map<PlatformThreadId, std::string>::iterator it =
thread_names_.begin();
it != thread_names_.end();
it++) {
if (!it->second.empty())
logged_events_.push_back(
TraceEvent(static_cast<unsigned long>(base::GetCurrentProcId()),
it->first,
TimeTicks(), base::debug::TRACE_EVENT_PHASE_METADATA,
g_category_metadata, "thread_name",
"name", it->second.c_str(),
NULL, 0,
false));
}
}
void TraceLog::Resurrect() {
StaticMemorySingletonTraits<TraceLog>::Resurrect();
}
......
......@@ -90,6 +90,7 @@
#include <vector>
#include "base/callback.h"
#include "base/hash_tables.h"
#include "base/memory/singleton.h"
#include "base/string_util.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
......@@ -296,7 +297,8 @@ const size_t kTraceMaxNumArgs = 2;
enum TraceEventPhase {
TRACE_EVENT_PHASE_BEGIN,
TRACE_EVENT_PHASE_END,
TRACE_EVENT_PHASE_INSTANT
TRACE_EVENT_PHASE_INSTANT,
TRACE_EVENT_PHASE_METADATA
};
// Simple union of values. This is much lighter weight than base::Value, which
......@@ -512,6 +514,7 @@ class BASE_EXPORT TraceLog {
TraceLog();
~TraceLog();
const TraceCategory* GetCategoryInternal(const char* name);
void AddCurrentMetadataEvents();
// TODO(nduca): switch to per-thread trace buffers to reduce thread
// synchronization.
......@@ -521,6 +524,8 @@ class BASE_EXPORT TraceLog {
BufferFullCallback buffer_full_callback_;
std::vector<TraceEvent> logged_events_;
base::hash_map<PlatformThreadId, std::string> thread_names_;
DISALLOW_COPY_AND_ASSIGN(TraceLog);
};
......
This diff is collapsed.
......@@ -74,9 +74,14 @@ class BASE_EXPORT PlatformThread {
// Sleeps for the specified duration (units are milliseconds).
static void Sleep(int duration_ms);
// Sets the thread name visible to a debugger. This has no effect otherwise.
// Sets the thread name visible to debuggers/tools. This has no effect
// otherwise. This name pointer is not copied internally. Thus, it must stay
// valid until the thread ends.
static void SetName(const char* name);
// Gets the thread name, if previously set by SetName.
static const char* GetName();
// Creates a new thread. The |stack_size| parameter can be 0 to indicate
// that the default stack size should be used. Upon success,
// |*thread_handle| will be assigned a handle to the newly created thread,
......
......@@ -11,9 +11,12 @@
#include <mach/thread_policy.h>
#include "base/logging.h"
#include "base/threading/thread_local.h"
namespace base {
static ThreadLocalPointer<char> current_thread_name;
// If Cocoa is to be used on more than one thread, it must know that the
// application is multithreaded. Since it's possible to enter Cocoa code
// from threads created by pthread_thread_create, Cocoa won't necessarily
......@@ -37,6 +40,8 @@ void InitThreading() {
// static
void PlatformThread::SetName(const char* name) {
current_thread_name.Set(const_cast<char*>(name));
// pthread_setname_np is only available in 10.6 or later, so test
// for it at runtime.
int (*dynamic_pthread_setname_np)(const char*);
......@@ -54,6 +59,11 @@ void PlatformThread::SetName(const char* name) {
dynamic_pthread_setname_np(shortened_name.c_str());
}
// static
const char* PlatformThread::GetName() {
return current_thread_name.Get();
}
namespace {
void SetPriorityNormal(mach_port_t mach_thread_id) {
......
......@@ -10,6 +10,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/safe_strerror_posix.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_restrictions.h"
#if defined(OS_MACOSX)
......@@ -41,6 +42,8 @@ void InitThreading();
namespace {
static ThreadLocalPointer<char> current_thread_name;
struct ThreadParams {
PlatformThread::Delegate* delegate;
bool joinable;
......@@ -168,6 +171,10 @@ void PlatformThread::Sleep(int duration_ms) {
#if 0 && defined(OS_LINUX)
// static
void PlatformThread::SetName(const char* name) {
// have to cast away const because ThreadLocalPointer does not support const
// void*
current_thread_name.Set(const_cast<char*>(name));
// http://0pointer.de/blog/projects/name-your-threads.html
// glibc recently added support for pthread_setname_np, but it's not
......@@ -198,14 +205,25 @@ void PlatformThread::SetName(const char* name) {
// Mac is implemented in platform_thread_mac.mm.
#else
// static
void PlatformThread::SetName(const char* /*name*/) {
// Leave it unimplemented.
void PlatformThread::SetName(const char* name) {
// have to cast away const because ThreadLocalPointer does not support const
// void*
current_thread_name.Set(const_cast<char*>(name));
// (This should be relatively simple to implement for the BSDs; I
// just don't have one handy to test the code on.)
}
#endif // defined(OS_LINUX)
#if !defined(OS_MACOSX)
// Mac is implemented in platform_thread_mac.mm.
// static
const char* PlatformThread::GetName() {
return current_thread_name.Get();
}
#endif
// static
bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle) {
......
......@@ -5,13 +5,17 @@
#include "base/threading/platform_thread.h"
#include "base/logging.h"
#include "base/threading/thread_local.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/windows_version.h"
namespace base {
namespace {
static ThreadLocalPointer<char> current_thread_name;
// The information on how to set the thread name comes from
// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
const DWORD kVCThreadNameException = 0x406D1388;
......@@ -94,6 +98,8 @@ void PlatformThread::Sleep(int duration_ms) {
// static
void PlatformThread::SetName(const char* name) {
current_thread_name.Set(const_cast<char*>(name));
// The debugger needs to be around to catch the name in the exception. If
// there isn't a debugger, we are just needlessly throwing an exception.
if (!::IsDebuggerPresent())
......@@ -112,6 +118,11 @@ void PlatformThread::SetName(const char* name) {
}
}
// static
const char* PlatformThread::GetName() {
return current_thread_name.Get();
}
// static
bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
PlatformThreadHandle* thread_handle) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment