trace_processor: add support for systrace output
Bug: 122513680
Change-Id: I399a4a52f6db4da3d56ae949dbcff1ab984b6b2e
diff --git a/Android.bp b/Android.bp
index a002229..72ca25d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1662,6 +1662,47 @@
],
}
+// GN target: //protos/perfetto/trace_processor:lite_gen
+genrule {
+ name: "perfetto_protos_perfetto_trace_processor_lite_gen",
+ srcs: [
+ "protos/perfetto/trace_processor/raw_query.proto",
+ "protos/perfetto/trace_processor/sched.proto",
+ "protos/perfetto/trace_processor/trace_processor.proto",
+ ],
+ tools: [
+ "aprotoc",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
+ out: [
+ "external/perfetto/protos/perfetto/trace_processor/raw_query.pb.cc",
+ "external/perfetto/protos/perfetto/trace_processor/sched.pb.cc",
+ "external/perfetto/protos/perfetto/trace_processor/trace_processor.pb.cc",
+ ],
+}
+
+// GN target: //protos/perfetto/trace_processor:lite_gen
+genrule {
+ name: "perfetto_protos_perfetto_trace_processor_lite_gen_headers",
+ srcs: [
+ "protos/perfetto/trace_processor/raw_query.proto",
+ "protos/perfetto/trace_processor/sched.proto",
+ "protos/perfetto/trace_processor/trace_processor.proto",
+ ],
+ tools: [
+ "aprotoc",
+ ],
+ cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
+ out: [
+ "external/perfetto/protos/perfetto/trace_processor/raw_query.pb.h",
+ "external/perfetto/protos/perfetto/trace_processor/sched.pb.h",
+ "external/perfetto/protos/perfetto/trace_processor/trace_processor.pb.h",
+ ],
+ export_include_dirs: [
+ "protos",
+ ],
+}
+
// GN target: //protos/perfetto/trace/profiling:lite_gen
genrule {
name: "perfetto_protos_perfetto_trace_profiling_lite_gen",
@@ -2816,6 +2857,7 @@
":perfetto_protos_perfetto_trace_lite_gen",
":perfetto_protos_perfetto_trace_minimal_lite_gen",
":perfetto_protos_perfetto_trace_power_lite_gen",
+ ":perfetto_protos_perfetto_trace_processor_lite_gen",
":perfetto_protos_perfetto_trace_profiling_lite_gen",
":perfetto_protos_perfetto_trace_ps_lite_gen",
":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
@@ -2833,6 +2875,49 @@
"src/base/unix_task_runner.cc",
"src/base/virtual_destructors.cc",
"src/base/watchdog_posix.cc",
+ "src/protozero/message.cc",
+ "src/protozero/message_handle.cc",
+ "src/protozero/proto_decoder.cc",
+ "src/protozero/proto_field_descriptor.cc",
+ "src/protozero/scattered_heap_buffer.cc",
+ "src/protozero/scattered_stream_null_delegate.cc",
+ "src/protozero/scattered_stream_writer.cc",
+ "src/trace_processor/android_logs_table.cc",
+ "src/trace_processor/args_table.cc",
+ "src/trace_processor/args_tracker.cc",
+ "src/trace_processor/clock_tracker.cc",
+ "src/trace_processor/counters_table.cc",
+ "src/trace_processor/event_tracker.cc",
+ "src/trace_processor/filtered_row_index.cc",
+ "src/trace_processor/ftrace_descriptors.cc",
+ "src/trace_processor/ftrace_utils.cc",
+ "src/trace_processor/instants_table.cc",
+ "src/trace_processor/process_table.cc",
+ "src/trace_processor/process_tracker.cc",
+ "src/trace_processor/proto_trace_parser.cc",
+ "src/trace_processor/proto_trace_tokenizer.cc",
+ "src/trace_processor/query_constraints.cc",
+ "src/trace_processor/raw_table.cc",
+ "src/trace_processor/row_iterators.cc",
+ "src/trace_processor/sched_slice_table.cc",
+ "src/trace_processor/slice_table.cc",
+ "src/trace_processor/slice_tracker.cc",
+ "src/trace_processor/span_join_operator_table.cc",
+ "src/trace_processor/sql_stats_table.cc",
+ "src/trace_processor/stats_table.cc",
+ "src/trace_processor/storage_columns.cc",
+ "src/trace_processor/storage_schema.cc",
+ "src/trace_processor/storage_table.cc",
+ "src/trace_processor/string_table.cc",
+ "src/trace_processor/table.cc",
+ "src/trace_processor/thread_table.cc",
+ "src/trace_processor/trace_processor.cc",
+ "src/trace_processor/trace_processor_context.cc",
+ "src/trace_processor/trace_processor_impl.cc",
+ "src/trace_processor/trace_sorter.cc",
+ "src/trace_processor/trace_storage.cc",
+ "src/trace_processor/virtual_destructors.cc",
+ "src/trace_processor/window_operator_table.cc",
"tools/trace_to_text/ftrace_event_formatter.cc",
"tools/trace_to_text/main.cc",
"tools/trace_to_text/proto_full_utils.cc",
@@ -2848,6 +2933,7 @@
],
static_libs: [
"libgtest_prod",
+ "libsqlite",
],
generated_headers: [
"perfetto_protos_perfetto_common_lite_gen_headers",
@@ -2859,6 +2945,7 @@
"perfetto_protos_perfetto_trace_lite_gen_headers",
"perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
"perfetto_protos_perfetto_trace_power_lite_gen_headers",
+ "perfetto_protos_perfetto_trace_processor_lite_gen_headers",
"perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
"perfetto_protos_perfetto_trace_ps_lite_gen_headers",
"perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
diff --git a/include/perfetto/base/scoped_file.h b/include/perfetto/base/scoped_file.h
index 59ca75f..0329003 100644
--- a/include/perfetto/base/scoped_file.h
+++ b/include/perfetto/base/scoped_file.h
@@ -100,6 +100,12 @@
using ScopedFstream = ScopedResource<FILE*, fclose, nullptr>;
+inline int FreeString(char* ptr) {
+ free(ptr);
+ return 0;
+}
+using ScopedString = ScopedResource<char*, FreeString, nullptr>;
+
} // namespace base
} // namespace perfetto
diff --git a/include/perfetto/base/string_writer.h b/include/perfetto/base/string_writer.h
index 73b0b13..c438626 100644
--- a/include/perfetto/base/string_writer.h
+++ b/include/perfetto/base/string_writer.h
@@ -21,6 +21,7 @@
#include <limits>
#include "perfetto/base/logging.h"
+#include "perfetto/base/scoped_file.h"
#include "perfetto/base/string_view.h"
namespace perfetto {
@@ -111,6 +112,11 @@
return buffer_;
}
+ // Creates a copy of the internal buffer.
+ base::ScopedString CreateStringCopy() {
+ return base::ScopedString(strndup(buffer_, pos_));
+ }
+
private:
char* buffer_ = nullptr;
size_t size_ = 0;
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6981806..c64b392 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -123,6 +123,7 @@
"../../gn:default_deps",
"../../include/perfetto/traced:sys_stats_counters",
"../../protos/perfetto/trace:lite",
+ "../../protos/perfetto/trace/ftrace:lite",
"../../protos/perfetto/trace_processor:lite",
"../base",
"../protozero",
diff --git a/src/trace_processor/ftrace_utils.cc b/src/trace_processor/ftrace_utils.cc
index d3cc831..8ca1657 100644
--- a/src/trace_processor/ftrace_utils.cc
+++ b/src/trace_processor/ftrace_utils.cc
@@ -16,14 +16,26 @@
#include "src/trace_processor/ftrace_utils.h"
+#include <stdint.h>
#include <algorithm>
#include "perfetto/base/logging.h"
+#include "perfetto/base/string_writer.h"
namespace perfetto {
namespace trace_processor {
namespace ftrace_utils {
+namespace {
+struct FtraceTime {
+ FtraceTime(int64_t ns)
+ : secs(ns / 1000000000LL), micros((ns - secs * 1000000000LL) / 1000) {}
+
+ const int64_t secs;
+ const int64_t micros;
+};
+} // namespace
+
TaskState::TaskState(const char* state_str) {
bool invalid_char = false;
bool is_runnable = false;
@@ -138,6 +150,37 @@
return output;
}
+void FormatSystracePrefix(int64_t timestamp,
+ uint32_t cpu,
+ uint32_t pid,
+ uint32_t tgid,
+ base::StringView name,
+ base::StringWriter* writer) {
+ FtraceTime ftrace_time(timestamp);
+ if (pid == 0) {
+ name = "<idle>";
+ }
+
+ writer->AppendString(name);
+ writer->AppendChar('-');
+ writer->AppendInt(pid);
+
+ writer->AppendLiteral(" (");
+ if (tgid == 0) {
+ writer->AppendLiteral("-----");
+ } else {
+ writer->AppendPaddedInt<' ', 5>(tgid);
+ }
+ writer->AppendLiteral(") [");
+ writer->AppendPaddedInt<'0', 3>(cpu);
+ writer->AppendLiteral("] .... ");
+
+ writer->AppendInt(ftrace_time.secs);
+ writer->AppendChar('.');
+ writer->AppendPaddedInt<'0', 6>(ftrace_time.micros);
+ writer->AppendChar(':');
+}
+
} // namespace ftrace_utils
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/ftrace_utils.h b/src/trace_processor/ftrace_utils.h
index e89904e..0bff23c 100644
--- a/src/trace_processor/ftrace_utils.h
+++ b/src/trace_processor/ftrace_utils.h
@@ -18,10 +18,11 @@
#define SRC_TRACE_PROCESSOR_FTRACE_UTILS_H_
#include <stddef.h>
-
#include <array>
-#include "perfetto/base/optional.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/base/string_view.h"
+#include "perfetto/base/string_writer.h"
namespace perfetto {
namespace trace_processor {
@@ -82,6 +83,13 @@
uint16_t state_ = 0;
};
+void FormatSystracePrefix(int64_t timestamp,
+ uint32_t cpu,
+ uint32_t pid,
+ uint32_t tgid,
+ base::StringView name,
+ base::StringWriter* writer);
+
} // namespace ftrace_utils
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/raw_table.cc b/src/trace_processor/raw_table.cc
index 22e335c..0585e59 100644
--- a/src/trace_processor/raw_table.cc
+++ b/src/trace_processor/raw_table.cc
@@ -16,12 +16,26 @@
#include "src/trace_processor/raw_table.h"
+#include <inttypes.h>
+
+#include "src/trace_processor/ftrace_descriptors.h"
#include "src/trace_processor/sqlite_utils.h"
+#include "perfetto/trace/ftrace/ftrace_event.pb.h"
+
namespace perfetto {
namespace trace_processor {
-RawTable::RawTable(sqlite3*, const TraceStorage* storage) : storage_(storage) {}
+RawTable::RawTable(sqlite3* db, const TraceStorage* storage)
+ : storage_(storage) {
+ auto fn = [](sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+ auto* thiz = static_cast<RawTable*>(sqlite3_user_data(ctx));
+ thiz->ToSystrace(ctx, argc, argv);
+ };
+ sqlite3_create_function(db, "to_ftrace", 1,
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC, this, fn, nullptr,
+ nullptr);
+}
void RawTable::RegisterTable(sqlite3* db, const TraceStorage* storage) {
Table::Register<RawTable>(db, storage, "raw");
@@ -56,5 +70,105 @@
return SQLITE_OK;
}
+void RawTable::FormatSystraceArgs(const std::string& event_name,
+ ArgSetId arg_set_id,
+ base::StringWriter* writer) {
+ const auto& set_ids = storage_->args().set_ids();
+ auto lb = std::lower_bound(set_ids.begin(), set_ids.end(), arg_set_id);
+ auto ub = std::find(lb, set_ids.end(), arg_set_id + 1);
+
+ auto start_row = static_cast<uint32_t>(std::distance(set_ids.begin(), lb));
+
+ using Variadic = TraceStorage::Args::Variadic;
+ using ValueWriter = std::function<void(const Variadic&)>;
+ auto write_value = [this, writer](const Variadic& value) {
+ switch (value.type) {
+ case TraceStorage::Args::Variadic::kInt:
+ writer->AppendInt(value.int_value);
+ break;
+ case TraceStorage::Args::Variadic::kReal:
+ writer->AppendDouble(value.real_value);
+ break;
+ case TraceStorage::Args::Variadic::kString: {
+ const auto& str = storage_->GetString(value.string_value);
+ writer->AppendString(str.c_str(), str.size());
+ }
+ }
+ };
+ auto write_arg = [this, writer, start_row](uint32_t arg_idx,
+ ValueWriter value_fn) {
+ uint32_t arg_row = start_row + arg_idx;
+ if (arg_row != 0)
+ writer->AppendChar(' ');
+
+ const auto& args = storage_->args();
+ const auto& key = storage_->GetString(args.keys()[arg_row]);
+ const auto& value = args.arg_values()[arg_row];
+
+ writer->AppendString(key.c_str(), key.length());
+ writer->AppendChar('=');
+ value_fn(value);
+ };
+
+ if (event_name == "sched_switch") {
+ using SS = protos::SchedSwitchFtraceEvent;
+ write_arg(SS::kPrevCommFieldNumber - 1, write_value);
+ write_arg(SS::kPrevPidFieldNumber - 1, write_value);
+ write_arg(SS::kPrevPrioFieldNumber - 1, write_value);
+ write_arg(SS::kPrevStateFieldNumber - 1, [writer](const Variadic& value) {
+ auto state = static_cast<uint16_t>(value.int_value);
+ writer->AppendString(ftrace_utils::TaskState(state).ToString().data());
+ });
+
+ writer->AppendLiteral(" ==>");
+ write_arg(SS::kNextCommFieldNumber - 1, write_value);
+ write_arg(SS::kNextPidFieldNumber - 1, write_value);
+ write_arg(SS::kNextPrioFieldNumber - 1, write_value);
+ }
+
+ uint32_t arg = 0;
+ for (auto it = lb; it != ub; it++) {
+ write_arg(arg++, write_value);
+ }
+}
+
+void RawTable::ToSystrace(sqlite3_context* ctx,
+ int argc,
+ sqlite3_value** argv) {
+ if (argc != 1 || sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
+ sqlite3_result_error(ctx, "Usage: systrace(id)", -1);
+ return;
+ }
+ RowId row_id = sqlite3_value_int64(argv[0]);
+ auto pair = TraceStorage::ParseRowId(row_id);
+ PERFETTO_DCHECK(pair.first == TableId::kRawEvents);
+ auto row = pair.second;
+
+ const auto& raw_evts = storage_->raw_events();
+
+ UniqueTid utid = raw_evts.utids()[row];
+ const auto& thread = storage_->GetThread(utid);
+ uint32_t tgid = 0;
+ if (thread.upid.has_value()) {
+ tgid = storage_->GetProcess(thread.upid.value()).pid;
+ }
+ const auto& name = storage_->GetString(thread.name_id);
+
+ char line[4096];
+ base::StringWriter writer(line, sizeof(line));
+
+ ftrace_utils::FormatSystracePrefix(raw_evts.timestamps()[row],
+ raw_evts.cpus()[row], thread.tid, tgid,
+ base::StringView(name), &writer);
+
+ const auto& event_name = storage_->GetString(raw_evts.name_ids()[row]);
+ writer.AppendChar(' ');
+ writer.AppendString(event_name.c_str(), event_name.size());
+ writer.AppendLiteral(": ");
+
+ FormatSystraceArgs(event_name, raw_evts.arg_set_ids()[row], &writer);
+ sqlite3_result_text(ctx, writer.CreateStringCopy().release(), -1, free);
+}
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/raw_table.h b/src/trace_processor/raw_table.h
index caa9aa7..e6bdbe4 100644
--- a/src/trace_processor/raw_table.h
+++ b/src/trace_processor/raw_table.h
@@ -34,6 +34,12 @@
uint32_t RowCount() override;
int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
+ private:
+ void FormatSystraceArgs(const std::string& event_name,
+ ArgSetId arg_set_id,
+ base::StringWriter* writer);
+ void ToSystrace(sqlite3_context* ctx, int argc, sqlite3_value** argv);
+
const TraceStorage* const storage_;
};
diff --git a/src/trace_processor/sqlite_utils.h b/src/trace_processor/sqlite_utils.h
index 37becd0..9974e05 100644
--- a/src/trace_processor/sqlite_utils.h
+++ b/src/trace_processor/sqlite_utils.h
@@ -34,6 +34,7 @@
namespace sqlite_utils {
const auto kSqliteStatic = reinterpret_cast<sqlite3_destructor_type>(0);
+const auto kSqliteTransient = reinterpret_cast<sqlite3_destructor_type>(-1);
template <typename T>
using is_numeric =
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index d3db6af..a8e2d2d 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -29,7 +29,6 @@
#include "src/trace_processor/counters_table.h"
#include "src/trace_processor/event_tracker.h"
#include "src/trace_processor/instants_table.h"
-#include "src/trace_processor/json_trace_parser.h"
#include "src/trace_processor/process_table.h"
#include "src/trace_processor/process_tracker.h"
#include "src/trace_processor/proto_trace_parser.h"
@@ -49,19 +48,32 @@
#include "perfetto/trace_processor/raw_query.pb.h"
+// JSON parsing is only supported in the standalone build.
+#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
+#include "src/trace_processor/json_trace_parser.h"
+#endif
+
+// In Android tree builds, we don't have the percentile module.
+// Just don't include it.
+#if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
// defined in sqlite_src/ext/misc/percentile.c
extern "C" int sqlite3_percentile_init(sqlite3* db,
char** error,
const sqlite3_api_routines* api);
+#endif
namespace {
void InitializeSqliteModules(sqlite3* db) {
+// In Android tree builds, we don't have the percentile module.
+// Just don't include it.
+#if !PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
char* error = nullptr;
sqlite3_percentile_init(db, &error, nullptr);
if (error) {
PERFETTO_ELOG("Error initializing: %s", error);
sqlite3_free(error);
}
+#endif
}
void CreateBuiltinTables(sqlite3* db) {
@@ -172,7 +184,11 @@
switch (trace_type) {
case kJsonTraceType:
PERFETTO_DLOG("Legacy JSON trace detected");
+#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
context_.chunk_reader.reset(new JsonTraceParser(&context_));
+#else
+ PERFETTO_FATAL("JSON traces only supported in standalone mode.");
+#endif
break;
case kProtoTraceType:
context_.chunk_reader.reset(new ProtoTraceTokenizer(&context_));
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 6345d68..ef18080 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -151,6 +151,9 @@
# libunwind is disabled on Darwin so we cannot depend on it.
pass
+def enable_sqlite(module):
+ module.static_libs.append('libsqlite')
+
# Android equivalents for third-party libraries that the upstream project
# depends on.
builtin_deps = {
@@ -163,6 +166,7 @@
'//buildtools:protobuf_lite': enable_protobuf_lite,
'//buildtools:protoc_lib': enable_protoc_lib,
'//buildtools:libunwindstack': enable_libunwindstack,
+ '//buildtools:sqlite': enable_sqlite,
}
# ----------------------------------------------------------------------------
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index bbfe75d..d06c057 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -22,12 +22,15 @@
public_deps = [
"../../gn:default_deps",
"../../include/perfetto/base",
+ "../../include/perfetto/trace_processor:trace_processor",
"../../include/perfetto/traced:sys_stats_counters",
"../../protos/perfetto/trace:lite",
"../../protos/perfetto/trace/ftrace:lite",
"../../protos/perfetto/trace/profiling:lite",
+ "../../protos/perfetto/trace_processor:lite",
"../../protos/third_party/pprof:lite",
"../../src/base",
+ "../../src/trace_processor:lib",
]
sources = [
"ftrace_event_formatter.cc",
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index 5ef76fd..1f8c9d2 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -76,6 +76,9 @@
if (format == "systrace")
return perfetto::trace_to_text::TraceToSystrace(input_stream, output_stream,
/*wrap_in_json=*/false);
+ if (format == "experimental_systrace")
+ return perfetto::trace_to_text::TraceToExperimentalSystrace(input_stream,
+ output_stream);
if (format == "text")
return perfetto::trace_to_text::TraceToText(input_stream, output_stream);
diff --git a/tools/trace_to_text/trace_to_systrace.cc b/tools/trace_to_text/trace_to_systrace.cc
index 6737777..292ec44 100644
--- a/tools/trace_to_text/trace_to_systrace.cc
+++ b/tools/trace_to_text/trace_to_systrace.cc
@@ -29,6 +29,7 @@
#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
+#include "perfetto/trace_processor/trace_processor.h"
#include "perfetto/traced/sys_stats_counters.h"
#include "tools/trace_to_text/ftrace_event_formatter.h"
#include "tools/trace_to_text/process_formatter.h"
@@ -36,6 +37,7 @@
#include "perfetto/trace/trace.pb.h"
#include "perfetto/trace/trace_packet.pb.h"
+#include "perfetto/trace_processor/raw_query.pb.h"
// When running in Web Assembly, fflush() is a no-op and the stdio buffering
// sends progress updates to JS only when a write ends with \n.
@@ -106,6 +108,102 @@
} // namespace
+int TraceToExperimentalSystrace(std::istream* input, std::ostream* output) {
+ trace_processor::Config config;
+ config.optimization_mode = trace_processor::OptimizationMode::kMaxBandwidth;
+ std::unique_ptr<trace_processor::TraceProcessor> tp =
+ trace_processor::TraceProcessor::CreateInstance(config);
+
+ // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
+ constexpr size_t kChunkSize = 1024 * 1024;
+
+ uint64_t file_size = 0;
+ for (int i = 0;; i++) {
+ if (i % 128 == 0)
+ fprintf(stderr, "\x1b[2K\rLoading trace: %.2f MB\r", file_size / 1E6);
+
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[kChunkSize]);
+ input->read(reinterpret_cast<char*>(buf.get()), kChunkSize);
+ if (input->bad()) {
+ PERFETTO_ELOG("Failed when reading trace");
+ return 1;
+ }
+
+ auto rsize = input->gcount();
+ if (rsize <= 0)
+ break;
+ file_size += static_cast<uint64_t>(rsize);
+ tp->Parse(std::move(buf), static_cast<size_t>(rsize));
+ }
+ tp->NotifyEndOfFile();
+
+ *output << "TRACE:\n";
+ *output << kFtraceHeader;
+
+ constexpr uint32_t kNumRowsToQuery = 100000;
+ int64_t start_ts = 0;
+ bool has_more = true;
+ for (uint32_t i = 0; has_more; i++) {
+ protos::RawQueryArgs query_args;
+
+ char buffer[1024];
+ sprintf(buffer,
+ "select ts, to_ftrace(id) from raw "
+ "where ts >= %" PRId64 " limit %" PRIu32,
+ start_ts, kNumRowsToQuery);
+ query_args.set_sql_query(buffer);
+
+ // This query is not actually async so just pull the result up to the
+ // function level so we can return on error.
+ protos::RawQueryResult result;
+ tp->ExecuteQuery(query_args, [&result](const protos::RawQueryResult& res) {
+ result = res;
+ });
+
+ if (result.has_error()) {
+ PERFETTO_ELOG("Error while writing systrace %s", result.error().c_str());
+ return 1;
+ }
+
+ // The code below relies on there being at least one row so just break if
+ // we don't.
+ auto num_rows = result.num_records();
+ if (num_rows == 0) {
+ has_more = false;
+ break;
+ }
+
+ // Store the end timestamp so we can start iterating from there next time.
+ const auto& ts_col = result.columns(0).long_values();
+ start_ts = ts_col.Get(ts_col.size() - 1);
+
+ // Compute how many rows we should print out - this should be the first
+ // index with the timestamp |start_ts|. Usually this is just |size - 1| but
+ // if multiple rows have the same timestamp, this can be earlier.
+ auto rit = std::find(ts_col.rbegin(), ts_col.rend(), start_ts);
+ auto rdistance = std::distance(ts_col.rbegin(), rit);
+ auto last_row = static_cast<uint32_t>(ts_col.size() - 1 - rdistance);
+
+ // Print out everything until this row.
+ for (uint64_t row = 0; row < last_row; row++) {
+ int idx = static_cast<int>(row);
+ const std::string& line = result.columns(1).string_values(idx);
+ *output << line << "\n";
+ }
+
+ output->flush();
+
+ // Update the seen count to the number of output rows and only continue if
+ // we saw exactly the number of rows we asked for.
+ has_more = kNumRowsToQuery == result.num_records();
+
+ uint64_t printed_rows = i * kNumRowsToQuery + result.num_records();
+ fprintf(stderr, "\x1b[2K\rWritten %" PRIu64 " rows\r", printed_rows);
+ }
+
+ return 0;
+}
+
int TraceToSystrace(std::istream* input,
std::ostream* output,
bool wrap_in_json) {
diff --git a/tools/trace_to_text/trace_to_systrace.h b/tools/trace_to_text/trace_to_systrace.h
index 32ded63..8dcaf5a 100644
--- a/tools/trace_to_text/trace_to_systrace.h
+++ b/tools/trace_to_text/trace_to_systrace.h
@@ -22,6 +22,8 @@
namespace perfetto {
namespace trace_to_text {
+int TraceToExperimentalSystrace(std::istream* input, std::ostream* output);
+
int TraceToSystrace(std::istream* input,
std::ostream* output,
bool wrap_in_json);