Каков хороший способ создать строку для отчетов о сбоях Win32 C++, которая отражает причину сбоя?
Мы используем Fogbugz для отслеживания проблем, и я нахожусь в процессе написания оболочки C++ вокруг XML API для Fogbugz.
Лучше всего использовать поле " scout", чтобы подобные / одинаковые сбои просто учитывались, но не регистрировались снова. Для этого нам нужна уникальная строка для конкретной причины сбоя.
В Win32 - после получения dmp-файла или другого обработчика сбоя, что является хорошим способом сделать уникальную строку для сбоя? (мы собираемся создать файл dmp и отправить его на сервер fogbugz)
В предыдущих публикациях / статьях / и т. Д. Джоэл делал различные предложения, но большинство из них рассчитывали на язык, такой как C#, который использует рефлексию и имеет много информации, которую либо трудно получить, либо невозможно получить.
Есть ли у других людей такие вещи, как следы стека или другие вещи, чтобы делать записи разведчика в fogbugz?
РЕДАКТИРОВАТЬ Чтобы уточнить - мы не хотим уникальный идентификатор для каждого инцидента - есть вероятность сбоев, которые имеют тот же путь кода. Мы хотим запечатлеть это. Я думал, что мы получим последние несколько вызовов стека, которые есть в нашем коде (а не из библиотек win32), но не уверен, как это сделать.
Сообщать о каждом сбое как уникальном не правильно. Сообщать обо всех сбоях в одном и том же случае неправильно. Различные пользователи, повторяющие сценарий, вызывающий сбой, должны сопоставить один и тот же инцидент.
РЕДАКТИРОВАТЬ
Я думаю, что нам нужна общая "сигнатура" сбоя, основанная на том, что находится в стеке. Подобные стеки должны иметь одинаковую подпись. Например, возьмите 5 лучших методов, которые есть в нашем приложении, а затем первый вызов (если есть), который мы делаем в MS DLL. Этого, вероятно, будет достаточно для подписи и, скорее всего, будет коррелировать сбои, которые являются "одинаковыми".
Так как же получить список методов в стеке? И как вы можете сказать, если они из вашего собственного приложения или в другой DLL?
РЕДАКТИРОВАТЬ - ПРИМЕЧАНИЕ. Мы хотим создать "идентификатор корзины"/ подпись в обработчике исключений, чтобы мы могли создать мини-дамп и отправить его в fogbugz в качестве описания разведчика. В качестве альтернативы мы можем загрузить дамп при следующем запуске приложения и затем отправить его с подписью, которую мы сгенерируем.
6 ответов
Здесь, в моем проекте, я использую адресную память Crash как "уникальный" идентификатор.
Возможно, уже немного поздно, но я также добавлю свое решение на случай, если оно может помочь другим людям. Вы можете сделать это, используя дураков из "Средства отладки для Windows", например windbg.exe или лучше kd.exe. Запустив команду "kd.exe -z "path_to_dump.dmp" -c "kd;q" >> dumpstack.txt, вы можете получить следующий результат:
Microsoft (R) Windows Debugger Версия 10.0.15063.400 X86 Copyright (c) Microsoft Corporation. Все права защищены.
Загрузка файла дампа [d:\work\bugs\14122\myexe.exe.2624.dmp] Файл мини-дампа пользователя с полной памятью: доступны только данные приложения
************* Symbol Path validation summary **************
Response Time (ms) Location
Deferred srv*C:\Symbols*http://msdl.microsoft.com/download/symbols
Symbol search path is: srv*C:\Symbols*http://msdl.microsoft.com/download/symbols
Executable search path is:
Windows 10 Version 15063 MP (4 procs) Free x86 compatible
Product: WinNt, suite: SingleUserTS
15063.0.x86fre.rs2_release.170317-1834
Machine Name:
Debug session time: Fri Oct 13 00:09:01.000 2017 (UTC + 1:00)
System Uptime: 0 days 0:18:33.797
Process Uptime: 0 days 0:03:40.000
................................................................
.....................................................
Loading unloaded module list
..............................
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(a40.2580): Security check failure or stack buffer overrun - code c0000409 (first/second chance not available)
eax=00000001 ebx=00000000 ecx=00000007 edx=77cc4350 esi=00000000 edi=00000000
eip=62ae7666 esp=0b75e17c ebp=0b75e1a8 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
msvcr120!abort+0x28:
62ae7666 cd29 int 29h
0:068> kd: Reading initial command 'kb;q'
ChildEBP RetAddr Args to Child
0b75e178 62addc5f 935dda1f 00000000 00000000 msvcr120!abort+0x28
0b75e1a8 0b75e7d4 62a9b436 0b75e1dc 62a52aa5 msvcr120!terminate+0x33
WARNING: Frame IP not in any known module. Following frames may be wrong.
0b75e1ac 62a9b436 0b75e1dc 62a52aa5 00000000 0xb75e7d4
0b75e1b4 62a52aa5 00000000 62a59740 0b75e7d4 msvcr120!__FrameUnwindToState+0x89
0b75e1c8 62a52b33 00000000 00000000 00000000 msvcr120!_EH4_CallFilterFunc+0x12
0b75e1f4 62a5a0f3 62b1f7b8 62a4f7c6 0b75e324 msvcr120!_except_handler4_common+0x8e
0b75e214 77cd6152 0b75e324 0b75e7c4 0b75e344 msvcr120!_except_handler4+0x1e
0b75e238 77cd6124 0b75e324 0b75e7c4 0b75e344 ntdll!ExecuteHandler2+0x26
0b75e30c 77cc4266 0b75e324 0b75e344 0b75e324 ntdll!ExecuteHandler+0x24
0b75e30c 74cf28f2 0b75e324 0b75e344 0b75e324 ntdll!KiUserExceptionDispatcher+0x26
0b75e684 62a59339 e06d7363 00000001 00000003 KERNELBASE!RaiseException+0x62
0b75e6c4 6001821c 0b75e6e4 6004e1bc 946a8f2a msvcr120!_CxxThrowException+0x5b
0b75e6f8 60018042 0b75e720 946a8efa ffffffff mymodule!FunctionC+0x7c
0b75e730 60016544 946a8ece ffffffff 092889d8 mymodule!FunctionB+0x32
0b75e754 600166b8 00842338 6000588d 00000001 myothermodule!FunctionB+0x44
Из этого стека вы можете создать уникальный сегмент, если вы берете, например, только ваши методы из стека и объединяете их в строку: "mymodule! FunctionC + 0x7c; mymodule! FunctionB + 0x32; myothermodule! FunctionB + 0x44". Чтобы это работало, вам нужно иметь доступ к вашему серверу персональных символов, используя либо переменную окружения _NT_SYMBOL_PATH, либо с ключом командной строки -y. В качестве альтернативы вы можете создать строку только из адресов возврата (второй столбец): "62addc5f, 0b75e7d4,62a9b436,62a52aa5,62a52b33,62a5a0f3,77cd6152,77cd6124,77cc4266,74cf28f2,62a593391660000160000160000216216230868000000000016166162582182568825864000000000000000000616664625821
Я использовал что-то вроде этого для генерации исключений в моем последнем приложении (MSVC), поэтому каждая ошибка будет записываться в файл исходного кода и указывать на нее:
class Error {
//...
public: Error(string file, string line, string error) ;
};
#define ERROR(err) Error(__FILE__, __LINE__, err)
IMO лучшее, что вы можете использовать, это идентификатор корзины из анализа дампа. Используйте правильно настроенные средства отладки для Windows (windbg), это можно сделать! Проанализировать -v и классифицировать ваши дампы в различные сегменты на основе идентификатора блока. Идентификатор корзины гарантируется, что если два дампа одинаковы, их идентификатор корзины будет одинаковым. Это решает часть головоломки.
Много раз два дампа с правами на одну и ту же проблему создают разные идентификаторы корзины (возможно, разница в версии, скажем, ваши 1.0 и 1.1 оба аварийно завершают работу в одной точке). Вы можете использовать ошибочный модуль и подпись стека для сопоставления ошибок с одной и той же точки отказа.
Будут определенные вещи, которые вызывают очень случайные дампы (например, повреждение кучи, неисправный модуль обычно является жертвой). Поэтому анализ дампа следует считать наилучшим. Когда ты не можешь, ты не можешь.
Просто используйте строку MD5, сгенерированную из файла дампа, и вы, вероятно, получите уникальную строку для каждого сбоя.
Я бы начал со сбора данных о том, как часто каждая функция в вашем коде "мигает" в трассировке стека отчетов о сбоях. Каждый отчет должен быть добавлен в какую-либо базу данных, и каждая функция должна быть проиндексирована, чтобы впоследствии можно было запрашивать, какие функции, по-видимому, аварийно завершают работу чаще других. (И, конечно, такие функции, как main(), будут присутствовать в каждом отчете, но это понятно).
Или вы думаете, что проблема заключается только в отчетах о сбоях, вы можете просто удалить все эти записи из трассировок стека аварийного сбоя, а затем хешировать все остальное (ваши функции). Таким образом, вы можете увидеть, вызывает ли какой-либо конкретный цикл вызовов ваших собственных функций многократный сбой, независимо от того, какие внешние функции были вызваны между ними.
Тогда, конечно, некоторые из более сложных проблем так или иначе не будут обнаружены, так как трассировка стека будет совершенно другой. Чтобы помочь этому, вы можете записывать другие данные из вашего приложения вместе с трассировкой стека в каждом отчете, например, размеры буферов, счетчики, состояния различных частей приложения и т. Д. И затем сделать некоторую статистику по этому.