mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-27 19:12:27 +00:00
Merge master 90ef7475cdb1 into doc
This commit is contained in:
commit
d3e355c95b
@ -92,7 +92,7 @@ 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) {
|
||||||
DCHECK_EQ(exception_port_, kMachPortNull);
|
DCHECK(!exception_port_.is_valid());
|
||||||
|
|
||||||
// Set up the arguments for execve() first. These aren’t needed until execve()
|
// Set up the arguments for execve() first. These aren’t needed until execve()
|
||||||
// is called, but it’s dangerous to do this in a child process after fork().
|
// is called, but it’s dangerous to do this in a child process after fork().
|
||||||
@ -211,13 +211,13 @@ bool CrashpadClient::StartHandler(
|
|||||||
// Rendezvous with the handler running in the grandchild process.
|
// Rendezvous with the handler running in the grandchild process.
|
||||||
exception_port_.reset(child_port_handshake.RunServer());
|
exception_port_.reset(child_port_handshake.RunServer());
|
||||||
|
|
||||||
return exception_port_ ? true : false;
|
return exception_port_.is_valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CrashpadClient::UseHandler() {
|
bool CrashpadClient::UseHandler() {
|
||||||
DCHECK_NE(exception_port_, kMachPortNull);
|
DCHECK(exception_port_.is_valid());
|
||||||
|
|
||||||
return SetCrashExceptionPorts(exception_port_);
|
return SetCrashExceptionPorts(exception_port_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -227,7 +227,7 @@ void CrashpadClient::UseSystemDefaultHandler() {
|
|||||||
|
|
||||||
// Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL
|
// Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL
|
||||||
// to clear the current exception ports.
|
// to clear the current exception ports.
|
||||||
if (!SetCrashExceptionPorts(system_crash_reporter_handler)) {
|
if (!SetCrashExceptionPorts(system_crash_reporter_handler.get())) {
|
||||||
SetCrashExceptionPorts(MACH_PORT_NULL);
|
SetCrashExceptionPorts(MACH_PORT_NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ void SimulateCrash(const NativeCPUContext& cpu_context) {
|
|||||||
DCHECK_LE(handlers.size(), 1u);
|
DCHECK_LE(handlers.size(), 1u);
|
||||||
if (handlers.size() == 1) {
|
if (handlers.size() == 1) {
|
||||||
DCHECK(handlers[0].mask & EXC_MASK_CRASH);
|
DCHECK(handlers[0].mask & EXC_MASK_CRASH);
|
||||||
success = DeliverException(thread,
|
success = DeliverException(thread.get(),
|
||||||
mach_task_self(),
|
mach_task_self(),
|
||||||
exception,
|
exception,
|
||||||
codes,
|
codes,
|
||||||
|
@ -157,6 +157,9 @@ enum MINIDUMP_STREAM_TYPE {
|
|||||||
//! \brief The stream type for MINIDUMP_SYSTEM_INFO.
|
//! \brief The stream type for MINIDUMP_SYSTEM_INFO.
|
||||||
SystemInfoStream = 7,
|
SystemInfoStream = 7,
|
||||||
|
|
||||||
|
//! \brief The stream contains information about active `HANDLE`s.
|
||||||
|
HandleDataStream = 12,
|
||||||
|
|
||||||
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
|
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
|
||||||
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
|
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
|
||||||
//!
|
//!
|
||||||
|
39
doc/appengine/README
Normal file
39
doc/appengine/README
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
This is the App Engine app that serves https://crashpad-home.appspot.com/.
|
||||||
|
|
||||||
|
To work on this app, obtain the App Engine SDK for Go from
|
||||||
|
https://cloud.google.com/appengine/downloads. Unpacking it produces a
|
||||||
|
go_appengine directory. This may be added to your $PATH for convenience,
|
||||||
|
although it is not necessary.
|
||||||
|
|
||||||
|
The commands in this README are expected to be run from the directory containing
|
||||||
|
app.yaml.
|
||||||
|
|
||||||
|
The App Engine SDK for Go provides App Engine packages at the “appengine” import
|
||||||
|
path, but not the newer “google.golang.org/appengine” path. The Crashpad app
|
||||||
|
uses the newer paths. See
|
||||||
|
https://github.com/golang/appengine#2-update-import-paths and
|
||||||
|
https://code.google.com/p/googleappengine/issues/detail?id=11670. To make these
|
||||||
|
available, obtain a Go release from https://golang.org/dl/, and run:
|
||||||
|
|
||||||
|
$ GOROOT=…/go_appengine/goroot GOPATH=…/go_appengine/gopath go get -d
|
||||||
|
|
||||||
|
To test locally:
|
||||||
|
|
||||||
|
$ goapp serve
|
||||||
|
|
||||||
|
Look for the “Starting module "default" running at: http://localhost:8080” line,
|
||||||
|
which tells you the URL of the local running instance of the app.
|
||||||
|
|
||||||
|
To deploy:
|
||||||
|
|
||||||
|
$ version=$(git rev-parse --short=12 HEAD)
|
||||||
|
$ [[ -n "$(git status --porcelain)" ]] && version+=-dirty
|
||||||
|
$ goapp deploy -version "${version}"
|
||||||
|
|
||||||
|
Note that app.yaml does not name a “version” to encourage you to use a git hash
|
||||||
|
as the version, as above.
|
||||||
|
|
||||||
|
Activate a newly-deployed version by visiting the App Engine console at
|
||||||
|
https://appengine.google.com/deployment?&app_id=s~crashpad-home, selecting it,
|
||||||
|
and choosing “Make Default”. It is also possible to delete old versions from
|
||||||
|
this page when they are no longer needed.
|
@ -1,5 +1,4 @@
|
|||||||
application: crashpad-home
|
application: crashpad-home
|
||||||
version: 1
|
|
||||||
runtime: go
|
runtime: go
|
||||||
api_version: go1
|
api_version: go1
|
||||||
|
|
||||||
|
@ -31,13 +31,16 @@ import (
|
|||||||
"google.golang.org/appengine/urlfetch"
|
"google.golang.org/appengine/urlfetch"
|
||||||
)
|
)
|
||||||
|
|
||||||
const baseURL = "https://chromium.googlesource.com/crashpad/crashpad/+/doc/doc/generated/?format=TEXT"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
http.HandleFunc("/", handler)
|
http.HandleFunc("/", handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handler(w http.ResponseWriter, r *http.Request) {
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
const (
|
||||||
|
baseURL = "https://chromium.googlesource.com/crashpad/crashpad/+/doc/doc/generated/?format=TEXT"
|
||||||
|
bugBaseURL = "https://bugs.chromium.org/p/crashpad/"
|
||||||
|
)
|
||||||
|
|
||||||
ctx := appengine.NewContext(r)
|
ctx := appengine.NewContext(r)
|
||||||
client := urlfetch.Client(ctx)
|
client := urlfetch.Client(ctx)
|
||||||
|
|
||||||
@ -47,6 +50,17 @@ func handler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.URL.Path == "/bug" || r.URL.Path == "/bug/" {
|
||||||
|
http.Redirect(w, r, bugBaseURL, http.StatusFound)
|
||||||
|
return
|
||||||
|
} else if r.URL.Path == "/bug/new" {
|
||||||
|
http.Redirect(w, r, bugBaseURL+"issues/entry", http.StatusFound)
|
||||||
|
return
|
||||||
|
} else if strings.HasPrefix(r.URL.Path, "/bug/") {
|
||||||
|
http.Redirect(w, r, bugBaseURL+"issues/detail?id="+r.URL.Path[5:], http.StatusFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
u, err := url.Parse(baseURL)
|
u, err := url.Parse(baseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
@ -28,15 +28,18 @@ https://chromium.googlesource.com/chromium/src/+/d413b2dcb54d523811d386f1ff4084f
|
|||||||
== In Progress
|
== In Progress
|
||||||
|
|
||||||
Crashpad is actively being bootstrapped on
|
Crashpad is actively being bootstrapped on
|
||||||
https://code.google.com/p/crashpad/issues/detail?id=1[Windows]. With the
|
https://code.google.com/p/crashpad/issues/detail?id=1[Windows]. All major
|
||||||
exception of the snapshot library, most major components are now working on
|
components are now working on Windows, and integration into Chromium is expected
|
||||||
Windows, and work is progressing on the Windows snapshot implementation.
|
shortly.
|
||||||
|
|
||||||
|
Initial work on a Crashpad client for
|
||||||
|
https://code.google.com/p/crashpad/issues/detail?id=30[Android] has begun. This
|
||||||
|
is currently in the design phase.
|
||||||
|
|
||||||
== Future
|
== Future
|
||||||
|
|
||||||
There are plans to bring Crashpad clients to other operating systems in the
|
There are plans to bring Crashpad clients to other operating systems in the
|
||||||
future, including
|
future, including a more generic non-Android Linux implementation. There are
|
||||||
https://code.google.com/p/crashpad/issues/detail?id=30[Android] and, more
|
also plans to implement a
|
||||||
generically, Linux. There are also plans to implement a
|
|
||||||
https://code.google.com/p/crashpad/issues/detail?id=29[crash report processor]
|
https://code.google.com/p/crashpad/issues/detail?id=29[crash report processor]
|
||||||
as part of Crashpad. No timeline for completing this work has been set yet.
|
as part of Crashpad. No timeline for completing this work has been set yet.
|
||||||
|
85
doc/support/generate_git.sh
Executable file
85
doc/support/generate_git.sh
Executable file
@ -0,0 +1,85 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Run from the Crashpad project root directory.
|
||||||
|
cd "$(dirname "${0}")/../.."
|
||||||
|
|
||||||
|
source doc/support/compat.sh
|
||||||
|
|
||||||
|
basename="$(basename "${0}")"
|
||||||
|
|
||||||
|
status="$(git status --porcelain)"
|
||||||
|
if [[ -n "${status}" ]]; then
|
||||||
|
echo "${basename}: the working directory must be clean" >& 2
|
||||||
|
git status
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# git symbolic-ref gives the current branch name, but fails for detached HEAD.
|
||||||
|
# In that case, git rev-parse will give the current hash.
|
||||||
|
original_branch="$(git symbolic-ref --short --quiet HEAD || git rev-parse HEAD)"
|
||||||
|
|
||||||
|
local_branch="\
|
||||||
|
$(${sed_ext} -e 's/(.*)\..*/\1/' <<< "${basename}").${$}.${RANDOM}"
|
||||||
|
|
||||||
|
remote_name=origin
|
||||||
|
remote_master_branch_name=master
|
||||||
|
remote_master_branch="${remote_name}/${remote_master_branch_name}"
|
||||||
|
remote_doc_branch_name=doc
|
||||||
|
remote_doc_branch="${remote_name}/${remote_doc_branch_name}"
|
||||||
|
|
||||||
|
git fetch
|
||||||
|
|
||||||
|
git checkout -b "${local_branch}" "${remote_doc_branch}"
|
||||||
|
|
||||||
|
dirty=
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
if [[ "${dirty}" ]]; then
|
||||||
|
git reset --hard HEAD
|
||||||
|
git clean --force
|
||||||
|
fi
|
||||||
|
|
||||||
|
git checkout "${original_branch}"
|
||||||
|
git branch -D "${local_branch}"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
master_hash=$(git rev-parse --short=12 "${remote_master_branch}")
|
||||||
|
git merge "${remote_master_branch}" \
|
||||||
|
-m "Merge ${remote_master_branch_name} ${master_hash} into doc"
|
||||||
|
|
||||||
|
dirty=y
|
||||||
|
|
||||||
|
doc/support/generate.sh
|
||||||
|
|
||||||
|
git add -A doc/generated
|
||||||
|
|
||||||
|
count="$(git diff --staged --numstat | wc -l)"
|
||||||
|
if [[ $count -gt 0 ]]; then
|
||||||
|
git commit \
|
||||||
|
-m "Update documentation to ${remote_master_branch_name} ${master_hash}"
|
||||||
|
dirty=
|
||||||
|
|
||||||
|
git push "${remote_name}" "HEAD:${remote_doc_branch_name}"
|
||||||
|
else
|
||||||
|
dirty=
|
||||||
|
fi
|
||||||
|
|
||||||
|
# cleanup will run on exit
|
@ -91,6 +91,24 @@
|
|||||||
'win/crashy_test_program.cc',
|
'win/crashy_test_program.cc',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'self_destroying_program',
|
||||||
|
'type': 'executable',
|
||||||
|
'dependencies': [
|
||||||
|
'../client/client.gyp:crashpad_client',
|
||||||
|
'../compat/compat.gyp:crashpad_compat',
|
||||||
|
'../snapshot/snapshot.gyp:crashpad_snapshot',
|
||||||
|
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||||
|
'../tools/tools.gyp:crashpad_tool_support',
|
||||||
|
'../util/util.gyp:crashpad_util',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'..',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'win/self_destroying_test_program.cc',
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
'targets': [],
|
'targets': [],
|
||||||
|
@ -186,7 +186,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
|
|||||||
// vendor.
|
// vendor.
|
||||||
base::mac::ScopedMachSendRight
|
base::mac::ScopedMachSendRight
|
||||||
system_crash_reporter_handler(SystemCrashReporterHandler());
|
system_crash_reporter_handler(SystemCrashReporterHandler());
|
||||||
if (system_crash_reporter_handler) {
|
if (system_crash_reporter_handler.get()) {
|
||||||
// Make copies of mutable out parameters so that the system crash reporter
|
// Make copies of mutable out parameters so that the system crash reporter
|
||||||
// can’t influence the state returned by this method.
|
// can’t influence the state returned by this method.
|
||||||
thread_state_flavor_t flavor_forward = *flavor;
|
thread_state_flavor_t flavor_forward = *flavor;
|
||||||
@ -205,7 +205,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
|
|||||||
// will be available.
|
// will be available.
|
||||||
kern_return_t kr = UniversalExceptionRaise(
|
kern_return_t kr = UniversalExceptionRaise(
|
||||||
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
|
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
|
||||||
system_crash_reporter_handler,
|
system_crash_reporter_handler.get(),
|
||||||
thread,
|
thread,
|
||||||
task,
|
task,
|
||||||
exception,
|
exception,
|
||||||
|
@ -41,7 +41,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
|
|||||||
exception_port_(exception_port),
|
exception_port_(exception_port),
|
||||||
notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)),
|
notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)),
|
||||||
running_(true) {
|
running_(true) {
|
||||||
CHECK_NE(notify_port_, kMachPortNull);
|
CHECK(notify_port_.is_valid());
|
||||||
|
|
||||||
composite_mach_message_server_.AddHandler(&mach_exc_server_);
|
composite_mach_message_server_.AddHandler(&mach_exc_server_);
|
||||||
composite_mach_message_server_.AddHandler(¬ify_server_);
|
composite_mach_message_server_.AddHandler(¬ify_server_);
|
||||||
@ -61,7 +61,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
|
|||||||
exception_port_,
|
exception_port_,
|
||||||
MACH_NOTIFY_NO_SENDERS,
|
MACH_NOTIFY_NO_SENDERS,
|
||||||
0,
|
0,
|
||||||
notify_port_,
|
notify_port_.get(),
|
||||||
MACH_MSG_TYPE_MAKE_SEND_ONCE,
|
MACH_MSG_TYPE_MAKE_SEND_ONCE,
|
||||||
&previous);
|
&previous);
|
||||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification";
|
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification";
|
||||||
@ -83,11 +83,11 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
|
|||||||
NewMachPort(MACH_PORT_RIGHT_PORT_SET));
|
NewMachPort(MACH_PORT_RIGHT_PORT_SET));
|
||||||
|
|
||||||
kr = mach_port_insert_member(
|
kr = mach_port_insert_member(
|
||||||
mach_task_self(), exception_port_, server_port_set);
|
mach_task_self(), exception_port_, server_port_set.get());
|
||||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
|
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
|
||||||
|
|
||||||
kr = mach_port_insert_member(
|
kr = mach_port_insert_member(
|
||||||
mach_task_self(), notify_port_, server_port_set);
|
mach_task_self(), notify_port_.get(), server_port_set.get());
|
||||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
|
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
|
||||||
|
|
||||||
// Run the server in kOneShot mode so that running_ can be reevaluated after
|
// Run the server in kOneShot mode so that running_ can be reevaluated after
|
||||||
@ -98,7 +98,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
|
|||||||
// DoMachNotifyNoSenders() as appropriate.
|
// DoMachNotifyNoSenders() as appropriate.
|
||||||
mach_msg_return_t mr =
|
mach_msg_return_t mr =
|
||||||
MachMessageServer::Run(&composite_mach_message_server_,
|
MachMessageServer::Run(&composite_mach_message_server_,
|
||||||
server_port_set,
|
server_port_set.get(),
|
||||||
kMachMessageReceiveAuditTrailer,
|
kMachMessageReceiveAuditTrailer,
|
||||||
MachMessageServer::kOneShot,
|
MachMessageServer::kOneShot,
|
||||||
MachMessageServer::kReceiveLargeIgnore,
|
MachMessageServer::kReceiveLargeIgnore,
|
||||||
@ -221,7 +221,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
|
|||||||
|
|
||||||
ExceptionHandlerServer::ExceptionHandlerServer()
|
ExceptionHandlerServer::ExceptionHandlerServer()
|
||||||
: receive_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)) {
|
: receive_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)) {
|
||||||
CHECK_NE(receive_port_, kMachPortNull);
|
CHECK(receive_port_.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionHandlerServer::~ExceptionHandlerServer() {
|
ExceptionHandlerServer::~ExceptionHandlerServer() {
|
||||||
@ -229,7 +229,7 @@ ExceptionHandlerServer::~ExceptionHandlerServer() {
|
|||||||
|
|
||||||
void ExceptionHandlerServer::Run(
|
void ExceptionHandlerServer::Run(
|
||||||
UniversalMachExcServer::Interface* exception_interface) {
|
UniversalMachExcServer::Interface* exception_interface) {
|
||||||
ExceptionHandlerServerRun run(receive_port_, exception_interface);
|
ExceptionHandlerServerRun run(receive_port_.get(), exception_interface);
|
||||||
run.Run();
|
run.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ class ExceptionHandlerServer {
|
|||||||
//!
|
//!
|
||||||
//! The caller does not take ownership of this port. The caller must not use
|
//! The caller does not take ownership of this port. The caller must not use
|
||||||
//! this port for any purpose other than to make send rights for clients.
|
//! this port for any purpose other than to make send rights for clients.
|
||||||
mach_port_t receive_port() const { return receive_port_; }
|
mach_port_t receive_port() const { return receive_port_.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::mac::ScopedMachReceiveRight receive_port_;
|
base::mac::ScopedMachReceiveRight receive_port_;
|
||||||
|
97
handler/win/self_destroying_test_program.cc
Normal file
97
handler/win/self_destroying_test_program.cc
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
// 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 <malloc.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/strings/stringprintf.h"
|
||||||
|
#include "client/crashpad_client.h"
|
||||||
|
#include "snapshot/win/process_reader_win.h"
|
||||||
|
#include "tools/tool_support.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// We VirtualFree a region in ourselves (the stack) to confirm that the
|
||||||
|
// exception reporter captures as much as possible in the minidump and doesn't
|
||||||
|
// abort. __debugbreak() immediately after doing so because the process is
|
||||||
|
// clearly in a very broken state at this point.
|
||||||
|
bool FreeOwnStackAndBreak() {
|
||||||
|
ProcessReaderWin process_reader;
|
||||||
|
if (!process_reader.Initialize(GetCurrentProcess(),
|
||||||
|
ProcessSuspensionState::kRunning)) {
|
||||||
|
LOG(ERROR) << "ProcessReaderWin Initialize";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<ProcessReaderWin::Thread> threads =
|
||||||
|
process_reader.Threads();
|
||||||
|
if (threads.empty()) {
|
||||||
|
LOG(ERROR) << "no threads";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the stack up a bit so that hopefully the crash handler can succeed,
|
||||||
|
// but won't be able to read the base of the stack.
|
||||||
|
_alloca(16384);
|
||||||
|
|
||||||
|
// We can't succeed at MEM_RELEASEing this memory, but MEM_DECOMMIT is good
|
||||||
|
// enough to make it inaccessible.
|
||||||
|
if (!VirtualFree(reinterpret_cast<void*>(threads[0].stack_region_address),
|
||||||
|
100,
|
||||||
|
MEM_DECOMMIT)) {
|
||||||
|
PLOG(ERROR) << "VirtualFree";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the VirtualFree() succeeds, we may have already crashed. __debugbreak()
|
||||||
|
// just to be sure.
|
||||||
|
__debugbreak();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SelfDestroyingMain(int argc, char* argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
fprintf(stderr, "Usage: %s <server_pipe_name>\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
CrashpadClient client;
|
||||||
|
if (!client.SetHandler(argv[1])) {
|
||||||
|
LOG(ERROR) << "SetHandler";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
if (!client.UseHandler()) {
|
||||||
|
LOG(ERROR) << "UseHandler";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FreeOwnStackAndBreak())
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
// This will never be reached. On success, we'll have crashed above, or
|
||||||
|
// otherwise returned before here.
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
int wmain(int argc, wchar_t* argv[]) {
|
||||||
|
return crashpad::ToolSupport::Wmain(argc, argv, crashpad::SelfDestroyingMain);
|
||||||
|
}
|
@ -44,6 +44,8 @@
|
|||||||
'minidump_extensions.h',
|
'minidump_extensions.h',
|
||||||
'minidump_file_writer.cc',
|
'minidump_file_writer.cc',
|
||||||
'minidump_file_writer.h',
|
'minidump_file_writer.h',
|
||||||
|
'minidump_handle_writer.cc',
|
||||||
|
'minidump_handle_writer.h',
|
||||||
'minidump_memory_info_writer.cc',
|
'minidump_memory_info_writer.cc',
|
||||||
'minidump_memory_info_writer.h',
|
'minidump_memory_info_writer.h',
|
||||||
'minidump_memory_writer.cc',
|
'minidump_memory_writer.cc',
|
||||||
|
@ -71,6 +71,11 @@ enum MinidumpStreamType : uint32_t {
|
|||||||
//! \sa SystemInfoStream
|
//! \sa SystemInfoStream
|
||||||
kMinidumpStreamTypeSystemInfo = SystemInfoStream,
|
kMinidumpStreamTypeSystemInfo = SystemInfoStream,
|
||||||
|
|
||||||
|
//! \brief The stream type for MINIDUMP_HANDLE_DATA_STREAM.
|
||||||
|
//!
|
||||||
|
//! \sa HandleDataStream
|
||||||
|
kMinidumpStreamTypeHandleData = HandleDataStream,
|
||||||
|
|
||||||
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
|
//! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2,
|
||||||
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
|
//! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4.
|
||||||
//!
|
//!
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "minidump/minidump_crashpad_info_writer.h"
|
#include "minidump/minidump_crashpad_info_writer.h"
|
||||||
#include "minidump/minidump_exception_writer.h"
|
#include "minidump/minidump_exception_writer.h"
|
||||||
|
#include "minidump/minidump_handle_writer.h"
|
||||||
#include "minidump/minidump_memory_info_writer.h"
|
#include "minidump/minidump_memory_info_writer.h"
|
||||||
#include "minidump/minidump_memory_writer.h"
|
#include "minidump/minidump_memory_writer.h"
|
||||||
#include "minidump/minidump_misc_info_writer.h"
|
#include "minidump/minidump_misc_info_writer.h"
|
||||||
@ -108,6 +109,13 @@ void MinidumpFileWriter::InitializeFromSnapshot(
|
|||||||
AddStream(memory_info_list.Pass());
|
AddStream(memory_info_list.Pass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<HandleSnapshot> handles_snapshot = process_snapshot->Handles();
|
||||||
|
if (!handles_snapshot.empty()) {
|
||||||
|
auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter());
|
||||||
|
handle_data_writer->InitializeFromSnapshot(handles_snapshot);
|
||||||
|
AddStream(handle_data_writer.Pass());
|
||||||
|
}
|
||||||
|
|
||||||
memory_list->AddFromSnapshot(process_snapshot->ExtraMemory());
|
memory_list->AddFromSnapshot(process_snapshot->ExtraMemory());
|
||||||
|
|
||||||
AddStream(memory_list.Pass());
|
AddStream(memory_list.Pass());
|
||||||
|
127
minidump/minidump_handle_writer.cc
Normal file
127
minidump/minidump_handle_writer.cc
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// 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 "minidump/minidump_handle_writer.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/stl_util.h"
|
||||||
|
#include "minidump/minidump_extensions.h"
|
||||||
|
#include "util/file/file_writer.h"
|
||||||
|
#include "util/numeric/safe_assignment.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
MinidumpHandleDataWriter::MinidumpHandleDataWriter()
|
||||||
|
: handle_data_stream_base_(), handle_descriptors_(), strings_() {
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpHandleDataWriter::~MinidumpHandleDataWriter() {
|
||||||
|
STLDeleteContainerPairSecondPointers(strings_.begin(), strings_.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MinidumpHandleDataWriter::InitializeFromSnapshot(
|
||||||
|
const std::vector<HandleSnapshot>& handle_snapshots) {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
|
||||||
|
DCHECK(handle_descriptors_.empty());
|
||||||
|
// Because we RegisterRVA() on the string writer below, we preallocate and
|
||||||
|
// never resize the handle_descriptors_ vector.
|
||||||
|
handle_descriptors_.resize(handle_snapshots.size());
|
||||||
|
for (size_t i = 0; i < handle_snapshots.size(); ++i) {
|
||||||
|
const HandleSnapshot& handle_snapshot = handle_snapshots[i];
|
||||||
|
MINIDUMP_HANDLE_DESCRIPTOR& descriptor = handle_descriptors_[i];
|
||||||
|
|
||||||
|
descriptor.Handle = handle_snapshot.handle;
|
||||||
|
|
||||||
|
if (handle_snapshot.type_name.empty()) {
|
||||||
|
descriptor.TypeNameRva = 0;
|
||||||
|
} else {
|
||||||
|
auto it = strings_.lower_bound(handle_snapshot.type_name);
|
||||||
|
internal::MinidumpUTF16StringWriter* writer;
|
||||||
|
if (it != strings_.end() && it->first == handle_snapshot.type_name) {
|
||||||
|
writer = it->second;
|
||||||
|
} else {
|
||||||
|
writer = new internal::MinidumpUTF16StringWriter();
|
||||||
|
strings_.insert(it, std::make_pair(handle_snapshot.type_name, writer));
|
||||||
|
writer->SetUTF8(handle_snapshot.type_name);
|
||||||
|
}
|
||||||
|
writer->RegisterRVA(&descriptor.TypeNameRva);
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptor.ObjectNameRva = 0;
|
||||||
|
descriptor.Attributes = handle_snapshot.attributes;
|
||||||
|
descriptor.GrantedAccess = handle_snapshot.granted_access;
|
||||||
|
descriptor.HandleCount = handle_snapshot.handle_count;
|
||||||
|
descriptor.PointerCount = handle_snapshot.pointer_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinidumpHandleDataWriter::Freeze() {
|
||||||
|
DCHECK_EQ(state(), kStateMutable);
|
||||||
|
|
||||||
|
if (!MinidumpStreamWriter::Freeze())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
handle_data_stream_base_.SizeOfHeader = sizeof(handle_data_stream_base_);
|
||||||
|
handle_data_stream_base_.SizeOfDescriptor = sizeof(handle_descriptors_[0]);
|
||||||
|
const size_t handle_count = handle_descriptors_.size();
|
||||||
|
if (!AssignIfInRange(&handle_data_stream_base_.NumberOfDescriptors,
|
||||||
|
handle_count)) {
|
||||||
|
LOG(ERROR) << "handle_count " << handle_count << " out of range";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
handle_data_stream_base_.Reserved = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MinidumpHandleDataWriter::SizeOfObject() {
|
||||||
|
DCHECK_GE(state(), kStateFrozen);
|
||||||
|
return sizeof(handle_data_stream_base_) +
|
||||||
|
sizeof(handle_descriptors_[0]) * handle_descriptors_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<internal::MinidumpWritable*> MinidumpHandleDataWriter::Children() {
|
||||||
|
DCHECK_GE(state(), kStateFrozen);
|
||||||
|
|
||||||
|
std::vector<MinidumpWritable*> children;
|
||||||
|
for (const auto& pair : strings_)
|
||||||
|
children.push_back(pair.second);
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MinidumpHandleDataWriter::WriteObject(FileWriterInterface* file_writer) {
|
||||||
|
DCHECK_EQ(state(), kStateWritable);
|
||||||
|
|
||||||
|
WritableIoVec iov;
|
||||||
|
iov.iov_base = &handle_data_stream_base_;
|
||||||
|
iov.iov_len = sizeof(handle_data_stream_base_);
|
||||||
|
std::vector<WritableIoVec> iovecs(1, iov);
|
||||||
|
|
||||||
|
for (const auto& descriptor : handle_descriptors_) {
|
||||||
|
iov.iov_base = &descriptor;
|
||||||
|
iov.iov_len = sizeof(descriptor);
|
||||||
|
iovecs.push_back(iov);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file_writer->WriteIoVec(&iovecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpStreamType MinidumpHandleDataWriter::StreamType() const {
|
||||||
|
return kMinidumpStreamTypeHandleData;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
76
minidump/minidump_handle_writer.h
Normal file
76
minidump/minidump_handle_writer.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_
|
||||||
|
#define CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <dbghelp.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "minidump/minidump_stream_writer.h"
|
||||||
|
#include "minidump/minidump_string_writer.h"
|
||||||
|
#include "minidump/minidump_writable.h"
|
||||||
|
#include "snapshot/handle_snapshot.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief The writer for a MINIDUMP_HANDLE_DATA_STREAM stream in a minidump
|
||||||
|
//! and its contained MINIDUMP_HANDLE_DESCRIPTOR s.
|
||||||
|
//!
|
||||||
|
//! As we currently do not track any data beyond what MINIDUMP_HANDLE_DESCRIPTOR
|
||||||
|
//! supports, we only write that type of record rather than the newer
|
||||||
|
//! MINIDUMP_HANDLE_DESCRIPTOR_2.
|
||||||
|
//!
|
||||||
|
//! Note that this writer writes both the header (MINIDUMP_HANDLE_DATA_STREAM)
|
||||||
|
//! and the list of objects (MINIDUMP_HANDLE_DESCRIPTOR), which is different
|
||||||
|
//! from some of the other list writers.
|
||||||
|
class MinidumpHandleDataWriter final : public internal::MinidumpStreamWriter {
|
||||||
|
public:
|
||||||
|
MinidumpHandleDataWriter();
|
||||||
|
~MinidumpHandleDataWriter() override;
|
||||||
|
|
||||||
|
//! \brief Adds a MINIDUMP_HANDLE_DESCRIPTOR for each handle in \a
|
||||||
|
//! handle_snapshot to the MINIDUMP_HANDLE_DATA_STREAM.
|
||||||
|
//!
|
||||||
|
//! \param[in] handle_snapshots The handle snapshots to use as source data.
|
||||||
|
//!
|
||||||
|
//! \note Valid in #kStateMutable.
|
||||||
|
void InitializeFromSnapshot(
|
||||||
|
const std::vector<HandleSnapshot>& handle_snapshots);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// MinidumpWritable:
|
||||||
|
bool Freeze() override;
|
||||||
|
size_t SizeOfObject() override;
|
||||||
|
std::vector<MinidumpWritable*> Children() override;
|
||||||
|
bool WriteObject(FileWriterInterface* file_writer) override;
|
||||||
|
|
||||||
|
// MinidumpStreamWriter:
|
||||||
|
MinidumpStreamType StreamType() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MINIDUMP_HANDLE_DATA_STREAM handle_data_stream_base_;
|
||||||
|
std::vector<MINIDUMP_HANDLE_DESCRIPTOR> handle_descriptors_;
|
||||||
|
std::map<std::string, internal::MinidumpUTF16StringWriter*> strings_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(MinidumpHandleDataWriter);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_
|
201
minidump/minidump_handle_writer_test.cc
Normal file
201
minidump/minidump_handle_writer_test.cc
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
// 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 "minidump/minidump_handle_writer.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "minidump/minidump_file_writer.h"
|
||||||
|
#include "minidump/test/minidump_file_writer_test_util.h"
|
||||||
|
#include "minidump/test/minidump_string_writer_test_util.h"
|
||||||
|
#include "minidump/test/minidump_writable_test_util.h"
|
||||||
|
#include "util/file/string_file.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// The handle data stream is expected to be the only stream.
|
||||||
|
void GetHandleDataStream(
|
||||||
|
const std::string& file_contents,
|
||||||
|
const MINIDUMP_HANDLE_DATA_STREAM** handle_data_stream) {
|
||||||
|
const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
|
||||||
|
const size_t kHandleDataStreamOffset =
|
||||||
|
kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
|
||||||
|
|
||||||
|
const MINIDUMP_DIRECTORY* directory;
|
||||||
|
const MINIDUMP_HEADER* header =
|
||||||
|
MinidumpHeaderAtStart(file_contents, &directory);
|
||||||
|
ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0));
|
||||||
|
ASSERT_TRUE(directory);
|
||||||
|
|
||||||
|
const size_t kDirectoryIndex = 0;
|
||||||
|
|
||||||
|
ASSERT_EQ(kMinidumpStreamTypeHandleData,
|
||||||
|
directory[kDirectoryIndex].StreamType);
|
||||||
|
EXPECT_EQ(kHandleDataStreamOffset, directory[kDirectoryIndex].Location.Rva);
|
||||||
|
|
||||||
|
*handle_data_stream =
|
||||||
|
MinidumpWritableAtLocationDescriptor<MINIDUMP_HANDLE_DATA_STREAM>(
|
||||||
|
file_contents, directory[kDirectoryIndex].Location);
|
||||||
|
ASSERT_TRUE(*handle_data_stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpHandleDataWriter, Empty) {
|
||||||
|
MinidumpFileWriter minidump_file_writer;
|
||||||
|
auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter());
|
||||||
|
minidump_file_writer.AddStream(handle_data_writer.Pass());
|
||||||
|
|
||||||
|
StringFile string_file;
|
||||||
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||||
|
|
||||||
|
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||||
|
sizeof(MINIDUMP_HANDLE_DATA_STREAM),
|
||||||
|
string_file.string().size());
|
||||||
|
|
||||||
|
const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
GetHandleDataStream(string_file.string(), &handle_data_stream));
|
||||||
|
|
||||||
|
EXPECT_EQ(0u, handle_data_stream->NumberOfDescriptors);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpHandleDataWriter, OneHandle) {
|
||||||
|
MinidumpFileWriter minidump_file_writer;
|
||||||
|
auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter());
|
||||||
|
|
||||||
|
HandleSnapshot handle_snapshot;
|
||||||
|
handle_snapshot.handle = 0x1234;
|
||||||
|
handle_snapshot.type_name = "Something";
|
||||||
|
handle_snapshot.attributes = 0x12345678;
|
||||||
|
handle_snapshot.granted_access = 0x9abcdef0;
|
||||||
|
handle_snapshot.pointer_count = 4567;
|
||||||
|
handle_snapshot.handle_count = 9876;
|
||||||
|
|
||||||
|
std::vector<HandleSnapshot> snapshot;
|
||||||
|
snapshot.push_back(handle_snapshot);
|
||||||
|
|
||||||
|
handle_data_writer->InitializeFromSnapshot(snapshot);
|
||||||
|
|
||||||
|
minidump_file_writer.AddStream(handle_data_writer.Pass());
|
||||||
|
|
||||||
|
StringFile string_file;
|
||||||
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||||
|
|
||||||
|
const size_t kTypeNameStringDataLength =
|
||||||
|
(handle_snapshot.type_name.size() + 1) * sizeof(base::char16);
|
||||||
|
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||||
|
sizeof(MINIDUMP_HANDLE_DATA_STREAM) +
|
||||||
|
sizeof(MINIDUMP_HANDLE_DESCRIPTOR) + sizeof(MINIDUMP_STRING) +
|
||||||
|
kTypeNameStringDataLength,
|
||||||
|
string_file.string().size());
|
||||||
|
|
||||||
|
const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
GetHandleDataStream(string_file.string(), &handle_data_stream));
|
||||||
|
|
||||||
|
EXPECT_EQ(1u, handle_data_stream->NumberOfDescriptors);
|
||||||
|
const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor =
|
||||||
|
reinterpret_cast<const MINIDUMP_HANDLE_DESCRIPTOR*>(
|
||||||
|
&handle_data_stream[1]);
|
||||||
|
EXPECT_EQ(handle_snapshot.handle, handle_descriptor->Handle);
|
||||||
|
EXPECT_EQ(handle_snapshot.type_name,
|
||||||
|
base::UTF16ToUTF8(MinidumpStringAtRVAAsString(
|
||||||
|
string_file.string(), handle_descriptor->TypeNameRva)));
|
||||||
|
EXPECT_EQ(0u, handle_descriptor->ObjectNameRva);
|
||||||
|
EXPECT_EQ(handle_snapshot.attributes, handle_descriptor->Attributes);
|
||||||
|
EXPECT_EQ(handle_snapshot.granted_access, handle_descriptor->GrantedAccess);
|
||||||
|
EXPECT_EQ(handle_snapshot.handle_count, handle_descriptor->HandleCount);
|
||||||
|
EXPECT_EQ(handle_snapshot.pointer_count, handle_descriptor->PointerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MinidumpHandleDataWriter, RepeatedTypeName) {
|
||||||
|
MinidumpFileWriter minidump_file_writer;
|
||||||
|
auto handle_data_writer = make_scoped_ptr(new MinidumpHandleDataWriter());
|
||||||
|
|
||||||
|
HandleSnapshot handle_snapshot;
|
||||||
|
handle_snapshot.handle = 0x1234;
|
||||||
|
handle_snapshot.type_name = "Something";
|
||||||
|
handle_snapshot.attributes = 0x12345678;
|
||||||
|
handle_snapshot.granted_access = 0x9abcdef0;
|
||||||
|
handle_snapshot.pointer_count = 4567;
|
||||||
|
handle_snapshot.handle_count = 9876;
|
||||||
|
|
||||||
|
HandleSnapshot handle_snapshot2;
|
||||||
|
handle_snapshot2.handle = 0x4321;
|
||||||
|
handle_snapshot2.type_name = "Something"; // Note: same as above.
|
||||||
|
handle_snapshot2.attributes = 0x87654321;
|
||||||
|
handle_snapshot2.granted_access = 0x0fedcba9;
|
||||||
|
handle_snapshot2.pointer_count = 7654;
|
||||||
|
handle_snapshot2.handle_count = 6789;
|
||||||
|
|
||||||
|
std::vector<HandleSnapshot> snapshot;
|
||||||
|
snapshot.push_back(handle_snapshot);
|
||||||
|
snapshot.push_back(handle_snapshot2);
|
||||||
|
|
||||||
|
handle_data_writer->InitializeFromSnapshot(snapshot);
|
||||||
|
|
||||||
|
minidump_file_writer.AddStream(handle_data_writer.Pass());
|
||||||
|
|
||||||
|
StringFile string_file;
|
||||||
|
ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
|
||||||
|
|
||||||
|
const size_t kTypeNameStringDataLength =
|
||||||
|
(handle_snapshot.type_name.size() + 1) * sizeof(base::char16);
|
||||||
|
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||||
|
sizeof(MINIDUMP_HANDLE_DATA_STREAM) +
|
||||||
|
(sizeof(MINIDUMP_HANDLE_DESCRIPTOR) * 2) +
|
||||||
|
sizeof(MINIDUMP_STRING) + kTypeNameStringDataLength,
|
||||||
|
string_file.string().size());
|
||||||
|
|
||||||
|
const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr;
|
||||||
|
ASSERT_NO_FATAL_FAILURE(
|
||||||
|
GetHandleDataStream(string_file.string(), &handle_data_stream));
|
||||||
|
|
||||||
|
EXPECT_EQ(2u, handle_data_stream->NumberOfDescriptors);
|
||||||
|
const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor =
|
||||||
|
reinterpret_cast<const MINIDUMP_HANDLE_DESCRIPTOR*>(
|
||||||
|
&handle_data_stream[1]);
|
||||||
|
EXPECT_EQ(handle_snapshot.handle, handle_descriptor->Handle);
|
||||||
|
EXPECT_EQ(handle_snapshot.type_name,
|
||||||
|
base::UTF16ToUTF8(MinidumpStringAtRVAAsString(
|
||||||
|
string_file.string(), handle_descriptor->TypeNameRva)));
|
||||||
|
EXPECT_EQ(0u, handle_descriptor->ObjectNameRva);
|
||||||
|
EXPECT_EQ(handle_snapshot.attributes, handle_descriptor->Attributes);
|
||||||
|
EXPECT_EQ(handle_snapshot.granted_access, handle_descriptor->GrantedAccess);
|
||||||
|
EXPECT_EQ(handle_snapshot.handle_count, handle_descriptor->HandleCount);
|
||||||
|
EXPECT_EQ(handle_snapshot.pointer_count, handle_descriptor->PointerCount);
|
||||||
|
|
||||||
|
const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor2 =
|
||||||
|
reinterpret_cast<const MINIDUMP_HANDLE_DESCRIPTOR*>(
|
||||||
|
reinterpret_cast<const unsigned char*>(&handle_data_stream[1]) +
|
||||||
|
sizeof(MINIDUMP_HANDLE_DESCRIPTOR));
|
||||||
|
EXPECT_EQ(handle_snapshot2.handle, handle_descriptor2->Handle);
|
||||||
|
EXPECT_EQ(handle_snapshot2.type_name,
|
||||||
|
base::UTF16ToUTF8(MinidumpStringAtRVAAsString(
|
||||||
|
string_file.string(), handle_descriptor2->TypeNameRva)));
|
||||||
|
EXPECT_EQ(0u, handle_descriptor2->ObjectNameRva);
|
||||||
|
EXPECT_EQ(handle_snapshot2.attributes, handle_descriptor2->Attributes);
|
||||||
|
EXPECT_EQ(handle_snapshot2.granted_access, handle_descriptor2->GrantedAccess);
|
||||||
|
EXPECT_EQ(handle_snapshot2.handle_count, handle_descriptor2->HandleCount);
|
||||||
|
EXPECT_EQ(handle_snapshot2.pointer_count, handle_descriptor2->PointerCount);
|
||||||
|
|
||||||
|
EXPECT_EQ(handle_descriptor->TypeNameRva, handle_descriptor2->TypeNameRva);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
@ -51,7 +51,7 @@ void GetMemoryInfoListStream(
|
|||||||
*memory_info_list =
|
*memory_info_list =
|
||||||
MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
|
MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_INFO_LIST>(
|
||||||
file_contents, directory[kDirectoryIndex].Location);
|
file_contents, directory[kDirectoryIndex].Location);
|
||||||
ASSERT_TRUE(memory_info_list);
|
ASSERT_TRUE(*memory_info_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MinidumpMemoryInfoWriter, Empty) {
|
TEST(MinidumpMemoryInfoWriter, Empty) {
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
'minidump_context_writer_test.cc',
|
'minidump_context_writer_test.cc',
|
||||||
'minidump_crashpad_info_writer_test.cc',
|
'minidump_crashpad_info_writer_test.cc',
|
||||||
'minidump_exception_writer_test.cc',
|
'minidump_exception_writer_test.cc',
|
||||||
|
'minidump_handle_writer_test.cc',
|
||||||
'minidump_file_writer_test.cc',
|
'minidump_file_writer_test.cc',
|
||||||
'minidump_memory_info_writer_test.cc',
|
'minidump_memory_info_writer_test.cc',
|
||||||
'minidump_memory_writer_test.cc',
|
'minidump_memory_writer_test.cc',
|
||||||
|
@ -181,6 +181,14 @@ struct MinidumpThreadListTraits {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MinidumpHandleDataStreamTraits {
|
||||||
|
using ListType = MINIDUMP_HANDLE_DATA_STREAM;
|
||||||
|
enum : size_t { kElementSize = sizeof(MINIDUMP_HANDLE_DESCRIPTOR) };
|
||||||
|
static size_t ElementCount(const ListType* list) {
|
||||||
|
return static_cast<size_t>(list->NumberOfDescriptors);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct MinidumpMemoryInfoListTraits {
|
struct MinidumpMemoryInfoListTraits {
|
||||||
using ListType = MINIDUMP_MEMORY_INFO_LIST;
|
using ListType = MINIDUMP_MEMORY_INFO_LIST;
|
||||||
enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_INFO) };
|
enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_INFO) };
|
||||||
@ -252,6 +260,14 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
|
|||||||
file_contents, location);
|
file_contents, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor<
|
||||||
|
MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents,
|
||||||
|
const MINIDUMP_LOCATION_DESCRIPTOR& location) {
|
||||||
|
return MinidumpListAtLocationDescriptor<MinidumpHandleDataStreamTraits>(
|
||||||
|
file_contents, location);
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
|
const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
|
||||||
MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
|
MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
|
||||||
|
@ -90,6 +90,7 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY);
|
|||||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST);
|
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST);
|
||||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST);
|
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST);
|
||||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST);
|
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST);
|
||||||
|
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_HANDLE_DATA_STREAM);
|
||||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST);
|
MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST);
|
||||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList);
|
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList);
|
||||||
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList);
|
MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList);
|
||||||
@ -190,6 +191,11 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor<
|
|||||||
MINIDUMP_THREAD_LIST>(const std::string& file_contents,
|
MINIDUMP_THREAD_LIST>(const std::string& file_contents,
|
||||||
const MINIDUMP_LOCATION_DESCRIPTOR& location);
|
const MINIDUMP_LOCATION_DESCRIPTOR& location);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
const MINIDUMP_HANDLE_DATA_STREAM* MinidumpWritableAtLocationDescriptor<
|
||||||
|
MINIDUMP_HANDLE_DATA_STREAM>(const std::string& file_contents,
|
||||||
|
const MINIDUMP_LOCATION_DESCRIPTOR& location);
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
|
const MINIDUMP_MEMORY_INFO_LIST* MinidumpWritableAtLocationDescriptor<
|
||||||
MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
|
MINIDUMP_MEMORY_INFO_LIST>(const std::string& file_contents,
|
||||||
|
@ -25,8 +25,8 @@ struct HandleSnapshot {
|
|||||||
HandleSnapshot();
|
HandleSnapshot();
|
||||||
~HandleSnapshot();
|
~HandleSnapshot();
|
||||||
|
|
||||||
//! \brief A string representation of the handle's type.
|
//! \brief A UTF-8 string representation of the handle's type.
|
||||||
std::wstring type_name;
|
std::string type_name;
|
||||||
|
|
||||||
//! \brief The handle's value.
|
//! \brief The handle's value.
|
||||||
uint32_t handle;
|
uint32_t handle;
|
||||||
|
@ -76,8 +76,8 @@ def GetCdbPath():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def GetDumpFromCrashyProgram(out_dir, pipe_name):
|
def GetDumpFromProgram(out_dir, pipe_name, executable_name):
|
||||||
"""Initialize a crash database, run crashpad_handler, run crashy_program
|
"""Initialize a crash database, run crashpad_handler, run |executable_name|
|
||||||
connecting to the crash_handler. Returns the minidump generated by
|
connecting to the crash_handler. Returns the minidump generated by
|
||||||
crash_handler for further testing.
|
crash_handler for further testing.
|
||||||
"""
|
"""
|
||||||
@ -97,7 +97,7 @@ def GetDumpFromCrashyProgram(out_dir, pipe_name):
|
|||||||
'--database=' + test_database
|
'--database=' + test_database
|
||||||
])
|
])
|
||||||
|
|
||||||
subprocess.call([os.path.join(out_dir, 'crashy_program.exe'), pipe_name])
|
subprocess.call([os.path.join(out_dir, executable_name), pipe_name])
|
||||||
|
|
||||||
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'),
|
||||||
@ -113,6 +113,14 @@ def GetDumpFromCrashyProgram(out_dir, pipe_name):
|
|||||||
handler.kill()
|
handler.kill()
|
||||||
|
|
||||||
|
|
||||||
|
def GetDumpFromCrashyProgram(out_dir, pipe_name):
|
||||||
|
return GetDumpFromProgram(out_dir, pipe_name, 'crashy_program.exe')
|
||||||
|
|
||||||
|
|
||||||
|
def GetDumpFromSelfDestroyingProgram(out_dir, pipe_name):
|
||||||
|
return GetDumpFromProgram(out_dir, pipe_name, 'self_destroying_program.exe')
|
||||||
|
|
||||||
|
|
||||||
class CdbRun(object):
|
class CdbRun(object):
|
||||||
"""Run cdb.exe passing it a cdb command and capturing the output.
|
"""Run cdb.exe passing it a cdb command and capturing the output.
|
||||||
`Check()` searches for regex patterns in sequence allowing verification of
|
`Check()` searches for regex patterns in sequence allowing verification of
|
||||||
@ -147,7 +155,7 @@ class CdbRun(object):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def RunTests(cdb_path, dump_path, pipe_name):
|
def RunTests(cdb_path, dump_path, destroyed_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.
|
||||||
@ -183,10 +191,26 @@ def RunTests(cdb_path, dump_path, pipe_name):
|
|||||||
out = CdbRun(cdb_path, dump_path, '!locks')
|
out = CdbRun(cdb_path, dump_path, '!locks')
|
||||||
out.Check(r'CritSec crashy_program!crashpad::`anonymous namespace\'::'
|
out.Check(r'CritSec crashy_program!crashpad::`anonymous namespace\'::'
|
||||||
r'g_test_critical_section', 'lock was captured')
|
r'g_test_critical_section', 'lock was captured')
|
||||||
if float(platform.win32_ver()[0]) != 7:
|
if platform.win32_ver()[0] != '7':
|
||||||
# We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7.
|
# We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7.
|
||||||
out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked')
|
out.Check(r'\*\*\* Locked', 'lock debug info was captured, and is locked')
|
||||||
|
|
||||||
|
out = CdbRun(cdb_path, dump_path, '!handle')
|
||||||
|
out.Check(r'\d+ Handles', 'captured handles')
|
||||||
|
out.Check(r'Event\s+\d+', 'capture some event handles')
|
||||||
|
out.Check(r'File\s+\d+', 'capture some file handles')
|
||||||
|
|
||||||
|
out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2')
|
||||||
|
out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', 'PEB_LDR_DATA saved')
|
||||||
|
out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE)
|
||||||
|
|
||||||
|
# Check that there is no stack trace in the self-destroyed process. Confirm
|
||||||
|
# that the top is where we expect it (that's based only on IP), but subsequent
|
||||||
|
# stack entries will not be available. This confirms that we have a mostly
|
||||||
|
# valid dump, but that the stack was omitted.
|
||||||
|
out.Check(r'self_destroying_program!crashpad::`anonymous namespace\'::'
|
||||||
|
r'FreeOwnStackAndBreak.*\nquit:',
|
||||||
|
'at correct location, no additional stack entries')
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
try:
|
try:
|
||||||
@ -202,17 +226,23 @@ def main(args):
|
|||||||
# Make sure we can download Windows symbols.
|
# Make sure we can download Windows symbols.
|
||||||
if not os.environ.get('_NT_SYMBOL_PATH'):
|
if not os.environ.get('_NT_SYMBOL_PATH'):
|
||||||
symbol_dir = MakeTempDir()
|
symbol_dir = MakeTempDir()
|
||||||
|
protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http'
|
||||||
os.environ['_NT_SYMBOL_PATH'] = (
|
os.environ['_NT_SYMBOL_PATH'] = (
|
||||||
'SRV*' + symbol_dir + '*https://msdl.microsoft.com/download/symbols')
|
'SRV*' + symbol_dir + '*' +
|
||||||
|
protocol + '://msdl.microsoft.com/download/symbols')
|
||||||
|
|
||||||
pipe_name = r'\\.\pipe\end-to-end_%s_%s' % (
|
pipe_name = r'\\.\pipe\end-to-end_%s_%s' % (
|
||||||
os.getpid(), str(random.getrandbits(64)))
|
os.getpid(), str(random.getrandbits(64)))
|
||||||
|
|
||||||
dump_path = GetDumpFromCrashyProgram(args[0], pipe_name)
|
crashy_dump_path = GetDumpFromCrashyProgram(args[0], pipe_name)
|
||||||
if not dump_path:
|
if not crashy_dump_path:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
RunTests(cdb_path, dump_path, pipe_name)
|
destroyed_dump_path = GetDumpFromSelfDestroyingProgram(args[0], pipe_name)
|
||||||
|
if not destroyed_dump_path:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
RunTests(cdb_path, crashy_dump_path, destroyed_dump_path, pipe_name)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
finally:
|
finally:
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "snapshot/win/memory_snapshot_win.h"
|
#include "snapshot/win/memory_snapshot_win.h"
|
||||||
#include "snapshot/win/module_snapshot_win.h"
|
#include "snapshot/win/module_snapshot_win.h"
|
||||||
#include "util/win/registration_protocol_win.h"
|
#include "util/win/registration_protocol_win.h"
|
||||||
@ -211,7 +212,10 @@ std::vector<HandleSnapshot> ProcessSnapshotWin::Handles() const {
|
|||||||
std::vector<HandleSnapshot> result;
|
std::vector<HandleSnapshot> result;
|
||||||
for (const auto& handle : process_reader_.GetProcessInfo().Handles()) {
|
for (const auto& handle : process_reader_.GetProcessInfo().Handles()) {
|
||||||
HandleSnapshot snapshot;
|
HandleSnapshot snapshot;
|
||||||
snapshot.type_name = handle.type_name;
|
// This is probably not strictly correct, but these are not localized so we
|
||||||
|
// expect them all to be in ASCII range anyway. This will need to be more
|
||||||
|
// carefully done if the object name is added.
|
||||||
|
snapshot.type_name = base::UTF16ToUTF8(handle.type_name);
|
||||||
snapshot.handle = handle.handle;
|
snapshot.handle = handle.handle;
|
||||||
snapshot.attributes = handle.attributes;
|
snapshot.attributes = handle.attributes;
|
||||||
snapshot.granted_access = handle.granted_access;
|
snapshot.granted_access = handle.granted_access;
|
||||||
@ -340,19 +344,8 @@ void ProcessSnapshotWin::AddMemorySnapshot(
|
|||||||
if (size == 0)
|
if (size == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Ensure that the entire range is readable. TODO(scottmg): Consider
|
if (!process_reader_.GetProcessInfo().LoggingRangeIsFullyReadable(
|
||||||
// generalizing this as part of
|
CheckedRange<WinVMAddress, WinVMSize>(address, size))) {
|
||||||
// https://code.google.com/p/crashpad/issues/detail?id=59.
|
|
||||||
auto ranges = process_reader_.GetProcessInfo().GetReadableRanges(
|
|
||||||
CheckedRange<WinVMAddress, WinVMSize>(address, size));
|
|
||||||
if (ranges.size() != 1) {
|
|
||||||
LOG(ERROR) << base::StringPrintf(
|
|
||||||
"range at 0x%llx, size 0x%llx fully unreadable", address, size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ranges[0].base() != address || ranges[0].size() != size) {
|
|
||||||
LOG(ERROR) << base::StringPrintf(
|
|
||||||
"some of range at 0x%llx, size 0x%llx unreadable", address, size);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,11 +41,23 @@ bool ThreadSnapshotWin::Initialize(
|
|||||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
|
||||||
thread_ = process_reader_thread;
|
thread_ = process_reader_thread;
|
||||||
// TODO(scottmg): Ensure these regions are readable
|
if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable(
|
||||||
// https://code.google.com/p/crashpad/issues/detail?id=59
|
CheckedRange<WinVMAddress, WinVMSize>(thread_.stack_region_address,
|
||||||
stack_.Initialize(
|
thread_.stack_region_size))) {
|
||||||
process_reader, thread_.stack_region_address, thread_.stack_region_size);
|
stack_.Initialize(process_reader,
|
||||||
teb_.Initialize(process_reader, thread_.teb_address, thread_.teb_size);
|
thread_.stack_region_address,
|
||||||
|
thread_.stack_region_size);
|
||||||
|
} else {
|
||||||
|
stack_.Initialize(process_reader, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable(
|
||||||
|
CheckedRange<WinVMAddress, WinVMSize>(thread_.teb_address,
|
||||||
|
thread_.teb_size))) {
|
||||||
|
teb_.Initialize(process_reader, thread_.teb_address, thread_.teb_size);
|
||||||
|
} else {
|
||||||
|
teb_.Initialize(process_reader, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(ARCH_CPU_X86_64)
|
#if defined(ARCH_CPU_X86_64)
|
||||||
if (process_reader->Is64Bit()) {
|
if (process_reader->Is64Bit()) {
|
||||||
|
@ -98,22 +98,22 @@ void MachMultiprocess::PreFork() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
info_->local_port = BootstrapCheckIn(info_->service_name);
|
info_->local_port = BootstrapCheckIn(info_->service_name);
|
||||||
ASSERT_NE(kMachPortNull, info_->local_port);
|
ASSERT_TRUE(info_->local_port.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
mach_port_t MachMultiprocess::LocalPort() const {
|
mach_port_t MachMultiprocess::LocalPort() const {
|
||||||
EXPECT_NE(kMachPortNull, info_->local_port);
|
EXPECT_TRUE(info_->local_port.is_valid());
|
||||||
return info_->local_port;
|
return info_->local_port.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
mach_port_t MachMultiprocess::RemotePort() const {
|
mach_port_t MachMultiprocess::RemotePort() const {
|
||||||
EXPECT_NE(kMachPortNull, info_->remote_port);
|
EXPECT_TRUE(info_->remote_port.is_valid());
|
||||||
return info_->remote_port;
|
return info_->remote_port.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
task_t MachMultiprocess::ChildTask() const {
|
task_t MachMultiprocess::ChildTask() const {
|
||||||
EXPECT_NE(TASK_NULL, info_->child_task);
|
EXPECT_TRUE(info_->child_task.is_valid());
|
||||||
return info_->child_task;
|
return info_->child_task.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MachMultiprocess::MultiprocessParent() {
|
void MachMultiprocess::MultiprocessParent() {
|
||||||
@ -123,7 +123,7 @@ void MachMultiprocess::MultiprocessParent() {
|
|||||||
MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
|
MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
|
||||||
0,
|
0,
|
||||||
sizeof(message),
|
sizeof(message),
|
||||||
info_->local_port,
|
info_->local_port.get(),
|
||||||
MACH_MSG_TIMEOUT_NONE,
|
MACH_MSG_TIMEOUT_NONE,
|
||||||
MACH_PORT_NULL);
|
MACH_PORT_NULL);
|
||||||
ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
|
ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
|
||||||
@ -198,7 +198,7 @@ void MachMultiprocess::MultiprocessParent() {
|
|||||||
|
|
||||||
// Verify that the child’s task port is what it purports to be.
|
// Verify that the child’s task port is what it purports to be.
|
||||||
int mach_pid;
|
int mach_pid;
|
||||||
kr = pid_for_task(info_->child_task, &mach_pid);
|
kr = pid_for_task(info_->child_task.get(), &mach_pid);
|
||||||
ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task");
|
ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "pid_for_task");
|
||||||
ASSERT_EQ(ChildPID(), mach_pid);
|
ASSERT_EQ(ChildPID(), mach_pid);
|
||||||
|
|
||||||
@ -229,8 +229,8 @@ void MachMultiprocess::MultiprocessChild() {
|
|||||||
MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |
|
MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) |
|
||||||
MACH_MSGH_BITS_COMPLEX;
|
MACH_MSGH_BITS_COMPLEX;
|
||||||
message.header.msgh_size = sizeof(message);
|
message.header.msgh_size = sizeof(message);
|
||||||
message.header.msgh_remote_port = info_->remote_port;
|
message.header.msgh_remote_port = info_->remote_port.get();
|
||||||
message.header.msgh_local_port = info_->local_port;
|
message.header.msgh_local_port = info_->local_port.get();
|
||||||
message.body.msgh_descriptor_count = 1;
|
message.body.msgh_descriptor_count = 1;
|
||||||
message.port_descriptor.name = mach_task_self();
|
message.port_descriptor.name = mach_task_self();
|
||||||
message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;
|
message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND;
|
||||||
|
@ -301,7 +301,7 @@ int CatchExceptionToolMain(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mach_msg_return_t mr = MachMessageServer::Run(&universal_mach_exc_server,
|
mach_msg_return_t mr = MachMessageServer::Run(&universal_mach_exc_server,
|
||||||
service_port,
|
service_port.get(),
|
||||||
MACH_MSG_OPTION_NONE,
|
MACH_MSG_OPTION_NONE,
|
||||||
options.persistent,
|
options.persistent,
|
||||||
receive_large,
|
receive_large,
|
||||||
|
@ -195,7 +195,7 @@ void ShowBootstrapService(const std::string& service_name,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mach_send_right_pool->AddSendRight(service_port);
|
mach_send_right_pool->AddSendRight(service_port.get());
|
||||||
|
|
||||||
printf("service %s %#x\n", service_name.c_str(), service_port.get());
|
printf("service %s %#x\n", service_name.c_str(), service_port.get());
|
||||||
}
|
}
|
||||||
@ -300,7 +300,7 @@ bool SetExceptionPort(const ExceptionHandlerDescription* description,
|
|||||||
|
|
||||||
ExceptionPorts exception_ports(description->target_type, target_port);
|
ExceptionPorts exception_ports(description->target_type, target_port);
|
||||||
if (!exception_ports.SetExceptionPort(description->mask,
|
if (!exception_ports.SetExceptionPort(description->mask,
|
||||||
service_port,
|
service_port.get(),
|
||||||
description->behavior,
|
description->behavior,
|
||||||
description->flavor)) {
|
description->flavor)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -105,7 +105,7 @@ mach_port_t ChildPortHandshake::RunServer() {
|
|||||||
// Check the new service in with the bootstrap server, obtaining a receive
|
// Check the new service in with the bootstrap server, obtaining a receive
|
||||||
// right for it.
|
// right for it.
|
||||||
base::mac::ScopedMachReceiveRight server_port(BootstrapCheckIn(service_name));
|
base::mac::ScopedMachReceiveRight server_port(BootstrapCheckIn(service_name));
|
||||||
CHECK_NE(server_port, kMachPortNull);
|
CHECK(server_port.is_valid());
|
||||||
|
|
||||||
// Share the service name with the client via the pipe.
|
// Share the service name with the client via the pipe.
|
||||||
uint32_t service_name_length = service_name.size();
|
uint32_t service_name_length = service_name.size();
|
||||||
@ -125,10 +125,10 @@ mach_port_t ChildPortHandshake::RunServer() {
|
|||||||
// requires a port set. Create a new port set and add the receive right to it.
|
// requires a port set. Create a new port set and add the receive right to it.
|
||||||
base::mac::ScopedMachPortSet server_port_set(
|
base::mac::ScopedMachPortSet server_port_set(
|
||||||
NewMachPort(MACH_PORT_RIGHT_PORT_SET));
|
NewMachPort(MACH_PORT_RIGHT_PORT_SET));
|
||||||
CHECK_NE(server_port_set, kMachPortNull);
|
CHECK(server_port_set.is_valid());
|
||||||
|
|
||||||
kern_return_t kr =
|
kern_return_t kr = mach_port_insert_member(
|
||||||
mach_port_insert_member(mach_task_self(), server_port, server_port_set);
|
mach_task_self(), server_port.get(), server_port_set.get());
|
||||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
|
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
|
||||||
|
|
||||||
// Set up a kqueue to monitor both the server’s receive right and the write
|
// Set up a kqueue to monitor both the server’s receive right and the write
|
||||||
@ -140,7 +140,7 @@ mach_port_t ChildPortHandshake::RunServer() {
|
|||||||
|
|
||||||
struct kevent changelist[2];
|
struct kevent changelist[2];
|
||||||
EV_SET(&changelist[0],
|
EV_SET(&changelist[0],
|
||||||
server_port_set,
|
server_port_set.get(),
|
||||||
EVFILT_MACHPORT,
|
EVFILT_MACHPORT,
|
||||||
EV_ADD | EV_CLEAR,
|
EV_ADD | EV_CLEAR,
|
||||||
0,
|
0,
|
||||||
@ -194,7 +194,7 @@ mach_port_t ChildPortHandshake::RunServer() {
|
|||||||
switch (event.filter) {
|
switch (event.filter) {
|
||||||
case EVFILT_MACHPORT: {
|
case EVFILT_MACHPORT: {
|
||||||
// There’s something to receive on the port set.
|
// There’s something to receive on the port set.
|
||||||
DCHECK_EQ(event.ident, server_port_set);
|
DCHECK_EQ(event.ident, server_port_set.get());
|
||||||
|
|
||||||
// Run the message server in an inner loop instead of using
|
// Run the message server in an inner loop instead of using
|
||||||
// MachMessageServer::kPersistent. This allows the loop to exit as soon
|
// MachMessageServer::kPersistent. This allows the loop to exit as soon
|
||||||
@ -207,7 +207,7 @@ mach_port_t ChildPortHandshake::RunServer() {
|
|||||||
// this will call HandleChildPortCheckIn().
|
// this will call HandleChildPortCheckIn().
|
||||||
mach_msg_return_t mr =
|
mach_msg_return_t mr =
|
||||||
MachMessageServer::Run(&child_port_server,
|
MachMessageServer::Run(&child_port_server,
|
||||||
server_port_set,
|
server_port_set.get(),
|
||||||
MACH_MSG_OPTION_NONE,
|
MACH_MSG_OPTION_NONE,
|
||||||
MachMessageServer::kOneShot,
|
MachMessageServer::kOneShot,
|
||||||
MachMessageServer::kReceiveLargeIgnore,
|
MachMessageServer::kReceiveLargeIgnore,
|
||||||
@ -329,10 +329,11 @@ void ChildPortHandshake::RunClientInternal_SendCheckIn(
|
|||||||
// Get a send right to the server by looking up the service with the bootstrap
|
// Get a send right to the server by looking up the service with the bootstrap
|
||||||
// server by name.
|
// server by name.
|
||||||
base::mac::ScopedMachSendRight server_port(BootstrapLookUp(service_name));
|
base::mac::ScopedMachSendRight server_port(BootstrapLookUp(service_name));
|
||||||
CHECK_NE(server_port, kMachPortNull);
|
CHECK(server_port.is_valid());
|
||||||
|
|
||||||
// Check in with the server.
|
// Check in with the server.
|
||||||
kern_return_t kr = child_port_check_in(server_port, token, port, right_type);
|
kern_return_t kr =
|
||||||
|
child_port_check_in(server_port.get(), token, port, right_type);
|
||||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "child_port_check_in";
|
MACH_CHECK(kr == KERN_SUCCESS, kr) << "child_port_check_in";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,9 +376,9 @@ class TestExceptionPorts : public MachMultiprocess,
|
|||||||
threads_need_owners.Disarm();
|
threads_need_owners.Disarm();
|
||||||
|
|
||||||
ExceptionPorts main_thread_ports(ExceptionPorts::kTargetTypeThread,
|
ExceptionPorts main_thread_ports(ExceptionPorts::kTargetTypeThread,
|
||||||
main_thread);
|
main_thread.get());
|
||||||
ExceptionPorts other_thread_ports(ExceptionPorts::kTargetTypeThread,
|
ExceptionPorts other_thread_ports(ExceptionPorts::kTargetTypeThread,
|
||||||
other_thread);
|
other_thread.get());
|
||||||
EXPECT_STREQ("thread", main_thread_ports.TargetTypeName());
|
EXPECT_STREQ("thread", main_thread_ports.TargetTypeName());
|
||||||
EXPECT_STREQ("thread", other_thread_ports.TargetTypeName());
|
EXPECT_STREQ("thread", other_thread_ports.TargetTypeName());
|
||||||
|
|
||||||
@ -586,7 +586,8 @@ TEST(ExceptionPorts, HostExceptionPorts) {
|
|||||||
const bool expect_success = geteuid() == 0;
|
const bool expect_success = geteuid() == 0;
|
||||||
|
|
||||||
base::mac::ScopedMachSendRight host(mach_host_self());
|
base::mac::ScopedMachSendRight host(mach_host_self());
|
||||||
ExceptionPorts explicit_host_ports(ExceptionPorts::kTargetTypeHost, host);
|
ExceptionPorts explicit_host_ports(ExceptionPorts::kTargetTypeHost,
|
||||||
|
host.get());
|
||||||
EXPECT_STREQ("host", explicit_host_ports.TargetTypeName());
|
EXPECT_STREQ("host", explicit_host_ports.TargetTypeName());
|
||||||
|
|
||||||
ExceptionPorts::ExceptionHandlerVector explicit_handlers;
|
ExceptionPorts::ExceptionHandlerVector explicit_handlers;
|
||||||
|
@ -34,7 +34,7 @@ TEST(MachExtensions, NewMachPort_Receive) {
|
|||||||
ASSERT_NE(kMachPortNull, port);
|
ASSERT_NE(kMachPortNull, port);
|
||||||
|
|
||||||
mach_port_type_t type;
|
mach_port_type_t type;
|
||||||
kern_return_t kr = mach_port_type(mach_task_self(), port, &type);
|
kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);
|
||||||
ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
|
ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
|
||||||
|
|
||||||
EXPECT_EQ(MACH_PORT_TYPE_RECEIVE, type);
|
EXPECT_EQ(MACH_PORT_TYPE_RECEIVE, type);
|
||||||
@ -45,7 +45,7 @@ TEST(MachExtensions, NewMachPort_PortSet) {
|
|||||||
ASSERT_NE(kMachPortNull, port);
|
ASSERT_NE(kMachPortNull, port);
|
||||||
|
|
||||||
mach_port_type_t type;
|
mach_port_type_t type;
|
||||||
kern_return_t kr = mach_port_type(mach_task_self(), port, &type);
|
kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);
|
||||||
ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
|
ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
|
||||||
|
|
||||||
EXPECT_EQ(MACH_PORT_TYPE_PORT_SET, type);
|
EXPECT_EQ(MACH_PORT_TYPE_PORT_SET, type);
|
||||||
@ -56,7 +56,7 @@ TEST(MachExtensions, NewMachPort_DeadName) {
|
|||||||
ASSERT_NE(kMachPortNull, port);
|
ASSERT_NE(kMachPortNull, port);
|
||||||
|
|
||||||
mach_port_type_t type;
|
mach_port_type_t type;
|
||||||
kern_return_t kr = mach_port_type(mach_task_self(), port, &type);
|
kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);
|
||||||
ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
|
ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_type");
|
||||||
|
|
||||||
EXPECT_EQ(MACH_PORT_TYPE_DEAD_NAME, type);
|
EXPECT_EQ(MACH_PORT_TYPE_DEAD_NAME, type);
|
||||||
@ -173,7 +173,7 @@ TEST(MachExtensions, BootstrapCheckInAndLookUp) {
|
|||||||
TEST(MachExtensions, SystemCrashReporterHandler) {
|
TEST(MachExtensions, SystemCrashReporterHandler) {
|
||||||
base::mac::ScopedMachSendRight
|
base::mac::ScopedMachSendRight
|
||||||
system_crash_reporter_handler(SystemCrashReporterHandler());
|
system_crash_reporter_handler(SystemCrashReporterHandler());
|
||||||
EXPECT_TRUE(system_crash_reporter_handler);
|
EXPECT_TRUE(system_crash_reporter_handler.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -467,8 +467,8 @@ class TestMachMessageServer : public MachMessageServer::Interface,
|
|||||||
// carried in the request message to the server. By the time the server
|
// carried in the request message to the server. By the time the server
|
||||||
// looks at the right, it will have become a dead name.
|
// looks at the right, it will have become a dead name.
|
||||||
local_receive_port_owner.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
local_receive_port_owner.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, local_receive_port_owner);
|
ASSERT_TRUE(local_receive_port_owner.is_valid());
|
||||||
request.header.msgh_local_port = local_receive_port_owner;
|
request.header.msgh_local_port = local_receive_port_owner.get();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -479,8 +479,8 @@ class TestMachMessageServer : public MachMessageServer::Interface,
|
|||||||
// properly handles ownership of resources received in complex messages.
|
// properly handles ownership of resources received in complex messages.
|
||||||
request.body.msgh_descriptor_count = 1;
|
request.body.msgh_descriptor_count = 1;
|
||||||
child_complex_message_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
child_complex_message_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, child_complex_message_port_);
|
ASSERT_TRUE(child_complex_message_port_.is_valid());
|
||||||
request.port_descriptor.name = child_complex_message_port_;
|
request.port_descriptor.name = child_complex_message_port_.get();
|
||||||
request.port_descriptor.disposition = MACH_MSG_TYPE_MAKE_SEND;
|
request.port_descriptor.disposition = MACH_MSG_TYPE_MAKE_SEND;
|
||||||
request.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;
|
request.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||||
} else {
|
} else {
|
||||||
|
@ -115,7 +115,7 @@ TEST(MachMessage, AuditPIDFromMachMessageTrailer) {
|
|||||||
mach_msg_empty_send_t send = {};
|
mach_msg_empty_send_t send = {};
|
||||||
send.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0);
|
send.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0);
|
||||||
send.header.msgh_size = sizeof(send);
|
send.header.msgh_size = sizeof(send);
|
||||||
send.header.msgh_remote_port = port;
|
send.header.msgh_remote_port = port.get();
|
||||||
mach_msg_return_t mr =
|
mach_msg_return_t mr =
|
||||||
MachMessageWithDeadline(&send.header,
|
MachMessageWithDeadline(&send.header,
|
||||||
MACH_SEND_MSG,
|
MACH_SEND_MSG,
|
||||||
@ -138,7 +138,7 @@ TEST(MachMessage, AuditPIDFromMachMessageTrailer) {
|
|||||||
mr = MachMessageWithDeadline(&receive.header,
|
mr = MachMessageWithDeadline(&receive.header,
|
||||||
MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
|
MACH_RCV_MSG | kMachMessageReceiveAuditTrailer,
|
||||||
sizeof(receive),
|
sizeof(receive),
|
||||||
port,
|
port.get(),
|
||||||
kMachMessageDeadlineNonblocking,
|
kMachMessageDeadlineNonblocking,
|
||||||
MACH_PORT_NULL,
|
MACH_PORT_NULL,
|
||||||
false);
|
false);
|
||||||
|
@ -256,12 +256,12 @@ class NotifyServerTestBase : public testing::Test,
|
|||||||
//! established for the current test. On failure, returns `MACH_PORT_NULL`
|
//! established for the current test. On failure, returns `MACH_PORT_NULL`
|
||||||
//! with a gtest failure added.
|
//! with a gtest failure added.
|
||||||
mach_port_t ServerPort() {
|
mach_port_t ServerPort() {
|
||||||
if (!server_port_) {
|
if (!server_port_.is_valid()) {
|
||||||
server_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
server_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
EXPECT_NE(kMachPortNull, server_port_);
|
EXPECT_TRUE(server_port_.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
return server_port_;
|
return server_port_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
// testing::Test:
|
// testing::Test:
|
||||||
@ -309,14 +309,14 @@ TEST_F(NotifyServerTest, NoNotification) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifyPortDeleted) {
|
TEST_F(NotifyServerTest, MachNotifyPortDeleted) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
base::mac::ScopedMachSendRight send_once_right(
|
base::mac::ScopedMachSendRight send_once_right(
|
||||||
SendOnceRightFromReceiveRight(receive_right));
|
SendOnceRightFromReceiveRight(receive_right.get()));
|
||||||
ASSERT_NE(kMachPortNull, send_once_right);
|
ASSERT_TRUE(send_once_right.is_valid());
|
||||||
|
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(RequestMachPortNotification(
|
||||||
RequestMachPortNotification(send_once_right, MACH_NOTIFY_DEAD_NAME, 0));
|
send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));
|
||||||
|
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*this,
|
*this,
|
||||||
@ -336,10 +336,10 @@ TEST_F(NotifyServerTest, MachNotifyPortDeleted) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifyPortDestroyed) {
|
TEST_F(NotifyServerTest, MachNotifyPortDestroyed) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
ASSERT_TRUE(RequestMachPortNotification(
|
ASSERT_TRUE(RequestMachPortNotification(
|
||||||
receive_right, MACH_NOTIFY_PORT_DESTROYED, 0));
|
receive_right.get(), MACH_NOTIFY_PORT_DESTROYED, 0));
|
||||||
|
|
||||||
EXPECT_CALL(
|
EXPECT_CALL(
|
||||||
*this,
|
*this,
|
||||||
@ -360,10 +360,10 @@ TEST_F(NotifyServerTest, MachNotifyPortDestroyed) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifyPortDestroyed_NoNotification) {
|
TEST_F(NotifyServerTest, MachNotifyPortDestroyed_NoNotification) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
ASSERT_TRUE(RequestMachPortNotification(
|
ASSERT_TRUE(RequestMachPortNotification(
|
||||||
receive_right, MACH_NOTIFY_PORT_DESTROYED, 0));
|
receive_right.get(), MACH_NOTIFY_PORT_DESTROYED, 0));
|
||||||
|
|
||||||
RunServer();
|
RunServer();
|
||||||
}
|
}
|
||||||
@ -373,10 +373,10 @@ TEST_F(NotifyServerTest, MachNotifyPortDestroyed_NoNotification) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifyNoSenders_NoSendRight) {
|
TEST_F(NotifyServerTest, MachNotifyNoSenders_NoSendRight) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(RequestMachPortNotification(
|
||||||
RequestMachPortNotification(receive_right, MACH_NOTIFY_NO_SENDERS, 0));
|
receive_right.get(), MACH_NOTIFY_NO_SENDERS, 0));
|
||||||
|
|
||||||
EXPECT_CALL(*this,
|
EXPECT_CALL(*this,
|
||||||
DoMachNotifyNoSenders(
|
DoMachNotifyNoSenders(
|
||||||
@ -393,14 +393,14 @@ TEST_F(NotifyServerTest, MachNotifyNoSenders_NoSendRight) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifyNoSenders_SendRightDeallocated) {
|
TEST_F(NotifyServerTest, MachNotifyNoSenders_SendRightDeallocated) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
base::mac::ScopedMachSendRight send_right(
|
base::mac::ScopedMachSendRight send_right(
|
||||||
SendRightFromReceiveRight(receive_right));
|
SendRightFromReceiveRight(receive_right.get()));
|
||||||
ASSERT_NE(kMachPortNull, send_right);
|
ASSERT_TRUE(send_right.is_valid());
|
||||||
|
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(RequestMachPortNotification(
|
||||||
RequestMachPortNotification(receive_right, MACH_NOTIFY_NO_SENDERS, 1));
|
receive_right.get(), MACH_NOTIFY_NO_SENDERS, 1));
|
||||||
|
|
||||||
EXPECT_CALL(*this,
|
EXPECT_CALL(*this,
|
||||||
DoMachNotifyNoSenders(
|
DoMachNotifyNoSenders(
|
||||||
@ -418,25 +418,25 @@ TEST_F(NotifyServerTest, MachNotifyNoSenders_SendRightDeallocated) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifyNoSenders_NoNotification) {
|
TEST_F(NotifyServerTest, MachNotifyNoSenders_NoNotification) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
base::mac::ScopedMachSendRight send_right_0(
|
base::mac::ScopedMachSendRight send_right_0(
|
||||||
SendRightFromReceiveRight(receive_right));
|
SendRightFromReceiveRight(receive_right.get()));
|
||||||
ASSERT_NE(kMachPortNull, send_right_0);
|
ASSERT_TRUE(send_right_0.is_valid());
|
||||||
|
|
||||||
base::mac::ScopedMachSendRight send_right_1(
|
base::mac::ScopedMachSendRight send_right_1(
|
||||||
SendRightFromReceiveRight(receive_right));
|
SendRightFromReceiveRight(receive_right.get()));
|
||||||
ASSERT_NE(kMachPortNull, send_right_1);
|
ASSERT_TRUE(send_right_1.is_valid());
|
||||||
|
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(RequestMachPortNotification(
|
||||||
RequestMachPortNotification(receive_right, MACH_NOTIFY_NO_SENDERS, 1));
|
receive_right.get(), MACH_NOTIFY_NO_SENDERS, 1));
|
||||||
|
|
||||||
send_right_1.reset();
|
send_right_1.reset();
|
||||||
|
|
||||||
RunServer();
|
RunServer();
|
||||||
|
|
||||||
EXPECT_EQ(1u, RightRefCount(receive_right, MACH_PORT_RIGHT_RECEIVE));
|
EXPECT_EQ(1u, RightRefCount(receive_right.get(), MACH_PORT_RIGHT_RECEIVE));
|
||||||
EXPECT_EQ(1u, RightRefCount(receive_right, MACH_PORT_RIGHT_SEND));
|
EXPECT_EQ(1u, RightRefCount(receive_right.get(), MACH_PORT_RIGHT_SEND));
|
||||||
}
|
}
|
||||||
|
|
||||||
// When a send-once right is deallocated without being used, a send-once
|
// When a send-once right is deallocated without being used, a send-once
|
||||||
@ -444,7 +444,7 @@ TEST_F(NotifyServerTest, MachNotifyNoSenders_NoNotification) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifySendOnce_ExplicitDeallocation) {
|
TEST_F(NotifyServerTest, MachNotifySendOnce_ExplicitDeallocation) {
|
||||||
base::mac::ScopedMachSendRight send_once_right(
|
base::mac::ScopedMachSendRight send_once_right(
|
||||||
SendOnceRightFromReceiveRight(ServerPort()));
|
SendOnceRightFromReceiveRight(ServerPort()));
|
||||||
ASSERT_NE(kMachPortNull, send_once_right);
|
ASSERT_TRUE(send_once_right.is_valid());
|
||||||
|
|
||||||
EXPECT_CALL(*this,
|
EXPECT_CALL(*this,
|
||||||
DoMachNotifySendOnce(ServerPort(),
|
DoMachNotifySendOnce(ServerPort(),
|
||||||
@ -463,13 +463,13 @@ TEST_F(NotifyServerTest, MachNotifySendOnce_ExplicitDeallocation) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifySendOnce_ImplicitDeallocation) {
|
TEST_F(NotifyServerTest, MachNotifySendOnce_ImplicitDeallocation) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
mach_msg_empty_send_t message = {};
|
mach_msg_empty_send_t message = {};
|
||||||
message.header.msgh_bits =
|
message.header.msgh_bits =
|
||||||
MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||||
message.header.msgh_size = sizeof(message);
|
message.header.msgh_size = sizeof(message);
|
||||||
message.header.msgh_remote_port = receive_right;
|
message.header.msgh_remote_port = receive_right.get();
|
||||||
message.header.msgh_local_port = ServerPort();
|
message.header.msgh_local_port = ServerPort();
|
||||||
mach_msg_return_t mr = mach_msg(&message.header,
|
mach_msg_return_t mr = mach_msg(&message.header,
|
||||||
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
|
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
|
||||||
@ -497,14 +497,14 @@ TEST_F(NotifyServerTest, MachNotifySendOnce_ImplicitDeallocation) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifyDeadName) {
|
TEST_F(NotifyServerTest, MachNotifyDeadName) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
base::mac::ScopedMachSendRight send_once_right(
|
base::mac::ScopedMachSendRight send_once_right(
|
||||||
SendOnceRightFromReceiveRight(receive_right));
|
SendOnceRightFromReceiveRight(receive_right.get()));
|
||||||
ASSERT_NE(kMachPortNull, send_once_right);
|
ASSERT_TRUE(send_once_right.is_valid());
|
||||||
|
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(RequestMachPortNotification(
|
||||||
RequestMachPortNotification(send_once_right, MACH_NOTIFY_DEAD_NAME, 0));
|
send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));
|
||||||
|
|
||||||
// send_once_right becomes a dead name with the send-once right’s original
|
// send_once_right becomes a dead name with the send-once right’s original
|
||||||
// user reference count of 1, but the dead-name notification increments the
|
// user reference count of 1, but the dead-name notification increments the
|
||||||
@ -523,10 +523,11 @@ TEST_F(NotifyServerTest, MachNotifyDeadName) {
|
|||||||
|
|
||||||
RunServer();
|
RunServer();
|
||||||
|
|
||||||
EXPECT_TRUE(IsRight(send_once_right, MACH_PORT_TYPE_DEAD_NAME));
|
EXPECT_TRUE(IsRight(send_once_right.get(), MACH_PORT_TYPE_DEAD_NAME));
|
||||||
|
|
||||||
EXPECT_EQ(0u, RightRefCount(send_once_right, MACH_PORT_RIGHT_SEND_ONCE));
|
EXPECT_EQ(0u,
|
||||||
EXPECT_EQ(1u, DeadNameRightRefCount(send_once_right));
|
RightRefCount(send_once_right.get(), MACH_PORT_RIGHT_SEND_ONCE));
|
||||||
|
EXPECT_EQ(1u, DeadNameRightRefCount(send_once_right.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the receive right corresponding to a send-once right with a dead-name
|
// When the receive right corresponding to a send-once right with a dead-name
|
||||||
@ -535,21 +536,22 @@ TEST_F(NotifyServerTest, MachNotifyDeadName) {
|
|||||||
TEST_F(NotifyServerTest, MachNotifyDeadName_NoNotification) {
|
TEST_F(NotifyServerTest, MachNotifyDeadName_NoNotification) {
|
||||||
base::mac::ScopedMachReceiveRight receive_right(
|
base::mac::ScopedMachReceiveRight receive_right(
|
||||||
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||||
ASSERT_NE(kMachPortNull, receive_right);
|
ASSERT_TRUE(receive_right.is_valid());
|
||||||
|
|
||||||
base::mac::ScopedMachSendRight send_once_right(
|
base::mac::ScopedMachSendRight send_once_right(
|
||||||
SendOnceRightFromReceiveRight(receive_right));
|
SendOnceRightFromReceiveRight(receive_right.get()));
|
||||||
ASSERT_NE(kMachPortNull, send_once_right);
|
ASSERT_TRUE(send_once_right.is_valid());
|
||||||
|
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(RequestMachPortNotification(
|
||||||
RequestMachPortNotification(send_once_right, MACH_NOTIFY_DEAD_NAME, 0));
|
send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));
|
||||||
|
|
||||||
RunServer();
|
RunServer();
|
||||||
|
|
||||||
EXPECT_FALSE(IsRight(send_once_right, MACH_PORT_TYPE_DEAD_NAME));
|
EXPECT_FALSE(IsRight(send_once_right.get(), MACH_PORT_TYPE_DEAD_NAME));
|
||||||
|
|
||||||
EXPECT_EQ(1u, RightRefCount(send_once_right, MACH_PORT_RIGHT_SEND_ONCE));
|
EXPECT_EQ(1u,
|
||||||
EXPECT_EQ(0u, DeadNameRightRefCount(send_once_right));
|
RightRefCount(send_once_right.get(), MACH_PORT_RIGHT_SEND_ONCE));
|
||||||
|
EXPECT_EQ(0u, DeadNameRightRefCount(send_once_right.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -600,6 +600,26 @@ ProcessInfo::GetReadableRanges(
|
|||||||
return GetReadableRangesOfMemoryMap(range, MemoryInfo());
|
return GetReadableRangesOfMemoryMap(range, MemoryInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProcessInfo::LoggingRangeIsFullyReadable(
|
||||||
|
const CheckedRange<WinVMAddress, WinVMSize>& range) const {
|
||||||
|
const auto ranges = GetReadableRanges(range);
|
||||||
|
if (ranges.size() != 1) {
|
||||||
|
LOG(ERROR) << base::StringPrintf(
|
||||||
|
"range at 0x%llx, size 0x%llx fully unreadable",
|
||||||
|
range.base(),
|
||||||
|
range.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ranges[0].base() != range.base() || ranges[0].size() != range.size()) {
|
||||||
|
LOG(ERROR) << base::StringPrintf(
|
||||||
|
"some of range at 0x%llx, size 0x%llx unreadable",
|
||||||
|
range.base(),
|
||||||
|
range.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() const {
|
const std::vector<ProcessInfo::Handle>& ProcessInfo::Handles() const {
|
||||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
if (handles_.empty())
|
if (handles_.empty())
|
||||||
|
@ -139,6 +139,16 @@ class ProcessInfo {
|
|||||||
std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRanges(
|
std::vector<CheckedRange<WinVMAddress, WinVMSize>> GetReadableRanges(
|
||||||
const CheckedRange<WinVMAddress, WinVMSize>& range) const;
|
const CheckedRange<WinVMAddress, WinVMSize>& range) const;
|
||||||
|
|
||||||
|
//! \brief Given a range in the target process, determines if the entire range
|
||||||
|
//! is readable.
|
||||||
|
//!
|
||||||
|
//! \param[in] range The range being inspected.
|
||||||
|
//!
|
||||||
|
//! \return `true` if the range is fully readable, otherwise `false` with a
|
||||||
|
//! message logged.
|
||||||
|
bool LoggingRangeIsFullyReadable(
|
||||||
|
const CheckedRange<WinVMAddress, WinVMSize>& range) const;
|
||||||
|
|
||||||
//! \brief Retrieves information about open handles in the target process.
|
//! \brief Retrieves information about open handles in the target process.
|
||||||
const std::vector<Handle>& Handles() const;
|
const std::vector<Handle>& Handles() const;
|
||||||
|
|
||||||
|
@ -623,6 +623,20 @@ TEST(ProcessInfo, Handles) {
|
|||||||
EXPECT_TRUE(found_mapping_handle);
|
EXPECT_TRUE(found_mapping_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ProcessInfo, OutOfRangeCheck) {
|
||||||
|
const size_t kAllocationSize = 12345;
|
||||||
|
scoped_ptr<char[]> safe_memory(new char[kAllocationSize]);
|
||||||
|
|
||||||
|
ProcessInfo info;
|
||||||
|
info.Initialize(GetCurrentProcess());
|
||||||
|
|
||||||
|
EXPECT_TRUE(
|
||||||
|
info.LoggingRangeIsFullyReadable(CheckedRange<WinVMAddress, WinVMSize>(
|
||||||
|
reinterpret_cast<WinVMAddress>(safe_memory.get()), kAllocationSize)));
|
||||||
|
EXPECT_FALSE(info.LoggingRangeIsFullyReadable(
|
||||||
|
CheckedRange<WinVMAddress, WinVMSize>(0, 1024)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
Loading…
x
Reference in New Issue
Block a user