0

[tracing] Fix process type for cloned sessions.

When a session is cloned, OnStop isn't called and thus
process descriptor doesn't have process type if it was set
after the tracing session started.
This brings back logic similar to https://chromium-review.googlesource.com/c/chromium/src/+/6163823
with a few difference:
- process type is updated (whereas only the process name was previously set)
- The descriptor is written by TrackNameRecorder
- process labels are kept and emitted in TrackNameRecorder

Change-Id: I65907390d8da74a34359cd2ff5d054a2d20d1be5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6298800
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1425303}
This commit is contained in:
Etienne Pierre-doray
2025-02-26 11:42:16 -08:00
committed by Chromium LUCI CQ
parent 93049ba857
commit 2a39a4e38e
7 changed files with 192 additions and 127 deletions

@ -106,8 +106,7 @@ const char* GetNameForProcessType(CurrentProcessType process_type) {
// Used for logging histograms for IPC metrics based on their process type.
ShortProcessType CurrentProcess::GetShortType(TypeKey key) {
#if BUILDFLAG(ENABLE_BASE_TRACING)
CurrentProcessType process = static_cast<CurrentProcessType>(
process_type_.load(std::memory_order_relaxed));
CurrentProcessType process = process_type_.load(std::memory_order_relaxed);
switch (process) {
case CurrentProcessType::PROCESS_UNSPECIFIED:
return ShortProcessType::kUnspecified;
@ -184,12 +183,22 @@ void CurrentProcess::SetProcessType(CurrentProcessType process_type) {
void CurrentProcess::SetProcessNameAndType(const std::string& process_name,
CurrentProcessType process_type) {
Delegate* delegate;
{
AutoLock lock(lock_);
process_name_ = process_name;
process_type_.store(static_cast<CurrentProcessType>(process_type),
std::memory_order_relaxed);
process_type_.store(process_type, std::memory_order_relaxed);
delegate = delegate_;
}
if (delegate) {
delegate->OnProcessNameChanged(process_name, process_type);
}
}
void CurrentProcess::SetDelegate(Delegate* delegate, NameKey) {
AutoLock lock(lock_);
DCHECK(delegate == nullptr || delegate_ == nullptr);
delegate_ = delegate;
}
} // namespace base

@ -9,6 +9,7 @@
#include <string>
#include "base/base_export.h"
#include "base/memory/raw_ptr.h"
#include "base/no_destructor.h"
#include "base/process/process_handle.h"
#include "base/synchronization/lock.h"
@ -18,7 +19,7 @@
namespace tracing {
class TraceEventDataSource;
class CustomEventRecorder;
void SetProcessTrackDescriptor(int64_t process_start_timestamp);
class TrackNameRecorder;
} // namespace tracing
namespace mojo::core {
@ -75,8 +76,7 @@ class BASE_EXPORT CurrentProcess {
friend class ::base::test::CurrentProcessForTest;
friend class ::tracing::TraceEventDataSource;
friend class ::tracing::CustomEventRecorder;
friend void ::tracing::SetProcessTrackDescriptor(
int64_t process_start_timestamp);
friend class ::tracing::TrackNameRecorder;
friend class ::mojo::core::Channel;
};
// Returns an enum corresponding to the type of the current process (e.g.
@ -89,8 +89,7 @@ class BASE_EXPORT CurrentProcess {
// "browser" or "renderer" process), the access to this function is controlled
// by an explicit list.
CurrentProcessType GetType(TypeKey key) {
return static_cast<CurrentProcessType>(
process_type_.load(std::memory_order_relaxed));
return process_type_.load(std::memory_order_relaxed);
}
ShortProcessType GetShortType(TypeKey key);
@ -100,22 +99,34 @@ class BASE_EXPORT CurrentProcess {
NameKey() = default;
friend class ::base::test::CurrentProcessForTest;
friend class ::tracing::TraceEventDataSource;
friend void ::tracing::SetProcessTrackDescriptor(
int64_t process_start_timestamp);
friend class ::tracing::TrackNameRecorder;
};
std::string GetName(NameKey key) {
AutoLock lock(lock_);
return process_name_;
}
class BASE_EXPORT Delegate {
public:
// Called on the main thread of the process whose name is changing,
// immediately after the name is set.
virtual void OnProcessNameChanged(const std::string& process_name,
CurrentProcessType process_type) = 0;
protected:
~Delegate() = default;
};
// Sets the name and type of the process for the metrics and tracing. This
// function should be called as early as possible in the process's lifetime
// before starting any threads, typically in *Main() function. Provide
// process_name as an argument if it can't be trivially derived from the
// process type.
void SetProcessType(CurrentProcessType process_type);
void SetProcessNameAndType(const std::string& process_name,
CurrentProcessType process_type);
// `delegate` might racily be invoked after resetting, thus its lifetime must
// match `CurrentProcess`.
void SetDelegate(Delegate* delegate, NameKey key);
bool IsProcessNameEmpty() const {
AutoLock lock(lock_);
@ -127,6 +138,9 @@ class BASE_EXPORT CurrentProcess {
CurrentProcess() = default;
void SetProcessNameAndType(const std::string& process_name,
CurrentProcessType process_type);
mutable Lock lock_;
std::string process_name_;
// The process_type_ is set at the startup before processes start running.
@ -136,6 +150,8 @@ class BASE_EXPORT CurrentProcess {
// where we don't have a guarantee that it will be called early enough in the
// process's lifetime, thus we use std::atomic here.
std::atomic<CurrentProcessType> process_type_;
raw_ptr<Delegate> delegate_;
};
} // namespace base

@ -348,7 +348,7 @@ struct TraceLog::RegisteredAsyncObserver {
// static
TraceLog* TraceLog::GetInstance() {
static base::NoDestructor<TraceLog> instance(0);
static base::NoDestructor<TraceLog> instance{};
return instance.get();
}
@ -362,7 +362,7 @@ void TraceLog::ResetForTesting() {
self->InitializePerfettoIfNeeded();
}
TraceLog::TraceLog(int generation) : process_id_(base::kNullProcessId) {
TraceLog::TraceLog() : process_id_(base::kNullProcessId) {
#if BUILDFLAG(IS_NACL) // NaCl shouldn't expose the process id.
SetProcessID(0);
#else
@ -763,26 +763,6 @@ void TraceLog::SetProcessID(ProcessId process_id) {
process_id_ = process_id;
}
int TraceLog::GetNewProcessLabelId() {
AutoLock lock(lock_);
return next_process_label_id_++;
}
void TraceLog::UpdateProcessLabel(int label_id,
const std::string& current_label) {
if (!current_label.length()) {
return RemoveProcessLabel(label_id);
}
AutoLock lock(lock_);
process_labels_[label_id] = current_label;
}
void TraceLog::RemoveProcessLabel(int label_id) {
AutoLock lock(lock_);
process_labels_.erase(label_id);
}
size_t TraceLog::GetObserverCountForTest() const {
AutoLock lock(observers_lock_);
return enabled_state_observers_.size();

@ -174,23 +174,12 @@ class BASE_EXPORT TraceLog : public perfetto::TrackEventSessionObserver {
ProcessId process_id() const { return process_id_; }
std::unordered_map<int, std::string> process_labels() const {
AutoLock lock(lock_);
return process_labels_;
}
// Exposed for unittesting:
// Allows clearing up our singleton instance.
static void ResetForTesting();
void SetProcessID(ProcessId process_id);
// Processes can have labels in addition to their names. Use labels, for
// instance, to list out the web page titles that a process is handling.
int GetNewProcessLabelId();
void UpdateProcessLabel(int label_id, const std::string& current_label);
void RemoveProcessLabel(int label_id);
size_t GetObserverCountForTest() const;
struct TrackEventSession {
@ -221,7 +210,7 @@ class BASE_EXPORT TraceLog : public perfetto::TrackEventSessionObserver {
struct RegisteredAsyncObserver;
explicit TraceLog(int generation);
explicit TraceLog();
~TraceLog() override;
void SetDisabledWhileLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_);
@ -248,9 +237,6 @@ class BASE_EXPORT TraceLog : public perfetto::TrackEventSessionObserver {
std::vector<std::unique_ptr<EnabledStateObserver>>
owned_enabled_state_observer_copy_ GUARDED_BY(observers_lock_);
int next_process_label_id_ GUARDED_BY(lock_) = 0;
std::unordered_map<int, std::string> process_labels_;
ProcessId process_id_;
// Set when asynchronous Flush is in progress.

@ -142,6 +142,7 @@
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "services/tracing/public/cpp/perfetto/track_name_recorder.h"
#include "services/viz/public/cpp/gpu/context_provider_command_buffer.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
#include "third_party/blink/public/common/chrome_debug_urls.h"
@ -1920,7 +1921,7 @@ RenderFrameImpl::RenderFrameImpl(CreateParams params)
routing_id_(params.routing_id),
#endif
process_label_id_(
base::trace_event::TraceLog::GetInstance()->GetNewProcessLabelId()),
tracing::TrackNameRecorder::GetInstance()->GetNewProcessLabelId()),
selection_text_offset_(0),
selection_range_(gfx::Range::InvalidRange()),
render_accessibility_manager_(
@ -1984,7 +1985,7 @@ RenderFrameImpl::~RenderFrameImpl() {
if (initialized_ && is_main_frame_)
MainFrameCounter::DecrementCount();
base::trace_event::TraceLog::GetInstance()->RemoveProcessLabel(
tracing::TrackNameRecorder::GetInstance()->RemoveProcessLabel(
process_label_id_);
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
g_routing_id_frame_map.Get().erase(routing_id_);
@ -4217,7 +4218,7 @@ void RenderFrameImpl::RunScriptsAtDocumentElementAvailable() {
void RenderFrameImpl::DidReceiveTitle(const blink::WebString& title) {
// Ignore all but top level navigations.
if (!frame_->Parent() && !title.IsEmpty()) {
base::trace_event::TraceLog::GetInstance()->UpdateProcessLabel(
tracing::TrackNameRecorder::GetInstance()->UpdateProcessLabel(
process_label_id_, title.Utf8());
} else {
// Set process title for sub-frames and title-less frames in traces.
@ -4229,7 +4230,7 @@ void RenderFrameImpl::DidReceiveTitle(const blink::WebString& title) {
frame_title += "Subframe: ";
}
frame_title += loading_url.DeprecatedGetOriginAsURL().spec();
base::trace_event::TraceLog::GetInstance()->UpdateProcessLabel(
tracing::TrackNameRecorder::GetInstance()->UpdateProcessLabel(
process_label_id_, frame_title);
}
}

@ -12,7 +12,6 @@
#include "build/build_config.h"
#include "services/tracing/public/cpp/perfetto/trace_string_lookup.h"
#include "third_party/perfetto/include/perfetto/tracing/internal/track_event_internal.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_thread_descriptor.gen.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/process_descriptor.gen.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/thread_descriptor.gen.h"
@ -23,71 +22,27 @@
namespace tracing {
// Set the track descriptor for the current process.
void SetProcessTrackDescriptor(int64_t process_start_timestamp) {
using perfetto::protos::gen::ChromeProcessDescriptor;
const auto* trace_log = base::trace_event::TraceLog::GetInstance();
int process_id = trace_log->process_id();
std::string process_name = base::CurrentProcess::GetInstance().GetName({});
auto process_type = static_cast<ChromeProcessDescriptor::ProcessType>(
base::CurrentProcess::GetInstance().GetType({}));
// We record a few (string) fields here that are stripped for background
// tracing. We rely on the post-process privacy filtering to remove them.
auto process_track = perfetto::ProcessTrack::Current();
auto process_track_desc = process_track.Serialize();
auto* process = process_track_desc.mutable_process();
process->set_pid(process_id);
process->set_process_name(process_name);
process->set_start_timestamp_ns(process_start_timestamp);
for (const auto& label : trace_log->process_labels()) {
process->add_process_labels(label.second);
}
auto* chrome_process = process_track_desc.mutable_chrome_process();
if (process_type != ChromeProcessDescriptor::PROCESS_UNSPECIFIED) {
chrome_process->set_process_type(process_type);
}
// Add the crash trace ID to all the traces uploaded. If there are crashes
// during this tracing session, then the crash will contain the process's
// trace ID as "chrome-trace-id" crash key. This should be emitted
// periodically to ensure it is present in the traces when the process
// crashes. Metadata can go missing if process crashes. So, record this in
// process descriptor.
static const std::optional<uint64_t> crash_trace_id = GetTraceCrashId();
if (crash_trace_id) {
chrome_process->set_crash_trace_id(*crash_trace_id);
}
#if BUILDFLAG(IS_ANDROID)
// Host app package name is only recorded if the corresponding TraceLog
// setting is set to true.
if (trace_log->ShouldRecordHostAppPackageName()) {
// Host app package name is used to group information from different
// processes that "belong" to the same WebView app.
if (process_type == ChromeProcessDescriptor::PROCESS_RENDERER ||
process_type == ChromeProcessDescriptor::PROCESS_BROWSER) {
chrome_process->set_host_app_package_name(
base::android::BuildInfo::GetInstance()->host_package_name());
}
}
#endif // BUILDFLAG(IS_ANDROID)
base::TrackEvent::SetTrackDescriptor(process_track,
std::move(process_track_desc));
}
using perfetto::protos::gen::ChromeProcessDescriptor;
namespace {
std::optional<uint64_t> GetTraceCrashId() {
static base::debug::CrashKeyString* key = base::debug::AllocateCrashKeyString(
"chrome-trace-id", base::debug::CrashKeySize::Size32);
if (!key) {
return std::nullopt;
}
uint64_t id = base::RandUint64();
base::debug::SetCrashKeyString(key, base::NumberToString(id));
return id;
}
void FillThreadTrack(const perfetto::ThreadTrack& track, const char* name) {
using perfetto::protos::gen::ChromeThreadDescriptor;
int process_id = static_cast<int>(
base::trace_event::TraceLog::GetInstance()->process_id());
auto desc = track.Serialize();
desc.mutable_thread()->set_pid(process_id);
desc.mutable_thread()->set_pid(static_cast<int>(
base::trace_event::TraceLog::GetInstance()->process_id()));
desc.mutable_thread()->set_thread_name(name);
auto thread_type =
static_cast<ChromeThreadDescriptor::ThreadType>(GetThreadType(name));
@ -117,23 +72,64 @@ void SetThreadTrackDescriptors() {
auto thread_track = perfetto::ThreadTrack::Current();
FillThreadTrack(thread_track, thread_name);
}
} // namespace
std::optional<uint64_t> GetTraceCrashId() {
static base::debug::CrashKeyString* key = base::debug::AllocateCrashKeyString(
"chrome-trace-id", base::debug::CrashKeySize::Size32);
if (!key) {
return std::nullopt;
void TrackNameRecorder::SetProcessTrackDescriptor(
const std::string& process_name,
ChromeProcessDescriptor::ProcessType process_type) {
// We record a few (string) fields here that are stripped for background
// tracing. We rely on the post-process privacy filtering to remove them.
auto process_track = perfetto::ProcessTrack::Current();
auto process_track_desc = process_track.Serialize();
auto* process = process_track_desc.mutable_process();
process->set_pid(base::trace_event::TraceLog::GetInstance()->process_id());
process->set_process_name(process_name);
process->set_start_timestamp_ns(process_start_timestamp_);
for (const auto& label : process_labels()) {
process->add_process_labels(label.second);
}
uint64_t id = base::RandUint64();
base::debug::SetCrashKeyString(key, base::NumberToString(id));
return id;
auto* chrome_process = process_track_desc.mutable_chrome_process();
if (process_type != ChromeProcessDescriptor::PROCESS_UNSPECIFIED) {
chrome_process->set_process_type(process_type);
}
// Add the crash trace ID to all the traces uploaded. If there are crashes
// during this tracing session, then the crash will contain the process's
// trace ID as "chrome-trace-id" crash key. This should be emitted
// periodically to ensure it is present in the traces when the process
// crashes. Metadata can go missing if process crashes. So, record this in
// process descriptor.
static const std::optional<uint64_t> crash_trace_id = GetTraceCrashId();
if (crash_trace_id) {
chrome_process->set_crash_trace_id(*crash_trace_id);
}
#if BUILDFLAG(IS_ANDROID)
// Host app package name is only recorded if the corresponding TraceLog
// setting is set to true.
if (base::trace_event::TraceLog::GetInstance()
->ShouldRecordHostAppPackageName()) {
// Host app package name is used to group information from different
// processes that "belong" to the same WebView app.
if (process_type == ChromeProcessDescriptor::PROCESS_RENDERER ||
process_type == ChromeProcessDescriptor::PROCESS_BROWSER) {
chrome_process->set_host_app_package_name(
base::android::BuildInfo::GetInstance()->host_package_name());
}
}
#endif // BUILDFLAG(IS_ANDROID)
base::TrackEvent::SetTrackDescriptor(process_track,
std::move(process_track_desc));
}
TrackNameRecorder::TrackNameRecorder()
: process_start_timestamp_(
TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds()) {
base::ThreadIdNameManager::GetInstance()->AddObserver(this);
base::CurrentProcess::GetInstance().SetDelegate(this, {});
base::TrackEvent::AddSessionObserver(this);
SetThreadTrackDescriptors();
}
@ -147,11 +143,11 @@ TrackNameRecorder* TrackNameRecorder::GetInstance() {
}
void TrackNameRecorder::OnSetup(const perfetto::DataSourceBase::SetupArgs&) {
SetProcessTrackDescriptor(process_start_timestamp_);
SetProcessTrackDescriptor();
}
void TrackNameRecorder::OnStop(const perfetto::DataSourceBase::StopArgs&) {
SetProcessTrackDescriptor(process_start_timestamp_);
SetProcessTrackDescriptor();
}
void TrackNameRecorder::OnThreadNameChanged(const char* name) {
@ -159,4 +155,49 @@ void TrackNameRecorder::OnThreadNameChanged(const char* name) {
// never happen outside of tests.
FillThreadTrack(perfetto::ThreadTrack::Current(), name);
}
void TrackNameRecorder::OnProcessNameChanged(
const std::string& process_name,
base::CurrentProcessType process_type) {
if (perfetto::Tracing::IsInitialized()) {
SetProcessTrackDescriptor(
process_name,
static_cast<ChromeProcessDescriptor::ProcessType>(process_type));
}
}
int TrackNameRecorder::GetNewProcessLabelId() {
base::AutoLock lock(lock_);
return next_process_label_id_++;
}
void TrackNameRecorder::UpdateProcessLabel(int label_id,
const std::string& current_label) {
if (!current_label.length()) {
return RemoveProcessLabel(label_id);
}
if (perfetto::Tracing::IsInitialized()) {
auto track = perfetto::ProcessTrack::Current();
auto desc = track.Serialize();
desc.mutable_process()->add_process_labels(current_label);
base::TrackEvent::SetTrackDescriptor(track, std::move(desc));
}
base::AutoLock lock(lock_);
process_labels_[label_id] = current_label;
}
void TrackNameRecorder::RemoveProcessLabel(int label_id) {
base::AutoLock lock(lock_);
process_labels_.erase(label_id);
}
void TrackNameRecorder::SetProcessTrackDescriptor() {
std::string process_name = base::CurrentProcess::GetInstance().GetName({});
auto process_type = static_cast<ChromeProcessDescriptor::ProcessType>(
base::CurrentProcess::GetInstance().GetType({}));
SetProcessTrackDescriptor(process_name, process_type);
}
} // namespace tracing

@ -6,20 +6,23 @@
#define SERVICES_TRACING_PUBLIC_CPP_PERFETTO_TRACK_NAME_RECORDER_H_
#include "base/component_export.h"
#include "base/process/current_process.h"
#include "base/process/process_handle.h"
#include "base/sequence_checker.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/trace_event/trace_config.h"
#include "base/trace_event/typed_macros.h"
#include "third_party/abseil-cpp/absl/container/flat_hash_map.h"
#include "third_party/perfetto/include/perfetto/tracing/internal/track_event_internal.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_process_descriptor.gen.h"
namespace tracing {
std::optional<uint64_t> GetTraceCrashId();
// A class that emits track descriptors for Chrome processes and threads.
class COMPONENT_EXPORT(TRACING_CPP) TrackNameRecorder
: public perfetto::TrackEventSessionObserver,
base::ThreadIdNameManager::Observer {
base::ThreadIdNameManager::Observer,
base::CurrentProcess::Delegate {
public:
static TrackNameRecorder* GetInstance();
@ -33,13 +36,42 @@ class COMPONENT_EXPORT(TRACING_CPP) TrackNameRecorder
// base::ThreadIdNameManager::Observer implementation.
void OnThreadNameChanged(const char* name) override;
// base::CurrentProcess::Delegate implementation.
void OnProcessNameChanged(const std::string& process_name,
base::CurrentProcessType process_type) override;
// Processes can have labels in addition to their names. Use labels, for
// instance, to list out the web page titles that a process is handling.
int GetNewProcessLabelId();
void UpdateProcessLabel(int label_id, const std::string& current_label);
void RemoveProcessLabel(int label_id);
private:
friend class base::NoDestructor<TrackNameRecorder>;
using ChromeProcessDescriptor =
perfetto::protos::gen::ChromeProcessDescriptor;
TrackNameRecorder();
~TrackNameRecorder() override;
uint64_t process_start_timestamp_;
// Set the track descriptor for the current process.
void SetProcessTrackDescriptor(
const std::string& process_name,
ChromeProcessDescriptor::ProcessType process_type);
void SetProcessTrackDescriptor();
absl::flat_hash_map<int, std::string> process_labels() const {
base::AutoLock lock(lock_);
return process_labels_;
}
int64_t process_start_timestamp_;
// This lock protects `process_labels_` member accesses from arbitrary
// threads.
mutable base::Lock lock_;
int next_process_label_id_ GUARDED_BY(lock_) = 0;
absl::flat_hash_map<int, std::string> process_labels_ GUARDED_BY(lock_);
};
} // namespace tracing