From 77d091bdc8e5d1f11ca7881ebc8170a76f0f76cf Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 27 Dec 2023 01:18:41 -0500 Subject: [PATCH 01/11] wip: import-fuchsia tool to import fuchsia traces See: https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format --- import-fuchsia/build/unix/Makefile | 16 + import-fuchsia/build/unix/build.mk | 12 + import-fuchsia/build/unix/debug.mk | 6 + import-fuchsia/build/unix/release.mk | 9 + import-fuchsia/build/win32/import-fuchsia.sln | 25 + .../build/win32/import-fuchsia.vcxproj | 206 ++++++ .../win32/import-fuchsia.vcxproj.filters | 356 ++++++++++ import-fuchsia/src/import-fuchsia.cpp | 653 ++++++++++++++++++ 8 files changed, 1283 insertions(+) create mode 100644 import-fuchsia/build/unix/Makefile create mode 100644 import-fuchsia/build/unix/build.mk create mode 100644 import-fuchsia/build/unix/debug.mk create mode 100644 import-fuchsia/build/unix/release.mk create mode 100644 import-fuchsia/build/win32/import-fuchsia.sln create mode 100644 import-fuchsia/build/win32/import-fuchsia.vcxproj create mode 100644 import-fuchsia/build/win32/import-fuchsia.vcxproj.filters create mode 100644 import-fuchsia/src/import-fuchsia.cpp diff --git a/import-fuchsia/build/unix/Makefile b/import-fuchsia/build/unix/Makefile new file mode 100644 index 00000000..f762d28a --- /dev/null +++ b/import-fuchsia/build/unix/Makefile @@ -0,0 +1,16 @@ +all: release + +debug: + @$(MAKE) -f debug.mk all + +release: + @$(MAKE) -f release.mk all + +clean: + @$(MAKE) -f build.mk clean + +db: clean + @bear -- $(MAKE) -f debug.mk all + @mv -f compile_commands.json ../../../ + +.PHONY: all clean debug release db diff --git a/import-fuchsia/build/unix/build.mk b/import-fuchsia/build/unix/build.mk new file mode 100644 index 00000000..5f62bc17 --- /dev/null +++ b/import-fuchsia/build/unix/build.mk @@ -0,0 +1,12 @@ +CFLAGS += +CXXFLAGS := $(CFLAGS) -std=gnu++17 +DEFINES += -DTRACY_NO_STATISTICS +INCLUDES := $(shell pkg-config --cflags capstone) +LIBS += $(shell pkg-config --libs capstone) -lpthread +PROJECT := import-fuchsia +IMAGE := $(PROJECT)-$(BUILD) + +FILTER := +include ../../../common/src-from-vcxproj.mk + +include ../../../common/unix.mk diff --git a/import-fuchsia/build/unix/debug.mk b/import-fuchsia/build/unix/debug.mk new file mode 100644 index 00000000..a4ec6b6a --- /dev/null +++ b/import-fuchsia/build/unix/debug.mk @@ -0,0 +1,6 @@ +CFLAGS := -g3 -Wall +DEFINES := -DDEBUG +BUILD := debug + +include ../../../common/unix-debug.mk +include build.mk diff --git a/import-fuchsia/build/unix/release.mk b/import-fuchsia/build/unix/release.mk new file mode 100644 index 00000000..ccf07661 --- /dev/null +++ b/import-fuchsia/build/unix/release.mk @@ -0,0 +1,9 @@ +CFLAGS := -O3 +ifndef TRACY_NO_LTO +CFLAGS += -flto +endif +DEFINES := -DNDEBUG +BUILD := release + +include ../../../common/unix-release.mk +include build.mk diff --git a/import-fuchsia/build/win32/import-fuchsia.sln b/import-fuchsia/build/win32/import-fuchsia.sln new file mode 100644 index 00000000..4b38515b --- /dev/null +++ b/import-fuchsia/build/win32/import-fuchsia.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "import-fuchsia", "import-fuchsia.vcxproj", "{447D58BF-94CD-4469-BB90-549C05D03E00}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {447D58BF-94CD-4469-BB90-549C05D03E00}.Debug|x64.ActiveCfg = Debug|x64 + {447D58BF-94CD-4469-BB90-549C05D03E00}.Debug|x64.Build.0 = Debug|x64 + {447D58BF-94CD-4469-BB90-549C05D03E00}.Release|x64.ActiveCfg = Release|x64 + {447D58BF-94CD-4469-BB90-549C05D03E00}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3E51386C-43EA-44AC-9F24-AFAFE4D63ADE} + EndGlobalSection +EndGlobal diff --git a/import-fuchsia/build/win32/import-fuchsia.vcxproj b/import-fuchsia/build/win32/import-fuchsia.vcxproj new file mode 100644 index 00000000..d57f8f84 --- /dev/null +++ b/import-fuchsia/build/win32/import-fuchsia.vcxproj @@ -0,0 +1,206 @@ + + + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {447D58BF-94CD-4469-BB90-549C05D03E00} + import-fuchsia + 10.0 + x64-windows-static + + + + Application + true + v143 + MultiByte + + + Application + false + v143 + true + MultiByte + + + + + + + + + + + + + + + + true + + + + Level3 + Disabled + true + true + true + TRACY_NO_STATISTICS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32_LEAN_AND_MEAN;NOMINMAX;_USE_MATH_DEFINES;%(PreprocessorDefinitions) + AdvancedVectorExtensions2 + stdcpplatest + $(ProjectDir)..\..\..\vcpkg_installed\$(VcpkgTriplet)\include;$(ProjectDir)..\..\..\vcpkg_installed\$(VcpkgTriplet)\include\capstone;$(VcpkgManifestRoot)\vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\include\capstone;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\capstone + + + ws2_32.lib;capstone.lib;%(AdditionalDependencies) + Console + $(ProjectDir)..\..\..\vcpkg_installed\$(VcpkgTriplet)\debug\lib + + + + + Level3 + MaxSpeed + true + true + true + true + true + TRACY_NO_STATISTICS;NDEBUG;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;WIN32_LEAN_AND_MEAN;NOMINMAX;_USE_MATH_DEFINES;%(PreprocessorDefinitions) + AdvancedVectorExtensions2 + stdcpplatest + $(ProjectDir)..\..\..\vcpkg_installed\$(VcpkgTriplet)\include;$(ProjectDir)..\..\..\vcpkg_installed\$(VcpkgTriplet)\include\capstone;$(VcpkgManifestRoot)\vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\include\capstone;$(VcpkgRoot)\installed\$(VcpkgTriplet)\include\capstone + + + true + true + ws2_32.lib;capstone.lib;%(AdditionalDependencies) + Console + $(ProjectDir)..\..\..\vcpkg_installed\$(VcpkgTriplet)\lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/import-fuchsia/build/win32/import-fuchsia.vcxproj.filters b/import-fuchsia/build/win32/import-fuchsia.vcxproj.filters new file mode 100644 index 00000000..3efa781a --- /dev/null +++ b/import-fuchsia/build/win32/import-fuchsia.vcxproj.filters @@ -0,0 +1,356 @@ + + + + + {729c80ee-4d26-4a5e-8f1f-6c075783eb56} + + + {cf23ef7b-7694-4154-830b-00cf053350ea} + + + {e39d3623-47cd-4752-8da9-3ea324f964c1} + + + {9ec18988-3ab7-4c05-a9d0-46c0a68037de} + + + {5ee9ba63-2914-4027-997e-e743a294bba6} + + + {a166d032-7be0-4d07-9f85-a8199cc1ec7c} + + + {438fff23-197c-4b6f-91f0-74f8b3878571} + + + {e5c7021a-e0e4-45c2-b461-e806bc036d5f} + + + + + server + + + server + + + src + + + server + + + server + + + server + + + server + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\decompress + + + zstd\decompress + + + zstd\decompress + + + zstd\decompress + + + zstd\dictBuilder + + + zstd\dictBuilder + + + zstd\dictBuilder + + + zstd\dictBuilder + + + common + + + common + + + common + + + common + + + common + + + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + server + + + zstd + + + zstd + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\common + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\compress + + + zstd\decompress + + + zstd\decompress + + + zstd\decompress + + + zstd + + + zstd\dictBuilder + + + zstd\dictBuilder + + + zstd\common + + + zstd\compress + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + common + + + + + zstd\decompress + + + diff --git a/import-fuchsia/src/import-fuchsia.cpp b/import-fuchsia/src/import-fuchsia.cpp new file mode 100644 index 00000000..f6762a0e --- /dev/null +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -0,0 +1,653 @@ +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef _MSC_VER +#define stat64 _stat64 +#endif +#if defined __APPLE__ +#define stat64 stat +#endif + +#include "../../server/TracyFileWrite.hpp" +#include "../../server/TracyMmap.hpp" +#include "../../server/TracyWorker.hpp" +#include "../../zstd/zstd.h" + +void Usage() { + printf("Usage: import-fuchsia input.json output.tracy\n\n"); + printf("See: " + "https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format\n\n"); + exit(1); +} + +#define ROUND_TO_WORD(n) ((n) + ((~((n)-1)) & 0x7)) + +struct ThreadRef { + uint64_t pid; + uint64_t tid; +}; + +inline bool operator==(const ThreadRef t1, const ThreadRef t2) { + return t1.pid == t2.pid && t1.tid == t2.tid; +} + +struct Unit {}; + +// arguments +using ArgumentValue = + std::variant; + +struct Argument { + std::string name; + ArgumentValue value; +}; + +// encode a pair of "real pid, real tid" from a trace into a +// pseudo thread ID living in the single namespace of Tracy threads. +struct PidTidEncoder { + ThreadRef thref; + uint64_t pseudo_tid; // fake thread id, unique within Tracy +}; + +// A span into the main buffer +struct Record { + const uint64_t *p; + ; + uint16_t len_word; + uint64_t header; +}; + +struct DecodeState { + std::vector tid_encoders; + std::unordered_map threadNames; + // compressed thread refs + std::unordered_map threadRefs; + // compressed strings + std::unordered_map stringRefs; +}; + +// Append a string representation of `val` to `res` +void appendArgumentValue(std::string &res, ArgumentValue &val) { + if (std::holds_alternative(val)) { + res += std::get(val); + } else if (std::holds_alternative(val)) { + char buf[32]; + snprintf(buf, 31, "%" PRIu64, std::get(val)); + res += buf; + } else if (std::holds_alternative(val)) { + char buf[32]; + snprintf(buf, 31, "%" PRId64, std::get(val)); + res += buf; + } else if (std::holds_alternative(val)) { + res += std::get(val) ? "true" : "false"; + } else if (std::holds_alternative(val)) { + char buf[32]; + snprintf(buf, 31, "%.5f", std::get(val)); + res += buf; + } +} + +// Read input into a local buffer +std::vector read_input(const char *input) { + std::vector buf; + + FILE *f = fopen(input, "rb"); + if (!f) { + fprintf(stderr, "Cannot open input file!\n"); + exit(1); + } + struct stat64 sb; + if (stat64(input, &sb) != 0) { + fprintf(stderr, "Cannot open input file!\n"); + fclose(f); + exit(1); + } + + const auto zsz = sb.st_size; + auto zbuf = (char *)mmap(nullptr, zsz, PROT_READ, MAP_SHARED, fileno(f), 0); + fclose(f); + if (!zbuf) { + fprintf(stderr, "Cannot mmap input file!\n"); + exit(1); + } + + const auto fnsz = strlen(input); + if (fnsz > 4 && memcmp(input + fnsz - 4, ".zst", 4) == 0) { + + auto zctx = ZSTD_createDStream(); + ZSTD_initDStream(zctx); + + enum { tmpSize = 64 * 1024 }; + auto tmp = new char[tmpSize]; + + ZSTD_inBuffer_s zin = {zbuf, (size_t)zsz}; + ZSTD_outBuffer_s zout = {tmp, (size_t)tmpSize}; + + buf.reserve(1024 * 1024); + + while (zin.pos < zin.size) { + const auto res = ZSTD_decompressStream(zctx, &zout, &zin); + if (ZSTD_isError(res)) { + ZSTD_freeDStream(zctx); + delete[] tmp; + fprintf(stderr, "Couldn't decompress input file (%s)!\n", + ZSTD_getErrorName(res)); + exit(1); + } + if (zout.pos > 0) { + const auto bsz = buf.size(); + buf.resize(bsz + zout.pos); + memcpy(buf.data() + bsz, tmp, zout.pos); + zout.pos = 0; + } + } + + ZSTD_freeDStream(zctx); + delete[] tmp; + } else { + // just copy to memory + buf.resize(zsz); + memcpy(buf.data(), zbuf, zsz); + } + + munmap(zbuf, zsz); + return buf; +} + +// read next span starting at `offset` +Record read_next_record(std::vector const &input, size_t &offset) { + uint64_t header = *((uint64_t *)&input[offset]); + uint16_t len_word = (header >> 4) & 0xfff; + Record sp{(uint64_t *)&input[offset], len_word, header}; + offset += 8 * len_word; + return sp; +} + +// there might be multiple processes so we allocate a pseudo-tid +// for each pair (pid, real_tid) +uint64_t getPseudoTid(DecodeState &dec, ThreadRef th) { + for (auto &pair : dec.tid_encoders) { + if (pair.thref == th) + return pair.pseudo_tid; + } + + // not found, invent a new one + assert(th.pid <= std::numeric_limits::max()); + assert(th.tid <= std::numeric_limits::max()); + + const auto pseudo_tid = (th.tid & 0xFFFFFFFF) | (th.pid << 32); + dec.tid_encoders.emplace_back(PidTidEncoder{th, pseudo_tid}); + return pseudo_tid; +} + +// decode thread info +ThreadRef readThread(DecodeState &dec, Record const &r, size_t &offset, + uint8_t ref) { + ThreadRef th; + if (ref == 0) { + // inline + th = {r.p[offset], r.p[offset + 1]}; + offset += 2; + } else { + th = dec.threadRefs[ref]; + } + return th; +} + +// Read a string reference into `res` +void readString(DecodeState &dec, std::string &res, Record const &r, + size_t &offset, uint16_t ref) { + res.clear(); + if (ref == 0) { + } else if ((ref & 0x8000) != 0) { + // inline string + size_t size_name = ref & 0x7fff; + res.resize(size_name + 1); + memcpy(res.data(), (uint8_t *)&r.p[offset], size_name); + res[size_name] = 0; + offset += ROUND_TO_WORD(size_name) >> 3; + } else { + res = dec.stringRefs[ref]; + } +} + +// Skip string reference (just modify offset) +void skipString(size_t &offset, uint16_t ref) { + if (ref != 0 && (ref & 0x80) != 0) { + size_t size = ref & 0x7f; + offset += ROUND_TO_WORD(size) >> 3; + } +} + +// Read a single argument +void readArgument(std::vector &args, DecodeState &dec, + Record const &r, size_t &offset) { + uint64_t header = r.p[offset]; + offset += 1; + + auto ty = (uint8_t)(header & 0xf); + + uint16_t name_ref = (header >> 16) & 0xffff; + std::string name; + readString(dec, name, r, offset, name_ref); + + ArgumentValue value; + switch (ty) { + case 0: + value = (Unit){}; + break; + case 1: { + int32_t i = header >> 32; + value = (int64_t)i; + } break; + case 2: { + uint32_t i = header >> 32; + value = (int64_t)i; + } break; + case 3: { + int64_t i = r.p[offset]; + offset += 1; + value = i; + } break; + case 4: { + uint64_t i = r.p[offset]; + offset += 1; + value = i; + } break; + case 5: { + double i = r.p[offset]; + offset += 1; + value = i; + } break; + case 6: { + uint16_t value_ref = (header >> 32) & 0xffff; + std::string res; + readString(dec, res, r, offset, value_ref); + value = res; + } break; + case 7: + // pointer + case 8: + // koid + { + uint64_t i = r.p[offset]; + offset += 1; + value = i; + } + break; + case 9: { + // bool + bool b = (bool)((header >> 32) & 1); + value = b; + } + + default: + assert(false); + } + + args.push_back({name, value}); +} + +/// Read `n_args` arguments from given offset +void readArguments(std::vector &args, DecodeState &dec, Record r, + size_t &offset, const int n_args) { + args.clear(); + + for (int i = 0; i < n_args; ++i) + readArgument(args, dec, r, offset); +} + +// text made of arguments +void printArgumentsToString(std::string &res, std::vector &args) { + for (auto &kv : args) { + res += kv.name; + res += ": "; + appendArgumentValue(res, kv.value); + res += "\n"; + } +} + +int main(int argc, char **argv) { +#ifdef _WIN32 + if (!AttachConsole(ATTACH_PARENT_PROCESS)) { + AllocConsole(); + SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), 0x07); + } +#endif + + tracy::FileWrite::Compression clev = tracy::FileWrite::Compression::Fast; + + if (argc != 3) + Usage(); + + const char *input = argv[1]; + const char *output = argv[2]; + + printf("Loading...\r"); + fflush(stdout); + + std::vector buf = read_input(input); + + printf("\33[2KParsing...\r"); + fflush(stdout); + + std::vector timeline; + std::vector messages; + std::vector plots; + DecodeState dec; + + size_t offset = 0; + int n_records = 0; + std::string name; + std::vector arguments; + + while (offset < buf.size()) { + Record r = read_next_record(buf, offset); + n_records++; + + uint8_t ty = r.header & 0xf; + + switch (ty) { + case 3: { + // thread record + uint8_t th_ref = (r.header >> 16) & 0xff; + uint64_t pid = r.p[1]; + uint64_t tid = r.p[2]; + ThreadRef th{pid, tid}; + dec.threadRefs[th_ref] = th; + break; + } + case 4: { + // event + uint8_t ev_ty = (r.header >> 16) & 0xf; + uint8_t n_args = (r.header >> 20) & 0xf; + + uint64_t timestamp = r.p[1]; + size_t offset = 2; + + // decode thread info + uint8_t th_ref = (r.header >> 24) & 0xff; + ThreadRef th = readThread(dec, r, offset, th_ref); + + // skip category + uint16_t cat_ref = (r.header >> 32) & 0xffff; + skipString(offset, cat_ref); + + // decode name + uint16_t name_ref = (r.header >> 48) & 0xffff; + readString(dec, name, r, offset, name_ref); + + readArguments(arguments, dec, r, offset, n_args); + + std::string locFile; + uint32_t locLine = 0; + + switch (ev_ty) { + case 0: { + // instant + messages.emplace_back(tracy::Worker::ImportEventMessages{ + getPseudoTid(dec, th), timestamp, name}); + break; + } + + case 4: { + // complete duration + const auto tid = getPseudoTid(dec, th); + const auto ts0 = timestamp; + const auto ts1 = r.p[offset]; // end timestamp + std::string zoneText; + printArgumentsToString(zoneText, arguments); + timeline.emplace_back(tracy::Worker::ImportEventTimeline{ + tid, ts0, name, std::move(zoneText), false, std::move(locFile), + locLine}); + timeline.emplace_back( + tracy::Worker::ImportEventTimeline{tid, ts1, "", "", true}); + break; + } + + default: { + } + } + + /* + if( type == "b" || type == "B" ) + { + timeline.emplace_back( tracy::Worker::ImportEventTimeline { + getPseudoTid(v), + uint64_t( v["ts"].get() * 1000. ), + v["name"].get(), + std::move(zoneText), + false, + std::move(locFile), + locLine + } ); + } + */ + + break; + } + + default: { + } + } + } + + printf("read %d records\n", n_records); + fflush(stdout); + + /* + if( j.is_object() && j.contains( "traceEvents" ) ) + { + j = j["traceEvents"]; + } + + if( !j.is_array() ) + { + fprintf( stderr, "Input must be either an array of events or an object + containing an array of events under \"traceEvents\" key.\n" ); exit( 1 ); + } + + for( auto& v : j ) + { + const auto type = v["ph"].get(); + + std::string zoneText = ""; + if( v.contains( "args" ) ) + { + for( auto& kv : v["args"].items() ) + { + const auto val = kv.value(); + const std::string s = val.is_string() ? val.get() : + val.dump(); zoneText += kv.key() + ": " + s + "\n"; + } + } + + std::string locFile; + uint32_t locLine = 0; + if( v.contains( "loc" ) ) + { + auto loc = v["loc"].get(); + const auto lpos = loc.find_last_of( ':' ); + if( lpos == std::string::npos ) + { + std::swap( loc, locFile ); + } + else + { + locFile = loc.substr( 0, lpos ); + locLine = atoi( loc.c_str() + lpos + 1 ); + } + } + + if( type == "b" || type == "B" ) + { + timeline.emplace_back( tracy::Worker::ImportEventTimeline { + getPseudoTid(v), + uint64_t( v["ts"].get() * 1000. ), + v["name"].get(), + std::move(zoneText), + false, + std::move(locFile), + locLine + } ); + } + else if( type == "e" || type == "E" ) + { + timeline.emplace_back( tracy::Worker::ImportEventTimeline { + getPseudoTid(v), + uint64_t( v["ts"].get() * 1000. ), + "", + std::move(zoneText), + true + } ); + } + else if( type == "X" ) + { + const auto tid = getPseudoTid(v); + const auto ts0 = uint64_t( v["ts"].get() * 1000. ); + const auto ts1 = ts0 + uint64_t( v["dur"].get() * 1000. ); + const auto name = v["name"].get(); + timeline.emplace_back( tracy::Worker::ImportEventTimeline { tid, + ts0, name, std::move(zoneText), false, std::move(locFile), locLine } ); + timeline.emplace_back( tracy::Worker::ImportEventTimeline { tid, + ts1, "", "", true } ); + } + else if( type == "i" || type == "I" ) + { + messages.emplace_back( tracy::Worker::ImportEventMessages { + getPseudoTid(v), + uint64_t( v["ts"].get() * 1000. ), + v["name"].get() + } ); + } + else if( type == "C" ) + { + auto timestamp = int64_t( v["ts"].get() * 1000 ); + for( auto& kv : v["args"].items() ) + { + bool plotFound = false; + auto& metricName = kv.key(); + auto dataPoint = std::make_pair( timestamp, + kv.value().get() ); + + // The input file is assumed to have only very few metrics, + // so iterating through plots is not a problem. + for( auto& plot : plots ) + { + if( plot.name == metricName ) + { + plot.data.emplace_back( dataPoint ); + plotFound = true; + break; + } + } + if( !plotFound ) + { + auto formatting = tracy::PlotValueFormatting::Number; + + // NOTE: With C++20 one could say metricName.ends_with( + "_bytes" ) instead of rfind auto metricNameLen = metricName.size(); if ( + metricNameLen >= 6 && metricName.rfind( "_bytes" ) == metricNameLen - 6 ) { + formatting = tracy::PlotValueFormatting::Memory; + } + + plots.emplace_back( tracy::Worker::ImportEventPlots { + std::move( metricName ), + formatting, + { dataPoint } + } ); + } + } + } + else if (type == "M") + { + if (v.contains("name") && v["name"] == "thread_name" && + v.contains("args") && v["args"].is_object() && v["args"].contains("name")) + { + const auto tid = getPseudoTid(v); + threadNames[tid] = v["args"]["name"].get(); + } + } + } + */ + + std::stable_sort( + timeline.begin(), timeline.end(), + [](const auto &l, const auto &r) { return l.timestamp < r.timestamp; }); + std::stable_sort( + messages.begin(), messages.end(), + [](const auto &l, const auto &r) { return l.timestamp < r.timestamp; }); + for (auto &v : plots) + std::stable_sort( + v.data.begin(), v.data.end(), + [](const auto &l, const auto &r) { return l.first < r.first; }); + + uint64_t mts = 0; + if (!timeline.empty()) { + mts = timeline[0].timestamp; + } + if (!messages.empty()) { + if (mts > messages[0].timestamp) + mts = messages[0].timestamp; + } + for (auto &plot : plots) { + if (mts > plot.data[0].first) + mts = plot.data[0].first; + } + for (auto &v : timeline) + v.timestamp -= mts; + for (auto &v : messages) + v.timestamp -= mts; + for (auto &plot : plots) { + for (auto &v : plot.data) + v.first -= mts; + } + + printf("\33[2KProcessing...\r"); + fflush(stdout); + + auto &&getFilename = [](const char *in) { + auto out = in; + while (*out) + ++out; + --out; + while (out > in && (*out != '/' || *out != '\\')) + out--; + return out; + }; + + tracy::Worker worker(getFilename(output), getFilename(input), timeline, + messages, plots, std::move(dec.threadNames)); + + auto w = + std::unique_ptr(tracy::FileWrite::Open(output, clev)); + if (!w) { + fprintf(stderr, "Cannot open output file!\n"); + exit(1); + } + printf("\33[2KSaving...\r"); + fflush(stdout); + worker.Write(*w, false); + + printf("\33[2KCleanup...\n"); + fflush(stdout); + + return 0; +} From bf75b9fab008404810506911136a634dd2a8b5fa Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 27 Dec 2023 01:39:06 -0500 Subject: [PATCH 02/11] bugfix --- import-fuchsia/src/import-fuchsia.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/import-fuchsia/src/import-fuchsia.cpp b/import-fuchsia/src/import-fuchsia.cpp index f6762a0e..1e2712f3 100644 --- a/import-fuchsia/src/import-fuchsia.cpp +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -171,7 +171,7 @@ std::vector read_input(const char *input) { return buf; } -// read next span starting at `offset` +// read next record starting at `offset` Record read_next_record(std::vector const &input, size_t &offset) { uint64_t header = *((uint64_t *)&input[offset]); uint16_t len_word = (header >> 4) & 0xfff; @@ -197,7 +197,7 @@ uint64_t getPseudoTid(DecodeState &dec, ThreadRef th) { return pseudo_tid; } -// decode thread info +// decode thread info from a ref ThreadRef readThread(DecodeState &dec, Record const &r, size_t &offset, uint8_t ref) { ThreadRef th; @@ -230,8 +230,8 @@ void readString(DecodeState &dec, std::string &res, Record const &r, // Skip string reference (just modify offset) void skipString(size_t &offset, uint16_t ref) { - if (ref != 0 && (ref & 0x80) != 0) { - size_t size = ref & 0x7f; + if (ref != 0 && (ref & 0x8000) != 0) { + size_t size = ref & 0x7fff; offset += ROUND_TO_WORD(size) >> 3; } } From 5ecd3a5e839cd04c361ce01508475f40890374f3 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 27 Dec 2023 12:30:24 -0500 Subject: [PATCH 03/11] fix printing of arguments --- import-fuchsia/src/import-fuchsia.cpp | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/import-fuchsia/src/import-fuchsia.cpp b/import-fuchsia/src/import-fuchsia.cpp index 1e2712f3..9f3cf8f4 100644 --- a/import-fuchsia/src/import-fuchsia.cpp +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -85,20 +85,18 @@ struct DecodeState { // Append a string representation of `val` to `res` void appendArgumentValue(std::string &res, ArgumentValue &val) { + char buf[32]; buf[31]=0; if (std::holds_alternative(val)) { res += std::get(val); } else if (std::holds_alternative(val)) { - char buf[32]; snprintf(buf, 31, "%" PRIu64, std::get(val)); - res += buf; + res.append(buf); } else if (std::holds_alternative(val)) { - char buf[32]; snprintf(buf, 31, "%" PRId64, std::get(val)); - res += buf; + res.append(buf); } else if (std::holds_alternative(val)) { res += std::get(val) ? "true" : "false"; } else if (std::holds_alternative(val)) { - char buf[32]; snprintf(buf, 31, "%.5f", std::get(val)); res += buf; } @@ -284,14 +282,12 @@ void readArgument(std::vector &args, DecodeState &dec, } break; case 7: // pointer - case 8: + case 8: { // koid - { - uint64_t i = r.p[offset]; - offset += 1; - value = i; - } - break; + uint64_t i = r.p[offset]; + offset += 1; + value = i; + } break; case 9: { // bool bool b = (bool)((header >> 32) & 1); @@ -309,7 +305,6 @@ void readArgument(std::vector &args, DecodeState &dec, void readArguments(std::vector &args, DecodeState &dec, Record r, size_t &offset, const int n_args) { args.clear(); - for (int i = 0; i < n_args; ++i) readArgument(args, dec, r, offset); } @@ -317,7 +312,7 @@ void readArguments(std::vector &args, DecodeState &dec, Record r, // text made of arguments void printArgumentsToString(std::string &res, std::vector &args) { for (auto &kv : args) { - res += kv.name; + res += kv.name.data(); res += ": "; appendArgumentValue(res, kv.value); res += "\n"; From 7f40e6cda95af6cf9a4e5d91a93f3437bd4c888b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 27 Dec 2023 16:57:25 -0500 Subject: [PATCH 04/11] handle counter, begin/end duration events, and fix decoding of double arguments --- import-fuchsia/src/import-fuchsia.cpp | 109 +++++++++++++++++++------- 1 file changed, 80 insertions(+), 29 deletions(-) diff --git a/import-fuchsia/src/import-fuchsia.cpp b/import-fuchsia/src/import-fuchsia.cpp index 9f3cf8f4..5487dc7f 100644 --- a/import-fuchsia/src/import-fuchsia.cpp +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -85,7 +85,8 @@ struct DecodeState { // Append a string representation of `val` to `res` void appendArgumentValue(std::string &res, ArgumentValue &val) { - char buf[32]; buf[31]=0; + char buf[32]; + buf[31] = 0; if (std::holds_alternative(val)) { res += std::get(val); } else if (std::holds_alternative(val)) { @@ -270,7 +271,7 @@ void readArgument(std::vector &args, DecodeState &dec, value = i; } break; case 5: { - double i = r.p[offset]; + double i = *((double*) &r.p[offset]); offset += 1; value = i; } break; @@ -309,6 +310,24 @@ void readArguments(std::vector &args, DecodeState &dec, Record r, readArgument(args, dec, r, offset); } +bool argumentIsNumber(Argument const &arg) { + return std::holds_alternative(arg.value) || + std::holds_alternative(arg.value) || + std::holds_alternative(arg.value); +} + +double argumentToNumber(Argument const &arg) { + if (std::holds_alternative(arg.value)) { + return std::get(arg.value); + } else if (std::holds_alternative(arg.value)) { + return static_cast(std::get(arg.value)); + } else if (std::holds_alternative(arg.value)) { + return static_cast(std::get(arg.value)); + } else { + assert(false); + } +} + // text made of arguments void printArgumentsToString(std::string &res, std::vector &args) { for (auto &kv : args) { @@ -402,18 +421,65 @@ int main(int argc, char **argv) { break; } - case 4: { - // complete duration - const auto tid = getPseudoTid(dec, th); - const auto ts0 = timestamp; - const auto ts1 = r.p[offset]; // end timestamp + case 1: { + // counter + for (auto &kv : arguments) { + bool plotFound = false; + auto &metricName = kv.name; + + if (!argumentIsNumber(kv)) + continue; + + auto dataPoint = std::make_pair(timestamp, argumentToNumber(kv)); + + // The input file is assumed to have only very few metrics, + // so iterating through plots is not a problem. + for (auto &plot : plots) { + if (plot.name == metricName) { + plot.data.emplace_back(dataPoint); + plotFound = true; + break; + } + } + if (!plotFound) { + auto formatting = tracy::PlotValueFormatting::Number; + plots.emplace_back(tracy::Worker::ImportEventPlots{ + std::move(metricName), formatting, {dataPoint}}); + } + } + break; + } + + case 2: { + // duration begin std::string zoneText; printArgumentsToString(zoneText, arguments); timeline.emplace_back(tracy::Worker::ImportEventTimeline{ - tid, ts0, name, std::move(zoneText), false, std::move(locFile), - locLine}); + getPseudoTid(dec, th), timestamp, name, std::move(zoneText), false, + std::move(locFile), locLine}); + break; + } + + case 3: { + // duration end + std::string zoneText; + printArgumentsToString(zoneText, arguments); + timeline.emplace_back(tracy::Worker::ImportEventTimeline{ + getPseudoTid(dec, th), timestamp, "", std::move(zoneText), true}); + break; + } + + case 4: { + // complete duration + const auto ts_end = r.p[offset]; // end timestamp + const auto tid = getPseudoTid(dec, th); + std::string zoneText; + printArgumentsToString(zoneText, arguments); + timeline.emplace_back(tracy::Worker::ImportEventTimeline{ + tid, timestamp, name, std::move(zoneText), false, + std::move(locFile), locLine}); timeline.emplace_back( - tracy::Worker::ImportEventTimeline{tid, ts1, "", "", true}); + tracy::Worker::ImportEventTimeline{tid, ts_end, "", "", true}); break; } @@ -421,21 +487,6 @@ int main(int argc, char **argv) { } } - /* - if( type == "b" || type == "B" ) - { - timeline.emplace_back( tracy::Worker::ImportEventTimeline { - getPseudoTid(v), - uint64_t( v["ts"].get() * 1000. ), - v["name"].get(), - std::move(zoneText), - false, - std::move(locFile), - locLine - } ); - } - */ - break; } @@ -469,8 +520,8 @@ int main(int argc, char **argv) { for( auto& kv : v["args"].items() ) { const auto val = kv.value(); - const std::string s = val.is_string() ? val.get() : - val.dump(); zoneText += kv.key() + ": " + s + "\n"; + const std::string s = val.is_string() ? val.get() + : val.dump(); zoneText += kv.key() + ": " + s + "\n"; } } @@ -559,8 +610,8 @@ int main(int argc, char **argv) { // NOTE: With C++20 one could say metricName.ends_with( "_bytes" ) instead of rfind auto metricNameLen = metricName.size(); if ( - metricNameLen >= 6 && metricName.rfind( "_bytes" ) == metricNameLen - 6 ) { - formatting = tracy::PlotValueFormatting::Memory; + metricNameLen >= 6 && metricName.rfind( "_bytes" ) == metricNameLen - 6 ) + { formatting = tracy::PlotValueFormatting::Memory; } plots.emplace_back( tracy::Worker::ImportEventPlots { From 90b2c986eeb2abd99c154df420d7c1482f075516 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 27 Dec 2023 17:13:46 -0500 Subject: [PATCH 05/11] decode thread names --- import-fuchsia/src/import-fuchsia.cpp | 55 ++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/import-fuchsia/src/import-fuchsia.cpp b/import-fuchsia/src/import-fuchsia.cpp index 5487dc7f..9ad08d07 100644 --- a/import-fuchsia/src/import-fuchsia.cpp +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -271,7 +271,7 @@ void readArgument(std::vector &args, DecodeState &dec, value = i; } break; case 5: { - double i = *((double*) &r.p[offset]); + double i = *((double *)&r.p[offset]); offset += 1; value = i; } break; @@ -490,6 +490,59 @@ int main(int argc, char **argv) { break; } + case 7: { + // kernel object + + uint8_t ty = (r.header >> 16) & 0xff; + uint16_t name_ref = (r.header >> 24) & 0xffff; + uint8_t n_args = (r.header >> 40) & 0xf; + size_t offset = 1; + + uint64_t koid = r.p[offset]; + offset++; + + readString(dec, name, r, offset, name_ref); + + readArguments(arguments, dec, r, offset, n_args); + + switch (ty) { + case 1: { + // process + break; + } + + case 2: { + // thread + auto real_tid = koid; + + // we need the pid as well + uint64_t pid; + bool foundPid = false; + for (auto &kv : arguments) { + if (strcmp(kv.name.data(), "process") == 0 && + std::holds_alternative(kv.value)) { + // koid (argument type 8) are decoded as uint64 + pid = std::get(kv.value); + foundPid = true; + break; + } + } + + if (!foundPid) + continue; + + ThreadRef th{pid, real_tid}; + const auto tid = getPseudoTid(dec, th); + dec.threadNames[tid] = name; + + break; + } + + default: { + } + } + } + default: { } } From a275f1a2e096e494bf206416525e1e0ab9600ede Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 27 Dec 2023 17:14:17 -0500 Subject: [PATCH 06/11] remove dead code --- import-fuchsia/src/import-fuchsia.cpp | 136 -------------------------- 1 file changed, 136 deletions(-) diff --git a/import-fuchsia/src/import-fuchsia.cpp b/import-fuchsia/src/import-fuchsia.cpp index 9ad08d07..720e7113 100644 --- a/import-fuchsia/src/import-fuchsia.cpp +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -551,142 +551,6 @@ int main(int argc, char **argv) { printf("read %d records\n", n_records); fflush(stdout); - /* - if( j.is_object() && j.contains( "traceEvents" ) ) - { - j = j["traceEvents"]; - } - - if( !j.is_array() ) - { - fprintf( stderr, "Input must be either an array of events or an object - containing an array of events under \"traceEvents\" key.\n" ); exit( 1 ); - } - - for( auto& v : j ) - { - const auto type = v["ph"].get(); - - std::string zoneText = ""; - if( v.contains( "args" ) ) - { - for( auto& kv : v["args"].items() ) - { - const auto val = kv.value(); - const std::string s = val.is_string() ? val.get() - : val.dump(); zoneText += kv.key() + ": " + s + "\n"; - } - } - - std::string locFile; - uint32_t locLine = 0; - if( v.contains( "loc" ) ) - { - auto loc = v["loc"].get(); - const auto lpos = loc.find_last_of( ':' ); - if( lpos == std::string::npos ) - { - std::swap( loc, locFile ); - } - else - { - locFile = loc.substr( 0, lpos ); - locLine = atoi( loc.c_str() + lpos + 1 ); - } - } - - if( type == "b" || type == "B" ) - { - timeline.emplace_back( tracy::Worker::ImportEventTimeline { - getPseudoTid(v), - uint64_t( v["ts"].get() * 1000. ), - v["name"].get(), - std::move(zoneText), - false, - std::move(locFile), - locLine - } ); - } - else if( type == "e" || type == "E" ) - { - timeline.emplace_back( tracy::Worker::ImportEventTimeline { - getPseudoTid(v), - uint64_t( v["ts"].get() * 1000. ), - "", - std::move(zoneText), - true - } ); - } - else if( type == "X" ) - { - const auto tid = getPseudoTid(v); - const auto ts0 = uint64_t( v["ts"].get() * 1000. ); - const auto ts1 = ts0 + uint64_t( v["dur"].get() * 1000. ); - const auto name = v["name"].get(); - timeline.emplace_back( tracy::Worker::ImportEventTimeline { tid, - ts0, name, std::move(zoneText), false, std::move(locFile), locLine } ); - timeline.emplace_back( tracy::Worker::ImportEventTimeline { tid, - ts1, "", "", true } ); - } - else if( type == "i" || type == "I" ) - { - messages.emplace_back( tracy::Worker::ImportEventMessages { - getPseudoTid(v), - uint64_t( v["ts"].get() * 1000. ), - v["name"].get() - } ); - } - else if( type == "C" ) - { - auto timestamp = int64_t( v["ts"].get() * 1000 ); - for( auto& kv : v["args"].items() ) - { - bool plotFound = false; - auto& metricName = kv.key(); - auto dataPoint = std::make_pair( timestamp, - kv.value().get() ); - - // The input file is assumed to have only very few metrics, - // so iterating through plots is not a problem. - for( auto& plot : plots ) - { - if( plot.name == metricName ) - { - plot.data.emplace_back( dataPoint ); - plotFound = true; - break; - } - } - if( !plotFound ) - { - auto formatting = tracy::PlotValueFormatting::Number; - - // NOTE: With C++20 one could say metricName.ends_with( - "_bytes" ) instead of rfind auto metricNameLen = metricName.size(); if ( - metricNameLen >= 6 && metricName.rfind( "_bytes" ) == metricNameLen - 6 ) - { formatting = tracy::PlotValueFormatting::Memory; - } - - plots.emplace_back( tracy::Worker::ImportEventPlots { - std::move( metricName ), - formatting, - { dataPoint } - } ); - } - } - } - else if (type == "M") - { - if (v.contains("name") && v["name"] == "thread_name" && - v.contains("args") && v["args"].is_object() && v["args"].contains("name")) - { - const auto tid = getPseudoTid(v); - threadNames[tid] = v["args"]["name"].get(); - } - } - } - */ - std::stable_sort( timeline.begin(), timeline.end(), [](const auto &l, const auto &r) { return l.timestamp < r.timestamp; }); From c4b644ecf894f99cc5c795d9c2bee2050a656676 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 27 Dec 2023 23:31:17 -0500 Subject: [PATCH 07/11] fix --- import-fuchsia/src/import-fuchsia.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/import-fuchsia/src/import-fuchsia.cpp b/import-fuchsia/src/import-fuchsia.cpp index 720e7113..d73a17a2 100644 --- a/import-fuchsia/src/import-fuchsia.cpp +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -250,7 +250,7 @@ void readArgument(std::vector &args, DecodeState &dec, ArgumentValue value; switch (ty) { case 0: - value = (Unit){}; + value = Unit{}; break; case 1: { int32_t i = header >> 32; @@ -324,7 +324,7 @@ double argumentToNumber(Argument const &arg) { } else if (std::holds_alternative(arg.value)) { return static_cast(std::get(arg.value)); } else { - assert(false); + abort(); } } From 7769f23c764a1e966b8d5579f51f2ffd33d1af01 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2023 17:47:21 -0500 Subject: [PATCH 08/11] add CI for import-fuchsia --- .github/workflows/linux.yml | 2 ++ .github/workflows/macos.yml | 2 ++ .github/workflows/msvc.yml | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index a8a2bad5..c0e67a16 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -25,6 +25,8 @@ jobs: run: make -j`nproc` -C csvexport/build/unix debug release - name: Import-chrome utility run: make -j`nproc` -C import-chrome/build/unix debug release + - name: Import-fuchsia utility + run: make -j`nproc` -C import-fuchsia/build/unix debug release - name: Library run: make -j`nproc` -C library/unix debug release - name: Library (meson) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 54697835..fee70b66 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -24,6 +24,8 @@ jobs: run: make -j`nproc` -C csvexport/build/unix debug release - name: Import-chrome utility run: make -j`nproc` -C import-chrome/build/unix debug release + - name: Import-fuchsia utility + run: make -j`nproc` -C import-fuchsia/build/unix debug release - name: Library run: make -j`nproc` -C library/unix debug release - name: Library (meson) diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 122b754b..d69bf32b 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -36,6 +36,10 @@ jobs: run: msbuild .\import-chrome\build\win32\import-chrome.vcxproj /property:Configuration=Debug /property:Platform=x64 - name: Import-chrome utility Release run: msbuild .\import-chrome\build\win32\import-chrome.vcxproj /property:Configuration=Release /property:Platform=x64 + - name: Import-fuchsia utility Debug + run: msbuild .\import-fuchsia\build\win32\import-fuchsia.vcxproj /property:Configuration=Debug /property:Platform=x64 + - name: Import-fuchsia utility Release + run: msbuild .\import-fuchsia\build\win32\import-fuchsia.vcxproj /property:Configuration=Release /property:Platform=x64 - name: Library run: msbuild .\library\win32\TracyProfiler.vcxproj /property:Configuration=Release /property:Platform=x64 - name: Package binaries From c6efbf6cb71ceb028ac5792ed1e146ed7aefecbe Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2023 18:03:16 -0500 Subject: [PATCH 09/11] add some documentation for import-fuchsia (and import-chrome) to manual --- manual/tracy.tex | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/manual/tracy.tex b/manual/tracy.tex index 64a3be56..b1ec8746 100644 --- a/manual/tracy.tex +++ b/manual/tracy.tex @@ -3847,7 +3847,28 @@ You can customize the output with the following command line options: \section{Importing external profiling data} \label{importingdata} -Tracy can import data generated by other profilers. This external data cannot be directly loaded but must be converted first. Currently, there's only support for converting chrome:tracing data through the \texttt{import-chrome} utility. +Tracy can import data generated by other profilers. This external data cannot be directly loaded but must be converted first. +Currently, there's support for the following formats: +\begin{itemize} + \item chrome:tracing data through the \texttt{import-chrome} utility. The trace files + typically have a \texttt{.json} or \texttt{.json.zst} extension. + To use this tool to process a file named \texttt{mytracefile.json}, assuming it's compiled, run: + \begin{lstlisting}[language=sh] + $ import-chrome mytracefile.json mytracefile.tracy + $ tracy mytracefile.tracy + \end{lstlisting} + \item \href{https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format}{Fuchsia's tracing format} + data through the \texttt{import-fuchsia} utility. + This format has many commonalities with the chrome:tracing format, but it uses a + compact and efficient binary encoding that can help lower tracing overhead. + The file extension is \texttt{.fxt} or \texttt{.fxt.zst}. + + To this this tool, assuming it's compiled, run: + \begin{lstlisting}[language=sh] + $ import-fuchsia mytracefile.fxt mytracefile.tracy + $ tracy mytracefile.tracy + \end{lstlisting} +\end{itemize} \begin{bclogo}[ noborder=true, @@ -3855,6 +3876,7 @@ couleur=black!5, logo=\bclampe ]{Compressed traces} Tracy can import traces compressed with the Zstandard algorithm (for example, using the \texttt{zstd} command-line utility). Traces ending with \texttt{.zst} extension are assumed to be compressed. +This applies for both chrome and fuchsia traces. \end{bclogo} \begin{bclogo}[ @@ -3862,7 +3884,8 @@ noborder=true, couleur=black!5, logo=\bclampe ]{Source locations} -Chrome tracing format doesn't document a way to provide source location data. The \texttt{import-chrome} utility will however recognize a custom \texttt{loc} tag in the root of zone begin events. You should be formatting this data in the usual \texttt{filename:line} style, for example: \texttt{hello.c:42}. Providing the line number (including a colon) is optional but highly recommended. +Chrome tracing format doesn't document a way to provide source location data. + The \texttt{import-chrome} and \texttt{import-fuchsia} utilities will however recognize a custom \texttt{loc} tag in the root of zone begin events. You should be formatting this data in the usual \texttt{filename:line} style, for example: \texttt{hello.c:42}. Providing the line number (including a colon) is optional but highly recommended. \end{bclogo} \begin{bclogo}[ From b558f650044b972ca84316cda09eedafa0852f0f Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 28 Dec 2023 18:08:08 -0500 Subject: [PATCH 10/11] handle `loc` in import-fuchsia --- import-fuchsia/src/import-fuchsia.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/import-fuchsia/src/import-fuchsia.cpp b/import-fuchsia/src/import-fuchsia.cpp index d73a17a2..756fab92 100644 --- a/import-fuchsia/src/import-fuchsia.cpp +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -338,6 +338,25 @@ void printArgumentsToString(std::string &res, std::vector &args) { } } +// Read location for a given span +void readLoc(std::string &locFile, uint32_t &locLine, + std::vector const &args) { + for (auto &kv : args) { + if (strcmp(kv.name.data(), "loc") == 0 && + std::holds_alternative(kv.value)) { + auto loc = std::get(kv.value); + const auto lpos = loc.find_last_of(':'); + if (lpos == std::string::npos) { + std::swap(loc, locFile); + } else { + locFile = loc.substr(0, lpos); + locLine = atoi(loc.c_str() + lpos + 1); + } + break; + } + } +} + int main(int argc, char **argv) { #ifdef _WIN32 if (!AttachConsole(ATTACH_PARENT_PROCESS)) { @@ -412,6 +431,7 @@ int main(int argc, char **argv) { std::string locFile; uint32_t locLine = 0; + readLoc(locFile, locLine, arguments); switch (ev_ty) { case 0: { From 737759bc432c65c88c586b6344b46279165172ad Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 29 Dec 2023 09:33:07 -0500 Subject: [PATCH 11/11] manual: remove use of \href --- manual/tracy.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/tracy.tex b/manual/tracy.tex index b1ec8746..517852e1 100644 --- a/manual/tracy.tex +++ b/manual/tracy.tex @@ -3857,7 +3857,7 @@ Currently, there's support for the following formats: $ import-chrome mytracefile.json mytracefile.tracy $ tracy mytracefile.tracy \end{lstlisting} - \item \href{https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format}{Fuchsia's tracing format} + \item Fuchsia's tracing format\footnote{\url{https://fuchsia.dev/fuchsia-src/reference/tracing/trace-format}} data through the \texttt{import-fuchsia} utility. This format has many commonalities with the chrome:tracing format, but it uses a compact and efficient binary encoding that can help lower tracing overhead.