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