blob: 3a390ceaf830e7453094a20217feba54ec636e92 [file] [log] [blame]
// Copyright 2020 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_proto.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/traced_value.h"
#include "build/build_config.h"
#include "services/tracing/public/cpp/perfetto/perfetto_producer.h"
#include "services/tracing/public/cpp/perfetto/perfetto_traced_process.h"
#include "services/tracing/public/cpp/perfetto/trace_time.h"
#include "third_party/perfetto/protos/perfetto/trace/memory_graph.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/profiling/smaps.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/ps/process_stats.pbzero.h"
#include "third_party/perfetto/protos/perfetto/trace/trace_packet.pbzero.h"
namespace memory_instrumentation {
using base::trace_event::ProcessMemoryDump;
namespace {
void OsDumpAsProtoInto(perfetto::protos::pbzero::ProcessStats::Process* process,
const mojom::OSMemDump& os_dump) {
process->set_chrome_private_footprint_kb(os_dump.private_footprint_kb);
process->set_chrome_peak_resident_set_kb(os_dump.peak_resident_set_kb);
process->set_is_peak_rss_resettable(os_dump.is_peak_rss_resettable);
}
perfetto::protos::pbzero::MemoryTrackerSnapshot::LevelOfDetail
MemoryDumpLevelOfDetailToProto(
const base::trace_event::MemoryDumpLevelOfDetail& level_of_detail) {
switch (level_of_detail) {
case base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND:
return perfetto::protos::pbzero::MemoryTrackerSnapshot::DETAIL_BACKGROUND;
case base::trace_event::MemoryDumpLevelOfDetail::LIGHT:
return perfetto::protos::pbzero::MemoryTrackerSnapshot::DETAIL_LIGHT;
case base::trace_event::MemoryDumpLevelOfDetail::DETAILED:
return perfetto::protos::pbzero::MemoryTrackerSnapshot::DETAIL_FULL;
}
NOTREACHED();
return perfetto::protos::pbzero::MemoryTrackerSnapshot::DETAIL_BACKGROUND;
}
} // namespace
TracingObserverProto::TracingObserverProto(
base::trace_event::TraceLog* trace_log,
base::trace_event::MemoryDumpManager* memory_dump_manager)
: TracingObserver(trace_log, memory_dump_manager),
tracing::PerfettoTracedProcess::DataSourceBase(
tracing::mojom::kMemoryInstrumentationDataSourceName) {
tracing::PerfettoTracedProcess::Get()->AddDataSource(this);
}
TracingObserverProto::~TracingObserverProto() = default;
bool TracingObserverProto::AddChromeDumpToTraceIfEnabled(
const base::trace_event::MemoryDumpRequestArgs& args,
const base::ProcessId pid,
const ProcessMemoryDump* process_memory_dump,
const base::TimeTicks& timestamp) {
if (!ShouldAddToTrace(args))
return false;
base::AutoLock lock(producer_lock_);
if (!trace_writer_)
return false;
perfetto::TraceWriter::TracePacketHandle handle =
trace_writer_->NewTracePacket();
handle->set_timestamp(timestamp.since_origin().InNanoseconds());
handle->set_timestamp_clock_id(tracing::kTraceClockId);
perfetto::protos::pbzero::MemoryTrackerSnapshot* memory_snapshot =
handle->set_memory_tracker_snapshot();
memory_snapshot->set_level_of_detail(
MemoryDumpLevelOfDetailToProto(args.level_of_detail));
process_memory_dump->SerializeAllocatorDumpsInto(memory_snapshot, pid);
return true;
}
bool TracingObserverProto::AddOsDumpToTraceIfEnabled(
const base::trace_event::MemoryDumpRequestArgs& args,
const base::ProcessId pid,
const mojom::OSMemDump& os_dump,
const std::vector<mojom::VmRegionPtr>& memory_maps,
const base::TimeTicks& timestamp) {
if (!ShouldAddToTrace(args))
return false;
base::AutoLock lock(producer_lock_);
if (!trace_writer_)
return false;
perfetto::TraceWriter::TracePacketHandle process_stats_packet =
trace_writer_->NewTracePacket();
process_stats_packet->set_timestamp(timestamp.since_origin().InNanoseconds());
process_stats_packet->set_timestamp_clock_id(tracing::kTraceClockId);
perfetto::protos::pbzero::ProcessStats* process_stats =
process_stats_packet->set_process_stats();
perfetto::protos::pbzero::ProcessStats::Process* process =
process_stats->add_processes();
process->set_pid(static_cast<int>(pid));
OsDumpAsProtoInto(process, os_dump);
process_stats_packet->Finalize();
if (memory_maps.size()) {
perfetto::TraceWriter::TracePacketHandle smaps_packet =
trace_writer_->NewTracePacket();
smaps_packet->set_timestamp(timestamp.since_origin().InNanoseconds());
smaps_packet->set_timestamp_clock_id(tracing::kTraceClockId);
perfetto::protos::pbzero::SmapsPacket* smaps =
smaps_packet->set_smaps_packet();
smaps->set_pid(static_cast<uint32_t>(pid));
MemoryMapsAsProtoInto(memory_maps, smaps, false);
smaps_packet->Finalize();
}
return true;
}
void TracingObserverProto::StartTracing(
tracing::PerfettoProducer* producer,
const perfetto::DataSourceConfig& data_source_config) {
base::AutoLock lock(producer_lock_);
producer_ = producer;
// We rely on concurrent setup of TraceLog categories by the
// TraceEventDataSource so don't look at the trace config ourselves.
trace_writer_ =
producer->CreateTraceWriter(data_source_config.target_buffer());
}
void TracingObserverProto::StopTracing(
base::OnceClosure stop_complete_callback) {
// Scope to avoid reentrancy in case from the stop callback.
{
base::AutoLock lock(producer_lock_);
trace_writer_.reset();
producer_ = nullptr;
}
if (stop_complete_callback) {
std::move(stop_complete_callback).Run();
}
}
void TracingObserverProto::Flush(
base::RepeatingClosure flush_complete_callback) {
base::AutoLock lock(producer_lock_);
if (trace_writer_)
trace_writer_->Flush();
}
void TracingObserverProto::MemoryMapsAsProtoInto(
const std::vector<mojom::VmRegionPtr>& memory_maps,
perfetto::protos::pbzero::SmapsPacket* smaps,
bool is_argument_filtering_enabled) {
for (const auto& region : memory_maps) {
perfetto::protos::pbzero::SmapsEntry* entry = smaps->add_entries();
entry->set_start_address(region->start_address);
entry->set_size_kb(region->size_in_bytes / 1024);
if (region->module_timestamp)
entry->set_module_timestamp(region->module_timestamp);
if (!region->module_debugid.empty())
entry->set_module_debugid(region->module_debugid);
if (!region->module_debug_path.empty()) {
entry->set_module_debug_path(ApplyPathFiltering(
region->module_debug_path, is_argument_filtering_enabled));
}
entry->set_protection_flags(region->protection_flags);
entry->set_file_name(
ApplyPathFiltering(region->mapped_file, is_argument_filtering_enabled));
// The following stats are only well defined on Linux-derived OSes.
#if !defined(OS_MAC) && !defined(OS_WIN)
entry->set_proportional_resident_kb(
region->byte_stats_proportional_resident / 1024);
entry->set_private_dirty_kb(region->byte_stats_private_dirty_resident /
1024);
entry->set_private_clean_resident_kb(
region->byte_stats_private_clean_resident / 1024);
entry->set_shared_dirty_resident_kb(
region->byte_stats_shared_dirty_resident / 1024);
entry->set_shared_clean_resident_kb(
region->byte_stats_shared_clean_resident / 1024);
entry->set_swap_kb(region->byte_stats_swapped / 1024);
#endif
}
}
} // namespace memory_instrumentation