blob: fa38f706ed13e40eca042cabca44c145e46d9b6e [file] [log] [blame] [edit]
/*
* Copyright (C) 2025 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/traceconv/trace_to_bundle.h"
#include <cstdlib>
#include <cstring>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
#include "perfetto/base/logging.h"
#include "perfetto/base/status.h"
#include "perfetto/trace_processor/basic_types.h"
#include "perfetto/trace_processor/read_trace.h"
#include "perfetto/trace_processor/trace_processor.h"
#include "src/profiling/symbolizer/local_symbolizer.h"
#include "src/profiling/symbolizer/symbolize_database.h"
#include "src/profiling/symbolizer/symbolizer.h"
#include "src/trace_processor/util/tar_writer.h"
#include "src/traceconv/utils.h"
namespace perfetto::trace_to_text {
namespace {
std::vector<std::string> GetAllMappingNames(
trace_processor::TraceProcessor* tp) {
std::vector<std::string> mapping_names;
auto it = tp->ExecuteQuery(R"(
SELECT DISTINCT name
FROM stack_profile_mapping
WHERE build_id != '' AND name != ''
)");
while (it.Next()) {
mapping_names.push_back(it.Get(0).AsString());
}
return mapping_names;
}
std::vector<std::string> GetDefaultSymbolPaths() {
std::vector<std::string> paths;
paths.emplace_back("/usr/lib/debug");
const char* home = getenv("HOME");
if (home) {
paths.emplace_back(std::string(home) + "/.debug");
}
return paths;
}
// Creates a symbolizer based on provided paths, context, and discovered
// mapping names
std::unique_ptr<profiling::Symbolizer> CreateSymbolizer(
const BundleContext& context,
const std::vector<std::string>& mapping_names) {
if (mapping_names.empty()) {
return nullptr;
}
std::unordered_set<std::string> dirs;
std::unordered_set<std::string> files;
// Always add paths from PERFETTO_BINARY_PATH environment variable
std::vector<std::string> env_binary_paths =
profiling::GetPerfettoBinaryPath();
if (!env_binary_paths.empty()) {
dirs.insert(env_binary_paths.begin(), env_binary_paths.end());
}
// Add automatic paths unless disabled
if (!context.no_auto_symbol_paths) {
std::vector<std::string> auto_paths = GetDefaultSymbolPaths();
dirs.insert(auto_paths.begin(), auto_paths.end());
}
// Add user-provided paths
if (!context.symbol_paths.empty()) {
dirs.insert(context.symbol_paths.begin(), context.symbol_paths.end());
}
// Add binary paths from mappings (they might contain embedded symbols)
for (const auto& name : mapping_names) {
if (!name.empty() && name[0] == '/') {
files.insert(name);
}
}
return profiling::MaybeLocalSymbolizer(
std::vector<std::string>(dirs.begin(), dirs.end()),
std::vector<std::string>(files.begin(), files.end()), "index");
}
} // namespace
int TraceToBundle(const std::string& input_file_path,
const std::string& output_file_path,
const BundleContext& context) {
auto tp = trace_processor::TraceProcessor::CreateInstance({});
auto status = trace_processor::ReadTrace(tp.get(), input_file_path.c_str());
if (!status.ok()) {
PERFETTO_ELOG("Failed to read trace: %s", status.c_message());
return 1;
}
// Check if this is an Android trace - bundle mode doesn't work for Android
// yet.
bool is_android;
{
auto android_check = tp->ExecuteQuery(R"(
SELECT COUNT(*)
FROM metadata
WHERE name = 'android_build_fingerprint'
OR (
name = 'system_release'
AND (value GLOB '*android*' OR value GLOB '*Android*')
)
)");
is_android = android_check.Next() && android_check.Get(0).AsLong() > 0;
}
if (is_android) {
PERFETTO_ELOG(R"(
Bundle mode does not currently support Android traces.
For Android traces, please use the existing 'symbolize' mode instead:
# Set up symbol paths (choose one):
export PERFETTO_BINARY_PATH="/path/to/android/symbols"
export PERFETTO_SYMBOLIZER_MODE=index
# OR
export BREAKPAD_SYMBOL_DIR="/path/to/breakpad/symbols"
# Generate symbols and create bundle:
traceconv symbolize input.perfetto symbols.pb
cat input.perfetto symbols.pb > output.perfetto
For more information on setting up Android symbols, see:
https://perfetto.dev/docs/data-sources/native-heap-profiler#symbolization
)");
return 1;
}
// Add original trace file directly (memory efficient)
trace_processor::util::TarWriter tar(output_file_path);
auto add_trace_status =
tar.AddFileFromPath("trace.perfetto", input_file_path);
if (!add_trace_status.ok()) {
PERFETTO_ELOG("Failed to add trace to TAR archive: %s",
add_trace_status.c_message());
return 1;
}
// Symbolize the trace if possible.
std::vector<std::string> mapping_names = GetAllMappingNames(tp.get());
if (auto symbolizer = CreateSymbolizer(context, mapping_names); symbolizer) {
std::string symbols_proto;
profiling::SymbolizeDatabase(tp.get(), symbolizer.get(),
[&symbols_proto](const std::string& packet) {
symbols_proto += packet;
});
auto add_symbols_status = tar.AddFile("symbols.pb", symbols_proto);
if (!add_symbols_status.ok()) {
PERFETTO_ELOG("Failed to add symbols to TAR archive: %s",
add_symbols_status.c_message());
return 1;
}
}
return 0;
}
} // namespace perfetto::trace_to_text