1
0
mirror of https://github.com/wolfpld/tracy.git synced 2025-03-20 07:40:02 +08:00
This commit is contained in:
Grégoire Roussel 2024-08-09 01:28:10 +02:00
parent 09ac6b0946
commit 2e684723e8

View File

@ -17,12 +17,14 @@
using namespace std::chrono_literals; using namespace std::chrono_literals;
bool orderImportTimelineEvents(tracy::Worker::ImportEventTimeline const& a, tracy::Worker::ImportEventTimeline const& b) bool orderImportTimelineEvents( tracy::Worker::ImportEventTimeline const& a,
tracy::Worker::ImportEventTimeline const& b )
{ {
return a.timestamp < b.timestamp; return a.timestamp < b.timestamp;
} }
bool orderImportMessageEvents(tracy::Worker::ImportEventMessages const& a, tracy::Worker::ImportEventMessages const& b) bool orderImportMessageEvents( tracy::Worker::ImportEventMessages const& a,
tracy::Worker::ImportEventMessages const& b )
{ {
return a.timestamp < b.timestamp; return a.timestamp < b.timestamp;
} }
@ -37,108 +39,101 @@ struct ExportedWorker
std::string name; std::string name;
std::string process; std::string process;
static ExportedWorker merge(std::vector<ExportedWorker const*> inputList) static ExportedWorker merge( std::vector<ExportedWorker const*> inputList )
{ {
uint64_t nextFreeThreadId = 1; uint64_t nextFreeThreadId = 1;
ExportedWorker out; ExportedWorker out;
// for some data, arbitrarily take the infos from the first trace // for some data, arbitrarily take the infos from the first trace
auto const& firstExport = *inputList[0]; auto const& firstExport = *inputList[0];
out.name = firstExport.name; out.name = firstExport.name;
out.process = firstExport.process; out.process = firstExport.process;
// quick pass to allocate output vectors // quick pass to allocate output vectors
size_t numTimelineEvents = 0, numMessages = 0, numPlots = 0; size_t numTimelineEvents = 0, numMessages = 0, numPlots = 0;
for (auto const& inputWorker : inputList) for( auto const& inputWorker : inputList )
{ {
numTimelineEvents += inputWorker->timeline.size(); numTimelineEvents += inputWorker->timeline.size();
numMessages += inputWorker->messages.size(); numMessages += inputWorker->messages.size();
numPlots += inputWorker->plots.size(); numPlots += inputWorker->plots.size();
} }
out.timeline.reserve(numTimelineEvents); out.timeline.reserve( numTimelineEvents );
out.messages.reserve(numMessages); out.messages.reserve( numMessages );
out.plots.reserve(numPlots); out.plots.reserve( numPlots );
size_t eventsSortedSoFar = 0; size_t eventsSortedSoFar = 0;
size_t messagesSortedSoFar = 0; size_t messagesSortedSoFar = 0;
// keep track of registered threads to avoid overlaps // keep track of registered threads to avoid overlaps
std::unordered_map<uint64_t, uint64_t> localThreadToMultiprocess; std::unordered_map<uint64_t, uint64_t> localThreadToMultiprocess;
for (auto exportPtr : inputList) for( auto exportPtr : inputList )
{ {
ExportedWorker const& exported = *exportPtr; ExportedWorker const& exported = *exportPtr;
// rebuild thread mapping // rebuild thread mapping
// we try to keep original thread IDs intact if possible, falling back to made-up IDs // we try to keep original thread IDs intact if possible, falling back to made-up IDs
// in case of conflict // in case of conflict
for (auto const& threadId : exported.threadNames) for( auto const& threadId : exported.threadNames )
{ {
uint64_t multiprocessId = threadId.first; uint64_t multiprocessId = threadId.first;
if (localThreadToMultiprocess.contains(multiprocessId)) if( localThreadToMultiprocess.contains( multiprocessId ) )
{ {
// oh-oh, conflict - let's take a random ID instead; // oh-oh, conflict - let's take a random ID instead;
multiprocessId = nextFreeThreadId++; multiprocessId = nextFreeThreadId++;
} }
localThreadToMultiprocess[threadId.first] = multiprocessId; localThreadToMultiprocess[threadId.first] = multiprocessId;
out.threadNames[multiprocessId] = threadId.second; out.threadNames[multiprocessId] = threadId.second;
} }
// translate all events with the right thread IDs // translate all events with the right thread IDs
for (auto&& event : exported.timeline) for( auto&& event : exported.timeline )
{ {
tracy::Worker::ImportEventTimeline& inserted = out.timeline.emplace_back(event); tracy::Worker::ImportEventTimeline& inserted = out.timeline.emplace_back( event );
inserted.tid = localThreadToMultiprocess[inserted.tid]; inserted.tid = localThreadToMultiprocess[inserted.tid];
} }
for (auto&& message : exported.messages) for( auto&& message : exported.messages )
{ {
tracy::Worker::ImportEventMessages& inserted = out.messages.emplace_back(message); tracy::Worker::ImportEventMessages& inserted = out.messages.emplace_back( message );
inserted.tid = localThreadToMultiprocess[inserted.tid]; inserted.tid = localThreadToMultiprocess[inserted.tid];
} }
for (auto&& plots : exported.plots) for( auto&& plots : exported.plots )
{ {
out.plots.emplace_back(plots); out.plots.emplace_back( plots );
} }
// sort timeline and messages events // sort timeline and messages events
std::inplace_merge(out.timeline.begin(), std::inplace_merge( out.timeline.begin(), out.timeline.begin() + eventsSortedSoFar, out.timeline.end(),
out.timeline.begin() + eventsSortedSoFar, orderImportTimelineEvents );
out.timeline.end(),
orderImportTimelineEvents);
eventsSortedSoFar += exported.timeline.size(); eventsSortedSoFar += exported.timeline.size();
std::inplace_merge(out.messages.begin(), std::inplace_merge( out.messages.begin(), out.messages.begin() + messagesSortedSoFar, out.messages.end(),
out.messages.begin() + messagesSortedSoFar, orderImportMessageEvents );
out.messages.end(),
orderImportMessageEvents);
messagesSortedSoFar += exported.messages.size(); messagesSortedSoFar += exported.messages.size();
} }
return out; return out;
} }
static std::optional<ExportedWorker> fromTracyFile( std::string const& filepath, bool exportPlots )
static std::optional<ExportedWorker> fromTracyFile(std::string const& filepath, bool exportPlots)
{ {
std::unique_ptr<tracy::FileRead> sourceFile{tracy::FileRead::Open((filepath.c_str()))}; std::unique_ptr<tracy::FileRead> sourceFile{ tracy::FileRead::Open( ( filepath.c_str() ) ) };
if (!sourceFile) if( !sourceFile )
{ {
std::cerr << "Could not find file" << std::endl; std::cerr << "Could not find file" << std::endl;
return std::nullopt; return std::nullopt;
} }
std::cout << "reading " << filepath << std::endl; std::cout << "reading " << filepath << std::endl;
tracy::Worker worker{*sourceFile, tracy::Worker worker{ *sourceFile, tracy::EventType::All,
tracy::EventType::All, true, // otherwise source zones are empty
true, // otherwise source zones are empty false };
false}; while( !worker.AreSourceLocationZonesReady() )
while (!worker.AreSourceLocationZonesReady())
{ {
std::cout << "Waiting for source locations" << std::endl; std::cout << "Waiting for source locations" << std::endl;
std::this_thread::sleep_for(1s); std::this_thread::sleep_for( 1s );
} }
ExportedWorker exportedData; ExportedWorker exportedData;
exportedData.name = worker.GetCaptureName(); exportedData.name = worker.GetCaptureName();
exportedData.process = worker.GetCaptureProgram(); exportedData.process = worker.GetCaptureProgram();
std::cout << exportedData.name << " (" << exportedData.process << ")" << std::endl; std::cout << exportedData.name << " (" << exportedData.process << ")" << std::endl;
@ -146,82 +141,82 @@ struct ExportedWorker
auto& sourceLocationZones = worker.GetSourceLocationZones(); auto& sourceLocationZones = worker.GetSourceLocationZones();
std::cout << "- " << sourceLocationZones.size() << " events" << std::endl; std::cout << "- " << sourceLocationZones.size() << " events" << std::endl;
for (auto&& zone_it : sourceLocationZones) for( auto&& zone_it : sourceLocationZones )
{ {
const tracy::SourceLocation& sourceLoc = worker.GetSourceLocation(zone_it.first); const tracy::SourceLocation& sourceLoc = worker.GetSourceLocation( zone_it.first );
std::string zoneFilePath = worker.GetString(sourceLoc.file); std::string zoneFilePath = worker.GetString( sourceLoc.file );
int zoneLine = sourceLoc.line; int zoneLine = sourceLoc.line;
std::string zoneName = worker.GetZoneName(sourceLoc); std::string zoneName = worker.GetZoneName( sourceLoc );
auto const& zones = zone_it.second; auto const& zones = zone_it.second;
for (auto&& zoneData : zones.zones) for( auto&& zoneData : zones.zones )
{ {
const auto zone_event = zoneData.Zone(); const auto zone_event = zoneData.Zone();
const uint64_t threadFullId = worker.DecompressThread(zoneData.Thread()); const uint64_t threadFullId = worker.DecompressThread( zoneData.Thread() );
const auto start = zone_event->Start(); const auto start = zone_event->Start();
const auto end = zone_event->End(); const auto end = zone_event->End();
seenThreadIds.emplace(threadFullId); seenThreadIds.emplace( threadFullId );
auto& startEvent = exportedData.timeline.emplace_back(); auto& startEvent = exportedData.timeline.emplace_back();
startEvent.locFile = zoneFilePath; startEvent.locFile = zoneFilePath;
startEvent.locLine = zoneLine; startEvent.locLine = zoneLine;
startEvent.name = zoneName; startEvent.name = zoneName;
startEvent.tid = threadFullId; startEvent.tid = threadFullId;
startEvent.isEnd = false; startEvent.isEnd = false;
startEvent.timestamp = zone_event->Start(); startEvent.timestamp = zone_event->Start();
auto& endEvent = exportedData.timeline.emplace_back(); auto& endEvent = exportedData.timeline.emplace_back();
endEvent.locFile = zoneFilePath; endEvent.locFile = zoneFilePath;
endEvent.locLine = zoneLine; endEvent.locLine = zoneLine;
endEvent.name = zoneName; endEvent.name = zoneName;
endEvent.tid = threadFullId; endEvent.tid = threadFullId;
endEvent.isEnd = true; endEvent.isEnd = true;
endEvent.timestamp = zone_event->End(); endEvent.timestamp = zone_event->End();
} }
} }
// need to sort because we split 'begin' and 'end' events // need to sort because we split 'begin' and 'end' events
std::sort(exportedData.timeline.begin(), exportedData.timeline.end(), orderImportTimelineEvents); std::sort( exportedData.timeline.begin(), exportedData.timeline.end(), orderImportTimelineEvents );
auto const& messages = worker.GetMessages(); auto const& messages = worker.GetMessages();
std::cout << "- " << messages.size() << " messages" << std::endl; std::cout << "- " << messages.size() << " messages" << std::endl;
for (auto const& messages_it : worker.GetMessages()) for( auto const& messages_it : worker.GetMessages() )
{ {
tracy::MessageData const& messageData = *messages_it; tracy::MessageData const& messageData = *messages_it;
tracy::Worker::ImportEventMessages importMessage; tracy::Worker::ImportEventMessages importMessage;
uint64_t const threadId = worker.DecompressThread(messageData.thread); uint64_t const threadId = worker.DecompressThread( messageData.thread );
importMessage.tid = threadId; importMessage.tid = threadId;
importMessage.message = worker.GetString(messageData.ref); importMessage.message = worker.GetString( messageData.ref );
importMessage.timestamp = messageData.time; importMessage.timestamp = messageData.time;
exportedData.messages.push_back(importMessage); exportedData.messages.push_back( importMessage );
seenThreadIds.emplace(threadId); seenThreadIds.emplace( threadId );
} }
// to be sure, but should not do a lot // to be sure, but should not do a lot
std::sort(exportedData.messages.begin(), exportedData.messages.end(), orderImportMessageEvents); std::sort( exportedData.messages.begin(), exportedData.messages.end(), orderImportMessageEvents );
if (exportPlots) if( exportPlots )
{ {
auto const& plots = worker.GetPlots(); auto const& plots = worker.GetPlots();
std::cout << "- " << plots.size() << " plots" << std::endl; std::cout << "- " << plots.size() << " plots" << std::endl;
for (auto const& plots_it : worker.GetPlots()) for( auto const& plots_it : worker.GetPlots() )
{ {
tracy::Worker::ImportEventPlots importPlot; tracy::Worker::ImportEventPlots importPlot;
importPlot.name = worker.GetString(plots_it->name); importPlot.name = worker.GetString( plots_it->name );
importPlot.format = plots_it->format; importPlot.format = plots_it->format;
importPlot.data.resize(plots_it->data.size()); importPlot.data.resize( plots_it->data.size() );
for (auto const& elt : plots_it->data) for( auto const& elt : plots_it->data )
{ {
std::pair<int64_t, double> dataPoint{elt.time.Val(), elt.val}; std::pair<int64_t, double> dataPoint{ elt.time.Val(), elt.val };
importPlot.data.push_back(dataPoint); importPlot.data.push_back( dataPoint );
} }
exportedData.plots.push_back(importPlot); exportedData.plots.push_back( importPlot );
} }
} }
for (auto&& tid : seenThreadIds) for( auto&& tid : seenThreadIds )
{ {
std::string name = worker.GetThreadName(tid); std::string name = worker.GetThreadName( tid );
exportedData.threadNames[tid] = exportedData.process + "/" + name; exportedData.threadNames[tid] = exportedData.process + "/" + name;
} }
@ -229,24 +224,23 @@ struct ExportedWorker
} }
}; };
[[noreturn]] void Usage() [[noreturn]] void Usage()
{ {
printf("Usage: merge [-fp] -o output.tracy input1.tracy [input2.tracy]...\n\n"); printf( "Usage: merge [-fp] -o output.tracy input1.tracy [input2.tracy]...\n\n" );
printf("Options\n"); printf( "Options\n" );
printf(" --output/-o <filepath> Output file path\n"); printf( " --output/-o <filepath> Output file path\n" );
printf(" --force/-f Overwrite output file if it exists\n"); printf( " --force/-f Overwrite output file if it exists\n" );
printf(" --export-plots/-p (experimental) Also exports plots\n"); printf( " --export-plots/-p (experimental) Also exports plots\n" );
exit(1); exit( 1 );
} }
struct Args struct Args
{ {
std::vector<std::string> inputPaths; std::vector<std::string> inputPaths;
std::string outputPath; std::string outputPath;
bool exportPlots=false; bool exportPlots = false;
static Args parse(int argc, char* argv[]) static Args parse( int argc, char* argv[] )
{ {
Args args; Args args;
// option parsing // option parsing
@ -276,68 +270,64 @@ struct Args
break; break;
} }
} }
for (int argIndex = optind; argIndex < argc; argIndex++) for( int argIndex = optind; argIndex < argc; argIndex++ )
{ {
args.inputPaths.push_back(argv[argIndex]); args.inputPaths.push_back( argv[argIndex] );
} }
if (args.inputPaths.size() == 0 or args.outputPath.empty()) if( args.inputPaths.size() == 0 or args.outputPath.empty() )
{ {
Usage(); Usage();
} }
if (std::filesystem::exists(args.outputPath) and not overwrite) if( std::filesystem::exists( args.outputPath ) and not overwrite )
{ {
printf("Output file %s already exists! Use -f to force overwrite.\n", args.outputPath.c_str()); printf( "Output file %s already exists! Use -f to force overwrite.\n", args.outputPath.c_str() );
exit(4); exit( 4 );
} }
for (auto const& input : args.inputPaths) for( auto const& input : args.inputPaths )
{ {
if (not std::filesystem::exists(input)) if( not std::filesystem::exists( input ) )
{ {
printf("Input file %s does not exist!\n", input.c_str()); printf( "Input file %s does not exist!\n", input.c_str() );
exit(4); exit( 4 );
} }
} }
return args; return args;
} }
}; };
int main(int argc, char* argv[]) int main( int argc, char* argv[] )
{ {
auto args = Args::parse(argc, argv); auto args = Args::parse( argc, argv );
std::vector<ExportedWorker> exports; std::vector<ExportedWorker> exports;
for (auto path : args.inputPaths) for( auto path : args.inputPaths )
{ {
auto importedOpt = ExportedWorker::fromTracyFile(path, args.exportPlots); auto importedOpt = ExportedWorker::fromTracyFile( path, args.exportPlots );
if (not importedOpt.has_value()) if( not importedOpt.has_value() )
{ {
std::cerr << "Error importing " << path << std::endl; std::cerr << "Error importing " << path << std::endl;
return 1; return 1;
} }
exports.push_back((importedOpt.value())); exports.push_back( ( importedOpt.value() ) );
} }
std::vector<ExportedWorker const*> exportRefs; std::vector<ExportedWorker const*> exportRefs;
std::transform( std::transform( exports.cbegin(), exports.cend(), std::back_inserter( exportRefs ),
exports.cbegin(), exports.cend(), std::back_inserter(exportRefs), [](ExportedWorker const& ex) { return &ex; }); []( ExportedWorker const& ex ) { return &ex; } );
auto mergedImport = ExportedWorker::merge(exportRefs); auto mergedImport = ExportedWorker::merge( exportRefs );
{ {
std::cout << "Writing " << args.outputPath << std::endl; std::cout << "Writing " << args.outputPath << std::endl;
auto outputFileWrite = std::unique_ptr<tracy::FileWrite>(tracy::FileWrite::Open(args.outputPath.c_str())); auto outputFileWrite = std::unique_ptr<tracy::FileWrite>( tracy::FileWrite::Open( args.outputPath.c_str() ) );
if (!outputFileWrite) if( !outputFileWrite )
{ {
fprintf(stderr, "Cannot open output file!\n"); fprintf( stderr, "Cannot open output file!\n" );
exit(1); exit( 1 );
} }
tracy::Worker outputWorker(mergedImport.name.c_str(), tracy::Worker outputWorker( mergedImport.name.c_str(), mergedImport.process.c_str(), mergedImport.timeline,
mergedImport.process.c_str(), mergedImport.messages, mergedImport.plots, mergedImport.threadNames );
mergedImport.timeline, outputWorker.Write( *outputFileWrite, false );
mergedImport.messages,
mergedImport.plots,
mergedImport.threadNames);
outputWorker.Write(*outputFileWrite, false);
outputFileWrite->Finish(); outputFileWrite->Finish();
} }