blob: 8f9337fc5a28480cfd343d6f98a793f650fc1737 [file]
/*
* Copyright (C) 2026 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/trace_processor/shell/bundle_subcommand.h"
#include <cstdlib>
#include <string>
#include <vector>
#include "perfetto/base/status.h"
#include "perfetto/ext/base/file_utils.h"
#include "perfetto/ext/base/string_utils.h"
#include "src/trace_processor/shell/subcommand.h"
#include "src/traceconv/trace_to_bundle.h"
namespace perfetto::trace_processor::shell {
const char* BundleSubcommand::name() const {
return "bundle";
}
const char* BundleSubcommand::description() const {
return "Bundle a trace with symbols and deobfuscation data.";
}
const char* BundleSubcommand::usage_args() const {
return "<input> <output>";
}
const char* BundleSubcommand::detailed_help() const {
return R"(Create a self-contained bundle from a trace.
Outputs a TAR containing the trace plus the symbols and deobfuscation
mappings needed to make it self-contained. Both <input> and <output> must be
real file paths (stdin/stdout are not supported).)";
}
std::vector<FlagSpec> BundleSubcommand::GetFlags() {
return {
StringFlag("symbol-paths", '\0', "PATH1,PATH2,...",
"Additional paths to search for symbols.", &symbol_paths_),
BoolFlag("no-auto-symbol-paths", '\0',
"Disable automatic symbol path discovery.",
&no_auto_symbol_paths_),
FlagSpec{
"proguard-map", '\0', true, "[pkg=]PATH",
"ProGuard/R8 mapping.txt for deobfuscation (may be repeated). The "
"pkg= prefix scopes the map to a package.",
[this](const char* v) { proguard_maps_.emplace_back(v); }},
BoolFlag("no-auto-proguard-maps", '\0',
"Disable automatic ProGuard/R8 mapping discovery.",
&no_auto_proguard_maps_),
BoolFlag("verbose", '\0', "Print more detailed output.", &verbose_),
};
}
base::Status BundleSubcommand::Run(const SubcommandContext& ctx) {
if (ctx.positional_args.size() < 2) {
return base::ErrStatus(
"bundle requires both an input and an output file path.");
}
const std::string& input_file = ctx.positional_args[0];
const std::string& output_file = ctx.positional_args[1];
if (input_file == "-") {
return base::ErrStatus(
"bundle does not support stdin input; provide a file path.");
}
if (output_file == "-") {
return base::ErrStatus(
"bundle does not support stdout output; provide a file path.");
}
if (!base::FileExists(input_file)) {
return base::ErrStatus("Input file does not exist: %s", input_file.c_str());
}
trace_to_text::BundleContext context;
if (!symbol_paths_.empty())
context.symbol_paths = base::SplitString(symbol_paths_, ",");
for (const std::string& map : proguard_maps_) {
trace_to_text::ProguardMapSpec spec;
size_t eq = map.find('=');
if (eq == std::string::npos) {
spec.path = map;
} else {
spec.package = map.substr(0, eq);
spec.path = map.substr(eq + 1);
}
context.proguard_maps.push_back(std::move(spec));
}
context.no_auto_symbol_paths = no_auto_symbol_paths_;
context.no_auto_proguard_maps = no_auto_proguard_maps_;
context.verbose = verbose_;
if (const char* val = getenv("ANDROID_PRODUCT_OUT"))
context.android_product_out = val;
if (const char* val = getenv("HOME"))
context.home_dir = val;
context.root_dir = "/";
if (trace_to_text::TraceToBundle(input_file, output_file, context) != 0)
return base::ErrStatus("bundle: failed to create bundle.");
return base::OkStatus();
}
} // namespace perfetto::trace_processor::shell