mudgangster

Tiny, scriptable MUD client
Log | Files | Refs | README

TracySysTrace.cpp (28016B)


      1 #include "TracySysTrace.hpp"
      2 
      3 #ifdef TRACY_HAS_SYSTEM_TRACING
      4 
      5 #  if defined _WIN32 || defined __CYGWIN__
      6 
      7 #    ifndef NOMINMAX
      8 #      define NOMINMAX
      9 #    endif
     10 
     11 #    define INITGUID
     12 #    include <assert.h>
     13 #    include <string.h>
     14 #    include <windows.h>
     15 #    include <dbghelp.h>
     16 #    include <evntrace.h>
     17 #    include <evntcons.h>
     18 #    include <psapi.h>
     19 #    include <winternl.h>
     20 
     21 #    include "../common/TracyAlloc.hpp"
     22 #    include "../common/TracySystem.hpp"
     23 #    include "TracyProfiler.hpp"
     24 
     25 namespace tracy
     26 {
     27 
     28 TRACEHANDLE s_traceHandle;
     29 TRACEHANDLE s_traceHandle2;
     30 EVENT_TRACE_PROPERTIES* s_prop;
     31 
     32 struct CSwitch
     33 {
     34     uint32_t    newThreadId;
     35     uint32_t    oldThreadId;
     36     int8_t      newThreadPriority;
     37     int8_t      oldThreadPriority;
     38     uint8_t     previousCState;
     39     int8_t      spareByte;
     40     int8_t      oldThreadWaitReason;
     41     int8_t      oldThreadWaitMode;
     42     int8_t      oldThreadState;
     43     int8_t      oldThreadWaitIdealProcessor;
     44     uint32_t    newThreadWaitTime;
     45     uint32_t    reserved;
     46 };
     47 
     48 struct ReadyThread
     49 {
     50     uint32_t    threadId;
     51     int8_t      adjustReason;
     52     int8_t      adjustIncrement;
     53     int8_t      flag;
     54     int8_t      reserverd;
     55 };
     56 
     57 void WINAPI EventRecordCallback( PEVENT_RECORD record )
     58 {
     59 #ifdef TRACY_ON_DEMAND
     60     if( !GetProfiler().IsConnected() ) return;
     61 #endif
     62 
     63     const auto& hdr = record->EventHeader;
     64     if( hdr.EventDescriptor.Opcode == 36 )
     65     {
     66         const auto cswitch = (const CSwitch*)record->UserData;
     67 
     68         Magic magic;
     69         auto token = GetToken();
     70         auto& tail = token->get_tail_index();
     71         auto item = token->enqueue_begin( magic );
     72         MemWrite( &item->hdr.type, QueueType::ContextSwitch );
     73         MemWrite( &item->contextSwitch.time, hdr.TimeStamp.QuadPart );
     74         memcpy( &item->contextSwitch.oldThread, &cswitch->oldThreadId, sizeof( cswitch->oldThreadId ) );
     75         memcpy( &item->contextSwitch.newThread, &cswitch->newThreadId, sizeof( cswitch->newThreadId ) );
     76         memset( ((char*)&item->contextSwitch.oldThread)+4, 0, 4 );
     77         memset( ((char*)&item->contextSwitch.newThread)+4, 0, 4 );
     78         MemWrite( &item->contextSwitch.cpu, record->BufferContext.ProcessorNumber );
     79         MemWrite( &item->contextSwitch.reason, cswitch->oldThreadWaitReason );
     80         MemWrite( &item->contextSwitch.state, cswitch->oldThreadState );
     81         tail.store( magic + 1, std::memory_order_release );
     82     }
     83     else if( hdr.EventDescriptor.Opcode == 50 )
     84     {
     85         const auto rt = (const ReadyThread*)record->UserData;
     86 
     87         Magic magic;
     88         auto token = GetToken();
     89         auto& tail = token->get_tail_index();
     90         auto item = token->enqueue_begin( magic );
     91         MemWrite( &item->hdr.type, QueueType::ThreadWakeup );
     92         MemWrite( &item->threadWakeup.time, hdr.TimeStamp.QuadPart );
     93         memcpy( &item->threadWakeup.thread, &rt->threadId, sizeof( rt->threadId ) );
     94         memset( ((char*)&item->threadWakeup.thread)+4, 0, 4 );
     95         tail.store( magic + 1, std::memory_order_release );
     96     }
     97 }
     98 
     99 bool SysTraceStart()
    100 {
    101     TOKEN_PRIVILEGES priv = {};
    102     priv.PrivilegeCount = 1;
    103     priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    104     if( LookupPrivilegeValue( nullptr, SE_SYSTEM_PROFILE_NAME, &priv.Privileges[0].Luid ) == 0 ) return false;
    105 
    106     HANDLE pt;
    107     if( OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &pt ) == 0 ) return false;
    108     const auto adjust = AdjustTokenPrivileges( pt, FALSE, &priv, 0, nullptr, nullptr );
    109     CloseHandle( pt );
    110     if( adjust == 0 ) return false;
    111     const auto status = GetLastError();
    112     if( status != ERROR_SUCCESS ) return false;
    113 
    114     const auto psz = sizeof( EVENT_TRACE_PROPERTIES ) + sizeof( KERNEL_LOGGER_NAME );
    115     s_prop = (EVENT_TRACE_PROPERTIES*)tracy_malloc( psz );
    116     memset( s_prop, 0, sizeof( EVENT_TRACE_PROPERTIES ) );
    117     s_prop->EnableFlags = EVENT_TRACE_FLAG_CSWITCH | EVENT_TRACE_FLAG_DISPATCHER;
    118     s_prop->LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
    119     s_prop->Wnode.BufferSize = psz;
    120     s_prop->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
    121     s_prop->Wnode.ClientContext = 3;
    122     s_prop->Wnode.Guid = SystemTraceControlGuid;
    123     s_prop->LoggerNameOffset = sizeof( EVENT_TRACE_PROPERTIES );
    124     memcpy( ((char*)s_prop) + sizeof( EVENT_TRACE_PROPERTIES ), KERNEL_LOGGER_NAME, sizeof( KERNEL_LOGGER_NAME ) );
    125 
    126     auto backup = tracy_malloc( psz );
    127     memcpy( backup, s_prop, psz );
    128 
    129     const auto controlStatus = ControlTrace( 0, KERNEL_LOGGER_NAME, s_prop, EVENT_TRACE_CONTROL_STOP );
    130     if( controlStatus != ERROR_SUCCESS && controlStatus != ERROR_WMI_INSTANCE_NOT_FOUND )
    131     {
    132         tracy_free( s_prop );
    133         return false;
    134     }
    135 
    136     memcpy( s_prop, backup, psz );
    137     tracy_free( backup );
    138 
    139     const auto startStatus = StartTrace( &s_traceHandle, KERNEL_LOGGER_NAME, s_prop );
    140     if( startStatus != ERROR_SUCCESS )
    141     {
    142         tracy_free( s_prop );
    143         return false;
    144     }
    145 
    146 #ifdef UNICODE
    147     WCHAR KernelLoggerName[sizeof( KERNEL_LOGGER_NAME )];
    148 #else
    149     char KernelLoggerName[sizeof( KERNEL_LOGGER_NAME )];
    150 #endif
    151     memcpy( KernelLoggerName, KERNEL_LOGGER_NAME, sizeof( KERNEL_LOGGER_NAME ) );
    152     EVENT_TRACE_LOGFILE log = {};
    153     log.LoggerName = KernelLoggerName;
    154     log.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
    155     log.EventRecordCallback = EventRecordCallback;
    156 
    157     s_traceHandle2 = OpenTrace( &log );
    158     if( s_traceHandle2 == (TRACEHANDLE)INVALID_HANDLE_VALUE )
    159     {
    160         CloseTrace( s_traceHandle );
    161         tracy_free( s_prop );
    162         return false;
    163     }
    164 
    165     return true;
    166 }
    167 
    168 void SysTraceStop()
    169 {
    170     CloseTrace( s_traceHandle2 );
    171     CloseTrace( s_traceHandle );
    172 }
    173 
    174 void SysTraceWorker( void* ptr )
    175 {
    176     SetThreadName( "Tracy SysTrace" );
    177     ProcessTrace( &s_traceHandle2, 1, 0, 0 );
    178     ControlTrace( 0, KERNEL_LOGGER_NAME, s_prop, EVENT_TRACE_CONTROL_STOP );
    179     tracy_free( s_prop );
    180 }
    181 
    182 #ifdef __CYGWIN__
    183 extern "C" typedef DWORD (WINAPI *t_GetProcessIdOfThread)( HANDLE );
    184 extern "C" typedef DWORD (WINAPI *t_GetProcessImageFileNameA)( HANDLE, LPSTR, DWORD );
    185 #  ifdef UNICODE
    186 t_GetProcessIdOfThread GetProcessIdOfThread = (t_GetProcessIdOfThread)GetProcAddress( GetModuleHandle( L"kernel32.dll" ), "GetProcessIdOfThread" );
    187 t_GetProcessImageFileNameA GetProcessImageFileNameA = (t_GetProcessImageFileNameA)GetProcAddress( GetModuleHandle( L"kernel32.dll" ), "K32GetProcessImageFileNameA" );
    188 #  else
    189 t_GetProcessIdOfThread GetProcessIdOfThread = (t_GetProcessIdOfThread)GetProcAddress( GetModuleHandle( "kernel32.dll" ), "GetProcessIdOfThread" );
    190 t_GetProcessImageFileNameA GetProcessImageFileNameA = (t_GetProcessImageFileNameA)GetProcAddress( GetModuleHandle( "kernel32.dll" ), "K32GetProcessImageFileNameA" );
    191 #  endif
    192 #endif
    193 
    194 extern "C" typedef NTSTATUS (WINAPI *t_NtQueryInformationThread)( HANDLE, THREADINFOCLASS, PVOID, ULONG, PULONG );
    195 extern "C" typedef BOOL (WINAPI *t_EnumProcessModules)( HANDLE, HMODULE*, DWORD, LPDWORD );
    196 extern "C" typedef BOOL (WINAPI *t_GetModuleInformation)( HANDLE, HMODULE, LPMODULEINFO, DWORD );
    197 extern "C" typedef DWORD (WINAPI *t_GetModuleBaseNameA)( HANDLE, HMODULE, LPSTR, DWORD );
    198 #ifdef UNICODE
    199 t_NtQueryInformationThread NtQueryInformationThread = (t_NtQueryInformationThread)GetProcAddress( GetModuleHandle( L"ntdll.dll" ), "NtQueryInformationThread" );
    200 t_EnumProcessModules _EnumProcessModules = (t_EnumProcessModules)GetProcAddress( GetModuleHandle( L"kernel32.dll" ), "K32EnumProcessModules" );
    201 t_GetModuleInformation _GetModuleInformation = (t_GetModuleInformation)GetProcAddress( GetModuleHandle( L"kernel32.dll" ), "K32GetModuleInformation" );
    202 t_GetModuleBaseNameA _GetModuleBaseNameA = (t_GetModuleBaseNameA)GetProcAddress( GetModuleHandle( L"kernel32.dll" ), "K32GetModuleBaseNameA" );
    203 #else
    204 t_NtQueryInformationThread NtQueryInformationThread = (t_NtQueryInformationThread)GetProcAddress( GetModuleHandle( "ntdll.dll" ), "NtQueryInformationThread" );
    205 t_EnumProcessModules _EnumProcessModules = (t_EnumProcessModules)GetProcAddress( GetModuleHandle( "kernel32.dll" ), "K32EnumProcessModules" );
    206 t_GetModuleInformation _GetModuleInformation = (t_GetModuleInformation)GetProcAddress( GetModuleHandle( "kernel32.dll" ), "K32GetModuleInformation" );
    207 t_GetModuleBaseNameA _GetModuleBaseNameA = (t_GetModuleBaseNameA)GetProcAddress( GetModuleHandle( "kernel32.dll" ), "K32GetModuleBaseNameA" );
    208 #endif
    209 
    210 
    211 void SysTraceSendExternalName( uint64_t thread )
    212 {
    213     bool threadSent = false;
    214     auto hnd = OpenThread( THREAD_QUERY_INFORMATION, FALSE, DWORD( thread ) );
    215     if( hnd == 0 )
    216     {
    217         hnd = OpenThread( THREAD_QUERY_LIMITED_INFORMATION, FALSE, DWORD( thread ) );
    218     }
    219     if( hnd != 0 )
    220     {
    221 #if defined NTDDI_WIN10_RS2 && NTDDI_VERSION >= NTDDI_WIN10_RS2
    222         PWSTR tmp;
    223         GetThreadDescription( hnd, &tmp );
    224         char buf[256];
    225         if( tmp )
    226         {
    227             auto ret = wcstombs( buf, tmp, 256 );
    228             if( ret != 0 )
    229             {
    230                 GetProfiler().SendString( thread, buf, QueueType::ExternalThreadName );
    231                 threadSent = true;
    232             }
    233         }
    234 #endif
    235         const auto pid = GetProcessIdOfThread( hnd );
    236         if( !threadSent && NtQueryInformationThread && _EnumProcessModules && _GetModuleInformation && _GetModuleBaseNameA )
    237         {
    238             void* ptr;
    239             ULONG retlen;
    240             auto status = NtQueryInformationThread( hnd, (THREADINFOCLASS)9 /*ThreadQuerySetWin32StartAddress*/, &ptr, sizeof( &ptr ), &retlen );
    241             if( status == 0 )
    242             {
    243                 const auto phnd = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );
    244                 if( phnd != INVALID_HANDLE_VALUE )
    245                 {
    246                     HMODULE modules[1024];
    247                     DWORD needed;
    248                     if( _EnumProcessModules( phnd, modules, 1024 * sizeof( HMODULE ), &needed ) != 0 )
    249                     {
    250                         const auto sz = std::min( DWORD( needed / sizeof( HMODULE ) ), DWORD( 1024 ) );
    251                         for( DWORD i=0; i<sz; i++ )
    252                         {
    253                             MODULEINFO info;
    254                             if( _GetModuleInformation( phnd, modules[i], &info, sizeof( info ) ) != 0 )
    255                             {
    256                                 if( (uint64_t)ptr >= (uint64_t)info.lpBaseOfDll && (uint64_t)ptr <= (uint64_t)info.lpBaseOfDll + (uint64_t)info.SizeOfImage )
    257                                 {
    258                                     char buf[1024];
    259                                     if( _GetModuleBaseNameA( phnd, modules[i], buf, 1024 ) != 0 )
    260                                     {
    261                                         GetProfiler().SendString( thread, buf, QueueType::ExternalThreadName );
    262                                         threadSent = true;
    263                                     }
    264                                 }
    265                             }
    266                         }
    267                     }
    268                     CloseHandle( phnd );
    269                 }
    270             }
    271         }
    272         CloseHandle( hnd );
    273         if( !threadSent )
    274         {
    275             GetProfiler().SendString( thread, "???", QueueType::ExternalThreadName );
    276             threadSent = true;
    277         }
    278         if( pid != 0 )
    279         {
    280             {
    281                 uint64_t _pid = pid;
    282                 Magic magic;
    283                 auto token = GetToken();
    284                 auto& tail = token->get_tail_index();
    285                 auto item = token->enqueue_begin( magic );
    286                 MemWrite( &item->hdr.type, QueueType::TidToPid );
    287                 MemWrite( &item->tidToPid.tid, thread );
    288                 MemWrite( &item->tidToPid.pid, _pid );
    289                 tail.store( magic + 1, std::memory_order_release );
    290             }
    291             if( pid == 4 )
    292             {
    293                 GetProfiler().SendString( thread, "System", QueueType::ExternalName );
    294                 return;
    295             }
    296             else
    297             {
    298                 const auto phnd = OpenProcess( PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid );
    299                 if( phnd != INVALID_HANDLE_VALUE )
    300                 {
    301                     char buf[1024];
    302                     const auto sz = GetProcessImageFileNameA( phnd, buf, 1024 );
    303                     CloseHandle( phnd );
    304                     if( sz != 0 )
    305                     {
    306                         auto ptr = buf + sz - 1;
    307                         while( ptr > buf && *ptr != '\\' ) ptr--;
    308                         if( *ptr == '\\' ) ptr++;
    309                         GetProfiler().SendString( thread, ptr, QueueType::ExternalName );
    310                         return;
    311                     }
    312                 }
    313             }
    314         }
    315     }
    316 
    317     if( !threadSent )
    318     {
    319         GetProfiler().SendString( thread, "???", QueueType::ExternalThreadName );
    320     }
    321     GetProfiler().SendString( thread, "???", QueueType::ExternalName );
    322 }
    323 
    324 }
    325 
    326 #  elif defined __linux__
    327 
    328 #    include <sys/types.h>
    329 #    include <sys/stat.h>
    330 #    include <sys/wait.h>
    331 #    include <fcntl.h>
    332 #    include <inttypes.h>
    333 #    include <limits>
    334 #    include <poll.h>
    335 #    include <stdio.h>
    336 #    include <stdlib.h>
    337 #    include <string.h>
    338 #    include <unistd.h>
    339 
    340 #    include "TracyProfiler.hpp"
    341 
    342 #    ifdef __ANDROID__
    343 #      include "TracySysTracePayload.hpp"
    344 #    endif
    345 
    346 namespace tracy
    347 {
    348 
    349 static const char BasePath[] = "/sys/kernel/debug/tracing/";
    350 static const char TracingOn[] = "tracing_on";
    351 static const char CurrentTracer[] = "current_tracer";
    352 static const char TraceOptions[] = "trace_options";
    353 static const char TraceClock[] = "trace_clock";
    354 static const char SchedSwitch[] = "events/sched/sched_switch/enable";
    355 static const char SchedWakeup[] = "events/sched/sched_wakeup/enable";
    356 static const char BufferSizeKb[] = "buffer_size_kb";
    357 static const char TracePipe[] = "trace_pipe";
    358 
    359 #ifdef __ANDROID__
    360 static bool TraceWrite( const char* path, size_t psz, const char* val, size_t vsz )
    361 {
    362     char tmp[256];
    363     sprintf( tmp, "su -c 'echo \"%s\" > %s%s'", val, BasePath, path );
    364     return system( tmp ) == 0;
    365 }
    366 #else
    367 static bool TraceWrite( const char* path, size_t psz, const char* val, size_t vsz )
    368 {
    369     char tmp[256];
    370     memcpy( tmp, BasePath, sizeof( BasePath ) - 1 );
    371     memcpy( tmp + sizeof( BasePath ) - 1, path, psz );
    372 
    373     int fd = open( tmp, O_WRONLY );
    374     if( fd < 0 ) return false;
    375 
    376     for(;;)
    377     {
    378         ssize_t cnt = write( fd, val, vsz );
    379         if( cnt == (ssize_t)vsz )
    380         {
    381             close( fd );
    382             return true;
    383         }
    384         if( cnt < 0 )
    385         {
    386             close( fd );
    387             return false;
    388         }
    389         vsz -= cnt;
    390         val += cnt;
    391     }
    392 }
    393 #endif
    394 
    395 #ifdef __ANDROID__
    396 void SysTraceInjectPayload()
    397 {
    398     int pipefd[2];
    399     if( pipe( pipefd ) == 0 )
    400     {
    401         const auto pid = fork();
    402         if( pid == 0 )
    403         {
    404             // child
    405             close( pipefd[1] );
    406             if( dup2( pipefd[0], STDIN_FILENO ) >= 0 )
    407             {
    408                 close( pipefd[0] );
    409                 execlp( "su", "su", "-c", "cat > /data/tracy_systrace", (char*)nullptr );
    410                 exit( 1 );
    411             }
    412         }
    413         else if( pid > 0 )
    414         {
    415             // parent
    416             close( pipefd[0] );
    417 
    418 #ifdef __aarch64__
    419             write( pipefd[1], tracy_systrace_aarch64_data, tracy_systrace_aarch64_size );
    420 #else
    421             write( pipefd[1], tracy_systrace_armv7_data, tracy_systrace_armv7_size );
    422 #endif
    423             close( pipefd[1] );
    424             waitpid( pid, nullptr, 0 );
    425 
    426             system( "su -c 'chmod 700 /data/tracy_systrace'" );
    427         }
    428     }
    429 }
    430 #endif
    431 
    432 bool SysTraceStart()
    433 {
    434     if( !TraceWrite( TracingOn, sizeof( TracingOn ), "0", 2 ) ) return false;
    435     if( !TraceWrite( CurrentTracer, sizeof( CurrentTracer ), "nop", 4 ) ) return false;
    436     TraceWrite( TraceOptions, sizeof( TraceOptions ), "norecord-cmd", 13 );
    437     TraceWrite( TraceOptions, sizeof( TraceOptions ), "norecord-tgid", 14 );
    438     TraceWrite( TraceOptions, sizeof( TraceOptions ), "noirq-info", 11 );
    439 #if defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
    440     if( !TraceWrite( TraceClock, sizeof( TraceClock ), "x86-tsc", 8 ) ) return false;
    441 #elif __ARM_ARCH >= 6
    442     if( !TraceWrite( TraceClock, sizeof( TraceClock ), "mono_raw", 9 ) ) return false;
    443 #endif
    444     if( !TraceWrite( SchedSwitch, sizeof( SchedSwitch ), "1", 2 ) ) return false;
    445     if( !TraceWrite( SchedWakeup, sizeof( SchedWakeup ), "1", 2 ) ) return false;
    446     if( !TraceWrite( BufferSizeKb, sizeof( BufferSizeKb ), "512", 4 ) ) return false;
    447 
    448 #if defined __ANDROID__ && ( defined __aarch64__ || defined __ARM_ARCH )
    449     SysTraceInjectPayload();
    450 #endif
    451 
    452     if( !TraceWrite( TracingOn, sizeof( TracingOn ), "1", 2 ) ) return false;
    453 
    454     return true;
    455 }
    456 
    457 void SysTraceStop()
    458 {
    459     TraceWrite( TracingOn, sizeof( TracingOn ), "0", 2 );
    460 }
    461 
    462 static uint64_t ReadNumber( const char*& ptr )
    463 {
    464     uint64_t val = 0;
    465     for(;;)
    466     {
    467         if( *ptr >= '0' && *ptr <= '9' )
    468         {
    469             val = val * 10 + ( *ptr - '0' );
    470             ptr++;
    471         }
    472         else
    473         {
    474             return val;
    475         }
    476     }
    477 }
    478 
    479 static uint8_t ReadState( char state )
    480 {
    481     switch( state )
    482     {
    483     case 'D': return 101;
    484     case 'I': return 102;
    485     case 'R': return 103;
    486     case 'S': return 104;
    487     case 'T': return 105;
    488     case 't': return 106;
    489     case 'W': return 107;
    490     case 'X': return 108;
    491     case 'Z': return 109;
    492     default: return 100;
    493     }
    494 }
    495 
    496 #if defined __ANDROID__ && defined __ANDROID_API__ && __ANDROID_API__ < 18
    497 /*-
    498  * Copyright (c) 2011 The NetBSD Foundation, Inc.
    499  * All rights reserved.
    500  *
    501  * This code is derived from software contributed to The NetBSD Foundation
    502  * by Christos Zoulas.
    503  *
    504  * Redistribution and use in source and binary forms, with or without
    505  * modification, are permitted provided that the following conditions
    506  * are met:
    507  * 1. Redistributions of source code must retain the above copyright
    508  *    notice, this list of conditions and the following disclaimer.
    509  * 2. Redistributions in binary form must reproduce the above copyright
    510  *    notice, this list of conditions and the following disclaimer in the
    511  *    documentation and/or other materials provided with the distribution.
    512  *
    513  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
    514  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    515  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    516  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
    517  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    518  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    519  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    520  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    521  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    522  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    523  * POSSIBILITY OF SUCH DAMAGE.
    524  */
    525 
    526 ssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)
    527 {
    528 	char *ptr, *eptr;
    529 
    530 	if (*buf == NULL || *bufsiz == 0) {
    531 		*bufsiz = BUFSIZ;
    532 		if ((*buf = (char*)malloc(*bufsiz)) == NULL)
    533 			return -1;
    534 	}
    535 
    536 	for (ptr = *buf, eptr = *buf + *bufsiz;;) {
    537 		int c = fgetc(fp);
    538 		if (c == -1) {
    539 			if (feof(fp))
    540 				return ptr == *buf ? -1 : ptr - *buf;
    541 			else
    542 				return -1;
    543 		}
    544 		*ptr++ = c;
    545 		if (c == delimiter) {
    546 			*ptr = '\0';
    547 			return ptr - *buf;
    548 		}
    549 		if (ptr + 2 >= eptr) {
    550 			char *nbuf;
    551 			size_t nbufsiz = *bufsiz * 2;
    552 			ssize_t d = ptr - *buf;
    553 			if ((nbuf = (char*)realloc(*buf, nbufsiz)) == NULL)
    554 				return -1;
    555 			*buf = nbuf;
    556 			*bufsiz = nbufsiz;
    557 			eptr = nbuf + nbufsiz;
    558 			ptr = nbuf + d;
    559 		}
    560 	}
    561 }
    562 
    563 ssize_t getline(char **buf, size_t *bufsiz, FILE *fp)
    564 {
    565 	return getdelim(buf, bufsiz, '\n', fp);
    566 }
    567 #endif
    568 
    569 static void HandleTraceLine( const char* line )
    570 {
    571     line += 24;
    572     const auto cpu = (uint8_t)ReadNumber( line );
    573 
    574     line++;      // ']'
    575     while( *line == ' ' ) line++;
    576 
    577 #if defined TRACY_HW_TIMER && ( defined __i386 || defined _M_IX86 || defined __x86_64__ || defined _M_X64 )
    578     const auto time = ReadNumber( line );
    579 #elif __ARM_ARCH >= 6
    580     const auto ts = ReadNumber( line );
    581     line++;      // '.'
    582     const auto tus = ReadNumber( line );
    583     const auto time = ts * 1000000000ll + tus * 1000ll;
    584 #endif
    585 
    586     line += 2;   // ': '
    587     if( memcmp( line, "sched_switch", 12 ) == 0 )
    588     {
    589         line += 14;
    590 
    591         while( memcmp( line, "prev_pid", 8 ) != 0 ) line++;
    592         line += 9;
    593 
    594         const auto oldPid = ReadNumber( line );
    595         line++;
    596 
    597         while( memcmp( line, "prev_state", 10 ) != 0 ) line++;
    598         line += 11;
    599 
    600         const auto oldState = (uint8_t)ReadState( *line );
    601         line += 5;
    602 
    603         while( memcmp( line, "next_pid", 8 ) != 0 ) line++;
    604         line += 9;
    605 
    606         const auto newPid = ReadNumber( line );
    607 
    608         uint8_t reason = 100;
    609 
    610         Magic magic;
    611         auto token = GetToken();
    612         auto& tail = token->get_tail_index();
    613         auto item = token->enqueue_begin( magic );
    614         MemWrite( &item->hdr.type, QueueType::ContextSwitch );
    615         MemWrite( &item->contextSwitch.time, time );
    616         MemWrite( &item->contextSwitch.oldThread, oldPid );
    617         MemWrite( &item->contextSwitch.newThread, newPid );
    618         MemWrite( &item->contextSwitch.cpu, cpu );
    619         MemWrite( &item->contextSwitch.reason, reason );
    620         MemWrite( &item->contextSwitch.state, oldState );
    621         tail.store( magic + 1, std::memory_order_release );
    622     }
    623     else if( memcmp( line, "sched_wakeup", 12 ) == 0 )
    624     {
    625         line += 14;
    626 
    627         while( memcmp( line, "pid", 3 ) != 0 ) line++;
    628         line += 4;
    629 
    630         const auto pid = ReadNumber( line );
    631 
    632         Magic magic;
    633         auto token = GetToken();
    634         auto& tail = token->get_tail_index();
    635         auto item = token->enqueue_begin( magic );
    636         MemWrite( &item->hdr.type, QueueType::ThreadWakeup );
    637         MemWrite( &item->threadWakeup.time, time );
    638         MemWrite( &item->threadWakeup.thread, pid );
    639         tail.store( magic + 1, std::memory_order_release );
    640     }
    641 }
    642 
    643 #ifdef __ANDROID__
    644 static void ProcessTraceLines( int fd )
    645 {
    646     // Linux pipe buffer is 64KB, additional 1KB is for unfinished lines
    647     char* buf = (char*)tracy_malloc( (64+1)*1024 );
    648     char* line = buf;
    649 
    650     for(;;)
    651     {
    652         const auto rd = read( fd, line, 64*1024 );
    653         if( rd <= 0 ) break;
    654 
    655 #ifdef TRACY_ON_DEMAND
    656         if( !GetProfiler().IsConnected() )
    657         {
    658             if( rd < 64*1024 )
    659             {
    660                 assert( line[rd-1] == '\n' );
    661                 line = buf;
    662                 std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
    663             }
    664             else
    665             {
    666                 const auto end = line + rd;
    667                 line = end - 1;
    668                 while( line > buf && *line != '\n' ) line--;
    669                 if( line > buf )
    670                 {
    671                     line++;
    672                     const auto lsz = end - line;
    673                     memmove( buf, line, lsz );
    674                     line = buf + lsz;
    675                 }
    676             }
    677             continue;
    678         }
    679 #endif
    680 
    681         const auto end = line + rd;
    682         line = buf;
    683         for(;;)
    684         {
    685             auto next = line;
    686             while( next < end && *next != '\n' ) next++;
    687             next++;
    688             if( next >= end )
    689             {
    690                 const auto lsz = end - line;
    691                 memmove( buf, line, lsz );
    692                 line = buf + lsz;
    693                 break;
    694             }
    695 
    696             HandleTraceLine( line );
    697             line = next;
    698         }
    699         if( rd < 64*1024 )
    700         {
    701             std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
    702         }
    703     }
    704 
    705     tracy_free( buf );
    706 }
    707 
    708 void SysTraceWorker( void* ptr )
    709 {
    710     SetThreadName( "Tracy SysTrace" );
    711     int pipefd[2];
    712     if( pipe( pipefd ) == 0 )
    713     {
    714         const auto pid = fork();
    715         if( pid == 0 )
    716         {
    717             // child
    718             close( pipefd[0] );
    719             dup2( pipefd[1], STDERR_FILENO );
    720             if( dup2( pipefd[1], STDOUT_FILENO ) >= 0 )
    721             {
    722                 close( pipefd[1] );
    723 #if defined __ANDROID__ && ( defined __aarch64__ || defined __ARM_ARCH )
    724                 execlp( "su", "su", "-c", "/data/tracy_systrace", (char*)nullptr );
    725 #endif
    726                 execlp( "su", "su", "-c", "cat /sys/kernel/debug/tracing/trace_pipe", (char*)nullptr );
    727                 exit( 1 );
    728             }
    729         }
    730         else if( pid > 0 )
    731         {
    732             // parent
    733             close( pipefd[1] );
    734             ProcessTraceLines( pipefd[0] );
    735             close( pipefd[0] );
    736         }
    737     }
    738 }
    739 #else
    740 static void ProcessTraceLines( int fd )
    741 {
    742     char* buf = (char*)tracy_malloc( 64*1024 );
    743 
    744     struct pollfd pfd;
    745     pfd.fd = fd;
    746     pfd.events = POLLIN | POLLERR;
    747 
    748     for(;;)
    749     {
    750         while( poll( &pfd, 1, 0 ) <= 0 ) std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) );
    751 
    752         const auto rd = read( fd, buf, 64*1024 );
    753         if( rd <= 0 ) break;
    754 
    755 #ifdef TRACY_ON_DEMAND
    756         if( !GetProfiler().IsConnected() ) continue;
    757 #endif
    758 
    759         auto line = buf;
    760         const auto end = buf + rd;
    761         for(;;)
    762         {
    763             auto next = line;
    764             while( next < end && *next != '\n' ) next++;
    765             if( next == end ) break;
    766             assert( *next == '\n' );
    767             next++;
    768 
    769             HandleTraceLine( line );
    770             line = next;
    771         }
    772     }
    773 
    774     tracy_free( buf );
    775 }
    776 
    777 void SysTraceWorker( void* ptr )
    778 {
    779     SetThreadName( "Tracy SysTrace" );
    780     char tmp[256];
    781     memcpy( tmp, BasePath, sizeof( BasePath ) - 1 );
    782     memcpy( tmp + sizeof( BasePath ) - 1, TracePipe, sizeof( TracePipe ) );
    783 
    784     int fd = open( tmp, O_RDONLY );
    785     if( fd < 0 ) return;
    786     ProcessTraceLines( fd );
    787     close( fd );
    788 }
    789 #endif
    790 
    791 void SysTraceSendExternalName( uint64_t thread )
    792 {
    793     FILE* f;
    794     char fn[256];
    795     sprintf( fn, "/proc/%" PRIu64 "/comm", thread );
    796     f = fopen( fn, "rb" );
    797     if( f )
    798     {
    799         char buf[256];
    800         const auto sz = fread( buf, 1, 256, f );
    801         if( sz > 0 && buf[sz-1] == '\n' ) buf[sz-1] = '\0';
    802         GetProfiler().SendString( thread, buf, QueueType::ExternalThreadName );
    803         fclose( f );
    804     }
    805     else
    806     {
    807         GetProfiler().SendString( thread, "???", QueueType::ExternalThreadName );
    808     }
    809 
    810     sprintf( fn, "/proc/%" PRIu64 "/status", thread );
    811     f = fopen( fn, "rb" );
    812     if( f )
    813     {
    814         int pid = -1;
    815         size_t lsz = 1024;
    816         auto line = (char*)malloc( lsz );
    817         for(;;)
    818         {
    819             auto rd = getline( &line, &lsz, f );
    820             if( rd <= 0 ) break;
    821             if( memcmp( "Tgid:\t", line, 6 ) == 0 )
    822             {
    823                 pid = atoi( line + 6 );
    824                 break;
    825             }
    826         }
    827         free( line );
    828         fclose( f );
    829         if( pid >= 0 )
    830         {
    831             {
    832                 uint64_t _pid = pid;
    833                 Magic magic;
    834                 auto token = GetToken();
    835                 auto& tail = token->get_tail_index();
    836                 auto item = token->enqueue_begin( magic );
    837                 MemWrite( &item->hdr.type, QueueType::TidToPid );
    838                 MemWrite( &item->tidToPid.tid, thread );
    839                 MemWrite( &item->tidToPid.pid, _pid );
    840                 tail.store( magic + 1, std::memory_order_release );
    841             }
    842             sprintf( fn, "/proc/%i/comm", pid );
    843             f = fopen( fn, "rb" );
    844             if( f )
    845             {
    846                 char buf[256];
    847                 const auto sz = fread( buf, 1, 256, f );
    848                 if( sz > 0 && buf[sz-1] == '\n' ) buf[sz-1] = '\0';
    849                 GetProfiler().SendString( thread, buf, QueueType::ExternalName );
    850                 fclose( f );
    851                 return;
    852             }
    853         }
    854     }
    855     GetProfiler().SendString( thread, "???", QueueType::ExternalName );
    856 }
    857 
    858 }
    859 
    860 #  endif
    861 
    862 #endif