Skip to content
This repository has been archived by the owner on Oct 3, 2020. It is now read-only.

Commit

Permalink
bug 501034 - add a Win32 helper app to crash a process, and make auto…
Browse files Browse the repository at this point in the history
…mation.py use it when the app hangs. r=Waldo

--HG--
extra : rebase_source : 845326f8cfcd3dfe7ca5b0a98bd0810966cf2809
  • Loading branch information
Ted Mielczarek committed Oct 16, 2009
1 parent 018ffae commit 943273d
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 8 deletions.
1 change: 1 addition & 0 deletions allmakefiles.sh
Expand Up @@ -63,6 +63,7 @@ build/pgo/blueprint/Makefile
build/pgo/js-input/Makefile
build/unix/Makefile
build/win32/Makefile
build/win32/crashinjectdll/Makefile
config/Makefile
config/autoconf.mk
config/mkdepend/Makefile
Expand Down
26 changes: 18 additions & 8 deletions build/automation.py.in
Expand Up @@ -475,6 +475,23 @@ else:
return ('', True)
return (f.readline(), False)

def triggerBreakpad(proc, utilityPath):
"""Attempt to kill this process in a way that triggers Breakpad crash
reporting, if we know how for this platform. Otherwise just .kill() it."""
if CRASHREPORTER:
if UNIXISH:
# SEGV will get picked up by Breakpad's signal handler
os.kill(proc.pid, signal.SIGSEGV)
return
elif IS_WIN32:
# We should have a "crashinject" program in our utility path
crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(proc.pid)]).wait() == 0:
return
#TODO: kill the process such that it triggers Breakpad on OS X
log.info("Can't trigger Breakpad, just killing process")
proc.kill()

###############
# RUN THE APP #
###############
Expand Down Expand Up @@ -568,14 +585,7 @@ def runApp(testURL, env, app, profileDir, extraArgs,
(line, didTimeout) = readWithTimeout(logsource, timeout)
if didTimeout:
log.info("TEST-UNEXPECTED-FAIL | automation.py | application timed out after %d seconds with no output", int(timeout))
if CRASHREPORTER:
if UNIXISH:
os.kill(proc.pid, signal.SIGSEGV)
else:
#TODO: kill the process such that it triggers Breakpad
proc.kill()
else:
proc.kill()
triggerBreakpad(proc, utilityPath)

status = proc.wait()
if status != 0 and not didTimeout:
Expand Down
9 changes: 9 additions & 0 deletions build/win32/Makefile.in
Expand Up @@ -42,6 +42,15 @@ VPATH = @srcdir@

include $(DEPTH)/config/autoconf.mk

ifdef ENABLE_TESTS
DIRS += crashinjectdll

PROGRAM = crashinject$(BIN_SUFFIX)
USE_STATIC_LIBS = 1
CPPSRCS = crashinject.cpp

endif

include $(topsrcdir)/config/rules.mk

ifdef WIN32_REDIST_DIR
Expand Down
128 changes: 128 additions & 0 deletions build/win32/crashinject.cpp
@@ -0,0 +1,128 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Crash Injection Utility
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

/*
* Given a PID, this program attempts to inject a DLL into the process
* with that PID. The DLL it attempts to inject, "crashinjectdll.dll",
* must exist alongside this exe. The DLL will then crash the process.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>

int main(int argc, char** argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: crashinject <PID>\n");
return 1;
}

int pid = atoi(argv[1]);
if (pid <= 0) {
fprintf(stderr, "Usage: crashinject <PID>\n");
return 1;
}

// find our DLL to inject
wchar_t filename[_MAX_PATH];
if (GetModuleFileNameW(NULL, filename, sizeof(filename) / sizeof(wchar_t)) == 0)
return 1;

wchar_t* slash = wcsrchr(filename, L'\\');
if (slash == NULL)
return 1;

slash++;
wcscpy(slash, L"crashinjectdll.dll");

// now find our target process
HANDLE targetProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD,
FALSE,
pid);
if (targetProc == NULL) {
fprintf(stderr, "Error %d opening target process\n", GetLastError());
return 1;
}

/*
* This is sort of insane, but we're implementing a technique described here:
* http://www.codeproject.com/KB/threads/winspy.aspx#section_2
*
* The gist is to use CreateRemoteThread to create a thread in the other
* process, but cheat and make the thread function kernel32!LoadLibrary,
* so that the only remote data we have to pass to the other process
* is the path to the library we want to load. The library we're loading
* will then do its dirty work inside the other process.
*/
HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
// allocate some memory to hold the path in the remote process
void* pLibRemote = VirtualAllocEx(targetProc, NULL, sizeof(filename),
MEM_COMMIT, PAGE_READWRITE);
if (pLibRemote == NULL) {
fprintf(stderr, "Error %d in VirtualAllocEx\n", GetLastError());
CloseHandle(targetProc);
return 1;
}

if (!WriteProcessMemory(targetProc, pLibRemote, (void*)filename,
sizeof(filename), NULL)) {
fprintf(stderr, "Error %d in WriteProcessMemory\n", GetLastError());
VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE);
CloseHandle(targetProc);
return 1;
}
// Now create a thread in the target process that will load our DLL
HANDLE hThread = CreateRemoteThread(
targetProc, NULL, 0,
(LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32,
"LoadLibraryW"),
pLibRemote, 0, NULL);
if (hThread == NULL) {
fprintf(stderr, "Error %d in CreateRemoteThread\n", GetLastError());
VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE);
CloseHandle(targetProc);
return 1;
}
WaitForSingleObject(hThread, INFINITE);
// Cleanup, not that it's going to matter at this point
CloseHandle(hThread);
VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE);
CloseHandle(targetProc);

return 0;
}
53 changes: 53 additions & 0 deletions build/win32/crashinjectdll/Makefile.in
@@ -0,0 +1,53 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla core build scripts.
#
# The Initial Developer of the Original Code is
# The Mozilla Foundation
#
# Portions created by the Initial Developer are Copyright (C) 2009
# the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
#
# Contributor(s):
# Ted Mielczarek <ted.mielczarek@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****

DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@

include $(DEPTH)/config/autoconf.mk

LIBRARY_NAME = crashinjectdll
DEFFILE = $(srcdir)/crashinjectdll.def
FORCE_SHARED_LIB = 1
USE_STATIC_LIBS = 1

CPPSRCS = crashinjectdll.cpp

include $(topsrcdir)/config/rules.mk
34 changes: 34 additions & 0 deletions build/win32/crashinjectdll/crashinjectdll.cpp
@@ -0,0 +1,34 @@
#include <stdio.h>
#include <Windows.h>

// make sure we only ever spawn one thread
DWORD tid = -1;

DWORD WINAPI CrashingThread(
LPVOID lpParameter
)
{
// not a very friendly DLL
volatile int* x = (int *)0x0;
*x = 1;
return 0;
}

BOOL WINAPI DllMain(
HANDLE hinstDLL,
DWORD dwReason,
LPVOID lpvReserved
)
{
if (tid == -1)
// we have to crash on another thread because LoadLibrary() will
// catch memory access errors and return failure to the calling process
CreateThread(
NULL, // default security attributes
0, // use default stack size
CrashingThread , // thread function name
NULL, // argument to thread function
0, // use default creation flags
&tid); // returns the thread identifier
return TRUE;
}
3 changes: 3 additions & 0 deletions build/win32/crashinjectdll/crashinjectdll.def
@@ -0,0 +1,3 @@
LIBRARY crashinjectdll
EXPORTS
DllMain
7 changes: 7 additions & 0 deletions testing/mochitest/Makefile.in
Expand Up @@ -98,6 +98,13 @@ TEST_HARNESS_BINS := \
pk12util$(BIN_SUFFIX) \
$(NULL)

ifeq ($(OS_ARCH),WINNT)
TEST_HARNESS_BINS += \
crashinject$(BIN_SUFFIX) \
crashinjectdll$(DLL_SUFFIX) \
$(NULL)
endif

ifeq ($(OS_ARCH),Darwin)
TEST_HARNESS_BINS += fix-macosx-stack.pl
endif
Expand Down

0 comments on commit 943273d

Please sign in to comment.