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 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..756fab92 --- /dev/null +++ b/import-fuchsia/src/import-fuchsia.cpp @@ -0,0 +1,636 @@ +#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) { + char buf[32]; + buf[31] = 0; + if (std::holds_alternative(val)) { + res += std::get(val); + } else if (std::holds_alternative(val)) { + snprintf(buf, 31, "%" PRIu64, std::get(val)); + res.append(buf); + } else if (std::holds_alternative(val)) { + snprintf(buf, 31, "%" PRId64, std::get(val)); + res.append(buf); + } else if (std::holds_alternative(val)) { + res += std::get(val) ? "true" : "false"; + } else if (std::holds_alternative(val)) { + 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 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; + 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 from a ref +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 & 0x8000) != 0) { + size_t size = ref & 0x7fff; + 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 = *((double *)&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); +} + +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 { + abort(); + } +} + +// text made of arguments +void printArgumentsToString(std::string &res, std::vector &args) { + for (auto &kv : args) { + res += kv.name.data(); + res += ": "; + appendArgumentValue(res, kv.value); + res += "\n"; + } +} + +// 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)) { + 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; + readLoc(locFile, locLine, arguments); + + switch (ev_ty) { + case 0: { + // instant + messages.emplace_back(tracy::Worker::ImportEventMessages{ + getPseudoTid(dec, th), timestamp, name}); + break; + } + + 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{ + 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, ts_end, "", "", true}); + break; + } + + default: { + } + } + + 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: { + } + } + } + + printf("read %d records\n", n_records); + fflush(stdout); + + 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; +} diff --git a/manual/tracy.tex b/manual/tracy.tex index 64a3be56..517852e1 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 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. + 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}[