| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/perfetto_cmd/config.h" |
| |
| #include <stdlib.h> |
| |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/string_utils.h" |
| #include "perfetto/tracing/core/data_source_config.h" |
| #include "perfetto/tracing/core/trace_config.h" |
| |
| #include "protos/perfetto/config/ftrace/ftrace_config.gen.h" |
| #include "protos/perfetto/config/sys_stats/sys_stats_config.gen.h" |
| |
| namespace perfetto { |
| namespace { |
| using ValueUnit = std::pair<uint64_t, std::string>; |
| using UnitMultipler = std::pair<const char*, uint64_t>; |
| |
| bool SplitValueAndUnit(const std::string& arg, ValueUnit* out) { |
| char* end; |
| if (arg.empty()) |
| return false; |
| out->first = strtoull(arg.c_str(), &end, 10); |
| if (end == arg.data()) |
| return false; |
| std::string unit = arg.substr(static_cast<size_t>(end - arg.data())); |
| out->second = std::move(unit); |
| return true; |
| } |
| |
| bool ConvertValue(const std::string& arg, |
| std::vector<UnitMultipler> units, |
| uint64_t* out) { |
| if (arg.empty()) { |
| *out = 0; |
| return true; |
| } |
| |
| if (arg == "0") { |
| *out = 0; |
| return true; |
| } |
| |
| ValueUnit value_unit{}; |
| if (!SplitValueAndUnit(arg, &value_unit)) |
| return false; |
| |
| for (const auto& unit_multiplier : units) { |
| if (value_unit.second != unit_multiplier.first) |
| continue; |
| *out = value_unit.first * unit_multiplier.second; |
| return true; |
| } |
| return false; |
| } |
| |
| bool ConvertTimeToMs(const std::string& arg, uint64_t* out) { |
| return ConvertValue(arg, |
| { |
| {"ms", 1}, |
| {"s", 1000}, |
| {"m", 1000 * 60}, |
| {"h", 1000 * 60 * 60}, |
| }, |
| out); |
| } |
| |
| bool ConvertSizeToKb(const std::string& arg, uint64_t* out) { |
| return ConvertValue(arg, |
| { |
| {"kb", 1}, |
| {"mb", 1024}, |
| {"gb", 1024 * 1024}, |
| {"k", 1}, |
| {"m", 1024}, |
| {"g", 1024 * 1024}, |
| }, |
| out); |
| } |
| |
| } // namespace |
| |
| bool CreateConfigFromOptions(const ConfigOptions& options, |
| TraceConfig* config) { |
| uint64_t duration_ms = 0; |
| if (!ConvertTimeToMs(options.time, &duration_ms)) { |
| PERFETTO_ELOG("--time argument is invalid"); |
| return false; |
| } |
| |
| uint64_t buffer_size_kb = 0; |
| if (!ConvertSizeToKb(options.buffer_size, &buffer_size_kb)) { |
| PERFETTO_ELOG("--buffer argument is invalid"); |
| return false; |
| } |
| |
| uint64_t max_file_size_kb = 0; |
| if (!ConvertSizeToKb(options.max_file_size, &max_file_size_kb)) { |
| PERFETTO_ELOG("--size argument is invalid"); |
| return false; |
| } |
| |
| std::vector<std::string> ftrace_events; |
| std::vector<std::string> atrace_categories; |
| std::vector<std::string> atrace_apps = options.atrace_apps; |
| bool has_hyp_category = false; |
| |
| for (const auto& category : options.categories) { |
| if (base::Contains(category, '/')) { |
| ftrace_events.push_back(category); |
| } else if (category == "hyp") { |
| has_hyp_category = true; |
| } else { |
| atrace_categories.push_back(category); |
| } |
| |
| // For the gfx category, also add the frame timeline data source |
| // as it's very useful for debugging gfx issues. |
| if (category == "gfx") { |
| auto* frame_timeline = config->add_data_sources(); |
| frame_timeline->mutable_config()->set_name( |
| "android.surfaceflinger.frametimeline"); |
| } |
| |
| // For the disk category, add the diskstat data source |
| // to figure out disk io statistics. |
| if (category == "disk") { |
| protos::gen::SysStatsConfig cfg; |
| cfg.set_diskstat_period_ms(1000); |
| |
| auto* sys_stats_ds = config->add_data_sources(); |
| sys_stats_ds->mutable_config()->set_name("linux.sys_stats"); |
| sys_stats_ds->mutable_config()->set_sys_stats_config_raw( |
| cfg.SerializeAsString()); |
| } |
| } |
| |
| config->set_duration_ms(static_cast<unsigned int>(duration_ms)); |
| config->set_max_file_size_bytes(max_file_size_kb * 1024); |
| config->set_flush_period_ms(30 * 1000); |
| if (max_file_size_kb) |
| config->set_write_into_file(true); |
| config->add_buffers()->set_size_kb(static_cast<unsigned int>(buffer_size_kb)); |
| |
| if (!ftrace_events.empty() || !atrace_categories.empty() || |
| !atrace_apps.empty()) { |
| auto* ds_config = config->add_data_sources()->mutable_config(); |
| ds_config->set_name("linux.ftrace"); |
| protos::gen::FtraceConfig ftrace_cfg; |
| for (const auto& evt : ftrace_events) |
| ftrace_cfg.add_ftrace_events(evt); |
| for (const auto& cat : atrace_categories) |
| ftrace_cfg.add_atrace_categories(cat); |
| for (const auto& app : atrace_apps) |
| ftrace_cfg.add_atrace_apps(app); |
| ftrace_cfg.set_symbolize_ksyms(true); |
| ds_config->set_ftrace_config_raw(ftrace_cfg.SerializeAsString()); |
| } |
| |
| // pKVM hypervisor events are coming from a separate special instance called |
| // "hyp", we need a separate config for it. |
| if (has_hyp_category) { |
| auto* ds_config = config->add_data_sources()->mutable_config(); |
| ds_config->set_name("linux.ftrace"); |
| protos::gen::FtraceConfig ftrace_cfg; |
| ftrace_cfg.set_instance_name("hyp"); |
| // Collect all known hypervisor traces. |
| ftrace_cfg.add_ftrace_events("hyp/*"); |
| ds_config->set_ftrace_config_raw(ftrace_cfg.SerializeAsString()); |
| } |
| |
| auto* ps_config = config->add_data_sources()->mutable_config(); |
| ps_config->set_name("linux.process_stats"); |
| ps_config->set_target_buffer(0); |
| |
| return true; |
| } |
| |
| } // namespace perfetto |