Найти, кто держит блокировку SRW для собственного процесса с WinDbg - PullRequest
0 голосов
/ 31 марта 2020

У меня есть программа, написанная на c ++, и я не могу определить, какой поток получил блокировки Slim Reader / Writer (SRW) . Я гуглил и нашел Определение, какой метод удерживает ReaderWriterLockSlim WriteLock , но речь идет о программе, написанной C#. Кроме того, некоторые команды, например, .rwlock, недоступны.

0:796> !handle 0 ff Mutant
Handle c
  Type          Mutant
  Attributes    0
  GrantedAccess 0x1f0001:
         Delete,ReadControl,WriteDac,WriteOwner,Synch
         QueryState
  HandleCount   4
  PointerCount  103240
  Name          \BaseNamedObjects\DBWinMutex
  Object Specific Information
    Mutex is Free
Handle 474
  Type          Mutant
  Attributes    0
  GrantedAccess 0x1f0001:
         Delete,ReadControl,WriteDac,WriteOwner,Synch
         QueryState
  HandleCount   2
  PointerCount  65536
  Name          \BaseNamedObjects\SM0:928:304:WilStaging_02
  Object Specific Information
    Mutex is Free
2 handles of type Mutant
0:796> kb
RetAddr           : Args to Child                                                           : Call Site
00007ff9`b6e3d33a : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!ZwWaitForAlertByThreadId+0x14
00007ff9`a85726a9 : 00000000`00000000 00000000`00000000 00000192`83338180 00000000`00000000 : ntdll!RtlAcquireSRWLockExclusive+0x13a
00007ff9`a6231724 : c000000d`00000000 00000000`00000000 00000192`83338180 00000000`00000002 : MSVCP140!mtx_do_lock+0x7d [d:\agent\_work\2\s\src\vctools\crt\crtw32\stdcpp\thr\mutex.cpp @ 106]
00007ff9`a626749e : 00000192`f6a26e38 00000193`4aaa3d80 00000052`897fea60 00000000`00000000 : AZSDK!AZConnection::Post+0x54 [g:\prod\sdk\src\connection.cpp @ 1147]
...
00007ff9`9c8ba9c1 : 00000192`c3b3d770 00000000`00000000 00000192`f5d616b0 00000000`00000000 : prod!Task::Execute+0x28 [g:\prod\src\task.cpp @ 51]
00007ff9`b6e97529 : 00000193`491b9830 00000000`7ffe0386 00000052`897ff998 00000193`491b98f8 : prod!Proxy::TaskExecuter+0x11 [g:\prod\src\proxy.cpp @ 2042]
00007ff9`b6e3bec4 : 00000000`00000000 00000192`f1dd03a0 00000000`00000000 00000000`00000000 : ntdll!TppSimplepExecuteCallback+0x99
00007ff9`b6c47e94 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!TppWorkerThread+0x644
00007ff9`b6e87ad1 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
0:796> !rwlock
No export rwlock found

Фрагмент кода C ++:

std::mutex m_mutex;

Status AZConnection::Post(const Request* request, Result** pResult)
{
    std::lock_guard<std::mutex> sbguard(m_mutex);
}

Обновлено:

Согласно Ответ ржавчины , я вижу. Теперь я должен сдаться.

На самом деле, моя программа все еще работает, но она не работает. Я должен найти причину. Я нашел 806 потоков, и большинство из них с нетерпением ждут Post. Кроме того, в случае, если это не воспроизводит, я не могу перезапустить, чтобы добавить печать журнала, который приобрел замок. Поэтому я просто хочу проверить, что делает поток, удерживающий блокировку.

Ответы [ 2 ]

2 голосов
/ 31 марта 2020

Win32 SRWLock не хранит эту информацию. В неконтролируемом состоянии это просто флаг atomi c.

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

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

Подробнее о реализации SRWLock см. этот ответ .

0 голосов
/ 01 апреля 2020

Я могу использовать !foreachframe и !if в Расширение отладки MEX для WinDbg , чтобы grep стека вызовов и выполнить команду (несколько команд, разделенных ; (Разделитель команд) являются не поддерживается), чтобы найти поток, который не наблюдает за блокировкой, но чей предыдущий вызов Post. Расширение можно скачать с здесь . После загрузки его можно поместить в C:\WinDDK\7600.16385.1\Debuggers\winext (см. Также Загрузка DLL расширений отладчика ).

Я заменил MSVCP140!mtx_do_lock на msvcrt!_threadstartex и заменил AZSDK!AZConnection::Post на KERNEL32!BaseThreadInitThunk в следующем примере кода:

~*e r @$t0 = -1; !foreachframe -q -f 'KERNEL32!BaseThreadInitThunk' r @$t0= @#FrameNum - 1; .if(0<=@$t0) { !if -DoesNotContainRegex 'msvcrt!_threadstartex' -then '.printf /D "Thread: <link cmd=\"~~[%x]\">0x%x</link> (<link cmd=\"!mex.t %d\">%d</link>)", $tid, $tid, $dtid, $dtid' .frame @$t0 }

!foreachthread -q !foreachframe -q -f 'KERNEL32!BaseThreadInitThunk' !if -DoesNotContainRegex 'msvcrt!_threadstartex' -then '.printf /D "Thread: <link cmd=\"~~[%x]\">0x%x</link> (<link cmd=\"!mex.t %d\">%d</link>)", $tid, $tid, $dtid, $dtid' .frame @#FrameNum - 1

Пример его вывода для notepad процесса:

Thread: 0xd14c (0)
Thread: 0x4f88 (1)
Thread: 0xd198 (7)

Стеки вызовов всех потоков

0:001> !foreachthread k
Child-SP          RetAddr           Call Site
00000062`fabaf8c8 00007ffd`54c7409d win32u!ZwUserGetMessage+0x14
00000062`fabaf8d0 00007ff7`bb4c449f USER32!GetMessageW+0x2d
00000062`fabaf930 00007ff7`bb4dae07 notepad+0x449f
00000062`fabafa30 00007ffd`570f7974 notepad+0x1ae07
00000062`fabafaf0 00007ffd`5841a261 KERNEL32!BaseThreadInitThunk+0x14
00000062`fabafb20 00000000`00000000 ntdll!RtlUserThreadStart+0x21

Changing to thread: 0xda94 (1)
Child-SP          RetAddr           Call Site
00000062`fae7fbc8 00007ffd`5847f01b ntdll!DbgBreakPoint
00000062`fae7fbd0 00007ffd`570f7974 ntdll!DbgUiRemoteBreakin+0x4b
00000062`fae7fc00 00007ffd`5841a261 KERNEL32!BaseThreadInitThunk+0x14
00000062`fae7fc30 00000000`00000000 ntdll!RtlUserThreadStart+0x21

Changing to thread: 0xdb60 (7)
Child-SP          RetAddr           Call Site
00000062`fb17f968 00007ffd`54c7004d win32u!ZwUserMsgWaitForMultipleObjectsEx+0x14
00000062`fb17f970 00007ffc`e4e7d078 USER32!MsgWaitForMultipleObjectsEx+0x9d
00000062`fb17f9b0 00007ffc`e4e7cec2 DUser!GetMessageExA+0x2f8
00000062`fb17fa50 00007ffd`54c77004 DUser!GetMessageExA+0x142
00000062`fb17fab0 00007ffd`584534a4 USER32!Ordinal2582+0x64
00000062`fb17fb50 00007ffd`54101164 ntdll!KiUserCallbackDispatcher+0x24
00000062`fb17fbc8 00007ffd`54c7409d win32u!ZwUserGetMessage+0x14
00000062`fb17fbd0 00007ffd`2e4efa3c USER32!GetMessageW+0x2d
00000062`fb17fc30 00007ffd`1d0b30f8 DUI70!StartMessagePump+0x3c
00000062`fb17fc90 00007ffd`1d0b31ce msctfuimanager!DllCanUnloadNow+0xf3e8
00000062`fb17fd50 00007ffd`570f7974 msctfuimanager!DllCanUnloadNow+0xf4be
00000062`fb17fd80 00007ffd`5841a261 KERNEL32!BaseThreadInitThunk+0x14
00000062`fb17fdb0 00000000`00000000 ntdll!RtlUserThreadStart+0x21

Changing to thread: 0xc490 (8)
Child-SP          RetAddr           Call Site
00000062`fb1ff708 00007ffd`54c7004d win32u!ZwUserMsgWaitForMultipleObjectsEx+0x14
00000062`fb1ff710 00007ffc`e4e7d1ca USER32!MsgWaitForMultipleObjectsEx+0x9d
00000062`fb1ff750 00007ffc`e4e7cde7 DUser!GetMessageExA+0x44a
00000062`fb1ff7f0 00007ffc`e4e7ca53 DUser!GetMessageExA+0x67
00000062`fb1ff840 00007ffd`5505b0ea DUser!GetGadgetFocus+0x33b3
00000062`fb1ff8d0 00007ffd`5505b1bc msvcrt!_callthreadstartex+0x1e
00000062`fb1ff900 00007ffd`570f7974 msvcrt!_threadstartex+0x7c
00000062`fb1ff930 00007ffd`5841a261 KERNEL32!BaseThreadInitThunk+0x14
00000062`fb1ff960 00000000`00000000 ntdll!RtlUserThreadStart+0x21

Обновлено :

Я обнаружил Кто-то думал так же (в случае неработающих ссылок, часто):

Тонкие блокировки чтения / записи не помнят, кто владельцы есть, так что вам придется найти их другим способом

Raymond | 10 августа 2011 г.

Тонкая блокировка чтения / записи является очень удобным средством синхронизации, но одним из недостатков является то, что он не> отслеживает кем являются текущие владельцы.
Когда ваш поток застревает в ожидании получения тонкой блокировки чтения / записи, естественно знать, каким потокам принадлежит ресурс, которого застрял поток> ждет.

Поскольку нет возможности перейти от ожидающего потока к потокам-владельцам, вам просто нужно найти потоки-владельцы другим способом. Вот поток>, который ожидает блокировки в режиме совместного использования:

ntdll!ZwWaitForKeyedEvent+0xc
ntdll!RtlAcquireSRWLockShared+0x126
dbquery!CSearchSpace::Validate+0x10b
dbquery!CSearchSpace::DecomposeSearchSpace+0x3c
dbquery!CQuery::AddConfigs+0xdc
dbquery!CQuery::ResolveProviders+0x89
dbquery!CResults::CreateProviders+0x85
dbquery!CResults::GetProviders+0x61
dbquery!CResults::CreateResults+0x11c

Хорошо, как найти поток, которому принадлежит блокировка?

Во-первых, тонкие блокировки чтения / записи являются может использоваться только внутри процесса, поэтому потоки-кандидаты - это те, которые находятся внутри процесса.

Во-вторых, шаблон использования для блокировок почти всегда выглядит примерно так:

enter lock
do something
exit lock

Это весьма необычно для функция для взятия блокировки и выхода на внешний код
с удержанной блокировкой. (Он может выходить из другого кода в том же компоненте, передавая обязательство по выходу из блокировки для этого другого кода.)
Поэтому вы хотите искать потоки, которые все еще находятся внутри dbquery.dll, возможно, даже все еще внутри CSearch­Space (если блокировка является блокировкой для каждого объекта, а не> глобальной).

Конечно, возможно, что код, введенный в блокировку, испортился и забыл ее снять, но если это так В этом случае никакие поиски> ничего не найдут, поскольку виновник давно ушел.
Поскольку отладка - это упражнение в оптимизме , мы можем также исходить из предположения, что> мы находимся не по делу. Если он не может найти владельца блокировки, возможно, нам придется пересмотреть это предположение.

Наконец, последний трюк - это знать , какие потоки игнорировать .

На данный момент вы также можете игнорировать потоки, ожидающие блокировки, поскольку они являются жертвами, а не причиной. (Опять же, если нам не удается найти владельца блокировки, мы> можем вернуться к предположению, что они не являются причиной; например, они могут пытаться получить блокировку рекурсивно.)

Как это происходит,
есть только один поток в процессе, который проходит все вышеупомянутые фильтры.

dbquery!CProp::Marshall+0x3b
dbquery!CRequest::CRequest+0x24c
dbquery!CQuery::Execute+0x668
dbquery!CResults::FillParams+0x1c4
dbquery!CResults::AddProvider+0x4e
dbquery!CResults::AddConfigs+0x1c5
dbquery!CResults::CreateResults+0x145

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

...