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