Intereting Posts
Как мы можем переписать URL-адрес, используя htacess? МАУ не может получать сопоставимые точки для работы между выбором текста и documentrange в Internet Explorer git – двойная загрузка ubuntu и windows с отдельным разделом данных Как реализовать панель заголовков Google-chrome для приложения Java SWT Ubuntu 14.04, Ruby on Rails и GEOS Как получить результат команды Win32 :: Process в Perl? ограничить использование процессора Java-приложением на определенную сумму Запись процесса Unix в дескриптор файла заканчивается, пока дети все еще живы (perl) Как перенаправить субдомен на другой локальный ip: порт с IIS? Создание онлайн-системы classификации PHP в Linux: exec Behavior, Process ID и grep Как определить имя ОС в Perl для определенной версии? Установка Sails js на Ubuntu VPS 16.04? htaccess: удалить cgit.cgi из пути Может ли мой тест Nest проверить наличие локального устройства, чтобы определить, дома я или нет? Неуправляемое потребление памяти приложения VC ++ на сервере Windows

Win32 – Backtrace из кода C

В настоящее время я ищу способ получить обратную информацию в Windows, начиная с кода C (без C ++).

Я создаю кросс-платформенную библиотеку C с управлением памятью подсчета ссылок. Он также имеет встроенный отладчик памяти, который предоставляет информацию о ошибках памяти ( XEOS C Foundation Library ).

При возникновении сбоя запускается отладчик, приводящий сведения о сбое и записи в память.

введите описание изображения здесь

В Linux или Mac OS X я могу искать execinfo.h , чтобы использовать функцию backtrace , поэтому я могу отображать дополнительную информацию об ошибке памяти.

Я ищу то же самое в Windows.

Я видел, как можно захватить трассировку стека в C? на переполнение стека. Я не хочу использовать стороннюю библиотеку, поэтому функции CaptureStackBackTrace или StackWalk выглядят хорошо.

Единственная проблема заключается в том, что я просто не понимаю, как их использовать, даже с документацией Microsoft.

Я не привык к программированию Windows, поскольку я обычно работаю над совместимыми с POSIX системами.

Каковы некоторые объяснения этих функций и, возможно, некоторые примеры?

РЕДАКТИРОВАТЬ

Теперь я рассматриваю возможность использования функции CaptureStackBackTrace из DbgHelp.lib , так как кажется, что немного меньше накладных расходов …

Вот что я пробовал до сих пор:

 unsigned int i; void * stack[ 100 ]; unsigned short frames; SYMBOL_INFO symbol; HANDLE process; process = GetCurrentProcess(); SymInitialize( process, NULL, TRUE ); frames = CaptureStackBackTrace( 0, 100, stack, NULL ); for( i = 0; i < frames; i++ ) { SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, &symbol ); printf( "%s\n", symbol.Name ); } 

Я просто становлюсь барахлом. Думаю, я должен использовать что-то еще, чем SymFromAddr .

Хорошо, теперь я понял. 🙂

Проблема заключалась в структуре SYMBOL_INFO. Он должен быть выделен в куче, зарезервировать место для имени символа и правильно инициализироваться.

Вот окончательный код:

 void printStack( void ); void printStack( void ) { unsigned int i; void * stack[ 100 ]; unsigned short frames; SYMBOL_INFO * symbol; HANDLE process; process = GetCurrentProcess(); SymInitialize( process, NULL, TRUE ); frames = CaptureStackBackTrace( 0, 100, stack, NULL ); symbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 ); symbol->MaxNameLen = 255; symbol->SizeOfStruct = sizeof( SYMBOL_INFO ); for( i = 0; i < frames; i++ ) { SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol ); printf( "%i: %s - 0x%0X\n", frames - i - 1, symbol->Name, symbol->Address ); } free( symbol ); } 

Выход:

 6: printStack - 0xD2430 5: wmain - 0xD28F0 4: __tmainCRTStartup - 0xE5010 3: wmainCRTStartup - 0xE4FF0 2: BaseThreadInitThunk - 0x75BE3665 1: RtlInitializeExceptionChain - 0x770F9D0F 0: RtlInitializeExceptionChain - 0x770F9D0F 

Вот моя супер-низко-финская альтернатива, используемая для чтения стеков из приложения C ++ Builder. Этот код выполняется в самом процессе, когда он сбой и получает стек в массив cs.

  int cslev = 0; void* cs[300]; void* it = ; void* rm[2]; while(it && cslev<300) { /* Could just memcpy instead of ReadProcessMemory, but who knows if the stack's valid? If it's invalid, memcpy could cause an AV, which is pretty much exactly what we don't want */ err=ReadProcessMemory(GetCurrentProcess(),it,(LPVOID)rm,sizeof(rm),NULL); if(!err) break; it=rm[0]; cs[cslev++]=(void*)rm[1]; } 

ОБНОВИТЬ

Как только у меня есть стек, я потом переведу его в имена. Я делаю это, перекрестно ссылаясь на файл .map который выдает C ++ Builder. То же самое можно сделать с файлом карты из другого компилятора, хотя форматирование будет несколько иным. Следующий код работает для карт C ++ Builder. Это опять-таки довольно низко-fi и, вероятно, не канонический способ MS делать вещи, но он работает в моей ситуации. Код ниже не доставляется конечным пользователям.

 char linbuf[300]; char *pars; unsigned long coff,lngth,csect; unsigned long thisa,sect; char *fns[300]; unsigned int maxs[300]; FILE *map; map = fopen(mapname, "r"); if (!map) { ...Add error handling for missing map... } do { fgets(linbuf,300,map); } while (!strstr(linbuf,"CODE")); csect=strtoul(linbuf,&pars,16); /* Find out code segment number */ pars++; /* Skip colon */ coff=strtoul(pars,&pars,16); /* Find out code offset */ lngth=strtoul(pars,NULL,16); /* Find out code length */ do { fgets(linbuf,300,map); } while (!strstr(linbuf,"Publics by Name")); for(lop=0;lop!=cslev;lop++) { fns[lop] = NULL; maxs[lop] = 0; } do { fgets(linbuf,300,map); sect=strtoul(linbuf,&pars,16); if(sect!=csect) continue; pars++; thisa=strtoul(pars,&pars,16); for(lop=0;lop!=cslev;lop++) { if(cs[lop]coff+lngth) continue; if(thisamaxs[lop]) { maxs[lop]=thisa; while(*pars==' ') pars++; fns[lop] = fnsbuf+(100*lop); fnlen = strlen(pars); if (fnlen>100) fnlen = 100; strncpy(fns[lop], pars, 99); fns[lop][fnlen-1]='\0'; } } } while (!feof(map)); fclose(map); 

После запуска этого кода массив fns содержит функцию наилучшего соответствия из файла .map.

В моей ситуации у меня на самом деле есть стек вызовов, созданный первой частью кода, представляющей скрипт PHP - я делаю эквивалент кода C выше, используя часть PHP. Этот первый бит анализирует файл карты (опять же, это работает с картами C ++ Builder, но может быть легко адаптировано к другим форматам файлов карт):

  $file = fopen($mapdir.$app."-".$appversion.".map","r"); if (!$file) ... Error handling for missing map ... do { $mapline = fgets($file); } while (!strstr($mapline,"CODE")); $tokens = split("[[:space:]\:]", $mapline); $codeseg = $tokens[1]; $codestart = intval($tokens[2],16); $codelen = intval($tokens[3],16); do { $mapline = fgets($file); } while (!strstr($mapline,"Publics by Value")); fgets($file); // Blank $addrnum = 0; $lastaddr = 0; while (1) { if (feof($file)) break; $mapline = fgets($file); $tokens = split("[[:space:]\:]", $mapline); $thisseg = $tokens[1]; if ($thisseg!=$codeseg) break; $addrs[$addrnum] = intval($tokens[2],16); if ($addrs[$addrnum]==$lastaddr) continue; $lastaddr = $addrs[$addrnum]; $funcs[$addrnum] = trim(substr($mapline, 16)); $addrnum++; } fclose($file); 

Затем этот бит преобразует адрес (в $rowaddr ) в заданную функцию (а также смещение после функции):

  $thisaddr = intval($rowaddr,16); $thisaddr -= $codestart; if ($thisaddr>=0 && $thisaddr<=$codelen) { for ($lop=0; $lop!=$addrnum; $lop++) if ($thisaddr<$addrs[$lop]) break; } else $lop = $addrnum; if ($lop!=$addrnum) { $lop--; $lines[$ix] = substr($line,0,13).$rowaddr." : ".$funcs[$lop]." (+".sprintf("%04X",$thisaddr-$addrs[$lop]).")"; $stack .= $rowaddr; } else { $lines[$ix] = substr($line,0,13).$rowaddr." : external"; } .  $thisaddr = intval($rowaddr,16); $thisaddr -= $codestart; if ($thisaddr>=0 && $thisaddr<=$codelen) { for ($lop=0; $lop!=$addrnum; $lop++) if ($thisaddr<$addrs[$lop]) break; } else $lop = $addrnum; if ($lop!=$addrnum) { $lop--; $lines[$ix] = substr($line,0,13).$rowaddr." : ".$funcs[$lop]." (+".sprintf("%04X",$thisaddr-$addrs[$lop]).")"; $stack .= $rowaddr; } else { $lines[$ix] = substr($line,0,13).$rowaddr." : external"; } 

@Jon Bright: вы говорите: «Кто знает, действителен ли стек …»: Ну, есть способ узнать, как известны адреса стека. Предполагая, что вам нужна трассировка в текущем streamе, конечно:

  NT_TIB* pTEB = GetTEB(); UINT_PTR ebp = GetEBPForStackTrace(); HANDLE hCurProc = ::GetCurrentProcess(); while ( ((ebp & 3) == 0) && ebp + 2*sizeof(VOID*) < (UINT_PTR)pTEB->StackBase && ebp >= (UINT_PTR)pTEB->StackLimit && nAddresses < nTraceBuffers) { pTraces[nAddresses++]._EIP = ((UINT_PTR*)ebp)[1]; ebp = ((UINT_PTR*)ebp)[0]; } 

Мой «GetTEB ()» является NtCurrentTeb () из NTDLL.DLL - и это не только Windows 7 и выше, как указано в текущем MSDN. MS объединяет документацию. Это было там долгое время. Используя Блок ThreadEnvironment (TEB), вам не требуется ReadProcessMemory (), поскольку вы знаете нижний и верхний предел стека. Я предполагаю, что это самый быстрый способ сделать это.

Использование компилятора MS GetEBPForStackTrace () может быть

 inline __declspec(naked) UINT_PTR GetEBPForStackTrace() { __asm { mov eax, ebp ret } } 

как простой способ получить EBP текущего streamа (но вы можете передать любой действительный EBP в этот цикл, если он предназначен для текущего streamа).

Ограничение: Это допустимо для x86 под Windows.