mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:26:06 +00:00
win: Implement CrashpadClient::StartHandler()
BUG=crashpad:69 R=scottmg@chromium.org Review URL: https://codereview.chromium.org/1428803006 .
This commit is contained in:
parent
c295e9d748
commit
740c668e87
@ -146,6 +146,8 @@ class CrashpadClient {
|
|||||||
private:
|
private:
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
base::mac::ScopedMachSendRight exception_port_;
|
base::mac::ScopedMachSendRight exception_port_;
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
std::string ipc_port_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CrashpadClient);
|
DISALLOW_COPY_AND_ASSIGN(CrashpadClient);
|
||||||
|
@ -19,10 +19,13 @@
|
|||||||
|
|
||||||
#include "base/atomicops.h"
|
#include "base/atomicops.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "base/rand_util.h"
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
|
#include "base/strings/stringprintf.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "base/synchronization/lock.h"
|
#include "base/synchronization/lock.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
|
#include "util/win/command_line.h"
|
||||||
#include "util/win/critical_section_with_debug_info.h"
|
#include "util/win/critical_section_with_debug_info.h"
|
||||||
#include "util/win/registration_protocol_win.h"
|
#include "util/win/registration_protocol_win.h"
|
||||||
#include "util/win/scoped_handle.h"
|
#include "util/win/scoped_handle.h"
|
||||||
@ -102,11 +105,17 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) {
|
|||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::wstring FormatArgumentString(const std::string& name,
|
||||||
|
const std::wstring& value) {
|
||||||
|
return std::wstring(L"--") + base::UTF8ToUTF16(name) + L"=" + value;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
CrashpadClient::CrashpadClient() {
|
CrashpadClient::CrashpadClient()
|
||||||
|
: ipc_port_() {
|
||||||
}
|
}
|
||||||
|
|
||||||
CrashpadClient::~CrashpadClient() {
|
CrashpadClient::~CrashpadClient() {
|
||||||
@ -118,11 +127,81 @@ bool CrashpadClient::StartHandler(
|
|||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments) {
|
const std::vector<std::string>& arguments) {
|
||||||
LOG(FATAL) << "SetHandler should be used on Windows";
|
DCHECK(ipc_port_.empty());
|
||||||
return false;
|
|
||||||
|
ipc_port_ =
|
||||||
|
base::StringPrintf("\\\\.\\pipe\\crashpad_%d_", GetCurrentProcessId());
|
||||||
|
for (int index = 0; index < 16; ++index) {
|
||||||
|
ipc_port_.append(1, static_cast<char>(base::RandInt('A', 'Z')));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring command_line;
|
||||||
|
AppendCommandLineArgument(handler.value(), &command_line);
|
||||||
|
for (const std::string& argument : arguments) {
|
||||||
|
AppendCommandLineArgument(base::UTF8ToUTF16(argument), &command_line);
|
||||||
|
}
|
||||||
|
if (!database.value().empty()) {
|
||||||
|
AppendCommandLineArgument(FormatArgumentString("database",
|
||||||
|
database.value()),
|
||||||
|
&command_line);
|
||||||
|
}
|
||||||
|
if (!url.empty()) {
|
||||||
|
AppendCommandLineArgument(FormatArgumentString("url",
|
||||||
|
base::UTF8ToUTF16(url)),
|
||||||
|
&command_line);
|
||||||
|
}
|
||||||
|
for (const auto& kv : annotations) {
|
||||||
|
AppendCommandLineArgument(
|
||||||
|
FormatArgumentString("annotation",
|
||||||
|
base::UTF8ToUTF16(kv.first + '=' + kv.second)),
|
||||||
|
&command_line);
|
||||||
|
}
|
||||||
|
AppendCommandLineArgument(FormatArgumentString("pipe-name",
|
||||||
|
base::UTF8ToUTF16(ipc_port_)),
|
||||||
|
&command_line);
|
||||||
|
|
||||||
|
STARTUPINFO startup_info = {};
|
||||||
|
startup_info.cb = sizeof(startup_info);
|
||||||
|
startup_info.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||||
|
PROCESS_INFORMATION process_info;
|
||||||
|
BOOL rv = CreateProcess(handler.value().c_str(),
|
||||||
|
&command_line[0],
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
true,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
&startup_info,
|
||||||
|
&process_info);
|
||||||
|
if (!rv) {
|
||||||
|
PLOG(ERROR) << "CreateProcess";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = CloseHandle(process_info.hThread);
|
||||||
|
PLOG_IF(WARNING, !rv) << "CloseHandle thread";
|
||||||
|
|
||||||
|
rv = CloseHandle(process_info.hProcess);
|
||||||
|
PLOG_IF(WARNING, !rv) << "CloseHandle process";
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CrashpadClient::SetHandler(const std::string& ipc_port) {
|
bool CrashpadClient::SetHandler(const std::string& ipc_port) {
|
||||||
|
DCHECK(ipc_port_.empty());
|
||||||
|
DCHECK(!ipc_port.empty());
|
||||||
|
|
||||||
|
ipc_port_ = ipc_port;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CrashpadClient::UseHandler() {
|
||||||
|
DCHECK(!ipc_port_.empty());
|
||||||
DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE);
|
DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE);
|
||||||
DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE);
|
DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE);
|
||||||
DCHECK_EQ(g_non_crash_dump_done, INVALID_HANDLE_VALUE);
|
DCHECK_EQ(g_non_crash_dump_done, INVALID_HANDLE_VALUE);
|
||||||
@ -154,7 +233,7 @@ bool CrashpadClient::SetHandler(const std::string& ipc_port) {
|
|||||||
ServerToClientMessage response = {0};
|
ServerToClientMessage response = {0};
|
||||||
|
|
||||||
if (!SendToCrashHandlerServer(
|
if (!SendToCrashHandlerServer(
|
||||||
base::UTF8ToUTF16(ipc_port), message, &response)) {
|
base::UTF8ToUTF16(ipc_port_), message, &response)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,16 +247,6 @@ bool CrashpadClient::SetHandler(const std::string& ipc_port) {
|
|||||||
|
|
||||||
g_non_crash_dump_lock = new base::Lock();
|
g_non_crash_dump_lock = new base::Lock();
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CrashpadClient::UseHandler() {
|
|
||||||
if (g_signal_exception == INVALID_HANDLE_VALUE ||
|
|
||||||
g_signal_non_crash_dump == INVALID_HANDLE_VALUE ||
|
|
||||||
g_non_crash_dump_done == INVALID_HANDLE_VALUE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In theory we could store the previous handler but it is not clear what
|
// In theory we could store the previous handler but it is not clear what
|
||||||
// use we have for it.
|
// use we have for it.
|
||||||
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
SetUnhandledExceptionFilter(&UnhandledExceptionHandler);
|
||||||
@ -188,7 +257,7 @@ bool CrashpadClient::UseHandler() {
|
|||||||
void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {
|
void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {
|
||||||
if (g_signal_non_crash_dump == INVALID_HANDLE_VALUE ||
|
if (g_signal_non_crash_dump == INVALID_HANDLE_VALUE ||
|
||||||
g_non_crash_dump_done == INVALID_HANDLE_VALUE) {
|
g_non_crash_dump_done == INVALID_HANDLE_VALUE) {
|
||||||
LOG(ERROR) << "haven't called SetHandler()";
|
LOG(ERROR) << "haven't called UseHandler()";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,14 +33,23 @@ collection server. Uploads are disabled by default, and can only be enabled by a
|
|||||||
Crashpad client using the Crashpad client library, typically in response to a
|
Crashpad client using the Crashpad client library, typically in response to a
|
||||||
user requesting this behavior.
|
user requesting this behavior.
|
||||||
|
|
||||||
This server is normally started by its initial client, and it performs a
|
On OS X, this server is normally started by its initial client, and it performs
|
||||||
handshake with this client via a pipe established by the client that is
|
a handshake with this client via a pipe established by the client that is
|
||||||
inherited by the server, referenced by the *--handshake-fd* argument. During the
|
inherited by the server, referenced by the *--handshake-fd* argument. During the
|
||||||
handshake, the server furnishes the client with a send right that the client may
|
handshake, the server furnishes the client with a send right that the client may
|
||||||
use as an exception port. The server retains the corresponding receive right,
|
use as an exception port. The server retains the corresponding receive right,
|
||||||
which it monitors for exception messages. When the receive right loses all
|
which it monitors for exception messages. When the receive right loses all
|
||||||
senders, the server exits after allowing any upload in progress to complete.
|
senders, the server exits after allowing any upload in progress to complete.
|
||||||
|
|
||||||
|
On Windows, clients register with this server by communicating with it via the
|
||||||
|
named pipe identified by the *--pipe-name* argument. During registration, a
|
||||||
|
client provides the server with an OS event object that it will signal should it
|
||||||
|
crash. The server obtains the client’s process handle and waits on the crash
|
||||||
|
event object for a crash, as well as the client’s process handle for the client
|
||||||
|
to exit cleanly without crashing. When the server loses all clients and
|
||||||
|
*--persistent* is not specified, it exits after allowing any upload in progress
|
||||||
|
to complete.
|
||||||
|
|
||||||
It is not normally appropriate to invoke this program directly. Usually, it will
|
It is not normally appropriate to invoke this program directly. Usually, it will
|
||||||
be invoked by a Crashpad client using the Crashpad client library. Arbitrary
|
be invoked by a Crashpad client using the Crashpad client library. Arbitrary
|
||||||
programs may be run with a Crashpad handler by using
|
programs may be run with a Crashpad handler by using
|
||||||
@ -77,6 +86,12 @@ of 'PATH' exists.
|
|||||||
Perform the handshake with the initial client on the file descriptor at 'FD'.
|
Perform the handshake with the initial client on the file descriptor at 'FD'.
|
||||||
This option is required. This option is only valid on Mac OS X.
|
This option is required. This option is only valid on Mac OS X.
|
||||||
|
|
||||||
|
*--persistent*::
|
||||||
|
Continue running after the last client exits. If this option is not specified,
|
||||||
|
this server will exit as soon as it has no clients, although on startup, it
|
||||||
|
always waits for at least one client to connect. This option is only valid on
|
||||||
|
Windows.
|
||||||
|
|
||||||
*--pipe-name*='PIPE'::
|
*--pipe-name*='PIPE'::
|
||||||
Listen on the given pipe name for connections from clients. 'PIPE' must be of
|
Listen on the given pipe name for connections from clients. 'PIPE' must be of
|
||||||
the form +\\.\pipe\<somename>+. This option is required. This option is only
|
the form +\\.\pipe\<somename>+. This option is required. This option is only
|
||||||
|
@ -60,6 +60,7 @@ void Usage(const base::FilePath& me) {
|
|||||||
" --reset-own-crash-exception-port-to-system-default\n"
|
" --reset-own-crash-exception-port-to-system-default\n"
|
||||||
" reset the server's exception handler to default\n"
|
" reset the server's exception handler to default\n"
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
|
" --persistent continue running after all clients exit\n"
|
||||||
" --pipe-name=PIPE communicate with the client over PIPE\n"
|
" --pipe-name=PIPE communicate with the client over PIPE\n"
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
" --url=URL send crash reports to this Breakpad server URL,\n"
|
" --url=URL send crash reports to this Breakpad server URL,\n"
|
||||||
@ -84,6 +85,7 @@ int HandlerMain(int argc, char* argv[]) {
|
|||||||
kOptionHandshakeFD,
|
kOptionHandshakeFD,
|
||||||
kOptionResetOwnCrashExceptionPortToSystemDefault,
|
kOptionResetOwnCrashExceptionPortToSystemDefault,
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
|
kOptionPersistent,
|
||||||
kOptionPipeName,
|
kOptionPipeName,
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
kOptionURL,
|
kOptionURL,
|
||||||
@ -101,8 +103,9 @@ int HandlerMain(int argc, char* argv[]) {
|
|||||||
int handshake_fd;
|
int handshake_fd;
|
||||||
bool reset_own_crash_exception_port_to_system_default;
|
bool reset_own_crash_exception_port_to_system_default;
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
|
bool persistent;
|
||||||
std::string pipe_name;
|
std::string pipe_name;
|
||||||
#endif
|
#endif // OS_MACOSX
|
||||||
} options = {};
|
} options = {};
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
options.handshake_fd = -1;
|
options.handshake_fd = -1;
|
||||||
@ -119,8 +122,9 @@ int HandlerMain(int argc, char* argv[]) {
|
|||||||
nullptr,
|
nullptr,
|
||||||
kOptionResetOwnCrashExceptionPortToSystemDefault},
|
kOptionResetOwnCrashExceptionPortToSystemDefault},
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
|
{"persistent", no_argument, nullptr, kOptionPersistent},
|
||||||
{"pipe-name", required_argument, nullptr, kOptionPipeName},
|
{"pipe-name", required_argument, nullptr, kOptionPipeName},
|
||||||
#endif
|
#endif // OS_MACOSX
|
||||||
{"url", required_argument, nullptr, kOptionURL},
|
{"url", required_argument, nullptr, kOptionURL},
|
||||||
{"help", no_argument, nullptr, kOptionHelp},
|
{"help", no_argument, nullptr, kOptionHelp},
|
||||||
{"version", no_argument, nullptr, kOptionVersion},
|
{"version", no_argument, nullptr, kOptionVersion},
|
||||||
@ -163,6 +167,10 @@ int HandlerMain(int argc, char* argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
|
case kOptionPersistent: {
|
||||||
|
options.persistent = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case kOptionPipeName: {
|
case kOptionPipeName: {
|
||||||
options.pipe_name = optarg;
|
options.pipe_name = optarg;
|
||||||
break;
|
break;
|
||||||
@ -228,7 +236,8 @@ int HandlerMain(int argc, char* argv[]) {
|
|||||||
|
|
||||||
ExceptionHandlerServer exception_handler_server(receive_right.Pass());
|
ExceptionHandlerServer exception_handler_server(receive_right.Pass());
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
ExceptionHandlerServer exception_handler_server(options.pipe_name);
|
ExceptionHandlerServer exception_handler_server(options.pipe_name,
|
||||||
|
options.persistent);
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
|
|
||||||
scoped_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize(
|
scoped_ptr<CrashReportDatabase> database(CrashReportDatabase::Initialize(
|
||||||
|
@ -12,16 +12,23 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <winternl.h>
|
#include <winternl.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// ntstatus.h conflicts with windows.h so define this locally.
|
// ntstatus.h conflicts with windows.h so define this locally.
|
||||||
#ifndef STATUS_NO_SUCH_FILE
|
#ifndef STATUS_NO_SUCH_FILE
|
||||||
#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F)
|
#define STATUS_NO_SUCH_FILE static_cast<NTSTATUS>(0xC000000F)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "base/basictypes.h"
|
#include "base/basictypes.h"
|
||||||
|
#include "base/files/file_path.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "client/crashpad_client.h"
|
#include "client/crashpad_client.h"
|
||||||
#include "tools/tool_support.h"
|
#include "tools/tool_support.h"
|
||||||
#include "util/win/critical_section_with_debug_info.h"
|
#include "util/win/critical_section_with_debug_info.h"
|
||||||
@ -88,19 +95,31 @@ void SomeCrashyFunction() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int CrashyMain(int argc, char* argv[]) {
|
int CrashyMain(int argc, char* argv[]) {
|
||||||
if (argc != 2) {
|
CrashpadClient client;
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
if (!client.SetHandler(argv[1])) {
|
||||||
|
LOG(ERROR) << "SetHandler";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
} else if (argc == 3) {
|
||||||
|
if (!client.StartHandler(base::FilePath(base::UTF8ToUTF16(argv[1])),
|
||||||
|
base::FilePath(base::UTF8ToUTF16(argv[2])),
|
||||||
|
std::string(),
|
||||||
|
std::map<std::string, std::string>(),
|
||||||
|
std::vector<std::string>())) {
|
||||||
|
LOG(ERROR) << "StartHandler";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
fprintf(stderr, "Usage: %s <server_pipe_name>\n", argv[0]);
|
fprintf(stderr, "Usage: %s <server_pipe_name>\n", argv[0]);
|
||||||
return 1;
|
fprintf(stderr, " %s <handler_path> <database_path>\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
CrashpadClient client;
|
|
||||||
if (!client.SetHandler(argv[1])) {
|
|
||||||
LOG(ERROR) << "SetHandler";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (!client.UseHandler()) {
|
if (!client.UseHandler()) {
|
||||||
LOG(ERROR) << "UseHandler";
|
LOG(ERROR) << "UseHandler";
|
||||||
return 1;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocateMemoryOfVariousProtections();
|
AllocateMemoryOfVariousProtections();
|
||||||
@ -112,7 +131,7 @@ int CrashyMain(int argc, char* argv[]) {
|
|||||||
|
|
||||||
SomeCrashyFunction();
|
SomeCrashyFunction();
|
||||||
|
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -78,9 +78,11 @@ def GetCdbPath():
|
|||||||
|
|
||||||
|
|
||||||
def GetDumpFromProgram(out_dir, pipe_name, executable_name):
|
def GetDumpFromProgram(out_dir, pipe_name, executable_name):
|
||||||
"""Initialize a crash database, run crashpad_handler, run |executable_name|
|
"""Initialize a crash database, and run |executable_name| connecting to a
|
||||||
connecting to the crash_handler. Returns the minidump generated by
|
crash handler. If pipe_name is set, crashpad_handler will be started first. If
|
||||||
crash_handler for further testing.
|
pipe_name is empty, the executable is responsible for starting
|
||||||
|
crashpad_handler. Returns the minidump generated by crashpad_handler for
|
||||||
|
further testing.
|
||||||
"""
|
"""
|
||||||
test_database = MakeTempDir()
|
test_database = MakeTempDir()
|
||||||
handler = None
|
handler = None
|
||||||
@ -92,13 +94,18 @@ def GetDumpFromProgram(out_dir, pipe_name, executable_name):
|
|||||||
print 'could not initialize report database'
|
print 'could not initialize report database'
|
||||||
return None
|
return None
|
||||||
|
|
||||||
handler = subprocess.Popen([
|
if pipe_name is not None:
|
||||||
os.path.join(out_dir, 'crashpad_handler.exe'),
|
handler = subprocess.Popen([
|
||||||
'--pipe-name=' + pipe_name,
|
os.path.join(out_dir, 'crashpad_handler.exe'),
|
||||||
'--database=' + test_database
|
'--pipe-name=' + pipe_name,
|
||||||
])
|
'--database=' + test_database
|
||||||
|
])
|
||||||
|
|
||||||
subprocess.call([os.path.join(out_dir, executable_name), pipe_name])
|
subprocess.call([os.path.join(out_dir, executable_name), pipe_name])
|
||||||
|
else:
|
||||||
|
subprocess.call([os.path.join(out_dir, executable_name),
|
||||||
|
os.path.join(out_dir, 'crashpad_handler.exe'),
|
||||||
|
test_database])
|
||||||
|
|
||||||
out = subprocess.check_output([
|
out = subprocess.check_output([
|
||||||
os.path.join(out_dir, 'crashpad_database_util.exe'),
|
os.path.join(out_dir, 'crashpad_database_util.exe'),
|
||||||
@ -160,7 +167,12 @@ class CdbRun(object):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def RunTests(cdb_path, dump_path, destroyed_dump_path, z7_dump_path, pipe_name):
|
def RunTests(cdb_path,
|
||||||
|
dump_path,
|
||||||
|
start_handler_dump_path,
|
||||||
|
destroyed_dump_path,
|
||||||
|
z7_dump_path,
|
||||||
|
pipe_name):
|
||||||
"""Runs various tests in sequence. Runs a new cdb instance on the dump for
|
"""Runs various tests in sequence. Runs a new cdb instance on the dump for
|
||||||
each block of tests to reduce the chances that output from one command is
|
each block of tests to reduce the chances that output from one command is
|
||||||
confused for output from another.
|
confused for output from another.
|
||||||
@ -172,6 +184,13 @@ def RunTests(cdb_path, dump_path, destroyed_dump_path, z7_dump_path, pipe_name):
|
|||||||
'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction',
|
'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction',
|
||||||
'exception at correct location')
|
'exception at correct location')
|
||||||
|
|
||||||
|
out = CdbRun(cdb_path, start_handler_dump_path, '.ecxr')
|
||||||
|
out.Check('This dump file has an exception of interest stored in it',
|
||||||
|
'captured exception (using StartHandler())')
|
||||||
|
out.Check(
|
||||||
|
'crashy_program!crashpad::`anonymous namespace\'::SomeCrashyFunction',
|
||||||
|
'exception at correct location (using StartHandler())')
|
||||||
|
|
||||||
out = CdbRun(cdb_path, dump_path, '!peb')
|
out = CdbRun(cdb_path, dump_path, '!peb')
|
||||||
out.Check(r'PEB at', 'found the PEB')
|
out.Check(r'PEB at', 'found the PEB')
|
||||||
out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved')
|
out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved')
|
||||||
@ -255,6 +274,10 @@ def main(args):
|
|||||||
if not crashy_dump_path:
|
if not crashy_dump_path:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
start_handler_dump_path = GetDumpFromCrashyProgram(args[0], None)
|
||||||
|
if not start_handler_dump_path:
|
||||||
|
return 1
|
||||||
|
|
||||||
destroyed_dump_path = GetDumpFromSelfDestroyingProgram(args[0], pipe_name)
|
destroyed_dump_path = GetDumpFromSelfDestroyingProgram(args[0], pipe_name)
|
||||||
if not destroyed_dump_path:
|
if not destroyed_dump_path:
|
||||||
return 1
|
return 1
|
||||||
@ -265,7 +288,11 @@ def main(args):
|
|||||||
if not z7_dump_path:
|
if not z7_dump_path:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
RunTests(cdb_path, crashy_dump_path, destroyed_dump_path, z7_dump_path,
|
RunTests(cdb_path,
|
||||||
|
crashy_dump_path,
|
||||||
|
start_handler_dump_path,
|
||||||
|
destroyed_dump_path,
|
||||||
|
z7_dump_path,
|
||||||
pipe_name)
|
pipe_name)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
@ -128,7 +128,7 @@ void TestCrashingChild(const base::string16& directory_modification) {
|
|||||||
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
|
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
|
||||||
CrashingDelegate delegate(server_ready.get(), completed.get());
|
CrashingDelegate delegate(server_ready.get(), completed.get());
|
||||||
|
|
||||||
ExceptionHandlerServer exception_handler_server(pipe_name);
|
ExceptionHandlerServer exception_handler_server(pipe_name, true);
|
||||||
RunServerThread server_thread(&exception_handler_server, &delegate);
|
RunServerThread server_thread(&exception_handler_server, &delegate);
|
||||||
server_thread.Start();
|
server_thread.Start();
|
||||||
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
|
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
|
||||||
@ -230,7 +230,7 @@ void TestDumpWithoutCrashingChild(
|
|||||||
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
|
ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr));
|
||||||
SimulateDelegate delegate(server_ready.get(), completed.get());
|
SimulateDelegate delegate(server_ready.get(), completed.get());
|
||||||
|
|
||||||
ExceptionHandlerServer exception_handler_server(pipe_name);
|
ExceptionHandlerServer exception_handler_server(pipe_name, true);
|
||||||
RunServerThread server_thread(&exception_handler_server, &delegate);
|
RunServerThread server_thread(&exception_handler_server, &delegate);
|
||||||
server_thread.Start();
|
server_thread.Start();
|
||||||
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
|
ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread(
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "test/win/child_launcher.h"
|
#include "util/win/command_line.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
@ -119,7 +119,6 @@ void MultiprocessExec::PreFork() {
|
|||||||
command_line_.clear();
|
command_line_.clear();
|
||||||
AppendCommandLineArgument(base::UTF8ToUTF16(command_), &command_line_);
|
AppendCommandLineArgument(base::UTF8ToUTF16(command_), &command_line_);
|
||||||
for (size_t i = 0; i < arguments_.size(); ++i) {
|
for (size_t i = 0; i < arguments_.size(); ++i) {
|
||||||
command_line_ += L" ";
|
|
||||||
AppendCommandLineArgument(base::UTF8ToUTF16(arguments_[i]), &command_line_);
|
AppendCommandLineArgument(base::UTF8ToUTF16(arguments_[i]), &command_line_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "test/win/child_launcher.h"
|
#include "test/win/child_launcher.h"
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "util/win/command_line.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
@ -96,39 +97,5 @@ DWORD ChildLauncher::WaitForExit() {
|
|||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ref: http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
|
|
||||||
void AppendCommandLineArgument(const std::wstring& argument,
|
|
||||||
std::wstring* command_line) {
|
|
||||||
// Don't bother quoting if unnecessary.
|
|
||||||
if (!argument.empty() &&
|
|
||||||
argument.find_first_of(L" \t\n\v\"") == std::wstring::npos) {
|
|
||||||
command_line->append(argument);
|
|
||||||
} else {
|
|
||||||
command_line->push_back(L'"');
|
|
||||||
for (std::wstring::const_iterator i = argument.begin();; ++i) {
|
|
||||||
size_t backslash_count = 0;
|
|
||||||
while (i != argument.end() && *i == L'\\') {
|
|
||||||
++i;
|
|
||||||
++backslash_count;
|
|
||||||
}
|
|
||||||
if (i == argument.end()) {
|
|
||||||
// Escape all backslashes, but let the terminating double quotation mark
|
|
||||||
// we add below be interpreted as a metacharacter.
|
|
||||||
command_line->append(backslash_count * 2, L'\\');
|
|
||||||
break;
|
|
||||||
} else if (*i == L'"') {
|
|
||||||
// Escape all backslashes and the following double quotation mark.
|
|
||||||
command_line->append(backslash_count * 2 + 1, L'\\');
|
|
||||||
command_line->push_back(*i);
|
|
||||||
} else {
|
|
||||||
// Backslashes aren't special here.
|
|
||||||
command_line->append(backslash_count, L'\\');
|
|
||||||
command_line->push_back(*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
command_line->push_back(L'"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -67,14 +67,6 @@ class ChildLauncher {
|
|||||||
ScopedFileHANDLE stdin_write_handle_;
|
ScopedFileHANDLE stdin_write_handle_;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! \brief Utility function for building escaped command lines.
|
|
||||||
//!
|
|
||||||
//! \param[in] argument Appended to \a command_line surrounded by properly
|
|
||||||
//! escaped quotation marks, if necessary.
|
|
||||||
//! \param[inout] command_line The command line being constructed.
|
|
||||||
void AppendCommandLineArgument(const std::wstring& argument,
|
|
||||||
std::wstring* command_line);
|
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
|
||||||
|
@ -152,6 +152,8 @@
|
|||||||
'win/capture_context.asm',
|
'win/capture_context.asm',
|
||||||
'win/capture_context.h',
|
'win/capture_context.h',
|
||||||
'win/checked_win_address_range.h',
|
'win/checked_win_address_range.h',
|
||||||
|
'win/command_line.cc',
|
||||||
|
'win/command_line.h',
|
||||||
'win/critical_section_with_debug_info.cc',
|
'win/critical_section_with_debug_info.cc',
|
||||||
'win/critical_section_with_debug_info.h',
|
'win/critical_section_with_debug_info.h',
|
||||||
'win/exception_handler_server.cc',
|
'win/exception_handler_server.cc',
|
||||||
|
@ -80,6 +80,7 @@
|
|||||||
'thread/thread_log_messages_test.cc',
|
'thread/thread_log_messages_test.cc',
|
||||||
'thread/thread_test.cc',
|
'thread/thread_test.cc',
|
||||||
'win/capture_context_test.cc',
|
'win/capture_context_test.cc',
|
||||||
|
'win/command_line_test.cc',
|
||||||
'win/critical_section_with_debug_info_test.cc',
|
'win/critical_section_with_debug_info_test.cc',
|
||||||
'win/exception_handler_server_test.cc',
|
'win/exception_handler_server_test.cc',
|
||||||
'win/get_function_test.cc',
|
'win/get_function_test.cc',
|
||||||
|
58
util/win/command_line.cc
Normal file
58
util/win/command_line.cc
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright 2015 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "util/win/command_line.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
// Ref:
|
||||||
|
// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx
|
||||||
|
void AppendCommandLineArgument(const std::wstring& argument,
|
||||||
|
std::wstring* command_line) {
|
||||||
|
if (!command_line->empty()) {
|
||||||
|
command_line->push_back(L' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don’t bother quoting if unnecessary.
|
||||||
|
if (!argument.empty() &&
|
||||||
|
argument.find_first_of(L" \t\n\v\"") == std::wstring::npos) {
|
||||||
|
command_line->append(argument);
|
||||||
|
} else {
|
||||||
|
command_line->push_back(L'"');
|
||||||
|
for (std::wstring::const_iterator i = argument.begin();; ++i) {
|
||||||
|
size_t backslash_count = 0;
|
||||||
|
while (i != argument.end() && *i == L'\\') {
|
||||||
|
++i;
|
||||||
|
++backslash_count;
|
||||||
|
}
|
||||||
|
if (i == argument.end()) {
|
||||||
|
// Escape all backslashes, but let the terminating double quotation mark
|
||||||
|
// we add below be interpreted as a metacharacter.
|
||||||
|
command_line->append(backslash_count * 2, L'\\');
|
||||||
|
break;
|
||||||
|
} else if (*i == L'"') {
|
||||||
|
// Escape all backslashes and the following double quotation mark.
|
||||||
|
command_line->append(backslash_count * 2 + 1, L'\\');
|
||||||
|
command_line->push_back(*i);
|
||||||
|
} else {
|
||||||
|
// Backslashes aren’t special here.
|
||||||
|
command_line->append(backslash_count, L'\\');
|
||||||
|
command_line->push_back(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command_line->push_back(L'"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
38
util/win/command_line.h
Normal file
38
util/win/command_line.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2015 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#ifndef CRASHPAD_UTIL_WIN_COMMAND_LINE_H_
|
||||||
|
#define CRASHPAD_UTIL_WIN_COMMAND_LINE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief Utility function for building escaped command lines.
|
||||||
|
//!
|
||||||
|
//! This builds a command line so that individual arguments can be reliably
|
||||||
|
//! decoded by `CommandLineToArgvW()`.
|
||||||
|
//!
|
||||||
|
//! \a argument is appended to \a command_line. If necessary, it will be placed
|
||||||
|
//! in quotation marks and escaped properly. If \a command_line is initially
|
||||||
|
//! non-empty, a space will precede \a argument.
|
||||||
|
//!
|
||||||
|
//! \param[in] argument The argument to append to \a command_line.
|
||||||
|
//! \param[inout] command_line The command line being constructed.
|
||||||
|
void AppendCommandLineArgument(const std::wstring& argument,
|
||||||
|
std::wstring* command_line);
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_WIN_COMMAND_LINE_H_
|
177
util/win/command_line_test.cc
Normal file
177
util/win/command_line_test.cc
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// Copyright 2015 The Crashpad Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
#include "util/win/command_line.h"
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
#include "base/basictypes.h"
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/scoped_generic.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "test/errors.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct LocalAllocTraits {
|
||||||
|
static HLOCAL InvalidValue() {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Free(HLOCAL memory) {
|
||||||
|
PLOG_IF(ERROR, LocalFree(memory) != nullptr) << "LocalFree";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using ScopedLocalAlloc = base::ScopedGeneric<HLOCAL, LocalAllocTraits>;
|
||||||
|
|
||||||
|
// Calls AppendCommandLineArgument() for every argument in argv, then calls
|
||||||
|
// CommandLineToArgvW() to decode the string into a vector again, and compares
|
||||||
|
// the input and output.
|
||||||
|
void AppendCommandLineArgumentTest(size_t argc, const wchar_t* argv[]) {
|
||||||
|
std::wstring command_line;
|
||||||
|
for (size_t index = 0; index < argc; ++index) {
|
||||||
|
AppendCommandLineArgument(argv[index], &command_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
int test_argc;
|
||||||
|
wchar_t** test_argv = CommandLineToArgvW(command_line.c_str(), &test_argc);
|
||||||
|
|
||||||
|
ASSERT_TRUE(test_argv) << ErrorMessage("CommandLineToArgvW");
|
||||||
|
ScopedLocalAlloc test_argv_owner(test_argv);
|
||||||
|
ASSERT_EQ(argc, test_argc);
|
||||||
|
|
||||||
|
for (size_t index = 0; index < argc; ++index) {
|
||||||
|
EXPECT_STREQ(argv[index], test_argv[index]) << "index " << index;
|
||||||
|
}
|
||||||
|
EXPECT_FALSE(test_argv[argc]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CommandLine, AppendCommandLineArgument) {
|
||||||
|
// Most of these test cases come from
|
||||||
|
// http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx,
|
||||||
|
// which was also a reference for the implementation of
|
||||||
|
// AppendCommandLineArgument().
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("simple");
|
||||||
|
|
||||||
|
const wchar_t* kArguments[] = {
|
||||||
|
L"child.exe",
|
||||||
|
L"argument 1",
|
||||||
|
L"argument 2",
|
||||||
|
};
|
||||||
|
AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("path with spaces");
|
||||||
|
|
||||||
|
const wchar_t* kArguments[] = {
|
||||||
|
L"child.exe",
|
||||||
|
L"argument1",
|
||||||
|
L"argument 2",
|
||||||
|
L"\\some\\path with\\spaces",
|
||||||
|
};
|
||||||
|
AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("argument with embedded quotation marks");
|
||||||
|
|
||||||
|
const wchar_t* kArguments[] = {
|
||||||
|
L"child.exe",
|
||||||
|
L"argument1",
|
||||||
|
L"she said, \"you had me at hello\"",
|
||||||
|
L"\\some\\path with\\spaces",
|
||||||
|
};
|
||||||
|
AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("argument with unbalanced quotation marks");
|
||||||
|
|
||||||
|
const wchar_t* kArguments[] = {
|
||||||
|
L"child.exe",
|
||||||
|
L"argument1",
|
||||||
|
L"argument\"2",
|
||||||
|
L"argument3",
|
||||||
|
L"argument4",
|
||||||
|
};
|
||||||
|
AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("argument ending with backslash");
|
||||||
|
|
||||||
|
const wchar_t* kArguments[] = {
|
||||||
|
L"child.exe",
|
||||||
|
L"\\some\\directory with\\spaces\\",
|
||||||
|
L"argument2",
|
||||||
|
};
|
||||||
|
AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("empty argument");
|
||||||
|
|
||||||
|
const wchar_t* kArguments[] = {
|
||||||
|
L"child.exe",
|
||||||
|
L"",
|
||||||
|
L"argument2",
|
||||||
|
};
|
||||||
|
AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SCOPED_TRACE("funny nonprintable characters");
|
||||||
|
|
||||||
|
const wchar_t* kArguments[] = {
|
||||||
|
L"child.exe",
|
||||||
|
L"argument 1",
|
||||||
|
L"argument\t2",
|
||||||
|
L"argument\n3",
|
||||||
|
L"argument\v4",
|
||||||
|
L"argument\"5",
|
||||||
|
L" ",
|
||||||
|
L"\t",
|
||||||
|
L"\n",
|
||||||
|
L"\v",
|
||||||
|
L"\"",
|
||||||
|
L" x",
|
||||||
|
L"\tx",
|
||||||
|
L"\nx",
|
||||||
|
L"\vx",
|
||||||
|
L"\"x",
|
||||||
|
L"x ",
|
||||||
|
L"x\t",
|
||||||
|
L"x\n",
|
||||||
|
L"x\v",
|
||||||
|
L"x\"",
|
||||||
|
L" ",
|
||||||
|
L"\t\t",
|
||||||
|
L"\n\n",
|
||||||
|
L"\v\v",
|
||||||
|
L"\"\"",
|
||||||
|
L" \t\n\v\"",
|
||||||
|
};
|
||||||
|
AppendCommandLineArgumentTest(arraysize(kArguments), kArguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
@ -234,11 +234,13 @@ class ClientData {
|
|||||||
ExceptionHandlerServer::Delegate::~Delegate() {
|
ExceptionHandlerServer::Delegate::~Delegate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionHandlerServer::ExceptionHandlerServer(const std::string& pipe_name)
|
ExceptionHandlerServer::ExceptionHandlerServer(const std::string& pipe_name,
|
||||||
|
bool persistent)
|
||||||
: pipe_name_(pipe_name),
|
: pipe_name_(pipe_name),
|
||||||
port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),
|
port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),
|
||||||
clients_lock_(),
|
clients_lock_(),
|
||||||
clients_() {
|
clients_(),
|
||||||
|
persistent_(persistent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionHandlerServer::~ExceptionHandlerServer() {
|
ExceptionHandlerServer::~ExceptionHandlerServer() {
|
||||||
@ -297,11 +299,11 @@ void ExceptionHandlerServer::Run(Delegate* delegate) {
|
|||||||
// outstanding threadpool waits are complete. This is important because the
|
// outstanding threadpool waits are complete. This is important because the
|
||||||
// process handle can be signalled *before* the dump request is signalled.
|
// process handle can be signalled *before* the dump request is signalled.
|
||||||
internal::ClientData* client = reinterpret_cast<internal::ClientData*>(key);
|
internal::ClientData* client = reinterpret_cast<internal::ClientData*>(key);
|
||||||
{
|
base::AutoLock lock(clients_lock_);
|
||||||
base::AutoLock lock(clients_lock_);
|
clients_.erase(client);
|
||||||
clients_.erase(client);
|
|
||||||
}
|
|
||||||
delete client;
|
delete client;
|
||||||
|
if (!persistent_ && clients_.empty())
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal to the named pipe instances that they should terminate.
|
// Signal to the named pipe instances that they should terminate.
|
||||||
|
@ -64,7 +64,10 @@ class ExceptionHandlerServer {
|
|||||||
//!
|
//!
|
||||||
//! \param[in] pipe_name The name of the pipe to listen on. Must be of the
|
//! \param[in] pipe_name The name of the pipe to listen on. Must be of the
|
||||||
//! form "\\.\pipe\<some_name>".
|
//! form "\\.\pipe\<some_name>".
|
||||||
explicit ExceptionHandlerServer(const std::string& pipe_name);
|
//! \param[in] persistent `true` if Run() should not return until Stop() is
|
||||||
|
//! called. If `false`, Run() will return when all clients have exited,
|
||||||
|
//! although Run() will always wait for the first client to connect.
|
||||||
|
ExceptionHandlerServer(const std::string& pipe_name, bool persistent);
|
||||||
|
|
||||||
~ExceptionHandlerServer();
|
~ExceptionHandlerServer();
|
||||||
|
|
||||||
@ -92,6 +95,8 @@ class ExceptionHandlerServer {
|
|||||||
base::Lock clients_lock_;
|
base::Lock clients_lock_;
|
||||||
std::set<internal::ClientData*> clients_;
|
std::set<internal::ClientData*> clients_;
|
||||||
|
|
||||||
|
bool persistent_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
|
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class ExceptionHandlerServerTest : public testing::Test {
|
|||||||
base::StringPrintf("%08x", GetCurrentProcessId())),
|
base::StringPrintf("%08x", GetCurrentProcessId())),
|
||||||
server_ready_(CreateEvent(nullptr, false, false, nullptr)),
|
server_ready_(CreateEvent(nullptr, false, false, nullptr)),
|
||||||
delegate_(server_ready_.get()),
|
delegate_(server_ready_.get()),
|
||||||
server_(pipe_name_),
|
server_(pipe_name_, true),
|
||||||
server_thread_(&server_, &delegate_) {}
|
server_thread_(&server_, &delegate_) {}
|
||||||
|
|
||||||
TestDelegate& delegate() { return delegate_; }
|
TestDelegate& delegate() { return delegate_; }
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include "test/win/child_launcher.h"
|
#include "test/win/child_launcher.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
#include "util/misc/uuid.h"
|
#include "util/misc/uuid.h"
|
||||||
|
#include "util/win/command_line.h"
|
||||||
#include "util/win/get_function.h"
|
#include "util/win/get_function.h"
|
||||||
#include "util/win/scoped_handle.h"
|
#include "util/win/scoped_handle.h"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user