1 // Copyright 2016 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <stdlib.h>
16 #include <windows.h>
17 #include <tlhelp32.h>
18
19 #include "base/files/file_path.h"
20 #include "base/logging.h"
21 #include "base/strings/stringprintf.h"
22 #include "client/crashpad_client.h"
23 #include "gtest/gtest.h"
24 #include "test/test_paths.h"
25 #include "test/win/child_launcher.h"
26 #include "util/file/file_io.h"
27 #include "util/win/scoped_handle.h"
28 #include "util/win/xp_compat.h"
29
30 namespace crashpad {
31 namespace test {
32 namespace {
33
34 constexpr DWORD kCrashAndDumpTargetExitCode = 0xdeadbea7;
35
CrashAndDumpTarget(HANDLE process)36 bool CrashAndDumpTarget(HANDLE process) {
37 DWORD target_pid = GetProcessId(process);
38
39 ScopedFileHANDLE thread_snap(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
40 if (!thread_snap.is_valid()) {
41 PLOG(ERROR) << "CreateToolhelp32Snapshot";
42 return false;
43 }
44
45 THREADENTRY32 te32;
46 te32.dwSize = sizeof(THREADENTRY32);
47 if (!Thread32First(thread_snap.get(), &te32)) {
48 PLOG(ERROR) << "Thread32First";
49 return false;
50 }
51
52 do {
53 if (te32.th32OwnerProcessID == target_pid) {
54 // We set the thread priority of "Thread1" to a non-default value before
55 // going to sleep. Dump and blame this thread. For an explanation of "9",
56 // see https://msdn.microsoft.com/library/ms685100.aspx.
57 if (te32.tpBasePri == 9) {
58 ScopedKernelHANDLE thread(
59 OpenThread(kXPThreadAllAccess, false, te32.th32ThreadID));
60 if (!thread.is_valid()) {
61 PLOG(ERROR) << "OpenThread";
62 return false;
63 }
64 if (!CrashpadClient::DumpAndCrashTargetProcess(
65 process, thread.get(), kCrashAndDumpTargetExitCode)) {
66 return false;
67 }
68 return true;
69 }
70 }
71 } while (Thread32Next(thread_snap.get(), &te32));
72
73 LOG(ERROR) << "target not found";
74 return false;
75 }
76
CrashOtherProgram(int argc,wchar_t * argv[])77 int CrashOtherProgram(int argc, wchar_t* argv[]) {
78 CrashpadClient client;
79
80 if (argc == 2 || argc == 3) {
81 if (!client.SetHandlerIPCPipe(argv[1])) {
82 LOG(ERROR) << "SetHandlerIPCPipe";
83 return EXIT_FAILURE;
84 }
85 } else {
86 fprintf(stderr, "Usage: %ls <server_pipe_name> [noexception]\n", argv[0]);
87 return EXIT_FAILURE;
88 }
89
90 // Launch another process that hangs.
91 base::FilePath test_executable = TestPaths::Executable();
92 base::FilePath child_test_executable =
93 test_executable.DirName().Append(L"hanging_program.exe");
94 ChildLauncher child(child_test_executable, argv[1]);
95 child.Start();
96 if (testing::Test::HasFatalFailure()) {
97 LOG(ERROR) << "failed to start child";
98 return EXIT_FAILURE;
99 }
100
101 // Wait until it's ready.
102 char c;
103 if (!LoggingReadFileExactly(child.stdout_read_handle(), &c, sizeof(c)) ||
104 c != ' ') {
105 LOG(ERROR) << "failed child communication";
106 return EXIT_FAILURE;
107 }
108
109 DWORD expect_exit_code;
110 if (argc == 3 && wcscmp(argv[2], L"noexception") == 0) {
111 expect_exit_code = CrashpadClient::kTriggeredExceptionCode;
112 if (!CrashpadClient::DumpAndCrashTargetProcess(
113 child.process_handle(), 0, 0))
114 return EXIT_FAILURE;
115 } else {
116 expect_exit_code = kCrashAndDumpTargetExitCode;
117 if (!CrashAndDumpTarget(child.process_handle())) {
118 return EXIT_FAILURE;
119 }
120 }
121
122 DWORD exit_code = child.WaitForExit();
123 if (exit_code != expect_exit_code) {
124 LOG(ERROR) << base::StringPrintf(
125 "incorrect exit code, expected 0x%lx, observed 0x%lx",
126 expect_exit_code,
127 exit_code);
128 return EXIT_FAILURE;
129 }
130
131 return EXIT_SUCCESS;
132 }
133
134 } // namespace
135 } // namespace test
136 } // namespace crashpad
137
wmain(int argc,wchar_t * argv[])138 int wmain(int argc, wchar_t* argv[]) {
139 return crashpad::test::CrashOtherProgram(argc, argv);
140 }
141