| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "services/tracing/public/cpp/perfetto/metadata_data_source.h" |
| |
| #include "base/command_line.h" |
| #include "base/i18n/time_formatting.h" |
| #include "base/json/json_writer.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/system/sys_info.h" |
| #include "base/time/time_override.h" |
| #include "base/trace_event/common/trace_event_common.h" |
| #include "base/tracing/trace_time.h" |
| #include "components/variations/active_field_trials.h" |
| #include "services/tracing/public/mojom/perfetto_service.mojom.h" |
| #include "third_party/icu/source/i18n/unicode/timezone.h" |
| #include "third_party/perfetto/protos/perfetto/common/data_source_descriptor.gen.h" |
| #include "third_party/perfetto/protos/perfetto/config/chrome/chrome_config.gen.h" |
| #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_metadata.pbzero.h" |
| #include "third_party/perfetto/protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h" |
| #include "third_party/perfetto/protos/perfetto/trace/extension_descriptor.pbzero.h" |
| |
| #if BUILDFLAG(IS_ANDROID) && defined(OFFICIAL_BUILD) |
| #include "base/android/apk_info.h" |
| #endif |
| |
| namespace tracing { |
| namespace { |
| |
| inline constexpr char kCommandLineKey[] = "command_line"; |
| inline constexpr char kClockDomainMetadataKey[] = "clock-domain"; |
| inline constexpr char kTraceCaptureDatetimeKey[] = "trace-capture-datetime"; |
| |
| inline constexpr char kCpuCoresMetadataKey[] = "cpu-num-cores"; |
| inline constexpr char kOSNameMetadataKey[] = "os-name"; |
| inline constexpr char kOSVersionMetadataKey[] = "os-version"; |
| |
| } // namespace |
| |
| std::string_view GetClockString(base::TimeTicks::Clock clock) { |
| switch (clock) { |
| case base::TimeTicks::Clock::FUCHSIA_ZX_CLOCK_MONOTONIC: |
| return "FUCHSIA_ZX_CLOCK_MONOTONIC"; |
| case base::TimeTicks::Clock::LINUX_CLOCK_MONOTONIC: |
| return "LINUX_CLOCK_MONOTONIC"; |
| case base::TimeTicks::Clock::IOS_CF_ABSOLUTE_TIME_MINUS_KERN_BOOTTIME: |
| return "IOS_CF_ABSOLUTE_TIME_MINUS_KERN_BOOTTIME"; |
| case base::TimeTicks::Clock::MAC_MACH_ABSOLUTE_TIME: |
| return "MAC_MACH_ABSOLUTE_TIME"; |
| case base::TimeTicks::Clock::WIN_QPC: |
| return "WIN_QPC"; |
| case base::TimeTicks::Clock::WIN_ROLLOVER_PROTECTED_TIME_GET_TIME: |
| return "WIN_ROLLOVER_PROTECTED_TIME_GET_TIME"; |
| } |
| |
| NOTREACHED(); |
| } |
| |
| struct MetadataDataSourceTlsState { |
| explicit MetadataDataSourceTlsState( |
| const MetadataDataSource::TraceContext& trace_context) { |
| auto locked_ds = trace_context.GetDataSourceLocked(); |
| if (locked_ds.valid()) { |
| privacy_filtering_enabled = locked_ds->privacy_filtering_enabled(); |
| instance = reinterpret_cast<uintptr_t>(&(*locked_ds)); |
| } |
| } |
| bool privacy_filtering_enabled = false; |
| uintptr_t instance = 0; |
| }; |
| |
| void MetadataDataSource::Register( |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| std::vector<BundleRecorder> bundle_recorders, |
| std::vector<PacketRecorder> packet_recorders) { |
| perfetto::DataSourceDescriptor desc; |
| desc.set_name(tracing::mojom::kMetaData2SourceName); |
| perfetto::DataSource<MetadataDataSource, MetadataDataSourceTraits>::Register( |
| desc, std::move(task_runner), std::move(bundle_recorders), |
| std::move(packet_recorders)); |
| } |
| |
| MetadataDataSource::MetadataDataSource( |
| scoped_refptr<base::SequencedTaskRunner> task_runner, |
| std::vector<BundleRecorder> bundle_recorders, |
| std::vector<PacketRecorder> packet_recorders) |
| : task_runner_(std::move(task_runner)), |
| bundle_recorders_(std::move(bundle_recorders)), |
| packet_recorders_(std::move(packet_recorders)) {} |
| |
| MetadataDataSource::~MetadataDataSource() = default; |
| |
| void MetadataDataSource::OnSetup(const SetupArgs& args) { |
| const perfetto::protos::gen::ChromeConfig& chrome_config = |
| args.config->chrome_config(); |
| privacy_filtering_enabled_ = chrome_config.privacy_filtering_enabled(); |
| } |
| |
| void MetadataDataSource::OnStart(const StartArgs&) { |
| task_runner_->PostTask(FROM_HERE, |
| base::BindOnce(&MetadataDataSource::WriteMetadata, |
| reinterpret_cast<uintptr_t>(this), |
| std::move(bundle_recorders_), |
| std::move(packet_recorders_))); |
| } |
| |
| void MetadataDataSource::OnFlush(const FlushArgs&) {} |
| |
| void MetadataDataSource::OnStop(const StopArgs&) {} |
| |
| void MetadataDataSource::WriteMetadata( |
| uintptr_t instance, |
| std::vector<BundleRecorder> bundle_recorders, |
| std::vector<PacketRecorder> packet_recorders) { |
| MetadataDataSource::Trace([&](TraceContext ctx) { |
| if (instance != ctx.GetCustomTlsState()->instance) { |
| return; |
| } |
| bool privacy_filtering_enabled = |
| ctx.GetCustomTlsState()->privacy_filtering_enabled; |
| |
| auto now = TRACE_TIME_TICKS_NOW().since_origin().InNanoseconds(); |
| |
| if (!privacy_filtering_enabled) { |
| auto packet = ctx.NewTracePacket(); |
| packet->set_timestamp(now); |
| packet->set_timestamp_clock_id(base::tracing::kTraceClockId); |
| auto* bundle = packet->set_chrome_events(); |
| base::CommandLine::StringType command_line = |
| base::CommandLine::ForCurrentProcess()->GetCommandLineString(); |
| #if BUILDFLAG(IS_WIN) |
| AddMetadataToBundle(kCommandLineKey, base::WideToUTF8(command_line), |
| bundle); |
| #else |
| AddMetadataToBundle(kCommandLineKey, command_line, bundle); |
| #endif |
| AddMetadataToBundle(kClockDomainMetadataKey, |
| GetClockString(base::TimeTicks::GetClock()), bundle); |
| AddMetadataToBundle( |
| kTraceCaptureDatetimeKey, |
| base::UnlocalizedTimeFormatWithPattern( |
| TRACE_TIME_NOW(), "y-M-d H:m:s", icu::TimeZone::getGMT()), |
| bundle); |
| for (auto& recorder : bundle_recorders) { |
| if (recorder.is_null()) { |
| continue; |
| } |
| recorder.Run(bundle); |
| } |
| } |
| for (auto& recorder : packet_recorders) { |
| if (recorder.is_null()) { |
| continue; |
| } |
| auto packet = ctx.NewTracePacket(); |
| packet->set_timestamp(now); |
| packet->set_timestamp_clock_id(base::tracing::kTraceClockId); |
| |
| recorder.Run(packet.get(), privacy_filtering_enabled); |
| } |
| |
| auto packet = ctx.NewTracePacket(); |
| packet->set_timestamp(now); |
| packet->set_timestamp_clock_id(base::tracing::kTraceClockId); |
| auto* chrome_metadata = packet->set_chrome_metadata(); |
| |
| #if BUILDFLAG(IS_ANDROID) && defined(OFFICIAL_BUILD) |
| // Version code is only set for official builds on Android. |
| const std::string& version_code_str = |
| base::android::apk_info::package_version_code(); |
| if (!version_code_str.empty()) { |
| int version_code = 0; |
| bool res = base::StringToInt(version_code_str, &version_code); |
| DCHECK(res); |
| chrome_metadata->set_chrome_version_code(version_code); |
| } |
| #endif // BUILDFLAG(IS_ANDROID) && defined(OFFICIAL_BUILD) |
| |
| // Do not include low anonymity field trials, to prevent them from being |
| // included in chrometto reports. |
| std::vector<variations::ActiveGroupId> active_group_ids; |
| variations::GetFieldTrialActiveGroupIds(std::string_view(), |
| &active_group_ids); |
| |
| for (const auto& active_group_id : active_group_ids) { |
| perfetto::protos::pbzero::ChromeMetadataPacket::FinchHash* finch_hash = |
| chrome_metadata->add_field_trial_hashes(); |
| finch_hash->set_name(active_group_id.name); |
| finch_hash->set_group(active_group_id.group); |
| } |
| packet->Finalize(); |
| ctx.Flush(); |
| }); |
| } |
| |
| void MetadataDataSource::AddMetadataToBundle( |
| std::string_view name, |
| std::string_view value, |
| perfetto::protos::pbzero::ChromeEventBundle* bundle) { |
| auto* metadata = bundle->add_metadata(); |
| metadata->set_name(name.data(), name.size()); |
| metadata->set_string_value(value.data(), value.size()); |
| } |
| |
| void MetadataDataSource::AddMetadataToBundle( |
| std::string_view name, |
| const base::Value& value, |
| perfetto::protos::pbzero::ChromeEventBundle* bundle) { |
| auto* metadata = bundle->add_metadata(); |
| metadata->set_name(name.data(), name.size()); |
| |
| if (value.is_int()) { |
| metadata->set_int_value(value.GetInt()); |
| } else if (value.is_bool()) { |
| metadata->set_bool_value(value.GetBool()); |
| } else if (value.is_string()) { |
| metadata->set_string_value(value.GetString().c_str()); |
| } else { |
| metadata->set_json_value(base::WriteJson(value).value_or("")); |
| } |
| } |
| |
| void MetadataDataSource::RecordDefaultBundleMetadata( |
| perfetto::protos::pbzero::ChromeEventBundle* bundle) { |
| #if BUILDFLAG(IS_CHROMEOS) |
| MetadataDataSource::AddMetadataToBundle(kOSNameMetadataKey, "CrOS", bundle); |
| #else |
| MetadataDataSource::AddMetadataToBundle( |
| kOSNameMetadataKey, base::SysInfo::OperatingSystemName(), bundle); |
| #endif |
| MetadataDataSource::AddMetadataToBundle( |
| kOSVersionMetadataKey, base::SysInfo::OperatingSystemVersion(), bundle); |
| |
| MetadataDataSource::AddMetadataToBundle( |
| kCpuCoresMetadataKey, |
| base::Value(static_cast<int>(base::SysInfo::NumberOfProcessors())), |
| bundle); |
| } |
| |
| } // namespace tracing |
| |
| PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS( |
| COMPONENT_EXPORT(TRACING_CPP), |
| tracing::MetadataDataSource, |
| tracing::MetadataDataSourceTraits); |