blob: 29d07d2ecc59aff341d6f4b4ab71a0ac09db2b54 [file] [log] [blame]
// Copyright 2017 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 "services/resource_coordinator/public/cpp/memory_instrumentation/tracing_observer.h"
#include "base/files/file_path.h"
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/memory_dump_manager.h"
#include "base/trace_event/trace_event_argument.h"
#include "build/build_config.h"
namespace memory_instrumentation {
using base::trace_event::TracedValue;
using base::trace_event::ProcessMemoryDump;
namespace {
const int kTraceEventNumArgs = 1;
const char* const kTraceEventArgNames[] = {"dumps"};
const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
bool IsMemoryInfraTracingEnabled() {
bool enabled;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(
base::trace_event::MemoryDumpManager::kTraceCategory, &enabled);
return enabled;
}
void OsDumpAsValueInto(TracedValue* value, const mojom::OSMemDump& os_dump) {
value->SetString(
"private_footprint_bytes",
base::StringPrintf(
"%" PRIx64,
static_cast<uint64_t>(os_dump.private_footprint_kb) * 1024));
}
std::string ApplyPathFiltering(const std::string& file,
bool is_argument_filtering_enabled) {
if (is_argument_filtering_enabled) {
base::FilePath::StringType path(file.begin(), file.end());
return base::FilePath(path).BaseName().AsUTF8Unsafe();
}
return file;
}
}; // namespace
TracingObserver::TracingObserver(
base::trace_event::TraceLog* trace_log,
base::trace_event::MemoryDumpManager* memory_dump_manager)
: memory_dump_manager_(memory_dump_manager), trace_log_(trace_log) {
// If tracing was enabled before initializing MemoryDumpManager, we missed the
// OnTraceLogEnabled() event. Synthesize it so we can late-join the party.
// IsEnabled is called before adding observer to avoid calling
// OnTraceLogEnabled twice.
bool is_tracing_already_enabled = trace_log_->IsEnabled();
trace_log_->AddEnabledStateObserver(this);
if (is_tracing_already_enabled)
OnTraceLogEnabled();
}
TracingObserver::~TracingObserver() {
trace_log_->RemoveEnabledStateObserver(this);
}
void TracingObserver::OnTraceLogEnabled() {
if (!IsMemoryInfraTracingEnabled())
return;
// Initialize the TraceLog for the current thread. This is to avoids that the
// TraceLog memory dump provider is registered lazily during the MDM
// SetupForTracing().
base::trace_event::TraceLog::GetInstance()
->InitializeThreadLocalEventBufferIfSupported();
const base::trace_event::TraceConfig& trace_config =
base::trace_event::TraceLog::GetInstance()->GetCurrentTraceConfig();
const base::trace_event::TraceConfig::MemoryDumpConfig& memory_dump_config =
trace_config.memory_dump_config();
memory_dump_config_ =
std::make_unique<base::trace_event::TraceConfig::MemoryDumpConfig>(
memory_dump_config);
if (memory_dump_manager_)
memory_dump_manager_->SetupForTracing(memory_dump_config);
}
void TracingObserver::OnTraceLogDisabled() {
if (memory_dump_manager_)
memory_dump_manager_->TeardownForTracing();
memory_dump_config_.reset();
}
bool TracingObserver::ShouldAddToTrace(
const base::trace_event::MemoryDumpRequestArgs& args) {
// If tracing has been disabled early out to avoid the cost of serializing the
// dump then ignoring the result.
if (!IsMemoryInfraTracingEnabled())
return false;
// If the dump mode is too detailed don't add to trace to avoid accidentally
// including PII.
if (!IsDumpModeAllowed(args.level_of_detail))
return false;
return true;
}
void TracingObserver::AddToTrace(
const base::trace_event::MemoryDumpRequestArgs& args,
const base::ProcessId pid,
std::unique_ptr<TracedValue> traced_value) {
CHECK_NE(base::trace_event::MemoryDumpType::SUMMARY_ONLY, args.dump_type);
traced_value->SetString(
"level_of_detail",
base::trace_event::MemoryDumpLevelOfDetailToString(args.level_of_detail));
const uint64_t dump_guid = args.dump_guid;
const char* const event_name =
base::trace_event::MemoryDumpTypeToString(args.dump_type);
std::unique_ptr<base::trace_event::ConvertableToTraceFormat> event_value(
std::move(traced_value));
TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_PROCESS_ID(
TRACE_EVENT_PHASE_MEMORY_DUMP,
base::trace_event::TraceLog::GetCategoryGroupEnabled(
base::trace_event::MemoryDumpManager::kTraceCategory),
event_name, trace_event_internal::kGlobalScope, dump_guid, pid,
kTraceEventNumArgs, kTraceEventArgNames, kTraceEventArgTypes,
nullptr /* arg_values */, &event_value, TRACE_EVENT_FLAG_HAS_ID);
}
bool TracingObserver::AddChromeDumpToTraceIfEnabled(
const base::trace_event::MemoryDumpRequestArgs& args,
const base::ProcessId pid,
const ProcessMemoryDump* process_memory_dump) {
if (!ShouldAddToTrace(args))
return false;
std::unique_ptr<TracedValue> traced_value = std::make_unique<TracedValue>();
process_memory_dump->SerializeAllocatorDumpsInto(traced_value.get());
AddToTrace(args, pid, std::move(traced_value));
return true;
}
bool TracingObserver::AddOsDumpToTraceIfEnabled(
const base::trace_event::MemoryDumpRequestArgs& args,
const base::ProcessId pid,
const mojom::OSMemDump* os_dump,
const std::vector<mojom::VmRegionPtr>* memory_maps) {
if (!ShouldAddToTrace(args))
return false;
std::unique_ptr<TracedValue> traced_value = std::make_unique<TracedValue>();
traced_value->BeginDictionary("process_totals");
OsDumpAsValueInto(traced_value.get(), *os_dump);
traced_value->EndDictionary();
if (memory_maps->size()) {
traced_value->BeginDictionary("process_mmaps");
MemoryMapsAsValueInto(*memory_maps, traced_value.get(), false);
traced_value->EndDictionary();
}
AddToTrace(args, pid, std::move(traced_value));
return true;
}
// static
void TracingObserver::MemoryMapsAsValueInto(
const std::vector<mojom::VmRegionPtr>& memory_maps,
TracedValue* value,
bool is_argument_filtering_enabled) {
static const char kHexFmt[] = "%" PRIx64;
// Refer to the design doc goo.gl/sxfFY8 for the semantics of these fields.
value->BeginArray("vm_regions");
for (const auto& region : memory_maps) {
value->BeginDictionary();
value->SetString("sa", base::StringPrintf(kHexFmt, region->start_address));
value->SetString("sz", base::StringPrintf(kHexFmt, region->size_in_bytes));
if (region->module_timestamp)
value->SetString("ts",
base::StringPrintf(kHexFmt, region->module_timestamp));
if (!region->module_debugid.empty())
value->SetString("id", region->module_debugid);
if (!region->module_debug_path.empty()) {
value->SetString("df", ApplyPathFiltering(region->module_debug_path,
is_argument_filtering_enabled));
}
value->SetInteger("pf", region->protection_flags);
// The module path will be the basename when argument filtering is
// activated. The whitelisting implemented for filtering string values
// doesn't allow rewriting. Therefore, a different path is produced here
// when argument filtering is activated.
value->SetString("mf", ApplyPathFiltering(region->mapped_file,
is_argument_filtering_enabled));
// The following stats are only well defined on Linux-derived OSes.
#if !defined(OS_MACOSX) && !defined(OS_WIN)
value->BeginDictionary("bs"); // byte stats
value->SetString(
"pss",
base::StringPrintf(kHexFmt, region->byte_stats_proportional_resident));
value->SetString(
"pd",
base::StringPrintf(kHexFmt, region->byte_stats_private_dirty_resident));
value->SetString(
"pc",
base::StringPrintf(kHexFmt, region->byte_stats_private_clean_resident));
value->SetString(
"sd",
base::StringPrintf(kHexFmt, region->byte_stats_shared_dirty_resident));
value->SetString(
"sc",
base::StringPrintf(kHexFmt, region->byte_stats_shared_clean_resident));
value->SetString("sw",
base::StringPrintf(kHexFmt, region->byte_stats_swapped));
value->EndDictionary();
#endif
value->EndDictionary();
}
value->EndArray();
}
bool TracingObserver::IsDumpModeAllowed(
base::trace_event::MemoryDumpLevelOfDetail dump_mode) const {
if (!memory_dump_config_)
return false;
return memory_dump_config_->allowed_dump_modes.count(dump_mode) != 0;
}
} // namespace memory_instrumentation