mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-31 01:43:03 +08:00
56399b2553
nullptr ProcessReader::Module. Prior to 64b87325b9de, the alignment problem meant that the Module for dyld was looking at the wrong address instead of dyld’s correct load address when a 32-bit process attempted to examine a crashing 64-bit process. This resulted in a crash during the MachOImageAnnotationsReader.CrashDyld test. ProcessReader::Module pointers are permitted to be nullptr. This allows minimal module data (its name) to be preserved even when no sense can be made of the module based on its load address. The producer, ProcessReader::InitializeModules(), and the non-test consumer, ModuleSnapshotMac::Initialize(), both accept this correctly. The producer’s documentation is updated to call this out. The ProcessReader test is also updated to tolerate this case without crashing by adding assertions. TEST=snapshot_test MachOImageAnnotationsReader.*, ProcessReader.* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/989713002
240 lines
8.9 KiB
C++
240 lines
8.9 KiB
C++
// Copyright 2014 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_SNAPSHOT_MAC_PROCESS_READER_H_
|
||
#define CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_
|
||
|
||
#include <mach/mach.h>
|
||
#include <sys/time.h>
|
||
#include <sys/types.h>
|
||
#include <time.h>
|
||
|
||
#include <string>
|
||
#include <vector>
|
||
|
||
#include "base/basictypes.h"
|
||
#include "base/memory/scoped_ptr.h"
|
||
#include "build/build_config.h"
|
||
#include "util/mach/task_memory.h"
|
||
#include "util/misc/initialization_state_dcheck.h"
|
||
#include "util/posix/process_info.h"
|
||
#include "util/stdlib/pointer_container.h"
|
||
|
||
namespace crashpad {
|
||
|
||
class MachOImageReader;
|
||
|
||
//! \brief Accesses information about another process, identified by a Mach
|
||
//! task.
|
||
class ProcessReader {
|
||
public:
|
||
//! \brief Contains information about a thread that belongs to a task
|
||
//! (process).
|
||
struct Thread {
|
||
#if defined(ARCH_CPU_X86_FAMILY)
|
||
union ThreadContext {
|
||
x86_thread_state64_t t64;
|
||
x86_thread_state32_t t32;
|
||
};
|
||
union FloatContext {
|
||
x86_float_state64_t f64;
|
||
x86_float_state32_t f32;
|
||
};
|
||
union DebugContext {
|
||
x86_debug_state64_t d64;
|
||
x86_debug_state32_t d32;
|
||
};
|
||
#endif
|
||
|
||
Thread();
|
||
~Thread() {}
|
||
|
||
ThreadContext thread_context;
|
||
FloatContext float_context;
|
||
DebugContext debug_context;
|
||
uint64_t id;
|
||
mach_vm_address_t stack_region_address;
|
||
mach_vm_size_t stack_region_size;
|
||
mach_vm_address_t thread_specific_data_address;
|
||
thread_t port;
|
||
int suspend_count;
|
||
int priority;
|
||
};
|
||
|
||
//! \brief Contains information about a module loaded into a process.
|
||
struct Module {
|
||
Module();
|
||
~Module();
|
||
|
||
//! \brief The pathname used to load the module from disk.
|
||
std::string name;
|
||
|
||
//! \brief An image reader for the module.
|
||
//!
|
||
//! The lifetime of this MachOImageReader is scoped to the lifetime of the
|
||
//! ProcessReader that created it.
|
||
//!
|
||
//! This field may be `nullptr` if a reader could not be created for the
|
||
//! module.
|
||
const MachOImageReader* reader;
|
||
|
||
//! \brief The module’s timestamp.
|
||
//!
|
||
//! This field will be `0` if its value cannot be determined. It can only be
|
||
//! determined for images that are loaded by dyld, so it will be `0` for the
|
||
//! main executable and for dyld itself.
|
||
time_t timestamp;
|
||
};
|
||
|
||
ProcessReader();
|
||
~ProcessReader();
|
||
|
||
//! \brief Initializes this object. This method must be called before any
|
||
//! other.
|
||
//!
|
||
//! \param[in] task A send right to the target task’s task port. This object
|
||
//! does not take ownership of the send right.
|
||
//!
|
||
//! \return `true` on success, indicating that this object will respond
|
||
//! validly to further method calls. `false` on failure. On failure, no
|
||
//! further method calls should be made.
|
||
bool Initialize(task_t task);
|
||
|
||
//! \return `true` if the target task is a 64-bit process.
|
||
bool Is64Bit() const { return is_64_bit_; }
|
||
|
||
//! \return The target task’s process ID.
|
||
pid_t ProcessID() const { return process_info_.ProcessID(); }
|
||
|
||
//! \return The target task’s parent process ID.
|
||
pid_t ParentProcessID() const { return process_info_.ParentProcessID(); }
|
||
|
||
//! \brief Determines the target process’ start time.
|
||
//!
|
||
//! \param[out] start_time The time that the process started.
|
||
void StartTime(timeval* start_time) const {
|
||
process_info_.StartTime(start_time);
|
||
}
|
||
|
||
//! \brief Determines the target process’ execution time.
|
||
//!
|
||
//! \param[out] user_time The amount of time the process has executed code in
|
||
//! user mode.
|
||
//! \param[out] system_time The amount of time the process has executed code
|
||
//! in system mode.
|
||
//!
|
||
//! \return `true` on success, `false` on failure, with a warning logged. On
|
||
//! failure, \a user_time and \a system_time will be set to represent no
|
||
//! time spent executing code in user or system mode.
|
||
bool CPUTimes(timeval* user_time, timeval* system_time) const;
|
||
|
||
//! \return Accesses the memory of the target task.
|
||
TaskMemory* Memory() { return task_memory_.get(); }
|
||
|
||
//! \return The threads that are in the task (process). The first element (at
|
||
//! index `0`) corresponds to the main thread.
|
||
const std::vector<Thread>& Threads();
|
||
|
||
//! \return The modules loaded in the process. The first element (at index
|
||
//! `0`) corresponds to the main executable, and the final element
|
||
//! corresponds to the dynamic loader, dyld.
|
||
const std::vector<Module>& Modules();
|
||
|
||
private:
|
||
//! Performs lazy initialization of the \a threads_ vector on behalf of
|
||
//! Threads().
|
||
void InitializeThreads();
|
||
|
||
//! Performs lazy initialization of the \a modules_ vector on behalf of
|
||
//! Modules().
|
||
void InitializeModules();
|
||
|
||
//! \brief Calculates the base address and size of the region used as a
|
||
//! thread’s stack.
|
||
//!
|
||
//! The region returned by this method may be formed by merging multiple
|
||
//! adjacent regions in a process’ memory map if appropriate. The base address
|
||
//! of the returned region may be lower than the \a stack_pointer passed in
|
||
//! when the ABI mandates a red zone below the stack pointer.
|
||
//!
|
||
//! \param[in] stack_pointer The stack pointer, referring to the top (lowest
|
||
//! address) of a thread’s stack.
|
||
//! \param[out] stack_region_size The size of the memory region used as the
|
||
//! thread’s stack.
|
||
//!
|
||
//! \return The base address (lowest address) of the memory region used as the
|
||
//! thread’s stack.
|
||
mach_vm_address_t CalculateStackRegion(mach_vm_address_t stack_pointer,
|
||
mach_vm_size_t* stack_region_size);
|
||
|
||
//! \brief Adjusts the region for the red zone, if the ABI requires one.
|
||
//!
|
||
//! This method performs red zone calculation for CalculateStackRegion(). Its
|
||
//! parameters are local variables used within that method, and may be
|
||
//! modified as needed.
|
||
//!
|
||
//! Where a red zone is required, the region of memory captured for a thread’s
|
||
//! stack will be extended to include the red zone below the stack pointer,
|
||
//! provided that such memory is mapped, readable, and has the correct user
|
||
//! tag value. If these conditions cannot be met fully, as much of the red
|
||
//! zone will be captured as is possible while meeting these conditions.
|
||
//!
|
||
//! \param[inout] start_address The base address of the region to begin
|
||
//! capturing stack memory from. On entry, \a start_address is the stack
|
||
//! pointer. On return, \a start_address may be decreased to encompass a
|
||
//! red zone.
|
||
//! \param[inout] region_base The base address of the region that contains
|
||
//! stack memory. This is distinct from \a start_address in that \a
|
||
//! region_base will be page-aligned. On entry, \a region_base is the
|
||
//! base address of a region that contains \a start_address. On return,
|
||
//! if \a start_address is decremented and is outside of the region
|
||
//! originally described by \a region_base, \a region_base will also be
|
||
//! decremented appropriately.
|
||
//! \param[inout] region_size The size of the region that contains stack
|
||
//! memory. This region begins at \a region_base. On return, if \a
|
||
//! region_base is decremented, \a region_size will be incremented
|
||
//! appropriately.
|
||
//! \param[in] user_tag The Mach VM system’s user tag for the region described
|
||
//! by the initial values of \a region_base and \a region_size. The red
|
||
//! zone will only be allowed to extend out of the region described by
|
||
//! these initial values if the user tag is appropriate for stack memory
|
||
//! and the expanded region has the same user tag value.
|
||
void LocateRedZone(mach_vm_address_t* start_address,
|
||
mach_vm_address_t* region_base,
|
||
mach_vm_address_t* region_size,
|
||
unsigned int user_tag);
|
||
|
||
ProcessInfo process_info_;
|
||
std::vector<Thread> threads_; // owns send rights
|
||
std::vector<Module> modules_;
|
||
PointerVector<MachOImageReader> module_readers_;
|
||
scoped_ptr<TaskMemory> task_memory_;
|
||
task_t task_; // weak
|
||
InitializationStateDcheck initialized_;
|
||
|
||
// This shadows a method of process_info_, but it’s accessed so frequently
|
||
// that it’s given a first-class field to save a call and a few bit operations
|
||
// on each access.
|
||
bool is_64_bit_;
|
||
|
||
bool initialized_threads_;
|
||
bool initialized_modules_;
|
||
|
||
DISALLOW_COPY_AND_ASSIGN(ProcessReader);
|
||
};
|
||
|
||
} // namespace crashpad
|
||
|
||
#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_H_
|