2018-03-10 00:29:27 +00:00
# ifdef _WIN32
# include <windows.h>
2020-09-20 18:11:19 +00:00
# else
# include <unistd.h>
2018-03-10 00:29:27 +00:00
# endif
2022-04-12 17:33:47 +00:00
# include <atomic>
2018-03-10 00:29:27 +00:00
# include <chrono>
2018-03-10 01:25:29 +00:00
# include <inttypes.h>
2018-03-10 00:29:27 +00:00
# include <mutex>
2019-01-24 18:13:09 +00:00
# include <signal.h>
2018-03-10 01:25:29 +00:00
# include <stdint.h>
2018-03-10 00:29:27 +00:00
# include <stdio.h>
# include <stdlib.h>
2020-09-20 18:04:43 +00:00
# include <sys/stat.h>
2018-03-10 00:29:27 +00:00
2018-09-09 17:28:53 +00:00
# include "../../common/TracyProtocol.hpp"
2021-05-23 21:51:56 +00:00
# include "../../common/TracyStackFrames.hpp"
2018-03-10 00:29:27 +00:00
# include "../../server/TracyFileWrite.hpp"
# include "../../server/TracyMemory.hpp"
2019-06-18 18:43:28 +00:00
# include "../../server/TracyPrint.hpp"
2018-03-10 00:29:27 +00:00
# include "../../server/TracyWorker.hpp"
2021-01-26 23:32:38 +00:00
# ifdef _WIN32
# include ".. / .. / getopt / getopt.h"
# endif
2018-03-10 00:29:27 +00:00
2020-04-03 00:00:07 +00:00
2022-04-12 17:33:47 +00:00
// This atomic is written by a signal handler (SigInt). Traditionally that would
// have had to be `volatile sig_atomic_t`, and annoyingly, `bool` was
// technically not allowed there, even though in practice it would work.
// The good thing with C++11 atomics is that we can use atomic<bool> instead
// here and be on the actually supported path.
std : : atomic < bool > disconnect ;
2019-01-24 18:13:09 +00:00
void SigInt ( int )
{
2022-04-12 17:33:47 +00:00
// Relaxed order is closest to a traditional `volatile` write.
// We don't need stronger ordering since this signal handler doesn't do
// anything else that would need to be ordered relatively to this.
disconnect . store ( true , std : : memory_order_relaxed ) ;
2019-01-24 18:13:09 +00:00
}
2018-03-10 01:25:29 +00:00
2020-09-20 18:08:39 +00:00
[[noreturn]] void Usage ( )
2018-03-10 00:29:27 +00:00
{
2021-08-26 05:22:58 +00:00
printf ( " Usage: capture -o output.tracy [-a address] [-p port] [-f] [-s seconds] \n " ) ;
2018-03-10 00:29:27 +00:00
exit ( 1 ) ;
}
int main ( int argc , char * * argv )
{
# ifdef _WIN32
if ( ! AttachConsole ( ATTACH_PARENT_PROCESS ) )
{
AllocConsole ( ) ;
SetConsoleMode ( GetStdHandle ( STD_OUTPUT_HANDLE ) , 0x07 ) ;
}
# endif
2020-09-20 18:04:43 +00:00
bool overwrite = false ;
2020-09-23 14:38:20 +00:00
const char * address = " 127.0.0.1 " ;
2018-03-10 00:29:27 +00:00
const char * output = nullptr ;
2019-09-21 13:43:01 +00:00
int port = 8086 ;
2021-08-26 05:22:58 +00:00
int seconds = - 1 ;
2018-03-10 00:29:27 +00:00
int c ;
2021-08-26 05:22:58 +00:00
while ( ( c = getopt ( argc , argv , " a:o:p:fs: " ) ) ! = - 1 )
2018-03-10 00:29:27 +00:00
{
switch ( c )
{
case ' a ' :
address = optarg ;
break ;
case ' o ' :
output = optarg ;
break ;
2019-09-21 13:43:01 +00:00
case ' p ' :
port = atoi ( optarg ) ;
break ;
2020-09-20 18:04:43 +00:00
case ' f ' :
overwrite = true ;
break ;
2021-08-26 05:22:58 +00:00
case ' s ' :
seconds = atoi ( optarg ) ;
break ;
2018-03-10 00:29:27 +00:00
default :
Usage ( ) ;
break ;
}
}
if ( ! address | | ! output ) Usage ( ) ;
2020-09-20 18:04:43 +00:00
struct stat st ;
if ( stat ( output , & st ) = = 0 & & ! overwrite )
{
printf ( " Output file %s already exists! Use -f to force overwrite. \n " , output ) ;
return 4 ;
}
2020-09-20 18:11:19 +00:00
FILE * test = fopen ( output , " wb " ) ;
if ( ! test )
{
printf ( " Cannot open output file %s for writing! \n " , output ) ;
return 5 ;
}
fclose ( test ) ;
unlink ( output ) ;
2019-09-21 13:43:01 +00:00
printf ( " Connecting to %s:%i... " , address , port ) ;
2018-03-10 00:29:27 +00:00
fflush ( stdout ) ;
2019-09-21 13:43:01 +00:00
tracy : : Worker worker ( address , port ) ;
2019-02-12 10:13:53 +00:00
while ( ! worker . IsConnected ( ) )
2018-09-09 17:28:53 +00:00
{
const auto handshake = worker . GetHandshakeStatus ( ) ;
if ( handshake = = tracy : : HandshakeProtocolMismatch )
{
printf ( " \n The client you are trying to connect to uses incompatible protocol version. \n Make sure you are using the same Tracy version on both client and server. \n " ) ;
return 1 ;
}
2018-09-09 17:42:06 +00:00
if ( handshake = = tracy : : HandshakeNotAvailable )
{
printf ( " \n The client you are trying to connect to is no longer able to sent profiling data, \n because another server was already connected to it. \n You can do the following: \n \n 1. Restart the client application. \n 2. Rebuild the client application with on-demand mode enabled. \n " ) ;
return 2 ;
}
2019-02-12 10:13:53 +00:00
if ( handshake = = tracy : : HandshakeDropped )
{
printf ( " \n The client you are trying to connect to has disconnected during the initial \n connection handshake. Please check your network configuration. \n " ) ;
return 3 ;
}
2018-09-09 17:28:53 +00:00
}
2018-03-10 00:29:27 +00:00
while ( ! worker . HasData ( ) ) std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 100 ) ) ;
2019-06-18 18:43:28 +00:00
printf ( " \n Queue delay: %s \n Timer resolution: %s \n " , tracy : : TimeToString ( worker . GetDelay ( ) ) , tracy : : TimeToString ( worker . GetResolution ( ) ) ) ;
2018-03-10 00:29:27 +00:00
2020-04-03 00:00:07 +00:00
# ifdef _WIN32
signal ( SIGINT , SigInt ) ;
# else
struct sigaction sigint , oldsigint ;
2019-01-24 18:13:09 +00:00
memset ( & sigint , 0 , sizeof ( sigint ) ) ;
sigint . sa_handler = SigInt ;
sigaction ( SIGINT , & sigint , & oldsigint ) ;
2019-01-24 19:04:08 +00:00
# endif
2019-01-24 18:13:09 +00:00
2018-03-10 00:29:27 +00:00
auto & lock = worker . GetMbpsDataLock ( ) ;
2019-11-07 00:51:45 +00:00
const auto t0 = std : : chrono : : high_resolution_clock : : now ( ) ;
2018-03-10 00:29:27 +00:00
while ( worker . IsConnected ( ) )
{
2022-04-12 17:33:47 +00:00
// Relaxed order is sufficient here because `disconnect` is only ever
// set by this thread or by the SigInt handler, and that handler does
// nothing else than storing `disconnect`.
if ( disconnect . load ( std : : memory_order_relaxed ) )
2019-01-24 18:13:09 +00:00
{
worker . Disconnect ( ) ;
2022-04-12 17:33:47 +00:00
// Relaxed order is sufficient because only this thread ever reads
// this value.
disconnect . store ( false , std : : memory_order_relaxed ) ;
2021-08-26 05:22:58 +00:00
break ;
2019-01-24 18:13:09 +00:00
}
2018-03-10 00:29:27 +00:00
lock . lock ( ) ;
const auto mbps = worker . GetMbpsData ( ) . back ( ) ;
const auto compRatio = worker . GetCompRatio ( ) ;
2019-10-26 14:14:18 +00:00
const auto netTotal = worker . GetDataTransferred ( ) ;
2018-03-10 00:29:27 +00:00
lock . unlock ( ) ;
if ( mbps < 0.1f )
{
printf ( " \33 [2K \r \033 [36;1m%7.2f Kbps " , mbps * 1000.f ) ;
}
else
{
printf ( " \33 [2K \r \033 [36;1m%7.2f Mbps " , mbps ) ;
}
2021-12-30 15:21:25 +00:00
printf ( " \033 [0m / \033 [36;1m%5.1f%% \033 [0m= \033 [33;1m%7.2f Mbps \033 [0m| \033 [33mTx: \033 [32m%s \033 [0m| \033 [31;1m%s \033 [0m | \033 [33m%s \033 [0m " ,
2019-10-26 14:14:18 +00:00
compRatio * 100.f ,
mbps / compRatio ,
tracy : : MemSizeToString ( netTotal ) ,
2019-11-10 18:20:57 +00:00
tracy : : MemSizeToString ( tracy : : memUsage ) ,
2019-10-26 14:14:18 +00:00
tracy : : TimeToString ( worker . GetLastTime ( ) ) ) ;
2018-03-10 00:29:27 +00:00
fflush ( stdout ) ;
std : : this_thread : : sleep_for ( std : : chrono : : milliseconds ( 100 ) ) ;
2021-08-26 05:22:58 +00:00
if ( seconds ! = - 1 )
{
const auto dur = std : : chrono : : high_resolution_clock : : now ( ) - t0 ;
if ( std : : chrono : : duration_cast < std : : chrono : : seconds > ( dur ) . count ( ) > = seconds )
{
2022-04-12 17:33:47 +00:00
// Relaxed order is sufficient because only this thread ever reads
// this value.
disconnect . store ( true , std : : memory_order_relaxed ) ;
2021-08-26 05:22:58 +00:00
}
}
2018-03-10 00:29:27 +00:00
}
2019-11-07 00:51:45 +00:00
const auto t1 = std : : chrono : : high_resolution_clock : : now ( ) ;
2018-03-10 00:29:27 +00:00
2019-01-14 22:52:38 +00:00
const auto & failure = worker . GetFailureType ( ) ;
if ( failure ! = tracy : : Worker : : Failure : : None )
{
2019-01-15 17:42:15 +00:00
printf ( " \n \033 [31;1mInstrumentation failure: %s \033 [0m " , tracy : : Worker : : GetFailureString ( failure ) ) ;
2020-10-06 12:50:55 +00:00
auto & fd = worker . GetFailureData ( ) ;
2021-10-10 12:52:30 +00:00
if ( ! fd . message . empty ( ) )
{
printf ( " \n Context: %s " , fd . message . c_str ( ) ) ;
}
2020-10-06 12:50:55 +00:00
if ( fd . callstack ! = 0 )
{
printf ( " \n \033 [1mFailure callstack: \033 [0m \n " ) ;
auto & cs = worker . GetCallstack ( fd . callstack ) ;
int fidx = 0 ;
int bidx = 0 ;
for ( auto & entry : cs )
{
auto frameData = worker . GetCallstackFrame ( entry ) ;
if ( ! frameData )
{
printf ( " %3i. %p \n " , fidx + + , ( void * ) worker . GetCanonicalPointer ( entry ) ) ;
}
else
{
const auto fsz = frameData - > size ;
for ( uint8_t f = 0 ; f < fsz ; f + + )
{
const auto & frame = frameData - > data [ f ] ;
auto txt = worker . GetString ( frame . name ) ;
if ( fidx = = 0 & & f ! = fsz - 1 )
{
auto test = tracy : : s_tracyStackFrames ;
bool match = false ;
do
{
if ( strcmp ( txt , * test ) = = 0 )
{
match = true ;
break ;
}
}
while ( * + + test ) ;
if ( match ) continue ;
}
bidx + + ;
if ( f = = fsz - 1 )
{
printf ( " %3i. " , fidx + + ) ;
}
else
{
printf ( " \033 [30;1minl. " ) ;
}
printf ( " \033 [0;36m%s " , txt ) ;
txt = worker . GetString ( frame . file ) ;
if ( frame . line = = 0 )
{
printf ( " \033 [33m(%s) " , txt ) ;
}
else
{
printf ( " \033 [33m(%s:% " PRIu32 " ) " , txt , frame . line ) ;
}
if ( frameData - > imageName . Active ( ) )
{
printf ( " \033 [35m %s \033 [0m \n " , worker . GetString ( frameData - > imageName ) ) ;
}
else
{
printf ( " \033 [0m \n " ) ;
}
}
}
}
}
2019-01-14 22:52:38 +00:00
}
2019-11-07 00:51:45 +00:00
printf ( " \n Frames: % " PRIu64 " \n Time span: %s \n Zones: %s \n Elapsed time: %s \n Saving trace... " ,
2020-01-31 00:43:24 +00:00
worker . GetFrameCount ( * worker . GetFramesBase ( ) ) , tracy : : TimeToString ( worker . GetLastTime ( ) ) , tracy : : RealToString ( worker . GetZoneCount ( ) ) ,
2019-11-07 00:51:45 +00:00
tracy : : TimeToString ( std : : chrono : : duration_cast < std : : chrono : : nanoseconds > ( t1 - t0 ) . count ( ) ) ) ;
2018-03-10 00:29:27 +00:00
fflush ( stdout ) ;
2018-03-28 23:11:54 +00:00
auto f = std : : unique_ptr < tracy : : FileWrite > ( tracy : : FileWrite : : Open ( output ) ) ;
2018-03-10 00:29:27 +00:00
if ( f )
{
2021-05-15 13:50:20 +00:00
worker . Write ( * f , false ) ;
2018-03-10 00:29:27 +00:00
printf ( " \033 [32;1mdone! \033 [0m \n " ) ;
2020-02-08 12:10:41 +00:00
f - > Finish ( ) ;
const auto stats = f - > GetCompressionStatistics ( ) ;
printf ( " Trace size %s (%.2f%% ratio) \n " , tracy : : MemSizeToString ( stats . second ) , 100.f * stats . second / stats . first ) ;
2018-03-10 00:29:27 +00:00
}
else
{
printf ( " \033 [31;1failed! \033 [0m \n " ) ;
}
return 0 ;
}