Как проверить, был ли подпроцесс уничтожен сигналом в Windows - PullRequest
0 голосов
/ 14 января 2019

вопрос

Учитывая подпроцесс, запущенный в python с кодом, подобным:

import subprocess
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.communicate()
print('Return code: {}'.format(p.returncode))

Согласно официальной документации , можно проверить, был ли подпроцесс завершен сигналом:

Отрицательное значение -N указывает, что дочерний процесс был прерван сигналом N (только POSIX).

Но только на платформах POSIX.

Есть ли способ проверить, не завершился ли процесс сигналом (не важно, какой) на платформах Windows?

Фон

Я сталкиваюсь с этой проблемой при выполнении тестов googletest. Тест флага CLI об отказе при сбое не проходит на платформах Windows (VC14, VS2017), но хорошо работает на POSIX (2 Ubuntu, 2 MacOS).

Вручную в командной строке я получаю следующие результаты:

> .\googletest-break-on-failure-unittest_.exe --gtest_break_on_failure
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Foo
[ RUN      ] Foo.Bar
<some path>\googletest\test\googletest-break-on-failure-unittest_.cc(52): error: Expected equality of these values:
  2
  3

> echo %ERRORLEVEL%
-2147483645

Однако оболочка Python, которая вызывает этот тест, получает 2147483651 (положительное число).

(я только что добавил печать до этой строки )

Обратите внимание, что они относятся к числам 0xFFFFFFFF80000003 (отрицательное число) и 0x‭80000003‬ (положительное число) в шестнадцатеричном формате и что код возврата больше не обрабатывался. (См. здесь )

Почему код возврата будет изменен следующим образом?

PS: Да, я проверил, что GTEST_OS_WINDOWS и GTEST_HAS_SEH истинны в коде C ++.

1 Ответ

0 голосов
/ 28 января 2019

Переменная окружения ERRORLEVEL CMD является 32-битным значением со знаком. Положительный диапазон 0x00000000-0x7FFFFFFF (от 0 до 2147483647), а отрицательный диапазон 0x80000000-0xFFFFFFFF (от -2147483648 до -1). Когда процесс завершается из-за необработанного исключения, состояние обычно представляет собой код исключения, который обычно представляет собой 32-разрядное значение со знаком NTSTATUS. Тем не менее, ничто не мешает нам вызывать ExitProcess или TerminateProcess с неподписанным кодом завершения, который превышает 0x7FFFFFFF, поэтому отрицательный ERRORLEVEL в CMD сам по себе не означает, что процесс завершился ненормально.

В этом случае -2147483645 - это код NTSTATUS STATUS_BREAKPOINT (0x80000003). Это происходит от опции gtest_break_on_failure Google Test, которая вызывает WinAPI DebugBreak. Для архитектуры x86 последняя функция просто выполняет инструкцию int 3 (программное прерывание 3), которая захватывается ядром, чтобы вызвать исключение точки останова.

Обычно без подключенного отладчика выполняется обработчик необработанных исключений Windows по умолчанию, который вызывает Отчет об ошибках Windows (см. Ниже). Но код, связанный в вопросе, устанавливает ExitWithExceptionCode в качестве фильтра необработанных исключений приложения. Этот фильтр просто выходит через exit(exception_pointers->ExceptionRecord->ExceptionCode).

Что касается сигналов Unix, ядро ​​Windows их не реализует. Среда выполнения C реализует шесть, которые требуются стандартным C. В консольном приложении стандартный сигнал SIGINT связан с CTRL_C_EVENT консоли. Нестандартный сигнал SIGBREAK используется для других событий управления консоли, включая CTRL_BREAK_EVENT и CTRL_CLOSE_EVENT. Обработчик по умолчанию для этих событий вызывает ExitProcess(STATUS_CONTROL_C_EXIT). Таким образом, чтобы буквально ответить на вопрос, чтобы определить, был ли процесс убит «консольным« сигналом », проверьте значение STATUS_CONTROL_C_EXIT (0xC000013A или -1073741510).


Обработка необработанного исключения

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

  • Последний проверенный кадр - это кадр функции запуска потока, RtlUserThreadStart. Обработчику для этого кадра, _C_specific_handler, передается запись об отправке, которая позволяет ему определить фильтр исключений области. Этот фильтр, в свою очередь, вызывает фильтр необработанных исключений процесса, если он был предварительно установлен с помощью RtlSetUnhandledExceptionFilter. При запуске процесса подпрограмма инициализации kernelbase.dll устанавливает для нее метку с точно названной функцией, UnhandledExceptionFilter.

  • Если подключен отладчик, UnhandledExceptionFilter возвращает EXCEPTION_CONTINUE_SEARCH. Впоследствии ядро ​​отправляет отладчику событие-исключение второго шанса. Если отладчик не обрабатывает исключение, ядро ​​пытается отправить событие в подсистему, то есть на сервер сеансов Windows csrss.exe. Сервер, в свою очередь, передает ошибку службе отчетов об ошибках Windows (WER), которая в конечном итоге завершит процесс.

  • Если отладчик не подключен, UnhandledExceptionFilter вызывает фильтр необработанного исключения приложения, если он был предварительно установлен с помощью SetUnhandledExceptionFilter. Если фильтр приложения возвращает EXCEPTION_CONTINUE_EXECUTION или EXCEPTION_EXECUTE_HANDLER, то UnhandledExceptionFilter возвращает это значение обработчику фрейма.

  • Если приложение не установило свой собственный фильтр или его фильтр возвращает EXCEPTION_CONTINUE_SEARCH, то UnhandledExceptionFilter затем проверяет режимы ошибок задания, процесса и потока для флага SEM_NOGPFAULTERRORBOX. Этот флаг отключает отчеты об ошибках, в этом случае фильтр просто возвращает EXCEPTION_EXECUTE_HANDLER.

  • Если разрешено создание отчетов об ошибках, UnhandledExceptionFilter вызывает службу отчетов об ошибках Windows (WER). С WER в цикле поведение в конечном итоге зависит от того, как оно настроено в реестре, групповой политики и поведения по умолчанию для текущей версии Windows. WER может создавать и прикреплять процесс отладчика на основе настроек системы «AeDebug». Если он подключен к отладчику, UnhandledExceptionFilter возвращает EXCEPTION_CONTINUE_SEARCH, как если бы отладчик уже был подключен. В противном случае возвращается EXCEPTION_EXECUTE_HANDLER.

  • В конечном итоге, если он выполняет обработчик исключений для «необработанного» исключения, стек сначала разматывается в кадр RtlUserThreadStart с помощью RtlUnwindEx, который вызывает любые обработчики завершения finally в промежуточных кадрах. Контекст выполнения обработчика исключений восстанавливается с помощью RtlRestoreContext, а код исключения устанавливается в регистре целочисленного возврата (например, rax в x64). Наконец, обработчик завершает свою работу через NtTerminateProcess(NtCurrentProcess(), exceptionCode).

...