mirror of
https://github.com/wolfpld/tracy.git
synced 2025-03-20 07:40:02 +08:00
format
This commit is contained in:
parent
09ac6b0946
commit
2e684723e8
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user