Add a way for the client apps to specify custom information in case of out-of-process

scenarios that the OOP server can use in whatever way it wants to.

Fix a bug in CrashGenerationserver where CreateNamedPipe failure was not checked correctly.

TODO in near future: Add a custom stream to minidump files for the custom information.



git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@267 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
doshimun 2008-05-05 20:03:56 +00:00
parent 47df365bf8
commit 0ded3d718f
10 changed files with 187 additions and 25 deletions

View file

@ -32,10 +32,49 @@
#include <Windows.h> #include <Windows.h>
#include <DbgHelp.h> #include <DbgHelp.h>
#include <string>
#include <utility>
#include "google_breakpad/common/minidump_format.h" #include "google_breakpad/common/minidump_format.h"
namespace google_breakpad { namespace google_breakpad {
// Name/value pair for custom client information.
struct CustomInfoEntry {
// Maximum length for name and value for client custom info.
static const int kNameMaxLength = 64;
static const int kValueMaxLength = 64;
CustomInfoEntry() {
// Putting name and value in initializer list makes VC++ show warning 4351.
set_name(NULL);
set_value(NULL);
}
CustomInfoEntry(const wchar_t* name_arg, const wchar_t* value_arg) {
set_name(name_arg);
set_value(value_arg);
}
void set_name(const wchar_t* name_arg) {
if (!name_arg) {
name[0] = L'\0';
return;
}
wcscpy_s(name, kNameMaxLength, name_arg);
}
void set_value(const wchar_t* value_arg) {
if (!value_arg) {
value[0] = L'\0';
return;
}
wcscpy_s(value, kValueMaxLength, value_arg);
}
wchar_t name[kNameMaxLength];
wchar_t value[kValueMaxLength];
};
// Constants for the protocol between client and the server. // Constants for the protocol between client and the server.
// Tags sent with each message indicating the purpose of // Tags sent with each message indicating the purpose of
@ -47,6 +86,11 @@ enum MessageTag {
MESSAGE_TAG_REGISTRATION_ACK = 3 MESSAGE_TAG_REGISTRATION_ACK = 3
}; };
struct CustomClientInfo {
const CustomInfoEntry* entries;
int count;
};
// Message structure for IPC between crash client and crash server. // Message structure for IPC between crash client and crash server.
struct ProtocolMessage { struct ProtocolMessage {
ProtocolMessage() ProtocolMessage()
@ -56,6 +100,7 @@ struct ProtocolMessage {
thread_id(0), thread_id(0),
exception_pointers(NULL), exception_pointers(NULL),
assert_info(NULL), assert_info(NULL),
custom_client_info(),
dump_request_handle(NULL), dump_request_handle(NULL),
dump_generated_handle(NULL), dump_generated_handle(NULL),
server_alive_handle(NULL) { server_alive_handle(NULL) {
@ -68,6 +113,7 @@ struct ProtocolMessage {
DWORD* arg_thread_id, DWORD* arg_thread_id,
EXCEPTION_POINTERS** arg_exception_pointers, EXCEPTION_POINTERS** arg_exception_pointers,
MDRawAssertionInfo* arg_assert_info, MDRawAssertionInfo* arg_assert_info,
const CustomClientInfo& custom_info,
HANDLE arg_dump_request_handle, HANDLE arg_dump_request_handle,
HANDLE arg_dump_generated_handle, HANDLE arg_dump_generated_handle,
HANDLE arg_server_alive) HANDLE arg_server_alive)
@ -77,6 +123,7 @@ struct ProtocolMessage {
thread_id(arg_thread_id), thread_id(arg_thread_id),
exception_pointers(arg_exception_pointers), exception_pointers(arg_exception_pointers),
assert_info(arg_assert_info), assert_info(arg_assert_info),
custom_client_info(custom_info),
dump_request_handle(arg_dump_request_handle), dump_request_handle(arg_dump_request_handle),
dump_generated_handle(arg_dump_generated_handle), dump_generated_handle(arg_dump_generated_handle),
server_alive_handle(arg_server_alive) { server_alive_handle(arg_server_alive) {
@ -101,6 +148,9 @@ struct ProtocolMessage {
// pure call failure. // pure call failure.
MDRawAssertionInfo* assert_info; MDRawAssertionInfo* assert_info;
// Custom client information.
CustomClientInfo custom_client_info;
// Handle to signal the crash event. // Handle to signal the crash event.
HANDLE dump_request_handle; HANDLE dump_request_handle;

View file

@ -28,6 +28,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "client/windows/crash_generation/client_info.h" #include "client/windows/crash_generation/client_info.h"
#include "client/windows/common/ipc_protocol.h"
namespace google_breakpad { namespace google_breakpad {
@ -36,12 +37,14 @@ ClientInfo::ClientInfo(CrashGenerationServer* crash_server,
MINIDUMP_TYPE dump_type, MINIDUMP_TYPE dump_type,
DWORD* thread_id, DWORD* thread_id,
EXCEPTION_POINTERS** ex_info, EXCEPTION_POINTERS** ex_info,
MDRawAssertionInfo* assert_info) MDRawAssertionInfo* assert_info,
const CustomClientInfo& custom_client_info)
: crash_server_(crash_server), : crash_server_(crash_server),
pid_(pid), pid_(pid),
dump_type_(dump_type), dump_type_(dump_type),
ex_info_(ex_info), ex_info_(ex_info),
assert_info_(assert_info), assert_info_(assert_info),
custom_client_info_(custom_client_info),
thread_id_(thread_id), thread_id_(thread_id),
process_handle_(NULL), process_handle_(NULL),
dump_requested_handle_(NULL), dump_requested_handle_(NULL),
@ -117,8 +120,7 @@ bool ClientInfo::UnregisterWaits() {
return success; return success;
} }
bool ClientInfo::GetClientExceptionInfo( bool ClientInfo::GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const {
EXCEPTION_POINTERS** ex_info) const {
SIZE_T bytes_count = 0; SIZE_T bytes_count = 0;
if (!ReadProcessMemory(process_handle_, if (!ReadProcessMemory(process_handle_,
ex_info_, ex_info_,
@ -144,4 +146,32 @@ bool ClientInfo::GetClientThreadId(DWORD* thread_id) const {
return bytes_count == sizeof(*thread_id); return bytes_count == sizeof(*thread_id);
} }
bool ClientInfo::PopulateCustomInfo() {
SIZE_T bytes_count = 0;
SIZE_T read_count = sizeof(CustomInfoEntry) * custom_client_info_.count;
// If the scoped array for custom info already has an array, it will be
// the same size as what we need. This is because the number of custom info
// entries is always the same. So allocate memory only if scoped array has
// a NULL pointer.
if (!custom_info_entries_.get()) {
custom_info_entries_.reset(new CustomInfoEntry[custom_client_info_.count]);
}
if (!ReadProcessMemory(process_handle_,
custom_client_info_.entries,
custom_info_entries_.get(),
read_count,
&bytes_count)) {
return false;
}
return bytes_count == read_count;
}
int ClientInfo::GetCustomInfo(CustomInfoEntry const** custom_info) const {
*custom_info = custom_info_entries_.get();
return custom_client_info_.count;
}
} // namespace google_breakpad } // namespace google_breakpad

View file

@ -32,7 +32,9 @@
#include <Windows.h> #include <Windows.h>
#include <DbgHelp.h> #include <DbgHelp.h>
#include "client/windows/common/ipc_protocol.h"
#include "google_breakpad/common/minidump_format.h" #include "google_breakpad/common/minidump_format.h"
#include "processor/scoped_ptr.h"
namespace google_breakpad { namespace google_breakpad {
@ -49,7 +51,8 @@ class ClientInfo {
MINIDUMP_TYPE dump_type, MINIDUMP_TYPE dump_type,
DWORD* thread_id, DWORD* thread_id,
EXCEPTION_POINTERS** ex_info, EXCEPTION_POINTERS** ex_info,
MDRawAssertionInfo* assert_info); MDRawAssertionInfo* assert_info,
const CustomClientInfo& custom_client_info);
~ClientInfo(); ~ClientInfo();
@ -85,6 +88,10 @@ class ClientInfo {
bool Initialize(); bool Initialize();
bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const; bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const;
bool GetClientThreadId(DWORD* thread_id) const; bool GetClientThreadId(DWORD* thread_id) const;
// Reads the custom information from the client process address space.
bool PopulateCustomInfo();
// Returns the client custom information.
int GetCustomInfo(CustomInfoEntry const** custom_info) const;
private: private:
// Crash generation server. // Crash generation server.
@ -113,6 +120,14 @@ class ClientInfo {
// in the address space of another process. // in the address space of another process.
MDRawAssertionInfo* assert_info_; MDRawAssertionInfo* assert_info_;
// Custom information about the client.
CustomClientInfo custom_client_info_;
// Contains the custom client info entries read from the client process
// memory. This will be populated only if the method GetClientCustomInfo
// is called.
scoped_array<CustomInfoEntry> custom_info_entries_;
// Address of a variable in the client process address space that // Address of a variable in the client process address space that
// will contain the thread id of the crashing client thread. // will contain the thread id of the crashing client thread.
// //

View file

@ -29,6 +29,7 @@
#include "client/windows/crash_generation/crash_generation_client.h" #include "client/windows/crash_generation/crash_generation_client.h"
#include <cassert> #include <cassert>
#include <utility>
#include "client/windows/common/ipc_protocol.h" #include "client/windows/common/ipc_protocol.h"
namespace google_breakpad { namespace google_breakpad {
@ -89,17 +90,23 @@ static bool TransactNamedPipeDebugHelper(HANDLE pipe,
} }
**/ **/
CrashGenerationClient::CrashGenerationClient(const wchar_t* pipe_name, CrashGenerationClient::CrashGenerationClient(
MINIDUMP_TYPE dump_type) const wchar_t* pipe_name,
: pipe_name_(pipe_name), MINIDUMP_TYPE dump_type,
dump_type_(dump_type), const CustomClientInfo* custom_info)
thread_id_(0), : pipe_name_(pipe_name),
server_process_id_(0), dump_type_(dump_type),
crash_event_(NULL), thread_id_(0),
crash_generated_(NULL), server_process_id_(0),
server_alive_(NULL), crash_event_(NULL),
exception_pointers_(NULL) { crash_generated_(NULL),
server_alive_(NULL),
exception_pointers_(NULL),
custom_info_() {
memset(&assert_info_, 0, sizeof(assert_info_)); memset(&assert_info_, 0, sizeof(assert_info_));
if (custom_info) {
custom_info_ = *custom_info;
}
} }
CrashGenerationClient::~CrashGenerationClient() { CrashGenerationClient::~CrashGenerationClient() {
@ -184,6 +191,7 @@ bool CrashGenerationClient::RegisterClient(HANDLE pipe) {
&thread_id_, &thread_id_,
&exception_pointers_, &exception_pointers_,
&assert_info_, &assert_info_,
custom_info_,
NULL, NULL,
NULL, NULL,
NULL); NULL);

View file

@ -33,10 +33,14 @@
#include <windows.h> #include <windows.h>
#include <dbghelp.h> #include <dbghelp.h>
#include <string> #include <string>
#include <utility>
#include "client/windows/common/ipc_protocol.h" #include "client/windows/common/ipc_protocol.h"
#include "processor/scoped_ptr.h"
namespace google_breakpad { namespace google_breakpad {
struct CustomClientInfo;
// Abstraction of client-side implementation of out of process // Abstraction of client-side implementation of out of process
// crash generation. // crash generation.
// //
@ -59,7 +63,8 @@ namespace google_breakpad {
class CrashGenerationClient { class CrashGenerationClient {
public: public:
CrashGenerationClient(const wchar_t* pipe_name, CrashGenerationClient(const wchar_t* pipe_name,
MINIDUMP_TYPE dump_type); MINIDUMP_TYPE dump_type,
const CustomClientInfo* custom_info);
~CrashGenerationClient(); ~CrashGenerationClient();
@ -115,6 +120,9 @@ class CrashGenerationClient {
// Pipe name to use to talk to server. // Pipe name to use to talk to server.
std::wstring pipe_name_; std::wstring pipe_name_;
// Custom client information
CustomClientInfo custom_info_;
// Type of dump to generate. // Type of dump to generate.
MINIDUMP_TYPE dump_type_; MINIDUMP_TYPE dump_type_;

View file

@ -220,7 +220,7 @@ bool CrashGenerationServer::Start() {
kInBufferSize, kInBufferSize,
0, 0,
pipe_sec_attrs_); pipe_sec_attrs_);
if (!pipe_) { if (pipe_ == INVALID_HANDLE_VALUE) {
return false; return false;
} }
@ -400,7 +400,8 @@ void CrashGenerationServer::HandleReadDoneState() {
msg_.dump_type, msg_.dump_type,
msg_.thread_id, msg_.thread_id,
msg_.exception_pointers, msg_.exception_pointers,
msg_.assert_info)); msg_.assert_info,
msg_.custom_client_info));
if (!client_info->Initialize()) { if (!client_info->Initialize()) {
server_state_ = IPC_SERVER_STATE_DISCONNECTING; server_state_ = IPC_SERVER_STATE_DISCONNECTING;
@ -726,6 +727,7 @@ void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) {
void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) { void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) {
assert(context); assert(context);
ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context); ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
client_info->PopulateCustomInfo();
CrashGenerationServer* crash_server = client_info->crash_server(); CrashGenerationServer* crash_server = client_info->crash_server();
assert(crash_server); assert(crash_server);

View file

@ -34,6 +34,7 @@
#include "common/windows/string_utils-inl.h" #include "common/windows/string_utils-inl.h"
#include "client/windows/common/ipc_protocol.h"
#include "client/windows/handler/exception_handler.h" #include "client/windows/handler/exception_handler.h"
#include "common/windows/guid_string.h" #include "common/windows/guid_string.h"
@ -52,14 +53,16 @@ ExceptionHandler::ExceptionHandler(const wstring& dump_path,
void* callback_context, void* callback_context,
int handler_types, int handler_types,
MINIDUMP_TYPE dump_type, MINIDUMP_TYPE dump_type,
const wchar_t* pipe_name) { const wchar_t* pipe_name,
const CustomClientInfo* custom_info) {
Initialize(dump_path, Initialize(dump_path,
filter, filter,
callback, callback,
callback_context, callback_context,
handler_types, handler_types,
dump_type, dump_type,
pipe_name); pipe_name,
custom_info);
} }
ExceptionHandler::ExceptionHandler(const wstring &dump_path, ExceptionHandler::ExceptionHandler(const wstring &dump_path,
@ -73,6 +76,7 @@ ExceptionHandler::ExceptionHandler(const wstring &dump_path,
callback_context, callback_context,
handler_types, handler_types,
MiniDumpNormal, MiniDumpNormal,
NULL,
NULL); NULL);
} }
@ -82,7 +86,8 @@ void ExceptionHandler::Initialize(const wstring& dump_path,
void* callback_context, void* callback_context,
int handler_types, int handler_types,
MINIDUMP_TYPE dump_type, MINIDUMP_TYPE dump_type,
const wchar_t* pipe_name) { const wchar_t* pipe_name,
const CustomClientInfo* custom_info) {
filter_ = filter; filter_ = filter;
callback_ = callback; callback_ = callback;
callback_context_ = callback_context; callback_context_ = callback_context;
@ -112,7 +117,9 @@ void ExceptionHandler::Initialize(const wstring& dump_path,
// Attempt to use out-of-process if user has specified pipe name. // Attempt to use out-of-process if user has specified pipe name.
if (pipe_name != NULL) { if (pipe_name != NULL) {
scoped_ptr<CrashGenerationClient> client( scoped_ptr<CrashGenerationClient> client(
new CrashGenerationClient(pipe_name, dump_type_)); new CrashGenerationClient(pipe_name,
dump_type_,
custom_info));
// If successful in registering with the monitoring process, // If successful in registering with the monitoring process,
// there is no need to setup in-process crash generation. // there is no need to setup in-process crash generation.

View file

@ -68,6 +68,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "client/windows/common/ipc_protocol.h"
#include "client/windows/crash_generation/crash_generation_client.h" #include "client/windows/crash_generation/crash_generation_client.h"
#include "google_breakpad/common/minidump_format.h" #include "google_breakpad/common/minidump_format.h"
#include "processor/scoped_ptr.h" #include "processor/scoped_ptr.h"
@ -163,7 +164,8 @@ class ExceptionHandler {
void* callback_context, void* callback_context,
int handler_types, int handler_types,
MINIDUMP_TYPE dump_type, MINIDUMP_TYPE dump_type,
const wchar_t* pipe_name); const wchar_t* pipe_name,
const CustomClientInfo* custom_info);
~ExceptionHandler(); ~ExceptionHandler();
@ -213,7 +215,8 @@ class ExceptionHandler {
void* callback_context, void* callback_context,
int handler_types, int handler_types,
MINIDUMP_TYPE dump_type, MINIDUMP_TYPE dump_type,
const wchar_t* pipe_name); const wchar_t* pipe_name,
const CustomClientInfo* custom_info);
// Function pointer type for MiniDumpWriteDump, which is looked up // Function pointer type for MiniDumpWriteDump, which is looked up
// dynamically. // dynamically.

View file

@ -35,7 +35,7 @@
namespace google_breakpad { namespace google_breakpad {
const int kMaxLoadString = 100; const int kMaxLoadString = 100;
const wchar_t kPipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\S-1-5-21-39260824-743453154-142223018-195347"; const wchar_t kPipeName[] = L"\\\\.\\pipe\\GoogleCrashServices-Testing";
const DWORD kEditBoxStyles = WS_CHILD | const DWORD kEditBoxStyles = WS_CHILD |
WS_VISIBLE | WS_VISIBLE |
@ -63,6 +63,13 @@ BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
static int k_custom_info_count = 2;
static CustomInfoEntry k_custom_info_entries[] =
{
CustomInfoEntry(L"prod", L"CrashTestApp"),
CustomInfoEntry(L"ver", L"1.0"),
};
static ExceptionHandler* handler = NULL; static ExceptionHandler* handler = NULL;
static CrashGenerationServer* crash_server = NULL; static CrashGenerationServer* crash_server = NULL;
@ -211,6 +218,33 @@ static void _cdecl ShowClientCrashed(void* context,
} }
QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT);
const CustomInfoEntry* custom_info = NULL;
int custom_info_count = client_info->GetCustomInfo(&custom_info);
if (custom_info_count <= 0) {
return;
}
wstring str_line;
for (int i = 0; i < custom_info_count; ++i) {
if (i > 0) {
str_line += L", ";
}
str_line += custom_info[i].name;
str_line += L": ";
str_line += custom_info[i].value;
}
line = new TCHAR[kMaximumLineLength];
result = swprintf_s(line,
kMaximumLineLength,
L"%s\n",
str_line.c_str());
if (result == -1) {
delete[] line;
return;
}
QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT);
} }
static void _cdecl ShowClientExited(void* context, static void _cdecl ShowClientExited(void* context,
@ -276,6 +310,7 @@ void RequestDump() {
if (!handler->WriteMinidump()) { if (!handler->WriteMinidump()) {
MessageBoxW(NULL, L"Dump request failed", L"Dumper", MB_OK); MessageBoxW(NULL, L"Dump request failed", L"Dumper", MB_OK);
} }
k_custom_info_entries[1].set_value(L"1.1");
} }
void CleanUp() { void CleanUp() {
@ -427,6 +462,8 @@ int APIENTRY _tWinMain(HINSTANCE instance,
cs_edit = new CRITICAL_SECTION(); cs_edit = new CRITICAL_SECTION();
InitializeCriticalSection(cs_edit); InitializeCriticalSection(cs_edit);
CustomClientInfo custom_info = {k_custom_info_entries, k_custom_info_count};
// This is needed for CRT to not show dialog for invalid param // This is needed for CRT to not show dialog for invalid param
// failures and instead let the code handle it. // failures and instead let the code handle it.
_CrtSetReportMode(_CRT_ASSERT, 0); _CrtSetReportMode(_CRT_ASSERT, 0);
@ -436,7 +473,8 @@ int APIENTRY _tWinMain(HINSTANCE instance,
NULL, NULL,
ExceptionHandler::HANDLER_ALL, ExceptionHandler::HANDLER_ALL,
MiniDumpNormal, MiniDumpNormal,
kPipeName); kPipeName,
&custom_info);
// Initialize global strings. // Initialize global strings.
LoadString(instance, IDS_APP_TITLE, title, kMaxLoadString); LoadString(instance, IDS_APP_TITLE, title, kMaxLoadString);

View file

@ -77,6 +77,7 @@
#include <cassert> #include <cassert>
#include <list> #include <list>
#include "client/windows/common/ipc_protocol.h"
#include "client/windows/crash_generation/client_info.h" #include "client/windows/crash_generation/client_info.h"
#include "client/windows/crash_generation/crash_generation_client.h" #include "client/windows/crash_generation/crash_generation_client.h"
#include "client/windows/crash_generation/crash_generation_server.h" #include "client/windows/crash_generation/crash_generation_server.h"