mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1309172 - Updated breakpad to version 69c2c51dd89965d234eec16e3a9353634831916b; r=ted.mielczarek
This includes both the vanilla sources we haven't forked and the client sources that we have. Client patches were applied manually up to version 69c2c51dd89965d234eec16e3a9353634831916b. The following changes were not included as they break merging segments corresponding to libxul.so in the module list: 8915f7be39448d9257b6da3ad0233944d1d9a92a 17ad0c18b179c135fc5a3d2bba199c3fa4276035 94b6309aecaddfcf11672f6cfad9575d68ad3b40 With these changes applied two entries for libxul.so are generated, the second one is bogus and prevents symbolication from working correctly. The build system and some of the tools relying on breakpad were also updated to work with the new version. --HG-- extra : source : fe4d49307f8890a0c430c257c96f74a9552eeb31 extra : histedit_source : bc84861445bd93856cd0d0c864fd15ad7d9ccc12%2C1efd65797da46e33481afa61a302098780b0f107
This commit is contained in:
parent
aeb2ada3aa
commit
b0e9d95a41
@ -255,7 +255,7 @@ CrashGenerationServer::ClientEvent(short revents)
|
||||
}
|
||||
|
||||
if (crashing_pid == -1 || signal_fd == -1) {
|
||||
if (signal_fd)
|
||||
if (signal_fd != -1)
|
||||
close(signal_fd);
|
||||
return true;
|
||||
}
|
||||
|
@ -41,8 +41,21 @@ namespace google_breakpad {
|
||||
// One of these is produced for each mapping in the process (i.e. line in
|
||||
// /proc/$x/maps).
|
||||
struct MappingInfo {
|
||||
// On Android, relocation packing can mean that the reported start
|
||||
// address of the mapping must be adjusted by a bias in order to
|
||||
// compensate for the compression of the relocation section. The
|
||||
// following two members hold (after LateInit) the adjusted mapping
|
||||
// range. See crbug.com/606972 for more information.
|
||||
uintptr_t start_addr;
|
||||
size_t size;
|
||||
// When Android relocation packing causes |start_addr| and |size| to
|
||||
// be modified with a load bias, we need to remember the unbiased
|
||||
// address range. The following structure holds the original mapping
|
||||
// address range as reported by the operating system.
|
||||
struct {
|
||||
uintptr_t start_addr;
|
||||
uintptr_t end_addr;
|
||||
} system_mapping_info;
|
||||
size_t offset; // offset into the backed file.
|
||||
bool exec; // true if the mapping has the execute bit set.
|
||||
char name[NAME_MAX];
|
||||
|
@ -36,7 +36,7 @@ namespace google_breakpad {
|
||||
|
||||
// Minidump defines register structures which are different from the raw
|
||||
// structures which we get from the kernel. These are platform specific
|
||||
// functions to juggle the ucontext and user structures into minidump format.
|
||||
// functions to juggle the ucontext_t and user structures into minidump format.
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Wraps platform-dependent implementations of accessors to ucontext structs.
|
||||
// Wraps platform-dependent implementations of accessors to ucontext_t structs.
|
||||
struct UContextReader {
|
||||
static uintptr_t GetStackPointer(const ucontext_t* uc);
|
||||
|
||||
|
@ -105,12 +105,6 @@
|
||||
#define PR_SET_PTRACER 0x59616d61
|
||||
#endif
|
||||
|
||||
// A wrapper for the tgkill syscall: send a signal to a specific thread.
|
||||
static int _tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
return syscall(__NR_tgkill, tgid, tid, sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
@ -218,6 +212,7 @@ pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
// time can use |g_crash_context_|.
|
||||
ExceptionHandler::CrashContext g_crash_context_;
|
||||
|
||||
FirstChanceHandler g_first_chance_handler_ = nullptr;
|
||||
} // namespace
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
@ -331,6 +326,18 @@ void ExceptionHandler::RestoreHandlersLocked() {
|
||||
// Runs on the crashing thread.
|
||||
// static
|
||||
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
|
||||
// Give the first chance handler a chance to recover from this signal
|
||||
//
|
||||
// This is primarily used by V8. V8 uses guard regions to guarantee memory
|
||||
// safety in WebAssembly. This means some signals might be expected if they
|
||||
// originate from Wasm code while accessing the guard region. We give V8 the
|
||||
// chance to handle and recover from these signals first.
|
||||
if (g_first_chance_handler_ != nullptr &&
|
||||
g_first_chance_handler_(sig, info, uc)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// All the exception signals are blocked at this point.
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
|
||||
@ -346,6 +353,7 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// will call the function with the right arguments.
|
||||
struct sigaction cur_handler;
|
||||
if (sigaction(sig, NULL, &cur_handler) == 0 &&
|
||||
cur_handler.sa_sigaction == SignalHandler &&
|
||||
(cur_handler.sa_flags & SA_SIGINFO) == 0) {
|
||||
// Reset signal handler with the right flags.
|
||||
sigemptyset(&cur_handler.sa_mask);
|
||||
@ -387,7 +395,7 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// In order to retrigger it, we have to queue a new signal by calling
|
||||
// kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
|
||||
// due to the kernel sending a SIGABRT from a user request via SysRQ.
|
||||
if (_tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
|
||||
if (sys_tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
|
||||
// If we failed to kill ourselves (e.g. because a sandbox disallows us
|
||||
// to do so), we instead resort to terminating our process. This will
|
||||
// result in an incorrect exit code.
|
||||
@ -414,9 +422,14 @@ struct ThreadArgument {
|
||||
int ExceptionHandler::ThreadEntry(void *arg) {
|
||||
const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
|
||||
|
||||
// Close the write end of the pipe. This allows us to fail if the parent dies
|
||||
// while waiting for the continue signal.
|
||||
sys_close(thread_arg->handler->fdes[1]);
|
||||
|
||||
// Block here until the crashing process unblocks us when
|
||||
// we're allowed to use ptrace
|
||||
thread_arg->handler->WaitForContinueSignal();
|
||||
sys_close(thread_arg->handler->fdes[0]);
|
||||
|
||||
return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
|
||||
thread_arg->context_size) == false;
|
||||
@ -424,7 +437,7 @@ int ExceptionHandler::ThreadEntry(void *arg) {
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
bool ExceptionHandler::HandleSignal(int /*sig*/, siginfo_t* info, void* uc) {
|
||||
if (filter_ && !filter_(callback_context_))
|
||||
return false;
|
||||
|
||||
@ -523,8 +536,8 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
}
|
||||
|
||||
const pid_t child = sys_clone(
|
||||
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||
&thread_arg, NULL, NULL, NULL);
|
||||
ThreadEntry, stack, CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL,
|
||||
NULL);
|
||||
if (child == -1) {
|
||||
sys_close(fdes[0]);
|
||||
sys_close(fdes[1]);
|
||||
@ -548,13 +561,14 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
||||
logger::write(childMsg, my_strlen(childMsg));
|
||||
}
|
||||
|
||||
// Close the read end of the pipe.
|
||||
sys_close(fdes[0]);
|
||||
// Allow the child to ptrace us
|
||||
sys_prctl(PR_SET_PTRACER, child, 0, 0, 0);
|
||||
SendContinueSignalToChild();
|
||||
int status;
|
||||
int status = 0;
|
||||
const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL));
|
||||
|
||||
sys_close(fdes[0]);
|
||||
sys_close(fdes[1]);
|
||||
|
||||
if (r == -1) {
|
||||
@ -610,12 +624,20 @@ void ExceptionHandler::WaitForContinueSignal() {
|
||||
// Runs on the cloned process.
|
||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size) {
|
||||
const bool may_skip_dump =
|
||||
minidump_descriptor_.skip_dump_if_principal_mapping_not_referenced();
|
||||
const uintptr_t principal_mapping_address =
|
||||
minidump_descriptor_.address_within_principal_mapping();
|
||||
const bool sanitize_stacks = minidump_descriptor_.sanitize_stacks();
|
||||
if (minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||
return google_breakpad::WriteMicrodump(
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
may_skip_dump,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks,
|
||||
*minidump_descriptor_.microdump_extra_info());
|
||||
}
|
||||
if (minidump_descriptor_.IsFD()) {
|
||||
@ -625,7 +647,10 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
app_memory_list_,
|
||||
may_skip_dump,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
|
||||
minidump_descriptor_.size_limit(),
|
||||
@ -633,7 +658,10 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
app_memory_list_,
|
||||
may_skip_dump,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
// static
|
||||
@ -786,4 +814,8 @@ bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
|
||||
return callback ? callback(descriptor, callback_context, true) : true;
|
||||
}
|
||||
|
||||
void SetFirstChanceExceptionHandler(FirstChanceHandler callback) {
|
||||
g_first_chance_handler_ = callback;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -194,8 +194,8 @@ class ExceptionHandler {
|
||||
ucontext_t context;
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
|
||||
// In case of MIPS Linux FP state is already part of struct
|
||||
// ucontext so 'float_state' is not required.
|
||||
// In case of MIPS Linux FP state is already part of ucontext_t so
|
||||
// 'float_state' is not required.
|
||||
fpstate_t float_state;
|
||||
#endif
|
||||
};
|
||||
@ -262,7 +262,7 @@ class ExceptionHandler {
|
||||
// can do this. We create a pipe which we can use to block the
|
||||
// cloned process after creating it, until we have explicitly enabled
|
||||
// ptrace. This is used to store the file descriptors for the pipe
|
||||
int fdes[2];
|
||||
int fdes[2] = {-1, -1};
|
||||
|
||||
// Callers can add extra info about mappings for cases where the
|
||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
||||
@ -273,6 +273,10 @@ class ExceptionHandler {
|
||||
AppMemoryList app_memory_list_;
|
||||
};
|
||||
|
||||
|
||||
typedef bool (*FirstChanceHandler)(int, void*, void*);
|
||||
void SetFirstChanceExceptionHandler(FirstChanceHandler callback);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||
|
@ -27,6 +27,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
@ -257,6 +258,66 @@ TEST(ExceptionHandlerTest, ChildCrashWithFD) {
|
||||
ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
|
||||
}
|
||||
|
||||
#if !defined(__ANDROID_API__) || __ANDROID_API__ >= __ANDROID_API_N__
|
||||
static void* SleepFunction(void* unused) {
|
||||
while (true) usleep(1000000);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void* CrashFunction(void* b_ptr) {
|
||||
pthread_barrier_t* b = reinterpret_cast<pthread_barrier_t*>(b_ptr);
|
||||
pthread_barrier_wait(b);
|
||||
DoNullPointerDereference();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Tests that concurrent crashes do not enter a loop by alternately triggering
|
||||
// the signal handler.
|
||||
TEST(ExceptionHandlerTest, ParallelChildCrashesDontHang) {
|
||||
AutoTempDir temp_dir;
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
google_breakpad::scoped_ptr<ExceptionHandler> handler(
|
||||
new ExceptionHandler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
|
||||
NULL, true, -1));
|
||||
|
||||
// We start a number of threads to make sure handling the signal takes
|
||||
// enough time for the second thread to enter the signal handler.
|
||||
int num_sleep_threads = 100;
|
||||
google_breakpad::scoped_array<pthread_t> sleep_threads(
|
||||
new pthread_t[num_sleep_threads]);
|
||||
for (int i = 0; i < num_sleep_threads; ++i) {
|
||||
ASSERT_EQ(0, pthread_create(&sleep_threads[i], NULL, SleepFunction,
|
||||
NULL));
|
||||
}
|
||||
|
||||
int num_crash_threads = 2;
|
||||
google_breakpad::scoped_array<pthread_t> crash_threads(
|
||||
new pthread_t[num_crash_threads]);
|
||||
// Barrier to synchronize crashing both threads at the same time.
|
||||
pthread_barrier_t b;
|
||||
ASSERT_EQ(0, pthread_barrier_init(&b, NULL, num_crash_threads + 1));
|
||||
for (int i = 0; i < num_crash_threads; ++i) {
|
||||
ASSERT_EQ(0, pthread_create(&crash_threads[i], NULL, CrashFunction, &b));
|
||||
}
|
||||
pthread_barrier_wait(&b);
|
||||
for (int i = 0; i < num_crash_threads; ++i) {
|
||||
ASSERT_EQ(0, pthread_join(crash_threads[i], NULL));
|
||||
}
|
||||
}
|
||||
|
||||
// Wait a while until the child should have crashed.
|
||||
usleep(1000000);
|
||||
// Kill the child if it is still running.
|
||||
kill(child, SIGKILL);
|
||||
|
||||
// If the child process terminated by itself, it will have returned SIGSEGV.
|
||||
// If however it got stuck in a loop, it will have been killed by the
|
||||
// SIGKILL.
|
||||
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV));
|
||||
}
|
||||
#endif // !defined(__ANDROID_API__) || __ANDROID_API__ >= __ANDROID_API_N__
|
||||
|
||||
static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor,
|
||||
void* context,
|
||||
bool succeeded) {
|
||||
@ -465,6 +526,29 @@ TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) {
|
||||
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
|
||||
}
|
||||
|
||||
namespace {
|
||||
const int kSimpleFirstChanceReturnStatus = 42;
|
||||
bool SimpleFirstChanceHandler(int, void*, void*) {
|
||||
_exit(kSimpleFirstChanceReturnStatus);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExceptionHandlerTest, FirstChanceHandlerRuns) {
|
||||
AutoTempDir temp_dir;
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
ExceptionHandler handler(
|
||||
MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1);
|
||||
google_breakpad::SetFirstChanceExceptionHandler(SimpleFirstChanceHandler);
|
||||
DoNullPointerDereference();
|
||||
}
|
||||
int status;
|
||||
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
|
||||
ASSERT_TRUE(WIFEXITED(status));
|
||||
ASSERT_EQ(kSimpleFirstChanceReturnStatus, WEXITSTATUS(status));
|
||||
}
|
||||
|
||||
#endif // !ADDRESS_SANITIZER
|
||||
|
||||
const unsigned char kIllegalInstruction[] = {
|
||||
|
@ -44,6 +44,11 @@ MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
||||
directory_(descriptor.directory_),
|
||||
c_path_(NULL),
|
||||
size_limit_(descriptor.size_limit_),
|
||||
address_within_principal_mapping_(
|
||||
descriptor.address_within_principal_mapping_),
|
||||
skip_dump_if_principal_mapping_not_referenced_(
|
||||
descriptor.skip_dump_if_principal_mapping_not_referenced_),
|
||||
sanitize_stacks_(descriptor.sanitize_stacks_),
|
||||
microdump_extra_info_(descriptor.microdump_extra_info_) {
|
||||
// The copy constructor is not allowed to be called on a MinidumpDescriptor
|
||||
// with a valid path_, as getting its c_path_ would require the heap which
|
||||
@ -65,6 +70,11 @@ MinidumpDescriptor& MinidumpDescriptor::operator=(
|
||||
UpdatePath();
|
||||
}
|
||||
size_limit_ = descriptor.size_limit_;
|
||||
address_within_principal_mapping_ =
|
||||
descriptor.address_within_principal_mapping_;
|
||||
skip_dump_if_principal_mapping_not_referenced_ =
|
||||
descriptor.skip_dump_if_principal_mapping_not_referenced_;
|
||||
sanitize_stacks_ = descriptor.sanitize_stacks_;
|
||||
microdump_extra_info_ = descriptor.microdump_extra_info_;
|
||||
return *this;
|
||||
}
|
||||
|
@ -53,14 +53,19 @@ class MinidumpDescriptor {
|
||||
MinidumpDescriptor()
|
||||
: mode_(kUninitialized),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
size_limit_(-1),
|
||||
address_within_principal_mapping_(0),
|
||||
skip_dump_if_principal_mapping_not_referenced_(false) {}
|
||||
|
||||
explicit MinidumpDescriptor(const string& directory)
|
||||
: mode_(kWriteMinidumpToFile),
|
||||
fd_(-1),
|
||||
directory_(directory),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
size_limit_(-1),
|
||||
address_within_principal_mapping_(0),
|
||||
skip_dump_if_principal_mapping_not_referenced_(false),
|
||||
sanitize_stacks_(false) {
|
||||
assert(!directory.empty());
|
||||
}
|
||||
|
||||
@ -68,14 +73,20 @@ class MinidumpDescriptor {
|
||||
: mode_(kWriteMinidumpToFd),
|
||||
fd_(fd),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
size_limit_(-1),
|
||||
address_within_principal_mapping_(0),
|
||||
skip_dump_if_principal_mapping_not_referenced_(false),
|
||||
sanitize_stacks_(false) {
|
||||
assert(fd != -1);
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
|
||||
: mode_(kWriteMicrodumpToConsole),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
size_limit_(-1),
|
||||
address_within_principal_mapping_(0),
|
||||
skip_dump_if_principal_mapping_not_referenced_(false),
|
||||
sanitize_stacks_(false) {}
|
||||
|
||||
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
|
||||
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
|
||||
@ -101,6 +112,28 @@ class MinidumpDescriptor {
|
||||
off_t size_limit() const { return size_limit_; }
|
||||
void set_size_limit(off_t limit) { size_limit_ = limit; }
|
||||
|
||||
uintptr_t address_within_principal_mapping() const {
|
||||
return address_within_principal_mapping_;
|
||||
}
|
||||
void set_address_within_principal_mapping(
|
||||
uintptr_t address_within_principal_mapping) {
|
||||
address_within_principal_mapping_ = address_within_principal_mapping;
|
||||
}
|
||||
|
||||
bool skip_dump_if_principal_mapping_not_referenced() {
|
||||
return skip_dump_if_principal_mapping_not_referenced_;
|
||||
}
|
||||
void set_skip_dump_if_principal_mapping_not_referenced(
|
||||
bool skip_dump_if_principal_mapping_not_referenced) {
|
||||
skip_dump_if_principal_mapping_not_referenced_ =
|
||||
skip_dump_if_principal_mapping_not_referenced;
|
||||
}
|
||||
|
||||
bool sanitize_stacks() const { return sanitize_stacks_; }
|
||||
void set_sanitize_stacks(bool sanitize_stacks) {
|
||||
sanitize_stacks_ = sanitize_stacks;
|
||||
}
|
||||
|
||||
MicrodumpExtraInfo* microdump_extra_info() {
|
||||
assert(IsMicrodumpOnConsole());
|
||||
return µdump_extra_info_;
|
||||
@ -132,6 +165,23 @@ class MinidumpDescriptor {
|
||||
|
||||
off_t size_limit_;
|
||||
|
||||
// This member points somewhere into the main module for this
|
||||
// process (the module that is considerered interesting for the
|
||||
// purposes of debugging crashes).
|
||||
uintptr_t address_within_principal_mapping_;
|
||||
|
||||
// If set, threads that do not reference the address range
|
||||
// associated with |address_within_principal_mapping_| will not have their
|
||||
// stacks logged.
|
||||
bool skip_dump_if_principal_mapping_not_referenced_;
|
||||
|
||||
// If set, stacks are sanitized to remove PII. This involves
|
||||
// overwriting any pointer-aligned words that are not either
|
||||
// pointers into a process mapping or small integers (+/-4096). This
|
||||
// leaves enough information to unwind stacks, and preserve some
|
||||
// register values, but elides strings and other program data.
|
||||
bool sanitize_stacks_;
|
||||
|
||||
// The extra microdump data (e.g. product name/version, build
|
||||
// fingerprint, gpu fingerprint) that should be appended to the dump
|
||||
// (microdump only). Microdumps don't have the ability of appending
|
||||
|
@ -132,6 +132,9 @@ class MicrodumpWriter {
|
||||
public:
|
||||
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings,
|
||||
bool skip_dump_if_principal_mapping_not_referenced,
|
||||
uintptr_t address_within_principal_mapping,
|
||||
bool sanitize_stack,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
LinuxDumper* dumper)
|
||||
: ucontext_(context ? &context->context : NULL),
|
||||
@ -140,8 +143,16 @@ class MicrodumpWriter {
|
||||
#endif
|
||||
dumper_(dumper),
|
||||
mapping_list_(mappings),
|
||||
skip_dump_if_principal_mapping_not_referenced_(
|
||||
skip_dump_if_principal_mapping_not_referenced),
|
||||
address_within_principal_mapping_(address_within_principal_mapping),
|
||||
sanitize_stack_(sanitize_stack),
|
||||
microdump_extra_info_(microdump_extra_info),
|
||||
log_line_(NULL) {
|
||||
log_line_(NULL),
|
||||
stack_copy_(NULL),
|
||||
stack_len_(0),
|
||||
stack_lower_bound_(0),
|
||||
stack_pointer_(0) {
|
||||
log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
|
||||
if (log_line_)
|
||||
log_line_[0] = '\0'; // Clear out the log line buffer.
|
||||
@ -159,25 +170,32 @@ class MicrodumpWriter {
|
||||
return dumper_->ThreadsSuspend() && dumper_->LateInit();
|
||||
}
|
||||
|
||||
bool Dump() {
|
||||
bool success;
|
||||
void Dump() {
|
||||
CaptureResult stack_capture_result = CaptureCrashingThreadStack(-1);
|
||||
if (stack_capture_result == CAPTURE_UNINTERESTING) {
|
||||
LogLine("Microdump skipped (uninteresting)");
|
||||
return;
|
||||
}
|
||||
|
||||
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
||||
DumpProductInformation();
|
||||
DumpOSInformation();
|
||||
DumpProcessType();
|
||||
DumpCrashReason();
|
||||
DumpGPUInformation();
|
||||
#if !defined(__LP64__)
|
||||
DumpFreeSpace();
|
||||
#endif
|
||||
success = DumpCrashingThread();
|
||||
if (success)
|
||||
success = DumpMappings();
|
||||
if (stack_capture_result == CAPTURE_OK)
|
||||
DumpThreadStack();
|
||||
DumpCPUState();
|
||||
DumpMappings();
|
||||
LogLine("-----END BREAKPAD MICRODUMP-----");
|
||||
dumper_->ThreadsResume();
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
enum CaptureResult { CAPTURE_OK, CAPTURE_FAILED, CAPTURE_UNINTERESTING };
|
||||
|
||||
// Writes one line to the system log.
|
||||
void LogLine(const char* msg) {
|
||||
#if defined(__ANDROID__)
|
||||
@ -221,7 +239,44 @@ class MicrodumpWriter {
|
||||
// Writes out the current line buffer on the system log.
|
||||
void LogCommitLine() {
|
||||
LogLine(log_line_);
|
||||
my_strlcpy(log_line_, "", kLineBufferSize);
|
||||
log_line_[0] = 0;
|
||||
}
|
||||
|
||||
CaptureResult CaptureCrashingThreadStack(int max_stack_len) {
|
||||
stack_pointer_ = UContextReader::GetStackPointer(ucontext_);
|
||||
|
||||
if (!dumper_->GetStackInfo(reinterpret_cast<const void**>(&stack_lower_bound_),
|
||||
&stack_len_, stack_pointer_)) {
|
||||
return CAPTURE_FAILED;
|
||||
}
|
||||
|
||||
if (max_stack_len >= 0 &&
|
||||
stack_len_ > static_cast<size_t>(max_stack_len)) {
|
||||
stack_len_ = max_stack_len;
|
||||
}
|
||||
|
||||
stack_copy_ = reinterpret_cast<uint8_t*>(Alloc(stack_len_));
|
||||
dumper_->CopyFromProcess(stack_copy_, dumper_->crash_thread(),
|
||||
reinterpret_cast<const void*>(stack_lower_bound_),
|
||||
stack_len_);
|
||||
|
||||
if (!skip_dump_if_principal_mapping_not_referenced_) return CAPTURE_OK;
|
||||
|
||||
const MappingInfo* principal_mapping =
|
||||
dumper_->FindMappingNoBias(address_within_principal_mapping_);
|
||||
if (!principal_mapping) return CAPTURE_UNINTERESTING;
|
||||
|
||||
uintptr_t low_addr = principal_mapping->system_mapping_info.start_addr;
|
||||
uintptr_t high_addr = principal_mapping->system_mapping_info.end_addr;
|
||||
uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_);
|
||||
if (low_addr <= pc && pc <= high_addr) return CAPTURE_OK;
|
||||
|
||||
if (dumper_->StackHasPointerToMapping(stack_copy_, stack_len_,
|
||||
stack_pointer_ - stack_lower_bound_,
|
||||
*principal_mapping)) {
|
||||
return CAPTURE_OK;
|
||||
}
|
||||
return CAPTURE_UNINTERESTING;
|
||||
}
|
||||
|
||||
void DumpProductInformation() {
|
||||
@ -244,6 +299,16 @@ class MicrodumpWriter {
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpCrashReason() {
|
||||
LogAppend("R ");
|
||||
LogAppend(dumper_->crash_signal());
|
||||
LogAppend(" ");
|
||||
LogAppend(dumper_->GetCrashSignalString());
|
||||
LogAppend(" ");
|
||||
LogAppend(dumper_->crash_address());
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpOSInformation() {
|
||||
const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
|
||||
|
||||
@ -315,87 +380,42 @@ class MicrodumpWriter {
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
bool DumpThreadStack(uint32_t thread_id,
|
||||
uintptr_t stack_pointer,
|
||||
int max_stack_len,
|
||||
uint8_t** stack_copy) {
|
||||
*stack_copy = NULL;
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
|
||||
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
|
||||
// The stack pointer might not be available. In this case we don't hard
|
||||
// fail, just produce a (almost useless) microdump w/o a stack section.
|
||||
return true;
|
||||
void DumpThreadStack() {
|
||||
if (sanitize_stack_) {
|
||||
dumper_->SanitizeStackCopy(stack_copy_, stack_len_, stack_pointer_,
|
||||
stack_pointer_ - stack_lower_bound_);
|
||||
}
|
||||
|
||||
LogAppend("S 0 ");
|
||||
LogAppend(stack_pointer);
|
||||
LogAppend(stack_pointer_);
|
||||
LogAppend(" ");
|
||||
LogAppend(reinterpret_cast<uintptr_t>(stack));
|
||||
LogAppend(stack_lower_bound_);
|
||||
LogAppend(" ");
|
||||
LogAppend(stack_len);
|
||||
LogAppend(stack_len_);
|
||||
LogCommitLine();
|
||||
|
||||
if (max_stack_len >= 0 &&
|
||||
stack_len > static_cast<unsigned int>(max_stack_len)) {
|
||||
stack_len = max_stack_len;
|
||||
}
|
||||
|
||||
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||
dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len);
|
||||
|
||||
// Dump the content of the stack, splicing it into chunks which size is
|
||||
// compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD).
|
||||
const size_t STACK_DUMP_CHUNK_SIZE = 384;
|
||||
for (size_t stack_off = 0; stack_off < stack_len;
|
||||
for (size_t stack_off = 0; stack_off < stack_len_;
|
||||
stack_off += STACK_DUMP_CHUNK_SIZE) {
|
||||
LogAppend("S ");
|
||||
LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
|
||||
LogAppend(stack_lower_bound_ + stack_off);
|
||||
LogAppend(" ");
|
||||
LogAppend(*stack_copy + stack_off,
|
||||
std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off));
|
||||
LogAppend(stack_copy_ + stack_off,
|
||||
std::min(STACK_DUMP_CHUNK_SIZE, stack_len_ - stack_off));
|
||||
LogCommitLine();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write information about the crashing thread.
|
||||
bool DumpCrashingThread() {
|
||||
const unsigned num_threads = dumper_->threads().size();
|
||||
|
||||
for (unsigned i = 0; i < num_threads; ++i) {
|
||||
MDRawThread thread;
|
||||
my_memset(&thread, 0, sizeof(thread));
|
||||
thread.thread_id = dumper_->threads()[i];
|
||||
|
||||
// Dump only the crashing thread.
|
||||
if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread())
|
||||
continue;
|
||||
|
||||
assert(ucontext_);
|
||||
assert(!dumper_->IsPostMortem());
|
||||
|
||||
uint8_t* stack_copy;
|
||||
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
|
||||
if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy))
|
||||
return false;
|
||||
|
||||
RawContextCPU cpu;
|
||||
my_memset(&cpu, 0, sizeof(RawContextCPU));
|
||||
void DumpCPUState() {
|
||||
RawContextCPU cpu;
|
||||
my_memset(&cpu, 0, sizeof(RawContextCPU));
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
|
||||
#else
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_);
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_);
|
||||
#endif
|
||||
DumpCPUState(&cpu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpCPUState(RawContextCPU* cpu) {
|
||||
LogAppend("C ");
|
||||
LogAppend(cpu, sizeof(*cpu));
|
||||
LogAppend(&cpu, sizeof(cpu));
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
@ -474,6 +494,12 @@ class MicrodumpWriter {
|
||||
|
||||
#if !defined(__LP64__)
|
||||
void DumpFreeSpace() {
|
||||
const MappingInfo* stack_mapping = nullptr;
|
||||
ThreadInfo info;
|
||||
if (dumper_->GetThreadInfoByIndex(dumper_->GetMainThreadIndex(), &info)) {
|
||||
stack_mapping = dumper_->FindMappingNoBias(info.stack_pointer);
|
||||
}
|
||||
|
||||
const google_breakpad::wasteful_vector<MappingInfo*>& mappings =
|
||||
dumper_->mappings();
|
||||
if (mappings.size() == 0) return;
|
||||
@ -506,6 +532,14 @@ class MicrodumpWriter {
|
||||
++curr;
|
||||
}
|
||||
|
||||
if (mappings[curr] == stack_mapping) {
|
||||
// Because we can't determine the top of userspace mappable
|
||||
// memory we treat the start of the process stack as the top
|
||||
// of the allocatable address space. Once we reach
|
||||
// |stack_mapping| we are done scanning for free space regions.
|
||||
break;
|
||||
}
|
||||
|
||||
size_t next = NextOrderedMapping(mappings, curr);
|
||||
if (next == std::numeric_limits<size_t>::max())
|
||||
break;
|
||||
@ -547,7 +581,7 @@ class MicrodumpWriter {
|
||||
#endif
|
||||
|
||||
// Write information about the mappings in effect.
|
||||
bool DumpMappings() {
|
||||
void DumpMappings() {
|
||||
// First write all the mappings from the dumper
|
||||
for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
|
||||
const MappingInfo& mapping = *dumper_->mappings()[i];
|
||||
@ -566,7 +600,6 @@ class MicrodumpWriter {
|
||||
++iter) {
|
||||
DumpModule(iter->first, false, 0, iter->second);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
|
||||
@ -577,8 +610,25 @@ class MicrodumpWriter {
|
||||
#endif
|
||||
LinuxDumper* dumper_;
|
||||
const MappingList& mapping_list_;
|
||||
bool skip_dump_if_principal_mapping_not_referenced_;
|
||||
uintptr_t address_within_principal_mapping_;
|
||||
bool sanitize_stack_;
|
||||
const MicrodumpExtraInfo microdump_extra_info_;
|
||||
char* log_line_;
|
||||
|
||||
// The local copy of crashed process stack memory, beginning at
|
||||
// |stack_lower_bound_|.
|
||||
uint8_t* stack_copy_;
|
||||
|
||||
// The length of crashed process stack copy.
|
||||
size_t stack_len_;
|
||||
|
||||
// The address of the page containing the stack pointer in the
|
||||
// crashed process. |stack_lower_bound_| <= |stack_pointer_|
|
||||
uintptr_t stack_lower_bound_;
|
||||
|
||||
// The stack pointer of the crashed thread.
|
||||
uintptr_t stack_pointer_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -588,6 +638,9 @@ bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
bool skip_dump_if_principal_mapping_not_referenced,
|
||||
uintptr_t address_within_principal_mapping,
|
||||
bool sanitize_stack,
|
||||
const MicrodumpExtraInfo& microdump_extra_info) {
|
||||
LinuxPtraceDumper dumper(crashing_process);
|
||||
const ExceptionHandler::CrashContext* context = NULL;
|
||||
@ -595,15 +648,17 @@ bool WriteMicrodump(pid_t crashing_process,
|
||||
if (blob_size != sizeof(ExceptionHandler::CrashContext))
|
||||
return false;
|
||||
context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
|
||||
dumper.set_crash_address(
|
||||
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
|
||||
dumper.set_crash_signal(context->siginfo.si_signo);
|
||||
dumper.SetCrashInfoFromSigInfo(context->siginfo);
|
||||
dumper.set_crash_thread(context->tid);
|
||||
}
|
||||
MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper);
|
||||
MicrodumpWriter writer(context, mappings,
|
||||
skip_dump_if_principal_mapping_not_referenced,
|
||||
address_within_principal_mapping, sanitize_stack,
|
||||
microdump_extra_info, &dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
writer.Dump();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -58,6 +58,9 @@ bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
bool skip_dump_if_main_module_not_referenced,
|
||||
uintptr_t address_within_main_module,
|
||||
bool sanitize_stack,
|
||||
const MicrodumpExtraInfo& microdump_extra_info);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@ -47,6 +48,11 @@
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
extern "C" {
|
||||
extern char __executable_start;
|
||||
extern char __etext;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test MicrodumpWriterTest;
|
||||
@ -59,13 +65,24 @@ MicrodumpExtraInfo MakeMicrodumpExtraInfo(
|
||||
info.build_fingerprint = build_fingerprint;
|
||||
info.product_info = product_info;
|
||||
info.gpu_fingerprint = gpu_fingerprint;
|
||||
info.process_type = "Browser";
|
||||
return info;
|
||||
}
|
||||
|
||||
void CrashAndGetMicrodump(
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
scoped_array<char>* buf) {
|
||||
bool ContainsMicrodump(const std::string& buf) {
|
||||
return std::string::npos != buf.find("-----BEGIN BREAKPAD MICRODUMP-----") &&
|
||||
std::string::npos != buf.find("-----END BREAKPAD MICRODUMP-----");
|
||||
}
|
||||
|
||||
const char kIdentifiableString[] = "_IDENTIFIABLE_";
|
||||
const uintptr_t kCrashAddress = 0xdeaddeadu;
|
||||
|
||||
void CrashAndGetMicrodump(const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
std::string* microdump,
|
||||
bool skip_dump_if_principal_mapping_not_referenced = false,
|
||||
uintptr_t address_within_principal_mapping = 0,
|
||||
bool sanitize_stack = false) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
@ -74,6 +91,14 @@ void CrashAndGetMicrodump(
|
||||
int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
ASSERT_NE(-1, err_fd);
|
||||
|
||||
char identifiable_string[sizeof(kIdentifiableString)];
|
||||
|
||||
// This string should not appear in the resulting microdump if it
|
||||
// has been sanitized.
|
||||
strcpy(identifiable_string, kIdentifiableString);
|
||||
// Force the strcpy to not be optimized away.
|
||||
IGNORE_RET(write(STDOUT_FILENO, identifiable_string, 0));
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
@ -86,9 +111,14 @@ void CrashAndGetMicrodump(
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
|
||||
// Pretend the current context is the child context (which is
|
||||
// approximately right) so that we have a valid stack pointer, and
|
||||
// can fetch child stack data via ptrace.
|
||||
getcontext(&context.context);
|
||||
// Set a non-zero tid to avoid tripping asserts.
|
||||
context.tid = child;
|
||||
context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
|
||||
context.siginfo.si_addr = reinterpret_cast<void*>(kCrashAddress);
|
||||
|
||||
// Redirect temporarily stderr to the stderr.log file.
|
||||
int save_err = dup(STDERR_FILENO);
|
||||
@ -96,6 +126,8 @@ void CrashAndGetMicrodump(
|
||||
ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
|
||||
|
||||
ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
|
||||
skip_dump_if_principal_mapping_not_referenced,
|
||||
address_within_principal_mapping, sanitize_stack,
|
||||
microdump_extra_info));
|
||||
|
||||
// Revert stderr back to the console.
|
||||
@ -105,17 +137,38 @@ void CrashAndGetMicrodump(
|
||||
// Read back the stderr file and check for the microdump marker.
|
||||
fsync(err_fd);
|
||||
lseek(err_fd, 0, SEEK_SET);
|
||||
const size_t kBufSize = 64 * 1024;
|
||||
buf->reset(new char[kBufSize]);
|
||||
ASSERT_GT(read(err_fd, buf->get(), kBufSize), 0);
|
||||
|
||||
microdump->clear();
|
||||
char buf[1024];
|
||||
|
||||
while (true) {
|
||||
int bytes_read = IGNORE_EINTR(read(err_fd, buf, 1024));
|
||||
if (bytes_read <= 0) break;
|
||||
microdump->append(buf, buf + bytes_read);
|
||||
}
|
||||
close(err_fd);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf->get(), "-----BEGIN BREAKPAD MICRODUMP-----"));
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf->get(), "-----END BREAKPAD MICRODUMP-----"));
|
||||
void ExtractMicrodumpStackContents(const string& microdump_content,
|
||||
string* result) {
|
||||
std::istringstream iss(microdump_content);
|
||||
result->clear();
|
||||
for (string line; std::getline(iss, line);) {
|
||||
if (line.find("S ") == 0) {
|
||||
std::istringstream stack_data(line);
|
||||
std::string key;
|
||||
std::string addr;
|
||||
std::string data;
|
||||
stack_data >> key >> addr >> data;
|
||||
EXPECT_TRUE((data.size() & 1u) == 0u);
|
||||
result->reserve(result->size() + data.size() / 2);
|
||||
for (size_t i = 0; i < data.size(); i += 2) {
|
||||
std::string byte = data.substr(i, 2);
|
||||
result->push_back(static_cast<char>(strtoul(byte.c_str(), NULL, 16)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckMicrodumpContents(const string& microdump_content,
|
||||
@ -123,6 +176,8 @@ void CheckMicrodumpContents(const string& microdump_content,
|
||||
std::istringstream iss(microdump_content);
|
||||
bool did_find_os_info = false;
|
||||
bool did_find_product_info = false;
|
||||
bool did_find_process_type = false;
|
||||
bool did_find_crash_reason = false;
|
||||
bool did_find_gpu_info = false;
|
||||
for (string line; std::getline(iss, line);) {
|
||||
if (line.find("O ") == 0) {
|
||||
@ -141,9 +196,28 @@ void CheckMicrodumpContents(const string& microdump_content,
|
||||
|
||||
// Check that the build fingerprint is in the right place.
|
||||
os_info_tokens >> token;
|
||||
ASSERT_FALSE(os_info_tokens.fail());
|
||||
if (expected_info.build_fingerprint)
|
||||
ASSERT_EQ(expected_info.build_fingerprint, token);
|
||||
did_find_os_info = true;
|
||||
} else if (line.find("P ") == 0) {
|
||||
if (expected_info.process_type)
|
||||
ASSERT_EQ(string("P ") + expected_info.process_type, line);
|
||||
did_find_process_type = true;
|
||||
} else if (line.find("R ") == 0) {
|
||||
std::istringstream crash_reason_tokens(line);
|
||||
string token;
|
||||
unsigned crash_reason;
|
||||
string crash_reason_str;
|
||||
intptr_t crash_address;
|
||||
crash_reason_tokens.ignore(2); // Ignore the "R " preamble.
|
||||
crash_reason_tokens >> std::hex >> crash_reason >> crash_reason_str >>
|
||||
crash_address;
|
||||
ASSERT_FALSE(crash_reason_tokens.fail());
|
||||
ASSERT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, crash_reason);
|
||||
ASSERT_EQ("DUMP_REQUESTED", crash_reason_str);
|
||||
ASSERT_EQ(0xDEADDEADu, kCrashAddress);
|
||||
did_find_crash_reason = true;
|
||||
} else if (line.find("V ") == 0) {
|
||||
if (expected_info.product_info)
|
||||
ASSERT_EQ(string("V ") + expected_info.product_info, line);
|
||||
@ -156,9 +230,18 @@ void CheckMicrodumpContents(const string& microdump_content,
|
||||
}
|
||||
ASSERT_TRUE(did_find_os_info);
|
||||
ASSERT_TRUE(did_find_product_info);
|
||||
ASSERT_TRUE(did_find_process_type);
|
||||
ASSERT_TRUE(did_find_crash_reason);
|
||||
ASSERT_TRUE(did_find_gpu_info);
|
||||
}
|
||||
|
||||
bool MicrodumpStackContains(const string& microdump_content,
|
||||
const string& expected_content) {
|
||||
string result;
|
||||
ExtractMicrodumpStackContents(microdump_content, &result);
|
||||
return result.find(kIdentifiableString) != string::npos;
|
||||
}
|
||||
|
||||
void CheckMicrodumpContents(const string& microdump_content,
|
||||
const string& expected_fingerprint,
|
||||
const string& expected_product_info,
|
||||
@ -191,23 +274,101 @@ TEST(MicrodumpWriterTest, BasicWithMappings) {
|
||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
||||
mappings.push_back(mapping);
|
||||
|
||||
scoped_array<char> buf;
|
||||
std::string buf;
|
||||
CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
|
||||
#ifdef __LP64__
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 0000000000001000 000000000000002A 0000000000001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
ASSERT_NE(std::string::npos,
|
||||
buf.find("M 0000000000001000 000000000000002A 0000000000001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#else
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 00001000 0000002A 00001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
ASSERT_NE(std::string::npos,
|
||||
buf.find("M 00001000 0000002A 00001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#endif
|
||||
|
||||
// In absence of a product info in the minidump, the writer should just write
|
||||
// an unknown marker.
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "V UNKNOWN:0.0.0.0"));
|
||||
ASSERT_NE(std::string::npos, buf.find("V UNKNOWN:0.0.0.0"));
|
||||
}
|
||||
|
||||
// Ensure that no output occurs if the interest region is set, but
|
||||
// doesn't overlap anything on the stack.
|
||||
TEST(MicrodumpWriterTest, NoOutputIfUninteresting) {
|
||||
const char kProductInfo[] = "MockProduct:42.0.2311.99";
|
||||
const char kBuildFingerprint[] =
|
||||
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
|
||||
const char kGPUFingerprint[] =
|
||||
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
|
||||
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true, 0);
|
||||
ASSERT_FALSE(ContainsMicrodump(buf));
|
||||
}
|
||||
|
||||
// Ensure that stack content does not contain an identifiable string if the
|
||||
// stack is sanitized.
|
||||
TEST(MicrodumpWriterTest, StringRemovedBySanitization) {
|
||||
const char kProductInfo[] = "MockProduct:42.0.2311.99";
|
||||
const char kBuildFingerprint[] =
|
||||
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
|
||||
const char kGPUFingerprint[] =
|
||||
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
|
||||
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, true);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
ASSERT_FALSE(MicrodumpStackContains(buf, kIdentifiableString));
|
||||
}
|
||||
|
||||
// Ensure that stack content does contain an identifiable string if the
|
||||
// stack is not sanitized.
|
||||
TEST(MicrodumpWriterTest, StringPresentIfNotSanitized) {
|
||||
const char kProductInfo[] = "MockProduct:42.0.2311.99";
|
||||
const char kBuildFingerprint[] =
|
||||
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
|
||||
const char kGPUFingerprint[] =
|
||||
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
|
||||
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, false);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
ASSERT_TRUE(MicrodumpStackContains(buf, kIdentifiableString));
|
||||
}
|
||||
|
||||
// Ensure that output occurs if the interest region is set, and
|
||||
// does overlap something on the stack.
|
||||
TEST(MicrodumpWriterTest, OutputIfInteresting) {
|
||||
const char kProductInfo[] = "MockProduct:42.0.2311.99";
|
||||
const char kBuildFingerprint[] =
|
||||
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
|
||||
const char kGPUFingerprint[] =
|
||||
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
|
||||
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true,
|
||||
reinterpret_cast<uintptr_t>(CrashAndGetMicrodump));
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
}
|
||||
|
||||
// Ensure that the product info and build fingerprint metadata show up in the
|
||||
@ -220,38 +381,40 @@ TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
|
||||
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
|
||||
scoped_array<char> buf;
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kMicrodumpExtraInfo);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
CheckMicrodumpContents(buf, kMicrodumpExtraInfo);
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, NoProductInfo) {
|
||||
const char kBuildFingerprint[] = "foobar";
|
||||
const char kGPUFingerprint[] = "bazqux";
|
||||
scoped_array<char> buf;
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint));
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
|
||||
"UNKNOWN:0.0.0.0", kGPUFingerprint);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
CheckMicrodumpContents(buf, kBuildFingerprint, "UNKNOWN:0.0.0.0",
|
||||
kGPUFingerprint);
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, NoGPUInfo) {
|
||||
const char kProductInfo[] = "bazqux";
|
||||
const char kBuildFingerprint[] = "foobar";
|
||||
scoped_array<char> buf;
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL));
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
|
||||
kProductInfo, "UNKNOWN");
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
CheckMicrodumpContents(buf, kBuildFingerprint, kProductInfo, "UNKNOWN");
|
||||
}
|
||||
} // namespace
|
||||
|
@ -212,6 +212,7 @@ bool LinuxCoreDumper::EnumerateThreads() {
|
||||
if (first_thread) {
|
||||
crash_thread_ = pid;
|
||||
crash_signal_ = status->pr_info.si_signo;
|
||||
crash_signal_code_ = status->pr_info.si_code;
|
||||
}
|
||||
first_thread = false;
|
||||
threads_.push_back(pid);
|
||||
|
@ -41,7 +41,7 @@ using namespace google_breakpad;
|
||||
|
||||
TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) {
|
||||
const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root");
|
||||
const MappingInfo mapping = { 0, 0, 0, false, "/usr/lib/libc.so" };
|
||||
const MappingInfo mapping = {0, 0, {0, 0}, 0, false, "/usr/lib/libc.so"};
|
||||
|
||||
char path[PATH_MAX];
|
||||
dumper.GetMappingAbsolutePath(mapping, path);
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
#include "common/linux/safe_readlink.h"
|
||||
#include "google_breakpad/common/minidump_exception_linux.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
@ -85,10 +86,15 @@ inline static bool IsMappedFileOpenUnsafe(
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__CHROMEOS__)
|
||||
|
||||
namespace {
|
||||
|
||||
bool MappingContainsAddress(const MappingInfo& mapping, uintptr_t address) {
|
||||
return mapping.system_mapping_info.start_addr <= address &&
|
||||
address < mapping.system_mapping_info.end_addr;
|
||||
}
|
||||
|
||||
#if defined(__CHROMEOS__)
|
||||
|
||||
// Recover memory mappings before writing dump on ChromeOS
|
||||
//
|
||||
// On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from
|
||||
@ -225,20 +231,21 @@ void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
|
||||
l = m + 1;
|
||||
}
|
||||
|
||||
// Try to merge segments into the first.
|
||||
if (next < mappings.size()) {
|
||||
TryRecoverMappings(mappings[0], mappings[next]);
|
||||
if (next - 1 > 0)
|
||||
TryRecoverMappings(mappings[next - 1], mappings[0], mappings[next]);
|
||||
}
|
||||
// Shows the range that contains the entry point is
|
||||
// [first_start_addr, first_end_addr)
|
||||
size_t first_start_addr = mappings[0]->start_addr;
|
||||
size_t first_end_addr = mappings[0]->start_addr + mappings[0]->size;
|
||||
|
||||
// Put the out-of-order segment in order.
|
||||
std::rotate(mappings.begin(), mappings.begin() + 1, mappings.begin() + next);
|
||||
|
||||
// Iterate through normal, sorted cases.
|
||||
// Normal case 1.
|
||||
for (size_t i = 1; i < mappings.size() - 1; i++)
|
||||
for (size_t i = 0; i < mappings.size() - 1; i++)
|
||||
TryRecoverMappings(mappings[i], mappings[i + 1]);
|
||||
|
||||
// Normal case 2.
|
||||
for (size_t i = 1; i < mappings.size() - 2; i++)
|
||||
for (size_t i = 0; i < mappings.size() - 2; i++)
|
||||
TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]);
|
||||
|
||||
// Collect merged (size == 0) segments.
|
||||
@ -247,11 +254,28 @@ void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
|
||||
if (mappings[e]->size > 0)
|
||||
mappings[f++] = mappings[e];
|
||||
mappings.resize(f);
|
||||
|
||||
// The entry point is in the first mapping. We want to find the location
|
||||
// of the entry point after merging segment. To do this, we want to find
|
||||
// the mapping that covers the first mapping from the original mapping list.
|
||||
// If the mapping is not in the beginning, we move it to the begining via
|
||||
// a right rotate by using reverse iterators.
|
||||
for (l = 0; l < mappings.size(); l++) {
|
||||
if (mappings[l]->start_addr <= first_start_addr
|
||||
&& (mappings[l]->start_addr + mappings[l]->size >= first_end_addr))
|
||||
break;
|
||||
}
|
||||
if (l > 0) {
|
||||
r = mappings.size();
|
||||
std::rotate(mappings.rbegin() + r - l - 1, mappings.rbegin() + r - l,
|
||||
mappings.rend());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif // __CHROMEOS__
|
||||
|
||||
} // namespace
|
||||
|
||||
// All interesting auvx entry types are below AT_SYSINFO_EHDR
|
||||
#define AT_MAX AT_SYSINFO_EHDR
|
||||
|
||||
@ -260,6 +284,7 @@ LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix)
|
||||
root_prefix_(root_prefix),
|
||||
crash_address_(0),
|
||||
crash_signal_(0),
|
||||
crash_signal_code_(0),
|
||||
crash_thread_(pid),
|
||||
threads_(&allocator_, 8),
|
||||
mappings_(&allocator_),
|
||||
@ -331,6 +356,83 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
return success;
|
||||
}
|
||||
|
||||
void LinuxDumper::SetCrashInfoFromSigInfo(const siginfo_t& siginfo) {
|
||||
set_crash_address(reinterpret_cast<uintptr_t>(siginfo.si_addr));
|
||||
set_crash_signal(siginfo.si_signo);
|
||||
set_crash_signal_code(siginfo.si_code);
|
||||
}
|
||||
|
||||
const char* LinuxDumper::GetCrashSignalString() const {
|
||||
switch (static_cast<unsigned int>(crash_signal_)) {
|
||||
case MD_EXCEPTION_CODE_LIN_SIGHUP:
|
||||
return "SIGHUP";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGINT:
|
||||
return "SIGINT";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGQUIT:
|
||||
return "SIGQUIT";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGILL:
|
||||
return "SIGILL";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTRAP:
|
||||
return "SIGTRAP";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGABRT:
|
||||
return "SIGABRT";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGBUS:
|
||||
return "SIGBUS";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGFPE:
|
||||
return "SIGFPE";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGKILL:
|
||||
return "SIGKILL";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGUSR1:
|
||||
return "SIGUSR1";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGSEGV:
|
||||
return "SIGSEGV";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGUSR2:
|
||||
return "SIGUSR2";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGPIPE:
|
||||
return "SIGPIPE";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGALRM:
|
||||
return "SIGALRM";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTERM:
|
||||
return "SIGTERM";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGSTKFLT:
|
||||
return "SIGSTKFLT";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGCHLD:
|
||||
return "SIGCHLD";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGCONT:
|
||||
return "SIGCONT";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGSTOP:
|
||||
return "SIGSTOP";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTSTP:
|
||||
return "SIGTSTP";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTTIN:
|
||||
return "SIGTTIN";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTTOU:
|
||||
return "SIGTTOU";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGURG:
|
||||
return "SIGURG";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGXCPU:
|
||||
return "SIGXCPU";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGXFSZ:
|
||||
return "SIGXFSZ";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGVTALRM:
|
||||
return "SIGVTALRM";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGPROF:
|
||||
return "SIGPROF";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGWINCH:
|
||||
return "SIGWINCH";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGIO:
|
||||
return "SIGIO";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGPWR:
|
||||
return "SIGPWR";
|
||||
case MD_EXCEPTION_CODE_LIN_SIGSYS:
|
||||
return "SIGSYS";
|
||||
case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED:
|
||||
return "DUMP_REQUESTED";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping,
|
||||
char path[PATH_MAX]) const {
|
||||
return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX &&
|
||||
@ -347,17 +449,16 @@ bool ElfFileSoNameFromMappedFile(
|
||||
|
||||
const void* segment_start;
|
||||
size_t segment_size;
|
||||
int elf_class;
|
||||
if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC,
|
||||
&segment_start, &segment_size, &elf_class)) {
|
||||
if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC, &segment_start,
|
||||
&segment_size)) {
|
||||
// No dynamic section
|
||||
return false;
|
||||
}
|
||||
|
||||
const void* dynstr_start;
|
||||
size_t dynstr_size;
|
||||
if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB,
|
||||
&dynstr_start, &dynstr_size, &elf_class)) {
|
||||
if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB, &dynstr_start,
|
||||
&dynstr_size)) {
|
||||
// No dynstr section
|
||||
return false;
|
||||
}
|
||||
@ -546,7 +647,10 @@ bool LinuxDumper::EnumerateMappings() {
|
||||
}
|
||||
}
|
||||
MappingInfo* const module = new(allocator_) MappingInfo;
|
||||
mappings_.push_back(module);
|
||||
my_memset(module, 0, sizeof(MappingInfo));
|
||||
module->system_mapping_info.start_addr = start_addr;
|
||||
module->system_mapping_info.end_addr = end_addr;
|
||||
module->start_addr = start_addr;
|
||||
module->size = end_addr - start_addr;
|
||||
module->offset = offset;
|
||||
@ -556,31 +660,32 @@ bool LinuxDumper::EnumerateMappings() {
|
||||
if (l < sizeof(module->name))
|
||||
my_memcpy(module->name, name, l);
|
||||
}
|
||||
// If this is the entry-point mapping, and it's not already the
|
||||
// first one, then we need to make it be first. This is because
|
||||
// the minidump format assumes the first module is the one that
|
||||
// corresponds to the main executable (as codified in
|
||||
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
|
||||
if (entry_point_loc &&
|
||||
(entry_point_loc >=
|
||||
reinterpret_cast<void*>(module->start_addr)) &&
|
||||
(entry_point_loc <
|
||||
reinterpret_cast<void*>(module->start_addr+module->size)) &&
|
||||
!mappings_.empty()) {
|
||||
// push the module onto the front of the list.
|
||||
mappings_.resize(mappings_.size() + 1);
|
||||
for (size_t idx = mappings_.size() - 1; idx > 0; idx--)
|
||||
mappings_[idx] = mappings_[idx - 1];
|
||||
mappings_[0] = module;
|
||||
} else {
|
||||
mappings_.push_back(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
line_reader->PopLine(line_len);
|
||||
}
|
||||
|
||||
if (entry_point_loc) {
|
||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
||||
MappingInfo* module = mappings_[i];
|
||||
|
||||
// If this module contains the entry-point, and it's not already the first
|
||||
// one, then we need to make it be first. This is because the minidump
|
||||
// format assumes the first module is the one that corresponds to the main
|
||||
// executable (as codified in
|
||||
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
|
||||
if ((entry_point_loc >= reinterpret_cast<void*>(module->start_addr)) &&
|
||||
(entry_point_loc <
|
||||
reinterpret_cast<void*>(module->start_addr + module->size))) {
|
||||
for (size_t j = i; j > 0; j--)
|
||||
mappings_[j] = mappings_[j - 1];
|
||||
mappings_[0] = module;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sys_close(fd);
|
||||
|
||||
return !mappings_.empty();
|
||||
@ -720,6 +825,126 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinuxDumper::SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
|
||||
uintptr_t stack_pointer,
|
||||
uintptr_t sp_offset) {
|
||||
// We optimize the search for containing mappings in three ways:
|
||||
// 1) We expect that pointers into the stack mapping will be common, so
|
||||
// we cache that address range.
|
||||
// 2) The last referenced mapping is a reasonable predictor for the next
|
||||
// referenced mapping, so we test that first.
|
||||
// 3) We precompute a bitfield based upon bits 32:32-n of the start and
|
||||
// stop addresses, and use that to short circuit any values that can
|
||||
// not be pointers. (n=11)
|
||||
const uintptr_t defaced =
|
||||
#if defined(__LP64__)
|
||||
0x0defaced0defaced;
|
||||
#else
|
||||
0x0defaced;
|
||||
#endif
|
||||
// the bitfield length is 2^test_bits long.
|
||||
const unsigned int test_bits = 11;
|
||||
// byte length of the corresponding array.
|
||||
const unsigned int array_size = 1 << (test_bits - 3);
|
||||
const unsigned int array_mask = array_size - 1;
|
||||
// The amount to right shift pointers by. This captures the top bits
|
||||
// on 32 bit architectures. On 64 bit architectures this would be
|
||||
// uninformative so we take the same range of bits.
|
||||
const unsigned int shift = 32 - 11;
|
||||
const MappingInfo* last_hit_mapping = nullptr;
|
||||
const MappingInfo* hit_mapping = nullptr;
|
||||
const MappingInfo* stack_mapping = FindMappingNoBias(stack_pointer);
|
||||
// The magnitude below which integers are considered to be to be
|
||||
// 'small', and not constitute a PII risk. These are included to
|
||||
// avoid eliding useful register values.
|
||||
const ssize_t small_int_magnitude = 4096;
|
||||
|
||||
char could_hit_mapping[array_size];
|
||||
my_memset(could_hit_mapping, 0, array_size);
|
||||
|
||||
// Initialize the bitfield such that if the (pointer >> shift)'th
|
||||
// bit, modulo the bitfield size, is not set then there does not
|
||||
// exist a mapping in mappings_ that would contain that pointer.
|
||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
||||
if (!mappings_[i]->exec) continue;
|
||||
// For each mapping, work out the (unmodulo'ed) range of bits to
|
||||
// set.
|
||||
uintptr_t start = mappings_[i]->start_addr;
|
||||
uintptr_t end = start + mappings_[i]->size;
|
||||
start >>= shift;
|
||||
end >>= shift;
|
||||
for (size_t bit = start; bit <= end; ++bit) {
|
||||
// Set each bit in the range, applying the modulus.
|
||||
could_hit_mapping[(bit >> 3) & array_mask] |= 1 << (bit & 7);
|
||||
}
|
||||
}
|
||||
|
||||
// Zero memory that is below the current stack pointer.
|
||||
const uintptr_t offset =
|
||||
(sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
|
||||
if (offset) {
|
||||
my_memset(stack_copy, 0, offset);
|
||||
}
|
||||
|
||||
// Apply sanitization to each complete pointer-aligned word in the
|
||||
// stack.
|
||||
uint8_t* sp;
|
||||
for (sp = stack_copy + offset;
|
||||
sp <= stack_copy + stack_len - sizeof(uintptr_t);
|
||||
sp += sizeof(uintptr_t)) {
|
||||
uintptr_t addr;
|
||||
my_memcpy(&addr, sp, sizeof(uintptr_t));
|
||||
if (static_cast<intptr_t>(addr) <= small_int_magnitude &&
|
||||
static_cast<intptr_t>(addr) >= -small_int_magnitude) {
|
||||
continue;
|
||||
}
|
||||
if (stack_mapping && MappingContainsAddress(*stack_mapping, addr)) {
|
||||
continue;
|
||||
}
|
||||
if (last_hit_mapping && MappingContainsAddress(*last_hit_mapping, addr)) {
|
||||
continue;
|
||||
}
|
||||
uintptr_t test = addr >> shift;
|
||||
if (could_hit_mapping[(test >> 3) & array_mask] & (1 << (test & 7)) &&
|
||||
(hit_mapping = FindMappingNoBias(addr)) != nullptr &&
|
||||
hit_mapping->exec) {
|
||||
last_hit_mapping = hit_mapping;
|
||||
continue;
|
||||
}
|
||||
my_memcpy(sp, &defaced, sizeof(uintptr_t));
|
||||
}
|
||||
// Zero any partial word at the top of the stack, if alignment is
|
||||
// such that that is required.
|
||||
if (sp < stack_copy + stack_len) {
|
||||
my_memset(sp, 0, stack_copy + stack_len - sp);
|
||||
}
|
||||
}
|
||||
|
||||
bool LinuxDumper::StackHasPointerToMapping(const uint8_t* stack_copy,
|
||||
size_t stack_len,
|
||||
uintptr_t sp_offset,
|
||||
const MappingInfo& mapping) {
|
||||
// Loop over all stack words that would have been on the stack in
|
||||
// the target process (i.e. are word aligned, and at addresses >=
|
||||
// the stack pointer). Regardless of the alignment of |stack_copy|,
|
||||
// the memory starting at |stack_copy| + |offset| represents an
|
||||
// aligned word in the target process.
|
||||
const uintptr_t low_addr = mapping.system_mapping_info.start_addr;
|
||||
const uintptr_t high_addr = mapping.system_mapping_info.end_addr;
|
||||
const uintptr_t offset =
|
||||
(sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
|
||||
|
||||
for (const uint8_t* sp = stack_copy + offset;
|
||||
sp <= stack_copy + stack_len - sizeof(uintptr_t);
|
||||
sp += sizeof(uintptr_t)) {
|
||||
uintptr_t addr;
|
||||
my_memcpy(&addr, sp, sizeof(uintptr_t));
|
||||
if (low_addr <= addr && addr <= high_addr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the mapping which the given memory address falls in.
|
||||
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
||||
const uintptr_t addr = (uintptr_t) address;
|
||||
@ -733,6 +958,19 @@ const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find the mapping which the given memory address falls in. Uses the
|
||||
// unadjusted mapping address range from the kernel, rather than the
|
||||
// biased range.
|
||||
const MappingInfo* LinuxDumper::FindMappingNoBias(uintptr_t address) const {
|
||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
||||
if (address >= mappings_[i]->system_mapping_info.start_addr &&
|
||||
address < mappings_[i]->system_mapping_info.end_addr) {
|
||||
return mappings_[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
||||
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
|
||||
|
||||
|
@ -99,10 +99,22 @@ class LinuxDumper {
|
||||
// Returns true on success. One must have called |ThreadsSuspend| first.
|
||||
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
|
||||
|
||||
size_t GetMainThreadIndex() const {
|
||||
for (size_t i = 0; i < threads_.size(); ++i) {
|
||||
if (threads_[i] == pid_) return i;
|
||||
}
|
||||
return -1u;
|
||||
}
|
||||
|
||||
// These are only valid after a call to |Init|.
|
||||
const wasteful_vector<pid_t> &threads() { return threads_; }
|
||||
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
|
||||
const MappingInfo* FindMapping(const void* address) const;
|
||||
// Find the mapping which the given memory address falls in. Unlike
|
||||
// FindMapping, this method uses the unadjusted mapping address
|
||||
// ranges from the kernel, rather than the ranges that have had the
|
||||
// load bias applied.
|
||||
const MappingInfo* FindMappingNoBias(uintptr_t address) const;
|
||||
const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }
|
||||
|
||||
// Find a block of memory to take as the stack given the top of stack pointer.
|
||||
@ -111,6 +123,32 @@ class LinuxDumper {
|
||||
// stack_top: the current top of the stack
|
||||
bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
|
||||
|
||||
// Sanitize a copy of the stack by overwriting words that are not
|
||||
// pointers with a sentinel (0x0defaced).
|
||||
// stack_copy: a copy of the stack to sanitize. |stack_copy| might
|
||||
// not be word aligned, but it represents word aligned
|
||||
// data copied from another location.
|
||||
// stack_len: the length of the allocation pointed to by |stack_copy|.
|
||||
// stack_pointer: the address of the stack pointer (used to locate
|
||||
// the stack mapping, as an optimization).
|
||||
// sp_offset: the offset relative to stack_copy that reflects the
|
||||
// current value of the stack pointer.
|
||||
void SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
|
||||
uintptr_t stack_pointer, uintptr_t sp_offset);
|
||||
|
||||
// Test whether |stack_copy| contains a pointer-aligned word that
|
||||
// could be an address within a given mapping.
|
||||
// stack_copy: a copy of the stack to check. |stack_copy| might
|
||||
// not be word aligned, but it represents word aligned
|
||||
// data copied from another location.
|
||||
// stack_len: the length of the allocation pointed to by |stack_copy|.
|
||||
// sp_offset: the offset relative to stack_copy that reflects the
|
||||
// current value of the stack pointer.
|
||||
// mapping: the mapping against which to test stack words.
|
||||
bool StackHasPointerToMapping(const uint8_t* stack_copy, size_t stack_len,
|
||||
uintptr_t sp_offset,
|
||||
const MappingInfo& mapping);
|
||||
|
||||
PageAllocator* allocator() { return &allocator_; }
|
||||
|
||||
// Copy content of |length| bytes from a given process |child|,
|
||||
@ -132,6 +170,8 @@ class LinuxDumper {
|
||||
unsigned int mapping_id,
|
||||
wasteful_vector<uint8_t>& identifier);
|
||||
|
||||
void SetCrashInfoFromSigInfo(const siginfo_t& siginfo);
|
||||
|
||||
uintptr_t crash_address() const { return crash_address_; }
|
||||
void set_crash_address(uintptr_t crash_address) {
|
||||
crash_address_ = crash_address;
|
||||
@ -139,6 +179,10 @@ class LinuxDumper {
|
||||
|
||||
int crash_signal() const { return crash_signal_; }
|
||||
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
|
||||
const char* GetCrashSignalString() const;
|
||||
|
||||
void set_crash_signal_code(int code) { crash_signal_code_ = code; }
|
||||
int crash_signal_code() const { return crash_signal_code_; }
|
||||
|
||||
pid_t crash_thread() const { return crash_thread_; }
|
||||
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
|
||||
@ -189,6 +233,9 @@ class LinuxDumper {
|
||||
// Signal that terminated the crashed process.
|
||||
int crash_signal_;
|
||||
|
||||
// The code associated with |crash_signal_|.
|
||||
int crash_signal_code_;
|
||||
|
||||
// ID of the crashed thread.
|
||||
pid_t crash_thread_;
|
||||
|
||||
|
@ -57,14 +57,15 @@
|
||||
|
||||
void *thread_function(void *data) {
|
||||
int pipefd = *static_cast<int *>(data);
|
||||
volatile pid_t thread_id = syscall(__NR_gettid);
|
||||
volatile pid_t* thread_id = new pid_t;
|
||||
*thread_id = syscall(__NR_gettid);
|
||||
// Signal parent that a thread has started.
|
||||
uint8_t byte = 1;
|
||||
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
|
||||
perror("ERROR: parent notification failed");
|
||||
return NULL;
|
||||
}
|
||||
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
|
||||
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = thread_id;
|
||||
while (true)
|
||||
asm volatile ("" : : "r" (thread_id_ptr));
|
||||
return NULL;
|
||||
|
@ -149,6 +149,50 @@ bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::ReadRegisterSet(ThreadInfo* info, pid_t tid)
|
||||
{
|
||||
#ifdef PTRACE_GETREGSET
|
||||
struct iovec io;
|
||||
info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len);
|
||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len);
|
||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LinuxPtraceDumper::ReadRegisters(ThreadInfo* info, pid_t tid) {
|
||||
#ifdef PTRACE_GETREGS
|
||||
void* gp_addr;
|
||||
info->GetGeneralPurposeRegisters(&gp_addr, NULL);
|
||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !(defined(__ANDROID__) && defined(__ARM_EABI__))
|
||||
// When running an arm build on an arm64 device, attempting to get the
|
||||
// floating point registers fails. On Android, the floating point registers
|
||||
// aren't written to the cpu context anyway, so just don't get them here.
|
||||
// See http://crbug.com/508324
|
||||
void* fp_addr;
|
||||
info->GetFloatingPointRegisters(&fp_addr, NULL);
|
||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
|
||||
return false;
|
||||
}
|
||||
#endif // !(defined(__ANDROID__) && defined(__ARM_EABI__))
|
||||
return true;
|
||||
#else // PTRACE_GETREGS
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read thread info from /proc/$pid/status.
|
||||
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
|
||||
// these members are set to -1. Returns true iff all three members are
|
||||
@ -188,37 +232,12 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
if (info->ppid == -1 || info->tgid == -1)
|
||||
return false;
|
||||
|
||||
#ifdef PTRACE_GETREGSET
|
||||
struct iovec io;
|
||||
info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len);
|
||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
|
||||
return false;
|
||||
if (!ReadRegisterSet(info, tid)) {
|
||||
if (!ReadRegisters(info, tid)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len);
|
||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
|
||||
return false;
|
||||
}
|
||||
#else // PTRACE_GETREGSET
|
||||
void* gp_addr;
|
||||
info->GetGeneralPurposeRegisters(&gp_addr, NULL);
|
||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !(defined(__ANDROID__) && defined(__ARM_EABI__))
|
||||
// When running an arm build on an arm64 device, attempting to get the
|
||||
// floating point registers fails. On Android, the floating point registers
|
||||
// aren't written to the cpu context anyway, so just don't get them here.
|
||||
// See http://crbug.com/508324
|
||||
void* fp_addr;
|
||||
info->GetFloatingPointRegisters(&fp_addr, NULL);
|
||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif // PTRACE_GETREGSET
|
||||
|
||||
#if defined(__i386)
|
||||
#if !defined(bit_FXSAVE) // e.g. Clang
|
||||
#define bit_FXSAVE bit_FXSR
|
||||
@ -249,6 +268,8 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
#endif
|
||||
|
||||
#if defined(__mips__)
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(PC), &info->mcontext.pc);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
|
@ -85,6 +85,15 @@ class LinuxPtraceDumper : public LinuxDumper {
|
||||
private:
|
||||
// Set to true if all threads of the crashed process are suspended.
|
||||
bool threads_suspended_;
|
||||
|
||||
// Read the tracee's registers on kernel with PTRACE_GETREGSET support.
|
||||
// Returns false if PTRACE_GETREGSET is not defined.
|
||||
// Returns true on success.
|
||||
bool ReadRegisterSet(ThreadInfo* info, pid_t tid);
|
||||
|
||||
// Read the tracee's registers on kernel with PTRACE_GETREGS support.
|
||||
// Returns true on success.
|
||||
bool ReadRegisters(ThreadInfo* info, pid_t tid);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -66,6 +66,63 @@ using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
pid_t SetupChildProcess(int number_of_threads) {
|
||||
char kNumberOfThreadsArgument[2];
|
||||
sprintf(kNumberOfThreadsArgument, "%d", number_of_threads);
|
||||
|
||||
int fds[2];
|
||||
EXPECT_NE(-1, pipe(fds));
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
// In child process.
|
||||
close(fds[0]);
|
||||
|
||||
string helper_path(GetHelperBinary());
|
||||
if (helper_path.empty()) {
|
||||
fprintf(stderr, "Couldn't find helper binary\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Pass the pipe fd and the number of threads as arguments.
|
||||
char pipe_fd_string[8];
|
||||
sprintf(pipe_fd_string, "%d", fds[1]);
|
||||
execl(helper_path.c_str(),
|
||||
"linux_dumper_unittest_helper",
|
||||
pipe_fd_string,
|
||||
kNumberOfThreadsArgument,
|
||||
NULL);
|
||||
// Kill if we get here.
|
||||
printf("Errno from exec: %d", errno);
|
||||
std::string err_str = "Exec of " + helper_path + " failed";
|
||||
perror(err_str.c_str());
|
||||
_exit(1);
|
||||
}
|
||||
close(fds[1]);
|
||||
|
||||
// Wait for all child threads to indicate that they have started
|
||||
for (int threads = 0; threads < number_of_threads; threads++) {
|
||||
struct pollfd pfd;
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = fds[0];
|
||||
pfd.events = POLLIN | POLLERR;
|
||||
|
||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
|
||||
EXPECT_EQ(1, r);
|
||||
EXPECT_TRUE(pfd.revents & POLLIN);
|
||||
uint8_t junk;
|
||||
EXPECT_EQ(read(fds[0], &junk, sizeof(junk)),
|
||||
static_cast<ssize_t>(sizeof(junk)));
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
// There is a race here because we may stop a child thread before
|
||||
// it is actually running the busy loop. Empirically this sleep
|
||||
// is sufficient to avoid the race.
|
||||
usleep(100000);
|
||||
return child_pid;
|
||||
}
|
||||
|
||||
typedef wasteful_vector<uint8_t> id_vector;
|
||||
typedef testing::Test LinuxPtraceDumperTest;
|
||||
|
||||
@ -370,58 +427,9 @@ TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
|
||||
|
||||
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||
static const int kNumberOfThreadsInHelperProgram = 5;
|
||||
char kNumberOfThreadsArgument[2];
|
||||
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
|
||||
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
// In child process.
|
||||
close(fds[0]);
|
||||
|
||||
string helper_path(GetHelperBinary());
|
||||
if (helper_path.empty()) {
|
||||
FAIL() << "Couldn't find helper binary";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Pass the pipe fd and the number of threads as arguments.
|
||||
char pipe_fd_string[8];
|
||||
sprintf(pipe_fd_string, "%d", fds[1]);
|
||||
execl(helper_path.c_str(),
|
||||
"linux_dumper_unittest_helper",
|
||||
pipe_fd_string,
|
||||
kNumberOfThreadsArgument,
|
||||
NULL);
|
||||
// Kill if we get here.
|
||||
printf("Errno from exec: %d", errno);
|
||||
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
|
||||
exit(0);
|
||||
}
|
||||
close(fds[1]);
|
||||
|
||||
// Wait for all child threads to indicate that they have started
|
||||
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
|
||||
struct pollfd pfd;
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = fds[0];
|
||||
pfd.events = POLLIN | POLLERR;
|
||||
|
||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
|
||||
ASSERT_EQ(1, r);
|
||||
ASSERT_TRUE(pfd.revents & POLLIN);
|
||||
uint8_t junk;
|
||||
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
|
||||
static_cast<ssize_t>(sizeof(junk)));
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
// There is a race here because we may stop a child thread before
|
||||
// it is actually running the busy loop. Empirically this sleep
|
||||
// is sufficient to avoid the race.
|
||||
usleep(100000);
|
||||
pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram);
|
||||
ASSERT_NE(child_pid, -1);
|
||||
|
||||
// Children are ready now.
|
||||
LinuxPtraceDumper dumper(child_pid);
|
||||
@ -468,3 +476,99 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||
ASSERT_TRUE(WIFSIGNALED(status));
|
||||
ASSERT_EQ(SIGKILL, WTERMSIG(status));
|
||||
}
|
||||
|
||||
TEST_F(LinuxPtraceDumperTest, SanitizeStackCopy) {
|
||||
static const int kNumberOfThreadsInHelperProgram = 1;
|
||||
|
||||
pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram);
|
||||
ASSERT_NE(child_pid, -1);
|
||||
|
||||
LinuxPtraceDumper dumper(child_pid);
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
EXPECT_TRUE(dumper.ThreadsSuspend());
|
||||
|
||||
ThreadInfo thread_info;
|
||||
EXPECT_TRUE(dumper.GetThreadInfoByIndex(0, &thread_info));
|
||||
|
||||
const uintptr_t defaced =
|
||||
#if defined(__LP64__)
|
||||
0x0defaced0defaced;
|
||||
#else
|
||||
0x0defaced;
|
||||
#endif
|
||||
|
||||
uintptr_t simulated_stack[2];
|
||||
|
||||
// Pointers into the stack shouldn't be sanitized.
|
||||
memset(simulated_stack, 0xff, sizeof(simulated_stack));
|
||||
simulated_stack[1] = thread_info.stack_pointer;
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
sizeof(uintptr_t));
|
||||
ASSERT_NE(simulated_stack[1], defaced);
|
||||
|
||||
// Memory prior to the stack pointer should be cleared.
|
||||
ASSERT_EQ(simulated_stack[0], 0u);
|
||||
|
||||
// Small integers should not be sanitized.
|
||||
for (int i = -4096; i <= 4096; ++i) {
|
||||
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||
simulated_stack[0] = static_cast<uintptr_t>(i);
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
0u);
|
||||
ASSERT_NE(simulated_stack[0], defaced);
|
||||
}
|
||||
|
||||
// The instruction pointer definitely should point into an executable mapping.
|
||||
const MappingInfo* mapping_info = dumper.FindMappingNoBias(
|
||||
reinterpret_cast<uintptr_t>(thread_info.GetInstructionPointer()));
|
||||
ASSERT_NE(mapping_info, nullptr);
|
||||
ASSERT_TRUE(mapping_info->exec);
|
||||
|
||||
// Pointers to code shouldn't be sanitized.
|
||||
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||
simulated_stack[1] = thread_info.GetInstructionPointer();
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
0u);
|
||||
ASSERT_NE(simulated_stack[0], defaced);
|
||||
|
||||
// String fragments should be sanitized.
|
||||
memcpy(simulated_stack, "abcdefghijklmnop", sizeof(simulated_stack));
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
0u);
|
||||
ASSERT_EQ(simulated_stack[0], defaced);
|
||||
ASSERT_EQ(simulated_stack[1], defaced);
|
||||
|
||||
// Heap pointers should be sanititzed.
|
||||
#if defined(__ARM_EABI__)
|
||||
uintptr_t heap_addr = thread_info.regs.uregs[3];
|
||||
#elif defined(__aarch64__)
|
||||
uintptr_t heap_addr = thread_info.regs.regs[3];
|
||||
#elif defined(__i386)
|
||||
uintptr_t heap_addr = thread_info.regs.ecx;
|
||||
#elif defined(__x86_64)
|
||||
uintptr_t heap_addr = thread_info.regs.rcx;
|
||||
#elif defined(__mips__)
|
||||
uintptr_t heap_addr = thread_info.mcontext.gregs[1];
|
||||
#else
|
||||
#error This test has not been ported to this platform.
|
||||
#endif
|
||||
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||
simulated_stack[0] = heap_addr;
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
0u);
|
||||
ASSERT_EQ(simulated_stack[0], defaced);
|
||||
|
||||
EXPECT_TRUE(dumper.ThreadsResume());
|
||||
kill(child_pid, SIGKILL);
|
||||
|
||||
// Reap child.
|
||||
int status;
|
||||
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
|
||||
ASSERT_TRUE(WIFSIGNALED(status));
|
||||
ASSERT_EQ(SIGKILL, WTERMSIG(status));
|
||||
}
|
||||
|
@ -129,6 +129,9 @@ class MinidumpWriter {
|
||||
const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks,
|
||||
LinuxDumper* dumper)
|
||||
: fd_(minidump_fd),
|
||||
path_(minidump_path),
|
||||
@ -140,7 +143,12 @@ class MinidumpWriter {
|
||||
minidump_size_limit_(-1),
|
||||
memory_blocks_(dumper_->allocator()),
|
||||
mapping_list_(mappings),
|
||||
app_memory_list_(appmem) {
|
||||
app_memory_list_(appmem),
|
||||
skip_stacks_if_mapping_unreferenced_(
|
||||
skip_stacks_if_mapping_unreferenced),
|
||||
principal_mapping_address_(principal_mapping_address),
|
||||
principal_mapping_(nullptr),
|
||||
sanitize_stacks_(sanitize_stacks) {
|
||||
// Assert there should be either a valid fd or a valid path, not both.
|
||||
assert(fd_ != -1 || minidump_path);
|
||||
assert(fd_ == -1 || !minidump_path);
|
||||
@ -150,12 +158,22 @@ class MinidumpWriter {
|
||||
if (!dumper_->Init())
|
||||
return false;
|
||||
|
||||
if (!dumper_->ThreadsSuspend() || !dumper_->LateInit())
|
||||
return false;
|
||||
|
||||
if (skip_stacks_if_mapping_unreferenced_) {
|
||||
principal_mapping_ =
|
||||
dumper_->FindMappingNoBias(principal_mapping_address_);
|
||||
if (!CrashingThreadReferencesPrincipalMapping())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fd_ != -1)
|
||||
minidump_writer_.SetFile(fd_);
|
||||
else if (!minidump_writer_.Open(path_))
|
||||
return false;
|
||||
|
||||
return dumper_->ThreadsSuspend() && dumper_->LateInit();
|
||||
return true;
|
||||
}
|
||||
|
||||
~MinidumpWriter() {
|
||||
@ -166,6 +184,38 @@ class MinidumpWriter {
|
||||
dumper_->ThreadsResume();
|
||||
}
|
||||
|
||||
bool CrashingThreadReferencesPrincipalMapping() {
|
||||
if (!ucontext_ || !principal_mapping_)
|
||||
return false;
|
||||
|
||||
const uintptr_t low_addr =
|
||||
principal_mapping_->system_mapping_info.start_addr;
|
||||
const uintptr_t high_addr =
|
||||
principal_mapping_->system_mapping_info.end_addr;
|
||||
|
||||
const uintptr_t stack_pointer = UContextReader::GetStackPointer(ucontext_);
|
||||
const uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_);
|
||||
|
||||
if (pc >= low_addr && pc < high_addr)
|
||||
return true;
|
||||
|
||||
uint8_t* stack_copy;
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
|
||||
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer))
|
||||
return false;
|
||||
|
||||
stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||
dumper_->CopyFromProcess(stack_copy, GetCrashThread(), stack, stack_len);
|
||||
|
||||
uintptr_t stack_pointer_offset =
|
||||
stack_pointer - reinterpret_cast<uintptr_t>(stack);
|
||||
|
||||
return dumper_->StackHasPointerToMapping(
|
||||
stack_copy, stack_len, stack_pointer_offset, *principal_mapping_);
|
||||
}
|
||||
|
||||
bool Dump() {
|
||||
// A minidump file contains a number of tagged streams. This is the number
|
||||
// of stream which we write.
|
||||
@ -265,12 +315,16 @@ class MinidumpWriter {
|
||||
}
|
||||
|
||||
bool FillThreadStack(MDRawThread* thread, uintptr_t stack_pointer,
|
||||
int max_stack_len, uint8_t** stack_copy) {
|
||||
uintptr_t pc, int max_stack_len, uint8_t** stack_copy) {
|
||||
*stack_copy = NULL;
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
|
||||
thread->stack.start_of_memory_range = stack_pointer;
|
||||
thread->stack.memory.data_size = 0;
|
||||
thread->stack.memory.rva = minidump_writer_.position();
|
||||
|
||||
if (dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
|
||||
UntypedMDRVA memory(&minidump_writer_);
|
||||
if (max_stack_len >= 0 &&
|
||||
stack_len > static_cast<unsigned int>(max_stack_len)) {
|
||||
stack_len = max_stack_len;
|
||||
@ -283,20 +337,38 @@ class MinidumpWriter {
|
||||
}
|
||||
stack = reinterpret_cast<const void*>(int_stack);
|
||||
}
|
||||
if (!memory.Allocate(stack_len))
|
||||
return false;
|
||||
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||
dumper_->CopyFromProcess(*stack_copy, thread->thread_id, stack,
|
||||
stack_len);
|
||||
|
||||
uintptr_t stack_pointer_offset =
|
||||
stack_pointer - reinterpret_cast<uintptr_t>(stack);
|
||||
if (skip_stacks_if_mapping_unreferenced_) {
|
||||
if (!principal_mapping_) {
|
||||
return true;
|
||||
}
|
||||
uintptr_t low_addr = principal_mapping_->system_mapping_info.start_addr;
|
||||
uintptr_t high_addr = principal_mapping_->system_mapping_info.end_addr;
|
||||
if ((pc < low_addr || pc > high_addr) &&
|
||||
!dumper_->StackHasPointerToMapping(*stack_copy, stack_len,
|
||||
stack_pointer_offset,
|
||||
*principal_mapping_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sanitize_stacks_) {
|
||||
dumper_->SanitizeStackCopy(*stack_copy, stack_len, stack_pointer,
|
||||
stack_pointer_offset);
|
||||
}
|
||||
|
||||
UntypedMDRVA memory(&minidump_writer_);
|
||||
if (!memory.Allocate(stack_len))
|
||||
return false;
|
||||
memory.Copy(*stack_copy, stack_len);
|
||||
thread->stack.start_of_memory_range =
|
||||
reinterpret_cast<uintptr_t>(stack);
|
||||
thread->stack.start_of_memory_range = reinterpret_cast<uintptr_t>(stack);
|
||||
thread->stack.memory = memory.location();
|
||||
memory_blocks_.push_back(thread->stack);
|
||||
} else {
|
||||
thread->stack.start_of_memory_range = stack_pointer;
|
||||
thread->stack.memory.data_size = 0;
|
||||
thread->stack.memory.rva = minidump_writer_.position();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -343,7 +415,9 @@ class MinidumpWriter {
|
||||
!dumper_->IsPostMortem()) {
|
||||
uint8_t* stack_copy;
|
||||
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
|
||||
if (!FillThreadStack(&thread, stack_ptr, -1, &stack_copy))
|
||||
if (!FillThreadStack(&thread, stack_ptr,
|
||||
UContextReader::GetInstructionPointer(ucontext_),
|
||||
-1, &stack_copy))
|
||||
return false;
|
||||
|
||||
// Copy 256 bytes around crashing instruction pointer to minidump.
|
||||
@ -409,8 +483,9 @@ class MinidumpWriter {
|
||||
int max_stack_len = -1; // default to no maximum for this thread
|
||||
if (minidump_size_limit_ >= 0 && i >= kLimitBaseThreadCount)
|
||||
max_stack_len = extra_thread_stack_len;
|
||||
if (!FillThreadStack(&thread, info.stack_pointer, max_stack_len,
|
||||
&stack_copy))
|
||||
if (!FillThreadStack(&thread, info.stack_pointer,
|
||||
info.GetInstructionPointer(), max_stack_len,
|
||||
&stack_copy))
|
||||
return false;
|
||||
|
||||
TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
|
||||
@ -626,15 +701,18 @@ class MinidumpWriter {
|
||||
TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
|
||||
if (!exc.Allocate())
|
||||
return false;
|
||||
my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
|
||||
|
||||
MDRawExceptionStream* stream = exc.get();
|
||||
my_memset(stream, 0, sizeof(MDRawExceptionStream));
|
||||
|
||||
dirent->stream_type = MD_EXCEPTION_STREAM;
|
||||
dirent->location = exc.location();
|
||||
|
||||
exc.get()->thread_id = GetCrashThread();
|
||||
exc.get()->exception_record.exception_code = dumper_->crash_signal();
|
||||
exc.get()->exception_record.exception_address = dumper_->crash_address();
|
||||
exc.get()->thread_context = crashing_thread_context_;
|
||||
stream->thread_id = GetCrashThread();
|
||||
stream->exception_record.exception_code = dumper_->crash_signal();
|
||||
stream->exception_record.exception_flags = dumper_->crash_signal_code();
|
||||
stream->exception_record.exception_address = dumper_->crash_address();
|
||||
stream->thread_context = crashing_thread_context_;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1264,6 +1342,13 @@ class MinidumpWriter {
|
||||
// Additional memory regions to be included in the dump,
|
||||
// provided by the caller.
|
||||
const AppMemoryList& app_memory_list_;
|
||||
// If set, skip recording any threads that do not reference the
|
||||
// mapping containing principal_mapping_address_.
|
||||
bool skip_stacks_if_mapping_unreferenced_;
|
||||
uintptr_t principal_mapping_address_;
|
||||
const MappingInfo* principal_mapping_;
|
||||
// If true, apply stack sanitization to stored stack data.
|
||||
bool sanitize_stacks_;
|
||||
};
|
||||
|
||||
|
||||
@ -1273,20 +1358,22 @@ bool WriteMinidumpImpl(const char* minidump_path,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
LinuxPtraceDumper dumper(crashing_process);
|
||||
const ExceptionHandler::CrashContext* context = NULL;
|
||||
if (blob) {
|
||||
if (blob_size != sizeof(ExceptionHandler::CrashContext))
|
||||
return false;
|
||||
context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
|
||||
dumper.set_crash_address(
|
||||
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
|
||||
dumper.set_crash_signal(context->siginfo.si_signo);
|
||||
dumper.SetCrashInfoFromSigInfo(context->siginfo);
|
||||
dumper.set_crash_thread(context->tid);
|
||||
}
|
||||
MinidumpWriter writer(minidump_path, minidump_fd, context, mappings,
|
||||
appmem, &dumper);
|
||||
appmem, skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address, sanitize_stacks, &dumper);
|
||||
// Set desired limit for file size of minidump (-1 means no limit).
|
||||
writer.set_minidump_size_limit(minidump_size_limit);
|
||||
if (!writer.Init())
|
||||
@ -1299,17 +1386,29 @@ bool WriteMinidumpImpl(const char* minidump_path,
|
||||
namespace google_breakpad {
|
||||
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size) {
|
||||
const void* blob, size_t blob_size,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(minidump_path, -1, -1,
|
||||
crashing_process, blob, blob_size,
|
||||
MappingList(), AppMemoryList());
|
||||
MappingList(), AppMemoryList(),
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size) {
|
||||
const void* blob, size_t blob_size,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(NULL, minidump_fd, -1,
|
||||
crashing_process, blob, blob_size,
|
||||
MappingList(), AppMemoryList());
|
||||
MappingList(), AppMemoryList(),
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
@ -1319,7 +1418,7 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
dumper.set_crash_signal(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED);
|
||||
dumper.set_crash_thread(process_blamed_thread);
|
||||
MinidumpWriter writer(minidump_path, -1, NULL, MappingList(),
|
||||
AppMemoryList(), &dumper);
|
||||
AppMemoryList(), false, 0, false, &dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
@ -1328,46 +1427,71 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(minidump_path, -1, -1, crashing_process,
|
||||
blob, blob_size,
|
||||
mappings, appmem);
|
||||
mappings, appmem,
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(NULL, minidump_fd, -1, crashing_process,
|
||||
blob, blob_size,
|
||||
mappings, appmem);
|
||||
mappings, appmem,
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(minidump_path, -1, minidump_size_limit,
|
||||
crashing_process, blob, blob_size,
|
||||
mappings, appmem);
|
||||
mappings, appmem,
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(NULL, minidump_fd, minidump_size_limit,
|
||||
crashing_process, blob, blob_size,
|
||||
mappings, appmem);
|
||||
mappings, appmem,
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(const char* filename,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem,
|
||||
LinuxDumper* dumper) {
|
||||
MinidumpWriter writer(filename, -1, NULL, mappings, appmem, dumper);
|
||||
MinidumpWriter writer(filename, -1, NULL, mappings, appmem,
|
||||
false, 0, false, dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
|
@ -78,10 +78,16 @@ typedef std::list<AppMemory> AppMemoryList;
|
||||
//
|
||||
// Returns true iff successful.
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size);
|
||||
const void* blob, size_t blob_size,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
// Same as above but takes an open file descriptor instead of a path.
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size);
|
||||
const void* blob, size_t blob_size,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
|
||||
// Alternate form of WriteMinidump() that works with processes that
|
||||
// are not expected to have crashed. If |process_blamed_thread| is
|
||||
@ -96,23 +102,35 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
const AppMemoryList& appdata,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
const AppMemoryList& appdata,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
|
||||
// These overloads also allow passing a file size limit for the minidump.
|
||||
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
const AppMemoryList& appdata,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
const AppMemoryList& appdata,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
|
||||
bool WriteMinidump(const char* filename,
|
||||
const MappingList& mappings,
|
||||
|
@ -70,7 +70,7 @@ TEST(MinidumpWriterTest, SetupWithPath) {
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
@ -87,6 +87,7 @@ TEST(MinidumpWriterTest, SetupWithPath) {
|
||||
ASSERT_GT(st.st_size, 0);
|
||||
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
TEST(MinidumpWriterTest, SetupWithFD) {
|
||||
@ -99,7 +100,7 @@ TEST(MinidumpWriterTest, SetupWithFD) {
|
||||
char b;
|
||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
@ -117,6 +118,7 @@ TEST(MinidumpWriterTest, SetupWithFD) {
|
||||
ASSERT_GT(st.st_size, 0);
|
||||
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that mapping info can be specified when writing a minidump,
|
||||
@ -152,7 +154,7 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
@ -179,7 +181,7 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
||||
mappings.push_back(mapping);
|
||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
|
||||
mappings, memory_list));
|
||||
mappings, memory_list, false, 0, false));
|
||||
|
||||
// Read the minidump. Load the module list, and ensure that
|
||||
// the mmap'ed |memory| is listed with the given module name
|
||||
@ -213,6 +215,150 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
||||
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
|
||||
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that minidumping is skipped while writing minidumps if principal mapping
|
||||
// is not referenced.
|
||||
TEST(MinidumpWriterTest, MinidumpSkippedIfRequested) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
ASSERT_EQ(0, getcontext(&context.context));
|
||||
context.tid = child;
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||
|
||||
// pass an invalid principal mapping address, which will force
|
||||
// WriteMinidump to not write a minidump.
|
||||
ASSERT_FALSE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
|
||||
true, static_cast<uintptr_t>(0x0102030405060708ull),
|
||||
false));
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that minidumping is skipped while writing minidumps if principal mapping
|
||||
// is not referenced.
|
||||
TEST(MinidumpWriterTest, MinidumpStacksSkippedIfRequested) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
|
||||
// Create a thread that does not return, and only references libc (not the
|
||||
// current executable). This thread should not be captured in the minidump.
|
||||
pthread_t thread;
|
||||
pthread_attr_t thread_attributes;
|
||||
pthread_attr_init(&thread_attributes);
|
||||
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
pthread_create(&thread, &thread_attributes,
|
||||
reinterpret_cast<void* (*)(void*)>(&sigsuspend), &sigset);
|
||||
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
ASSERT_EQ(0, getcontext(&context.context));
|
||||
context.tid = child;
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||
|
||||
// Pass an invalid principal mapping address, which will force
|
||||
// WriteMinidump to not dump any thread stacks.
|
||||
ASSERT_TRUE(WriteMinidump(
|
||||
templ.c_str(), child, &context, sizeof(context), true,
|
||||
reinterpret_cast<uintptr_t>(google_breakpad::WriteFile), false));
|
||||
|
||||
// Read the minidump. And ensure that thread memory was dumped only for the
|
||||
// main thread.
|
||||
Minidump minidump(templ);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
|
||||
MinidumpThreadList *threads = minidump.GetThreadList();
|
||||
int threads_with_stacks = 0;
|
||||
for (unsigned int i = 0; i < threads->thread_count(); ++i) {
|
||||
MinidumpThread *thread = threads->GetThreadAtIndex(i);
|
||||
if (thread->GetMemory()) {
|
||||
++threads_with_stacks;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(1, threads_with_stacks);
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that stacks can be sanitized while writing minidumps.
|
||||
TEST(MinidumpWriterTest, StacksAreSanitizedIfRequested) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
ASSERT_EQ(0, getcontext(&context.context));
|
||||
context.tid = child;
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||
// pass an invalid principal mapping address, which will force
|
||||
// WriteMinidump to not dump any thread stacks.
|
||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
|
||||
false, 0, true));
|
||||
|
||||
// Read the minidump. And ensure that thread memory contains a defaced value.
|
||||
Minidump minidump(templ);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
|
||||
const uintptr_t defaced =
|
||||
#if defined(__LP64__)
|
||||
0x0defaced0defaced;
|
||||
#else
|
||||
0x0defaced;
|
||||
#endif
|
||||
MinidumpThreadList *threads = minidump.GetThreadList();
|
||||
for (unsigned int i = 0; i < threads->thread_count(); ++i) {
|
||||
MinidumpThread *thread = threads->GetThreadAtIndex(i);
|
||||
MinidumpMemoryRegion *mem = thread->GetMemory();
|
||||
ASSERT_TRUE(mem != nullptr);
|
||||
uint32_t sz = mem->GetSize();
|
||||
const uint8_t *data = mem->GetMemory();
|
||||
ASSERT_TRUE(memmem(data, sz, &defaced, sizeof(defaced)) != nullptr);
|
||||
}
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that a binary with a longer-than-usual build id note
|
||||
@ -229,7 +375,7 @@ TEST(MinidumpWriterTest, BuildIDLong) {
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
@ -260,6 +406,8 @@ TEST(MinidumpWriterTest, BuildIDLong) {
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
|
||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
||||
EXPECT_EQ(build_id, module->code_identifier());
|
||||
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that mapping info can be specified, and that it overrides
|
||||
@ -308,7 +456,7 @@ TEST(MinidumpWriterTest, MappingInfoContained) {
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
@ -354,6 +502,7 @@ TEST(MinidumpWriterTest, MappingInfoContained) {
|
||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
||||
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
TEST(MinidumpWriterTest, DeletedBinary) {
|
||||
@ -445,6 +594,8 @@ TEST(MinidumpWriterTest, DeletedBinary) {
|
||||
// which is always zero on Linux.
|
||||
module_identifier += "0";
|
||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
||||
|
||||
IGNORE_EINTR(waitpid(child_pid, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that an additional memory region can be added to the minidump.
|
||||
@ -472,7 +623,7 @@ TEST(MinidumpWriterTest, AdditionalMemory) {
|
||||
char b;
|
||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
@ -517,6 +668,7 @@ TEST(MinidumpWriterTest, AdditionalMemory) {
|
||||
|
||||
delete[] memory;
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that an invalid thread stack pointer still results in a minidump.
|
||||
@ -530,7 +682,7 @@ TEST(MinidumpWriterTest, InvalidStackPointer) {
|
||||
char b;
|
||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
syscall(__NR_exit_group);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
@ -596,6 +748,7 @@ TEST(MinidumpWriterTest, InvalidStackPointer) {
|
||||
#endif
|
||||
|
||||
close(fds[1]);
|
||||
IGNORE_EINTR(waitpid(child, nullptr, 0));
|
||||
}
|
||||
|
||||
// Test that limiting the size of the minidump works.
|
||||
@ -770,6 +923,7 @@ TEST(MinidumpWriterTest, MinidumpSizeLimit) {
|
||||
|
||||
// Kill the helper program.
|
||||
kill(child_pid, SIGKILL);
|
||||
IGNORE_EINTR(waitpid(child_pid, nullptr, 0));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -32,6 +32,7 @@
|
||||
// nlist function implemented to work on 64-bit.
|
||||
|
||||
#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
|
||||
#define CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__
|
||||
|
||||
#include <mach/machine.h>
|
||||
|
||||
|
@ -126,7 +126,7 @@ extern "C" {
|
||||
mach_msg_header_t* reply);
|
||||
|
||||
// This symbol must be visible to dlsym() - see
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
|
||||
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=345 for details.
|
||||
kern_return_t catch_exception_raise(mach_port_t target_port,
|
||||
mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
@ -356,6 +356,11 @@ bool ExceptionHandler::WriteMinidumpWithException(
|
||||
bool report_current_thread) {
|
||||
bool result = false;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
// _exit() should never be called on iOS.
|
||||
exit_after_write = false;
|
||||
#endif
|
||||
|
||||
if (directCallback_) {
|
||||
if (directCallback_(callback_context_,
|
||||
exception_type,
|
||||
@ -459,7 +464,7 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||
|
||||
kern_return_t result;
|
||||
// TODO: Handle the case where |target_behavior| has MACH_EXCEPTION_CODES
|
||||
// set. https://code.google.com/p/google-breakpad/issues/detail?id=551
|
||||
// set. https://bugs.chromium.org/p/google-breakpad/issues/detail?id=551
|
||||
switch (target_behavior) {
|
||||
case EXCEPTION_DEFAULT:
|
||||
result = exception_raise(target_port, failed_thread, task, exception,
|
||||
|
@ -68,7 +68,7 @@
|
||||
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
|
||||
F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
|
||||
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F310E8B07E800D7E813 /* dwarftests.mm */; };
|
||||
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.mm */; };
|
||||
F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.cc */; };
|
||||
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
|
||||
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
|
||||
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; };
|
||||
@ -151,7 +151,7 @@
|
||||
F9721F300E8B07E800D7E813 /* dwarftests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarftests.h; sourceTree = "<group>"; };
|
||||
F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = "<group>"; };
|
||||
F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; };
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; };
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.cc; path = ../../../common/mac/dump_syms.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
|
||||
@ -240,7 +240,7 @@
|
||||
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
|
||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
|
||||
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */,
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.cc */,
|
||||
F9721F380E8B0CFC00D7E813 /* dump_syms.h */,
|
||||
F917C4F70E03265A00F86017 /* breakpad_exc_server.c */,
|
||||
F917C4F80E03265A00F86017 /* breakpad_exc_server.h */,
|
||||
@ -597,7 +597,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */,
|
||||
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */,
|
||||
F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */,
|
||||
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
|
||||
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
|
||||
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
@ -93,7 +94,7 @@ static bool MDCallback(const char *dump_dir, const char *file_name,
|
||||
path.append(".dmp");
|
||||
|
||||
int fd = *reinterpret_cast<int*>(context);
|
||||
(void)write(fd, path.c_str(), path.length() + 1);
|
||||
IGNORE_RET(write(fd, path.c_str(), path.length() + 1));
|
||||
close(fd);
|
||||
exit(0);
|
||||
// not reached
|
||||
@ -293,7 +294,7 @@ TEST_F(ExceptionHandlerTest, DumpChildProcess) {
|
||||
|
||||
// Unblock child process
|
||||
uint8_t data = 1;
|
||||
(void)write(fds[1], &data, 1);
|
||||
IGNORE_RET(write(fds[1], &data, 1));
|
||||
|
||||
// Child process should have exited with a zero status.
|
||||
int ret;
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "client/mac/tests/spawn_child_process.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
@ -190,7 +191,7 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
|
||||
|
||||
// Unblock child process
|
||||
uint8_t data = 1;
|
||||
(void)write(fds[1], &data, 1);
|
||||
IGNORE_RET(write(fds[1], &data, 1));
|
||||
|
||||
// Child process should have exited with a zero status.
|
||||
int ret;
|
||||
|
@ -968,9 +968,20 @@ bool CrashGenerationServer::GenerateDump(const ClientInfo& client,
|
||||
dump_generator.SetCallback(&callback);
|
||||
}
|
||||
|
||||
if (!dump_generator.GenerateDumpFile(dump_path)) {
|
||||
if (!dump_generator.GenerateDumpFile(dump_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the client requests a full memory dump, we will write a normal mini
|
||||
// dump and a full memory dump. Both dump files use the same uuid as file
|
||||
// name prefix.
|
||||
if (client.dump_type() & MiniDumpWithFullMemory) {
|
||||
std::wstring full_dump_path;
|
||||
if (!dump_generator.GenerateFullDumpFile(&full_dump_path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return dump_generator.WriteMinidump();
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ bool HandleTraceData::CollectHandleData(
|
||||
stream_data->Reserved = 0;
|
||||
std::copy(operations_.begin(),
|
||||
operations_.end(),
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER) && !defined(_LIBCPP_STD_VER)
|
||||
stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>(
|
||||
reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1),
|
||||
operations_.size())
|
||||
@ -271,12 +271,14 @@ MinidumpGenerator::MinidumpGenerator(
|
||||
dump_type_(dump_type),
|
||||
is_client_pointers_(is_client_pointers),
|
||||
dump_path_(dump_path),
|
||||
uuid_generated_(false),
|
||||
dump_file_(INVALID_HANDLE_VALUE),
|
||||
full_dump_file_(INVALID_HANDLE_VALUE),
|
||||
dump_file_is_internal_(false),
|
||||
full_dump_file_is_internal_(false),
|
||||
additional_streams_(NULL),
|
||||
callback_info_(NULL) {
|
||||
uuid_ = {0};
|
||||
InitializeCriticalSection(&module_load_sync_);
|
||||
InitializeCriticalSection(&get_proc_address_sync_);
|
||||
}
|
||||
@ -562,15 +564,17 @@ MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() {
|
||||
}
|
||||
|
||||
bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
|
||||
UUID id = {0};
|
||||
if (!uuid_generated_) {
|
||||
UuidCreateType create_uuid = GetCreateUuid();
|
||||
if (!create_uuid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UuidCreateType create_uuid = GetCreateUuid();
|
||||
if (!create_uuid) {
|
||||
return false;
|
||||
create_uuid(&uuid_);
|
||||
uuid_generated_ = true;
|
||||
}
|
||||
|
||||
create_uuid(&id);
|
||||
wstring id_str = GUIDString::GUIDToWString(&id);
|
||||
wstring id_str = GUIDString::GUIDToWString(&uuid_);
|
||||
|
||||
*file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp");
|
||||
return true;
|
||||
|
@ -168,6 +168,10 @@ class MinidumpGenerator {
|
||||
// Folder path to store dump files.
|
||||
std::wstring dump_path_;
|
||||
|
||||
// UUID used to make dump file names.
|
||||
UUID uuid_;
|
||||
bool uuid_generated_;
|
||||
|
||||
// The file where the dump will be written.
|
||||
HANDLE dump_file_;
|
||||
|
||||
|
@ -41,6 +41,11 @@
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// This define is new to Windows 10.
|
||||
#ifndef DBG_PRINTEXCEPTION_WIDE_C
|
||||
#define DBG_PRINTEXCEPTION_WIDE_C ((DWORD)0x4001000A)
|
||||
#endif
|
||||
|
||||
vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
|
||||
LONG ExceptionHandler::handler_stack_index_ = 0;
|
||||
CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
|
||||
@ -473,7 +478,9 @@ LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) {
|
||||
DWORD code = exinfo->ExceptionRecord->ExceptionCode;
|
||||
LONG action;
|
||||
bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) ||
|
||||
(code == EXCEPTION_SINGLE_STEP);
|
||||
(code == EXCEPTION_SINGLE_STEP) ||
|
||||
(code == DBG_PRINTEXCEPTION_C) ||
|
||||
(code == DBG_PRINTEXCEPTION_WIDE_C);
|
||||
|
||||
if (code == EXCEPTION_INVALID_HANDLE &&
|
||||
current_handler->consume_invalid_handle_exceptions_) {
|
||||
|
@ -28,9 +28,7 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/windows/crash_generation/crash_generation_server.h"
|
||||
#include "client/windows/common/ipc_protocol.h"
|
||||
|
||||
|
@ -31,8 +31,8 @@
|
||||
#include <objbase.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/windows/unittests/dump_analysis.h" // NOLINT
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
DumpAnalysis::~DumpAnalysis() {
|
||||
if (dump_file_view_ != NULL) {
|
||||
|
@ -82,7 +82,7 @@ void ExceptionHandlerDeathTest::SetUp() {
|
||||
// The test case name is exposed as a c-style string,
|
||||
// convert it to a wchar_t string.
|
||||
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
|
||||
strlen(test_info->name()),
|
||||
static_cast<int>(strlen(test_info->name())),
|
||||
test_name_wide,
|
||||
MAX_PATH);
|
||||
if (!dwRet) {
|
||||
@ -293,8 +293,8 @@ wstring find_minidump_in_directory(const wstring &directory) {
|
||||
wstring filename;
|
||||
do {
|
||||
const wchar_t extension[] = L".dmp";
|
||||
const int extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
|
||||
const int filename_length = wcslen(find_data.cFileName);
|
||||
const size_t extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
|
||||
const size_t filename_length = wcslen(find_data.cFileName);
|
||||
if (filename_length > extension_length &&
|
||||
wcsncmp(extension,
|
||||
find_data.cFileName + filename_length - extension_length,
|
||||
@ -357,8 +357,8 @@ TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) {
|
||||
|
||||
// Read the minidump. Locate the exception record and the
|
||||
// memory list, and then ensure that there is a memory region
|
||||
// in the memory list that covers the instruction pointer from
|
||||
// the exception record.
|
||||
// in the memory list that covers at least 128 bytes on either
|
||||
// side of the instruction pointer from the exception record.
|
||||
{
|
||||
Minidump minidump(minidump_filename);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
@ -379,18 +379,23 @@ TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) {
|
||||
memory_list->GetMemoryRegionForAddress(instruction_pointer);
|
||||
ASSERT_TRUE(region);
|
||||
|
||||
EXPECT_EQ(kMemorySize, region->GetSize());
|
||||
EXPECT_LE(kMemorySize, region->GetSize());
|
||||
const uint8_t* bytes = region->GetMemory();
|
||||
ASSERT_TRUE(bytes);
|
||||
|
||||
uint64_t ip_offset = instruction_pointer - region->GetBase();
|
||||
EXPECT_GE(region->GetSize() - kOffset, ip_offset);
|
||||
EXPECT_LE(kOffset, ip_offset);
|
||||
|
||||
uint8_t prefix_bytes[kOffset];
|
||||
uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
|
||||
memset(prefix_bytes, 0, sizeof(prefix_bytes));
|
||||
memset(suffix_bytes, 0, sizeof(suffix_bytes));
|
||||
EXPECT_EQ(0, memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)));
|
||||
EXPECT_EQ(0, memcmp(bytes + kOffset, instructions, sizeof(instructions)));
|
||||
EXPECT_EQ(0, memcmp(bytes + kOffset + sizeof(instructions),
|
||||
suffix_bytes, sizeof(suffix_bytes)));
|
||||
EXPECT_EQ(0, memcmp(bytes + ip_offset - kOffset, prefix_bytes,
|
||||
sizeof(prefix_bytes)));
|
||||
EXPECT_EQ(0, memcmp(bytes + ip_offset, instructions, sizeof(instructions)));
|
||||
EXPECT_EQ(0, memcmp(bytes + ip_offset + sizeof(instructions), suffix_bytes,
|
||||
sizeof(suffix_bytes)));
|
||||
}
|
||||
|
||||
DeleteFileW(minidump_filename_wide.c_str());
|
||||
|
@ -120,7 +120,7 @@ void ExceptionHandlerTest::SetUp() {
|
||||
// THe test case name is exposed to use as a c-style string,
|
||||
// But we might be working in UNICODE here on Windows.
|
||||
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
|
||||
strlen(test_info->name()),
|
||||
static_cast<int>(strlen(test_info->name())),
|
||||
test_name_wide,
|
||||
MAX_PATH);
|
||||
if (!dwRet) {
|
||||
@ -247,6 +247,7 @@ TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) {
|
||||
EXPECT_EXIT(DoCrashInvalidParameter(), ::testing::ExitedWithCode(0), "");
|
||||
ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
|
||||
ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
|
||||
ASSERT_TRUE(DoesPathExist(full_dump_file.c_str()));
|
||||
|
||||
// Verify the dump for infos.
|
||||
DumpAnalysis mini(dump_file);
|
||||
@ -318,6 +319,7 @@ TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) {
|
||||
EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
|
||||
ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
|
||||
ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
|
||||
ASSERT_TRUE(DoesPathExist(full_dump_file.c_str()));
|
||||
|
||||
// Verify the dump for infos.
|
||||
DumpAnalysis mini(dump_file);
|
||||
|
@ -31,11 +31,10 @@
|
||||
#include <objbase.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/windows/crash_generation/minidump_generator.h"
|
||||
#include "client/windows/unittests/dump_analysis.h" // NOLINT
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Minidump with stacks, PEB, TEB, and unloaded module list.
|
||||
@ -94,7 +93,7 @@ class MinidumpTest: public testing::Test {
|
||||
STATUS_ACCESS_VIOLATION, // ExceptionCode
|
||||
0, // ExceptionFlags
|
||||
NULL, // ExceptionRecord;
|
||||
reinterpret_cast<void*>(0xCAFEBABE), // ExceptionAddress;
|
||||
reinterpret_cast<void*>(static_cast<uintptr_t>(0xCAFEBABE)), // ExceptionAddress;
|
||||
2, // NumberParameters;
|
||||
{ EXCEPTION_WRITE_FAULT, reinterpret_cast<ULONG_PTR>(this) }
|
||||
};
|
||||
|
@ -27,6 +27,7 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Ignore other VCSs.
|
||||
.repo/
|
||||
.svn/
|
||||
|
||||
# Ignore common compiled artifacts.
|
||||
@ -47,6 +48,12 @@ lib*.a
|
||||
/src/tools/linux/symupload/minidump_upload
|
||||
/src/tools/linux/symupload/sym_upload
|
||||
/src/tools/mac/dump_syms/dump_syms
|
||||
/src/tools/mac/dump_syms/dump_syms_mac
|
||||
|
||||
# Ignore unit test artifacts.
|
||||
*_unittest
|
||||
*.log
|
||||
*.trs
|
||||
|
||||
# Ignore autotools generated artifacts.
|
||||
.deps
|
||||
@ -77,7 +84,6 @@ src/Makefile
|
||||
|
||||
# Ignore directories gclient syncs.
|
||||
src/testing
|
||||
src/third_party/glog
|
||||
src/third_party/lss
|
||||
src/third_party/protobuf
|
||||
src/tools/gyp
|
||||
|
@ -33,18 +33,10 @@
|
||||
# 'gclient runhooks' rather than 'gclient sync'.
|
||||
|
||||
deps = {
|
||||
# Logging code.
|
||||
"src/src/third_party/glog":
|
||||
"https://github.com/google/glog.git" +
|
||||
"@v0.3.4",
|
||||
|
||||
# Testing libraries and utilities.
|
||||
"src/src/testing":
|
||||
"https://github.com/google/googlemock.git" +
|
||||
"@release-1.7.0",
|
||||
"src/src/testing/gtest":
|
||||
"https://github.com/google/googletest.git" +
|
||||
"@release-1.7.0",
|
||||
"@release-1.8.0",
|
||||
|
||||
# Protobuf.
|
||||
"src/src/third_party/protobuf/protobuf":
|
||||
@ -54,22 +46,39 @@ deps = {
|
||||
# GYP project generator.
|
||||
"src/src/tools/gyp":
|
||||
"https://chromium.googlesource.com/external/gyp/" +
|
||||
"@e8ab0833a42691cd2184bd4c45d779e43821d3e0",
|
||||
"@324dd166b7c0b39d513026fa52d6280ac6d56770",
|
||||
|
||||
# Linux syscall support.
|
||||
"src/src/third_party/lss":
|
||||
"https://chromium.googlesource.com/linux-syscall-support/" +
|
||||
"@3f6478ac95edf86cd3da300c2c0d34a438f5dbeb",
|
||||
"@e6527b0cd469e3ff5764785dadcb39bf7d787154",
|
||||
}
|
||||
|
||||
hooks = [
|
||||
{
|
||||
# TODO(chrisha): Fix the GYP files so that they work without
|
||||
# --no-circular-check.
|
||||
"pattern": ".",
|
||||
"action": ["python",
|
||||
"src/src/tools/gyp/gyp_main.py",
|
||||
"--no-circular-check",
|
||||
"src/src/client/windows/breakpad_client.gyp"],
|
||||
# Keep the manifest up to date.
|
||||
"action": ["python", "src/src/tools/python/deps-to-manifest.py",
|
||||
"src/DEPS", "src/default.xml"],
|
||||
},
|
||||
]
|
||||
|
||||
hooks_os = {
|
||||
'win': [
|
||||
{
|
||||
# TODO(chrisha): Fix the GYP files so that they work without
|
||||
# --no-circular-check.
|
||||
"pattern": ".",
|
||||
"action": ["python",
|
||||
"src/src/tools/gyp/gyp_main.py",
|
||||
"--no-circular-check",
|
||||
"src/src/client/windows/breakpad_client.gyp"],
|
||||
},
|
||||
{
|
||||
# XXX: this and above should all be wired into build/all.gyp ?
|
||||
"action": ["python",
|
||||
"src/src/tools/gyp/gyp_main.py",
|
||||
"--no-circular-check",
|
||||
"src/src/tools/windows/tools_windows.gyp"],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
704f41ec901c419f8c321742114b415e6f5ceacc
|
||||
69c2c51dd89965d234eec16e3a9353634831916b
|
||||
|
@ -43,17 +43,7 @@ AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/include
|
||||
AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/testing/include
|
||||
endif
|
||||
|
||||
if GCC
|
||||
# These are good warnings to be treated as errors
|
||||
AM_CXXFLAGS += \
|
||||
-Werror=missing-braces \
|
||||
-Werror=non-virtual-dtor \
|
||||
-Werror=overloaded-virtual \
|
||||
-Werror=reorder \
|
||||
-Werror=sign-compare \
|
||||
-Werror=unused-variable \
|
||||
-Werror=vla
|
||||
endif
|
||||
AM_CXXFLAGS += $(WARN_CXXFLAGS)
|
||||
|
||||
if LINUX_HOST
|
||||
# Build as PIC on Linux, for linux_client_unittest_shlib
|
||||
@ -120,8 +110,10 @@ TEST_DEPS =
|
||||
else
|
||||
TEST_CFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing/googletest/include \
|
||||
-I$(top_srcdir)/src/testing/googletest \
|
||||
-I$(top_srcdir)/src/testing/googlemock/include \
|
||||
-I$(top_srcdir)/src/testing/googlemock \
|
||||
-I$(top_srcdir)/src/testing
|
||||
TEST_LIBS = src/testing/libtesting.a
|
||||
TEST_DEPS = $(TEST_LIBS)
|
||||
@ -141,9 +133,9 @@ check_LIBRARIES += src/testing/libtesting.a
|
||||
if !SYSTEM_TEST_LIBS
|
||||
src_testing_libtesting_a_SOURCES = \
|
||||
src/breakpad_googletest_includes.h \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src/testing/googletest/src/gtest-all.cc \
|
||||
src/testing/googletest/src/gtest_main.cc \
|
||||
src/testing/googlemock/src/gmock-all.cc
|
||||
src_testing_libtesting_a_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) $(TEST_CFLAGS)
|
||||
endif
|
||||
@ -414,7 +406,8 @@ CLEANFILES += \
|
||||
src/client/linux/linux_client_unittest_shlib
|
||||
|
||||
check_PROGRAMS += \
|
||||
src/client/linux/linux_client_unittest
|
||||
src/client/linux/linux_client_unittest \
|
||||
src/common/linux/google_crashdump_uploader_test
|
||||
|
||||
if !DISABLE_TOOLS
|
||||
check_PROGRAMS += \
|
||||
@ -453,8 +446,12 @@ if ANDROID_HOST
|
||||
LOG_DRIVER = $(top_srcdir)/android/test-driver
|
||||
else
|
||||
# The default Autotools test driver script.
|
||||
if TESTS_AS_ROOT
|
||||
LOG_DRIVER = $(top_srcdir)/autotools/root-test-driver $(top_srcdir)/autotools/test-driver
|
||||
else
|
||||
LOG_DRIVER = $(top_srcdir)/autotools/test-driver
|
||||
endif
|
||||
endif !TESTS_AS_ROOT
|
||||
endif !ANDROID_HOST
|
||||
|
||||
if LINUX_HOST
|
||||
src_client_linux_linux_dumper_unittest_helper_SOURCES = \
|
||||
@ -576,6 +573,7 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \
|
||||
src/common/dwarf_line_to_module.cc \
|
||||
src/common/language.cc \
|
||||
src/common/module.cc \
|
||||
src/common/path_helper.cc \
|
||||
src/common/stabs_reader.cc \
|
||||
src/common/stabs_to_module.cc \
|
||||
src/common/dwarf/bytereader.cc \
|
||||
@ -593,9 +591,14 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \
|
||||
src/common/linux/memory_mapped_file.cc \
|
||||
src/common/linux/safe_readlink.cc \
|
||||
src/tools/linux/dump_syms/dump_syms.cc
|
||||
src_tools_linux_dump_syms_dump_syms_CXXFLAGS = \
|
||||
$(RUST_DEMANGLE_CFLAGS)
|
||||
src_tools_linux_dump_syms_dump_syms_LDADD = \
|
||||
$(RUST_DEMANGLE_LIBS)
|
||||
|
||||
src_tools_linux_md2core_minidump_2_core_SOURCES = \
|
||||
src/common/linux/memory_mapped_file.cc \
|
||||
src/common/path_helper.cc \
|
||||
src/tools/linux/md2core/minidump-2-core.cc \
|
||||
src/tools/linux/md2core/minidump_memory_range.h
|
||||
|
||||
@ -619,6 +622,7 @@ src_tools_mac_dump_syms_dump_syms_mac_SOURCES = \
|
||||
src/common/language.cc \
|
||||
src/common/md5.cc \
|
||||
src/common/module.cc \
|
||||
src/common/path_helper.cc \
|
||||
src/common/stabs_reader.cc \
|
||||
src/common/stabs_to_module.cc \
|
||||
src/common/dwarf/bytereader.cc \
|
||||
@ -641,7 +645,10 @@ src_tools_mac_dump_syms_dump_syms_mac_SOURCES = \
|
||||
src/tools/mac/dump_syms/dump_syms_tool.cc
|
||||
src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS= \
|
||||
-I$(top_srcdir)/src/third_party/mac_headers \
|
||||
$(RUST_DEMANGLE_CFLAGS) \
|
||||
-DHAVE_MACH_O_NLIST_H
|
||||
src_tools_mac_dump_syms_dump_syms_mac_LDADD= \
|
||||
$(RUST_DEMANGLE_LIBS)
|
||||
|
||||
src_common_dumper_unittest_SOURCES = \
|
||||
src/common/byte_cursor_unittest.cc \
|
||||
@ -655,6 +662,7 @@ src_common_dumper_unittest_SOURCES = \
|
||||
src/common/memory_range_unittest.cc \
|
||||
src/common/module.cc \
|
||||
src/common/module_unittest.cc \
|
||||
src/common/path_helper.cc \
|
||||
src/common/stabs_reader.cc \
|
||||
src/common/stabs_reader_unittest.cc \
|
||||
src/common/stabs_to_module.cc \
|
||||
@ -698,9 +706,11 @@ src_common_dumper_unittest_SOURCES = \
|
||||
src/common/tests/file_utils.cc
|
||||
src_common_dumper_unittest_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) $(TEST_CFLAGS) \
|
||||
$(RUST_DEMANGLE_CFLAGS) \
|
||||
$(PTHREAD_CFLAGS)
|
||||
src_common_dumper_unittest_LDADD = \
|
||||
$(TEST_LIBS) \
|
||||
$(RUST_DEMANGLE_LIBS) \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_common_mac_macho_reader_unittest_SOURCES = \
|
||||
@ -710,6 +720,7 @@ src_common_mac_macho_reader_unittest_SOURCES = \
|
||||
src/common/language.cc \
|
||||
src/common/md5.cc \
|
||||
src/common/module.cc \
|
||||
src/common/path_helper.cc \
|
||||
src/common/stabs_reader.cc \
|
||||
src/common/stabs_to_module.cc \
|
||||
src/common/test_assembler.cc \
|
||||
@ -736,6 +747,17 @@ src_common_mac_macho_reader_unittest_LDADD = \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
endif
|
||||
|
||||
src_common_linux_google_crashdump_uploader_test_SOURCES = \
|
||||
src/common/linux/google_crashdump_uploader.cc \
|
||||
src/common/linux/google_crashdump_uploader_test.cc \
|
||||
src/common/linux/libcurl_wrapper.cc
|
||||
src_common_linux_google_crashdump_uploader_test_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) $(TEST_CFLAGS)
|
||||
src_common_linux_google_crashdump_uploader_test_LDADD = \
|
||||
$(TEST_LIBS) \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \
|
||||
-ldl
|
||||
|
||||
src_tools_linux_md2core_minidump_2_core_unittest_SOURCES = \
|
||||
src/tools/linux/md2core/minidump_memory_range_unittest.cc
|
||||
src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS = \
|
||||
@ -1174,6 +1196,7 @@ src_processor_minidump_dump_LDADD = \
|
||||
src_processor_microdump_stackwalk_SOURCES = \
|
||||
src/processor/microdump_stackwalk.cc
|
||||
src_processor_microdump_stackwalk_LDADD = \
|
||||
src/common/path_helper.o \
|
||||
src/processor/basic_code_modules.o \
|
||||
src/processor/basic_source_line_resolver.o \
|
||||
src/processor/call_stack.o \
|
||||
@ -1207,6 +1230,7 @@ src_processor_microdump_stackwalk_LDADD = \
|
||||
src_processor_minidump_stackwalk_SOURCES = \
|
||||
src/processor/minidump_stackwalk.cc
|
||||
src_processor_minidump_stackwalk_LDADD = \
|
||||
src/common/path_helper.o \
|
||||
src/processor/basic_code_modules.o \
|
||||
src/processor/basic_source_line_resolver.o \
|
||||
src/processor/call_stack.o \
|
||||
@ -1321,6 +1345,7 @@ EXTRA_DIST = \
|
||||
src/common/windows/pdb_source_line_writer.h \
|
||||
src/common/windows/string_utils-inl.h \
|
||||
src/common/windows/string_utils.cc \
|
||||
src/processor/microdump_stackwalk_test_vars \
|
||||
src/processor/stackwalk_common.cc \
|
||||
src/processor/stackwalk_common.h \
|
||||
src/processor/stackwalker_selftest_sol.s \
|
||||
@ -1347,6 +1372,7 @@ EXTRA_DIST = \
|
||||
src/processor/testdata/linux_raise_sigabrt.dmp \
|
||||
src/processor/testdata/linux_stack_pointer_in_module.dmp \
|
||||
src/processor/testdata/linux_stack_pointer_in_stack.dmp \
|
||||
src/processor/testdata/linux_stack_pointer_in_stack_alt_name.dmp \
|
||||
src/processor/testdata/linux_stacksmash.dmp \
|
||||
src/processor/testdata/linux_write_to_nonwritable_module.dmp \
|
||||
src/processor/testdata/linux_write_to_nonwritable_region_math.dmp \
|
||||
@ -1362,6 +1388,7 @@ EXTRA_DIST = \
|
||||
src/processor/testdata/microdump.stackwalk-arm.out \
|
||||
src/processor/testdata/microdump.stackwalk.machine_readable-arm64.out \
|
||||
src/processor/testdata/microdump.stackwalk.machine_readable-arm.out \
|
||||
src/processor/testdata/microdump-withcrashreason.dmp \
|
||||
src/processor/testdata/microdump-x86.dmp \
|
||||
src/processor/testdata/minidump2.dmp \
|
||||
src/processor/testdata/minidump2.dump.out \
|
||||
@ -1391,54 +1418,61 @@ EXTRA_DIST = \
|
||||
src/processor/testdata/symbols/overflow/B0E1FC01EF48E39CAF5C881D2DF0C3840/overflow.sym \
|
||||
src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1/test_app.sym \
|
||||
src/processor/testdata/test_app.cc \
|
||||
src/testing/gtest/include/gtest/gtest.h \
|
||||
src/testing/gtest/include/gtest/gtest-death-test.h \
|
||||
src/testing/gtest/include/gtest/gtest-message.h \
|
||||
src/testing/gtest/include/gtest/gtest-param-test.h \
|
||||
src/testing/gtest/include/gtest/gtest-printers.h \
|
||||
src/testing/gtest/include/gtest/gtest-spi.h \
|
||||
src/testing/gtest/include/gtest/gtest-test-part.h \
|
||||
src/testing/gtest/include/gtest/gtest-typed-test.h \
|
||||
src/testing/gtest/include/gtest/gtest_pred_impl.h \
|
||||
src/testing/gtest/include/gtest/gtest_prod.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-death-test-internal.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-filepath.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-internal.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-linked_ptr.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-param-util-generated.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-param-util.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-port.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-string.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-tuple.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-type-util.h \
|
||||
src/testing/gtest/src/gtest.cc \
|
||||
src/testing/gtest/src/gtest-death-test.cc \
|
||||
src/testing/gtest/src/gtest-filepath.cc \
|
||||
src/testing/gtest/src/gtest-internal-inl.h \
|
||||
src/testing/gtest/src/gtest-port.cc \
|
||||
src/testing/gtest/src/gtest-printers.cc \
|
||||
src/testing/gtest/src/gtest-test-part.cc \
|
||||
src/testing/gtest/src/gtest-typed-test.cc \
|
||||
src/testing/include/gmock/gmock.h \
|
||||
src/testing/include/gmock/gmock-actions.h \
|
||||
src/testing/include/gmock/gmock-cardinalities.h \
|
||||
src/testing/include/gmock/gmock-generated-actions.h \
|
||||
src/testing/include/gmock/gmock-generated-function-mockers.h \
|
||||
src/testing/include/gmock/gmock-generated-matchers.h \
|
||||
src/testing/include/gmock/gmock-generated-nice-strict.h \
|
||||
src/testing/include/gmock/gmock-matchers.h \
|
||||
src/testing/include/gmock/gmock-more-actions.h \
|
||||
src/testing/include/gmock/gmock-more-matchers.h \
|
||||
src/testing/include/gmock/gmock-spec-builders.h \
|
||||
src/testing/include/gmock/internal/gmock-generated-internal-utils.h \
|
||||
src/testing/include/gmock/internal/gmock-internal-utils.h \
|
||||
src/testing/include/gmock/internal/gmock-port.h \
|
||||
src/testing/src/gmock.cc \
|
||||
src/testing/src/gmock-cardinalities.cc \
|
||||
src/testing/src/gmock-internal-utils.cc \
|
||||
src/testing/src/gmock-matchers.cc \
|
||||
src/testing/src/gmock-spec-builders.cc \
|
||||
src/testing/src/gmock_main.cc \
|
||||
src/testing/googletest/include/gtest/gtest.h \
|
||||
src/testing/googletest/include/gtest/gtest-death-test.h \
|
||||
src/testing/googletest/include/gtest/gtest-message.h \
|
||||
src/testing/googletest/include/gtest/gtest-param-test.h \
|
||||
src/testing/googletest/include/gtest/gtest-printers.h \
|
||||
src/testing/googletest/include/gtest/gtest-spi.h \
|
||||
src/testing/googletest/include/gtest/gtest-test-part.h \
|
||||
src/testing/googletest/include/gtest/gtest-typed-test.h \
|
||||
src/testing/googletest/include/gtest/gtest_pred_impl.h \
|
||||
src/testing/googletest/include/gtest/gtest_prod.h \
|
||||
src/testing/googletest/include/gtest/internal/custom/gtest-port.h \
|
||||
src/testing/googletest/include/gtest/internal/custom/gtest-printers.h \
|
||||
src/testing/googletest/include/gtest/internal/custom/gtest.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-death-test-internal.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-filepath.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-internal.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-linked_ptr.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-param-util-generated.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-param-util.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-port-arch.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-port.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-string.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-tuple.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-type-util.h \
|
||||
src/testing/googletest/src/gtest.cc \
|
||||
src/testing/googletest/src/gtest-death-test.cc \
|
||||
src/testing/googletest/src/gtest-filepath.cc \
|
||||
src/testing/googletest/src/gtest-internal-inl.h \
|
||||
src/testing/googletest/src/gtest-port.cc \
|
||||
src/testing/googletest/src/gtest-printers.cc \
|
||||
src/testing/googletest/src/gtest-test-part.cc \
|
||||
src/testing/googletest/src/gtest-typed-test.cc \
|
||||
src/testing/googlemock/include/gmock/gmock.h \
|
||||
src/testing/googlemock/include/gmock/gmock-actions.h \
|
||||
src/testing/googlemock/include/gmock/gmock-cardinalities.h \
|
||||
src/testing/googlemock/include/gmock/gmock-generated-actions.h \
|
||||
src/testing/googlemock/include/gmock/gmock-generated-function-mockers.h \
|
||||
src/testing/googlemock/include/gmock/gmock-generated-matchers.h \
|
||||
src/testing/googlemock/include/gmock/gmock-generated-nice-strict.h \
|
||||
src/testing/googlemock/include/gmock/gmock-matchers.h \
|
||||
src/testing/googlemock/include/gmock/gmock-more-actions.h \
|
||||
src/testing/googlemock/include/gmock/gmock-more-matchers.h \
|
||||
src/testing/googlemock/include/gmock/gmock-spec-builders.h \
|
||||
src/testing/googlemock/include/gmock/internal/custom/gmock-generated-actions.h \
|
||||
src/testing/googlemock/include/gmock/internal/custom/gmock-matchers.h \
|
||||
src/testing/googlemock/include/gmock/internal/custom/gmock-port.h \
|
||||
src/testing/googlemock/include/gmock/internal/gmock-generated-internal-utils.h \
|
||||
src/testing/googlemock/include/gmock/internal/gmock-internal-utils.h \
|
||||
src/testing/googlemock/include/gmock/internal/gmock-port.h \
|
||||
src/testing/googlemock/src/gmock.cc \
|
||||
src/testing/googlemock/src/gmock-cardinalities.cc \
|
||||
src/testing/googlemock/src/gmock-internal-utils.cc \
|
||||
src/testing/googlemock/src/gmock-matchers.cc \
|
||||
src/testing/googlemock/src/gmock-spec-builders.cc \
|
||||
src/testing/googlemock/src/gmock_main.cc \
|
||||
src/third_party/curl/COPYING \
|
||||
src/third_party/curl/curlbuild.h \
|
||||
src/third_party/curl/curl.h \
|
||||
|
@ -8,7 +8,7 @@ crash-reporting system.
|
||||
* [Bugs](https://bugs.chromium.org/p/google-breakpad/)
|
||||
* Discussion/Questions: [google-breakpad-discuss@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-discuss)
|
||||
* Developer/Reviews: [google-breakpad-dev@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-dev)
|
||||
* Tests: [![Build Status](https://travis-ci.org/google/breakpad.svg?branch=master)](https://travis-ci.org/google/breakpad)
|
||||
* Tests: [![Build Status](https://travis-ci.org/google/breakpad.svg?branch=master)](https://travis-ci.org/google/breakpad) [![Build status](https://ci.appveyor.com/api/projects/status/eguv4emv2rhq68u2?svg=true)](https://ci.appveyor.com/project/vapier/breakpad)
|
||||
* Coverage [![Coverity Status](https://scan.coverity.com/projects/9215/badge.svg)](https://scan.coverity.com/projects/google-breakpad)
|
||||
|
||||
## Getting started (from master)
|
||||
|
@ -1295,3 +1295,10 @@ AC_SUBST([am__tar])
|
||||
AC_SUBST([am__untar])
|
||||
]) # _AM_PROG_TAR
|
||||
|
||||
m4_include([m4/ax_append_compile_flags.m4])
|
||||
m4_include([m4/ax_append_flag.m4])
|
||||
m4_include([m4/ax_check_compile_flag.m4])
|
||||
m4_include([m4/ax_check_define.m4])
|
||||
m4_include([m4/ax_cxx_compile_stdcxx.m4])
|
||||
m4_include([m4/ax_pthread.m4])
|
||||
m4_include([m4/ax_require_defined.m4])
|
||||
|
@ -1,347 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2012-10-14.11; # UTC
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# We need space, tab and new line, in precisely that order. Quoting is
|
||||
# there to prevent tools from complaining about whitespace usage.
|
||||
IFS=" "" $nl"
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file lazy
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts. If the determined conversion
|
||||
# type is listed in (the comma separated) LAZY, no conversion will
|
||||
# take place.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv/,$2, in
|
||||
*,$file_conv,*)
|
||||
;;
|
||||
mingw/*)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin/*)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine/*)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_cl_dashL linkdir
|
||||
# Make cl look for libraries in LINKDIR
|
||||
func_cl_dashL ()
|
||||
{
|
||||
func_file_conv "$1"
|
||||
if test -z "$lib_path"; then
|
||||
lib_path=$file
|
||||
else
|
||||
lib_path="$lib_path;$file"
|
||||
fi
|
||||
linker_opts="$linker_opts -LIBPATH:$file"
|
||||
}
|
||||
|
||||
# func_cl_dashl library
|
||||
# Do a library search-path lookup for cl
|
||||
func_cl_dashl ()
|
||||
{
|
||||
lib=$1
|
||||
found=no
|
||||
save_IFS=$IFS
|
||||
IFS=';'
|
||||
for dir in $lib_path $LIB
|
||||
do
|
||||
IFS=$save_IFS
|
||||
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.dll.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/$lib.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/lib$lib.a"; then
|
||||
found=yes
|
||||
lib=$dir/lib$lib.a
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=$save_IFS
|
||||
|
||||
if test "$found" != yes; then
|
||||
lib=$lib.lib
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cl_wrapper cl arg...
|
||||
# Adjust compile command to suit cl
|
||||
func_cl_wrapper ()
|
||||
{
|
||||
# Assume a capable shell
|
||||
lib_path=
|
||||
shared=:
|
||||
linker_opts=
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.[oO][bB][jJ])
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fo"$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fe"$file"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-I)
|
||||
eat=1
|
||||
func_file_conv "$2" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-I*)
|
||||
func_file_conv "${1#-I}" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
eat=1
|
||||
func_cl_dashl "$2"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-l*)
|
||||
func_cl_dashl "${1#-l}"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-L)
|
||||
eat=1
|
||||
func_cl_dashL "$2"
|
||||
;;
|
||||
-L*)
|
||||
func_cl_dashL "${1#-L}"
|
||||
;;
|
||||
-static)
|
||||
shared=false
|
||||
;;
|
||||
-Wl,*)
|
||||
arg=${1#-Wl,}
|
||||
save_ifs="$IFS"; IFS=','
|
||||
for flag in $arg; do
|
||||
IFS="$save_ifs"
|
||||
linker_opts="$linker_opts $flag"
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
;;
|
||||
-Xlinker)
|
||||
eat=1
|
||||
linker_opts="$linker_opts $2"
|
||||
;;
|
||||
-*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||
func_file_conv "$1"
|
||||
set x "$@" -Tp"$file"
|
||||
shift
|
||||
;;
|
||||
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||
func_file_conv "$1" mingw
|
||||
set x "$@" "$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if test -n "$linker_opts"; then
|
||||
linker_opts="-link$linker_opts"
|
||||
fi
|
||||
exec "$@" $linker_opts
|
||||
exit 1
|
||||
}
|
||||
|
||||
eat=
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Wrapper for compilers which do not understand '-c -o'.
|
||||
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||
arguments, and rename the output as expected.
|
||||
|
||||
If you are trying to build a whole package this is not the
|
||||
right script to run: please start by reading the file 'INSTALL'.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
|
||||
ofile=
|
||||
cfile=
|
||||
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
# So we strip '-o arg' only if arg is an object.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.obj)
|
||||
ofile=$2
|
||||
;;
|
||||
*)
|
||||
set x "$@" -o "$2"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*.c)
|
||||
cfile=$1
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if test -z "$ofile" || test -z "$cfile"; then
|
||||
# If no '-o' option was seen then we might have been invoked from a
|
||||
# pattern rule where we don't need one. That is ok -- this is a
|
||||
# normal compilation that the losing compiler can handle. If no
|
||||
# '.c' file was seen then we are probably linking. That is also
|
||||
# ok.
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Name of file we expect compiler to create.
|
||||
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||
|
||||
# Create the lock directory.
|
||||
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||
# that we are using for the .o file. Also, base the name on the expected
|
||||
# object file name, since that is what matters with a parallel build.
|
||||
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||
while true; do
|
||||
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
# FIXME: race condition here if user kills between mkdir and trap.
|
||||
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||
|
||||
# Run the compile.
|
||||
"$@"
|
||||
ret=$?
|
||||
|
||||
if test -f "$cofile"; then
|
||||
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||
elif test -f "${cofile}bj"; then
|
||||
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||
fi
|
||||
|
||||
rmdir "$lockdir"
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,791 +0,0 @@
|
||||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2013-05-30.07; # UTC
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Run PROGRAMS ARGS to compile a file, generating dependencies
|
||||
as side-effects.
|
||||
|
||||
Environment variables:
|
||||
depmode Dependency tracking mode.
|
||||
source Source file read by 'PROGRAMS ARGS'.
|
||||
object Object file output by 'PROGRAMS ARGS'.
|
||||
DEPDIR directory where to store dependencies.
|
||||
depfile Dependency file to output.
|
||||
tmpdepfile Temporary file to use when outputting dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "depcomp $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
# Get the directory component of the given path, and save it in the
|
||||
# global variables '$dir'. Note that this directory component will
|
||||
# be either empty or ending with a '/' character. This is deliberate.
|
||||
set_dir_from ()
|
||||
{
|
||||
case $1 in
|
||||
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
|
||||
*) dir=;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Get the suffix-stripped basename of the given path, and save it the
|
||||
# global variable '$base'.
|
||||
set_base_from ()
|
||||
{
|
||||
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
|
||||
}
|
||||
|
||||
# If no dependency file was actually created by the compiler invocation,
|
||||
# we still have to create a dummy depfile, to avoid errors with the
|
||||
# Makefile "include basename.Plo" scheme.
|
||||
make_dummy_depfile ()
|
||||
{
|
||||
echo "#dummy" > "$depfile"
|
||||
}
|
||||
|
||||
# Factor out some common post-processing of the generated depfile.
|
||||
# Requires the auxiliary global variable '$tmpdepfile' to be set.
|
||||
aix_post_process_depfile ()
|
||||
{
|
||||
# If the compiler actually managed to produce a dependency file,
|
||||
# post-process it.
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form 'foo.o: dependency.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# $object: dependency.h
|
||||
# and one to simply output
|
||||
# dependency.h:
|
||||
# which is needed to avoid the deleted-header problem.
|
||||
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
|
||||
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
|
||||
} > "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
fi
|
||||
}
|
||||
|
||||
# A tabulation character.
|
||||
tab=' '
|
||||
# A newline character.
|
||||
nl='
|
||||
'
|
||||
# Character ranges might be problematic outside the C locale.
|
||||
# These definitions help.
|
||||
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
lower=abcdefghijklmnopqrstuvwxyz
|
||||
digits=0123456789
|
||||
alpha=${upper}${lower}
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
||||
depfile=${depfile-`echo "$object" |
|
||||
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
||||
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Avoid interferences from the environment.
|
||||
gccflag= dashmflag=
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
# here, because this file can only contain one case statement.
|
||||
if test "$depmode" = hp; then
|
||||
# HP compiler uses -M and no extra arg.
|
||||
gccflag=-M
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
cygpath_u="cygpath -u -f -"
|
||||
if test "$depmode" = msvcmsys; then
|
||||
# This is just like msvisualcpp but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvisualcpp
|
||||
fi
|
||||
|
||||
if test "$depmode" = msvc7msys; then
|
||||
# This is just like msvc7 but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvc7
|
||||
fi
|
||||
|
||||
if test "$depmode" = xlc; then
|
||||
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
|
||||
gccflag=-qmakedep=gcc,-MF
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
gcc3)
|
||||
## gcc 3 implements dependency tracking that does exactly what
|
||||
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
||||
## the command line argument order; so add the flags where they
|
||||
## appear in depend2.am. Note that the slowdown incurred here
|
||||
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
||||
*) set fnord "$@" "$arg" ;;
|
||||
esac
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
done
|
||||
"$@"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
mv "$tmpdepfile" "$depfile"
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
|
||||
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
|
||||
## (see the conditional assignment to $gccflag above).
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say). Also, it might not be
|
||||
## supported by the other compilers which use the 'gcc' depmode.
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
gccflag=-MD,
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The second -e expression handles DOS-style file names with drive
|
||||
# letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the "deleted header file" problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
## Some versions of gcc put a space before the ':'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||
## to the object. Take care to not repeat it in the output.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
sgi)
|
||||
if test "$libtool" = yes; then
|
||||
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||
else
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
|
||||
| tr "$nl" ' ' >> "$depfile"
|
||||
echo >> "$depfile"
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> "$depfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
xlc)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. In older versions, this file always lives in the
|
||||
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||
# start of each line; $object doesn't have directory information.
|
||||
# Version 6 uses the directory in both cases.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$base.u
|
||||
tmpdepfile3=$dir.libs/$base.u
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$dir$base.u
|
||||
tmpdepfile3=$dir$base.u
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
aix_post_process_depfile
|
||||
;;
|
||||
|
||||
tcc)
|
||||
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
|
||||
# FIXME: That version still under development at the moment of writing.
|
||||
# Make that this statement remains true also for stable, released
|
||||
# versions.
|
||||
# It will wrap lines (doesn't matter whether long or short) with a
|
||||
# trailing '\', as in:
|
||||
#
|
||||
# foo.o : \
|
||||
# foo.c \
|
||||
# foo.h \
|
||||
#
|
||||
# It will put a trailing '\' even on the last line, and will use leading
|
||||
# spaces rather than leading tabs (at least since its commit 0394caf7
|
||||
# "Emit spaces for -MD").
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
|
||||
# We have to change lines of the first kind to '$object: \'.
|
||||
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
|
||||
# And for each line of the second kind, we have to emit a 'dep.h:'
|
||||
# dummy dependency, to avoid the deleted-header problem.
|
||||
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
## The order of this option in the case statement is important, since the
|
||||
## shell code in configure will try each of these formats in the order
|
||||
## listed in this file. A plain '-MD' option would be understood by many
|
||||
## compilers, so we must ensure this comes after the gcc and icc options.
|
||||
pgcc)
|
||||
# Portland's C compiler understands '-MD'.
|
||||
# Will always output deps to 'file.d' where file is the root name of the
|
||||
# source file under compilation, even if file resides in a subdirectory.
|
||||
# The object file name does not affect the name of the '.d' file.
|
||||
# pgcc 10.2 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using '\' :
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
set_dir_from "$object"
|
||||
# Use the source, not the object, to determine the base name, since
|
||||
# that's sadly what pgcc will do too.
|
||||
set_base_from "$source"
|
||||
tmpdepfile=$base.d
|
||||
|
||||
# For projects that build the same source file twice into different object
|
||||
# files, the pgcc approach of using the *source* file root name can cause
|
||||
# problems in parallel builds. Use a locking strategy to avoid stomping on
|
||||
# the same $tmpdepfile.
|
||||
lockdir=$base.d-lock
|
||||
trap "
|
||||
echo '$0: caught signal, cleaning up...' >&2
|
||||
rmdir '$lockdir'
|
||||
exit 1
|
||||
" 1 2 13 15
|
||||
numtries=100
|
||||
i=$numtries
|
||||
while test $i -gt 0; do
|
||||
# mkdir is a portable test-and-set.
|
||||
if mkdir "$lockdir" 2>/dev/null; then
|
||||
# This process acquired the lock.
|
||||
"$@" -MD
|
||||
stat=$?
|
||||
# Release the lock.
|
||||
rmdir "$lockdir"
|
||||
break
|
||||
else
|
||||
# If the lock is being held by a different process, wait
|
||||
# until the winning process is done or we timeout.
|
||||
while test -d "$lockdir" && test $i -gt 0; do
|
||||
sleep 1
|
||||
i=`expr $i - 1`
|
||||
done
|
||||
fi
|
||||
i=`expr $i - 1`
|
||||
done
|
||||
trap - 1 2 13 15
|
||||
if test $i -le 0; then
|
||||
echo "$0: failed to acquire lock after $numtries attempts" >&2
|
||||
echo "$0: check lockdir '$lockdir'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each line is of the form `foo.o: dependent.h',
|
||||
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
|
||||
# Do two passes, one to just change these to
|
||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
||||
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp2)
|
||||
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
||||
# compilers, which have integrated preprocessors. The correct option
|
||||
# to use with these is +Maked; it writes dependencies to a file named
|
||||
# 'foo.d', which lands next to the object file, wherever that
|
||||
# happens to be.
|
||||
# Much of this is similar to the tru64 case; see comments there.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir.libs/$base.d
|
||||
"$@" -Wc,+Maked
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
"$@" +Maked
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add 'dependent.h:' lines.
|
||||
sed -ne '2,${
|
||||
s/^ *//
|
||||
s/ \\*$//
|
||||
s/$/:/
|
||||
p
|
||||
}' "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
make_dummy_depfile
|
||||
fi
|
||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in 'foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
set_dir_from "$object"
|
||||
set_base_from "$object"
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# Libtool generates 2 separate objects for the 2 libraries. These
|
||||
# two compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
|
||||
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
# Same post-processing that is required for AIX mode.
|
||||
aix_post_process_depfile
|
||||
;;
|
||||
|
||||
msvc7)
|
||||
if test "$libtool" = yes; then
|
||||
showIncludes=-Wc,-showIncludes
|
||||
else
|
||||
showIncludes=-showIncludes
|
||||
fi
|
||||
"$@" $showIncludes > "$tmpdepfile"
|
||||
stat=$?
|
||||
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||
if test $stat -ne 0; then
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The first sed program below extracts the file names and escapes
|
||||
# backslashes for cygpath. The second sed program outputs the file
|
||||
# name when reading, but also accumulates all include files in the
|
||||
# hold buffer in order to output them again at the end. This only
|
||||
# works with sed implementations that can handle large buffers.
|
||||
sed < "$tmpdepfile" -n '
|
||||
/^Note: including file: *\(.*\)/ {
|
||||
s//\1/
|
||||
s/\\/\\\\/g
|
||||
p
|
||||
}' | $cygpath_u | sort -u | sed -n '
|
||||
s/ /\\ /g
|
||||
s/\(.*\)/'"$tab"'\1 \\/p
|
||||
s/.\(.*\) \\/\1:/
|
||||
H
|
||||
$ {
|
||||
s/.*/'"$tab"'/
|
||||
G
|
||||
p
|
||||
}' >> "$depfile"
|
||||
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7msys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
# dependency tracking mechanisms from slower ones.
|
||||
|
||||
dashmstdout)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout, regardless of -o.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
# Require at least two characters before searching for ':'
|
||||
# in the target name. This is to cope with DOS-style filenames:
|
||||
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||
"$@" $dashmflag |
|
||||
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process this sed invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
dashXmstdout)
|
||||
# This case only exists to satisfy depend.m4. It is never actually
|
||||
# run, as this mode is specially recognized in the preamble.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
makedepend)
|
||||
"$@" || exit $?
|
||||
# Remove any Libtool call
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no eat=no
|
||||
for arg
|
||||
do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
if test $eat = yes; then
|
||||
eat=no
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
# Strip any option that makedepend may not understand. Remove
|
||||
# the object too, otherwise makedepend will parse it as a source file.
|
||||
-arch)
|
||||
eat=yes ;;
|
||||
-*|$object)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
esac
|
||||
done
|
||||
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
rm -f "$depfile"
|
||||
# makedepend may prepend the VPATH from the source file name to the object.
|
||||
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||
# Some versions of the HPUX 10.20 sed can't process the last invocation
|
||||
# correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed '1,2d' "$tmpdepfile" \
|
||||
| tr ' ' "$nl" \
|
||||
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
cpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
"$@" -E \
|
||||
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
| sed '$ s: \\$::' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E 2>/dev/null |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||
echo "$tab" >> "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvcmsys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown depmode $depmode" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
@ -1,501 +0,0 @@
|
||||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2013-12-25.23; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# 'make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
tab=' '
|
||||
nl='
|
||||
'
|
||||
IFS=" $tab$nl"
|
||||
|
||||
# Set DOITPROG to "echo" to test this script.
|
||||
|
||||
doit=${DOITPROG-}
|
||||
doit_exec=${doit:-exec}
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
is_target_a_directory=possibly
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-t)
|
||||
is_target_a_directory=always
|
||||
dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) is_target_a_directory=never;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# We allow the use of options -d and -T together, by making -d
|
||||
# take the precedence; this is for compatibility with GNU install.
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
echo "$0: target directory not allowed when installing a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call 'install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||
if test ! -d "$dst_arg"; then
|
||||
echo "$0: $dst_arg: Is not a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test "$is_target_a_directory" = never; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
dstdir=`dirname "$dst"`
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=$dstdir/_inst.$$_
|
||||
rmtmp=$dstdir/_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
set +f &&
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
File diff suppressed because it is too large
Load Diff
@ -1,215 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Common wrapper for a few potentially missing GNU programs.
|
||||
|
||||
scriptversion=2013-10-28.13; # UTC
|
||||
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try '$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case $1 in
|
||||
|
||||
--is-lightweight)
|
||||
# Used by our autoconf macros to check whether the available missing
|
||||
# script is modern enough.
|
||||
exit 0
|
||||
;;
|
||||
|
||||
--run)
|
||||
# Back-compat with the calling convention used by older automake.
|
||||
shift
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
|
||||
to PROGRAM being missing or too old.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal autoconf autoheader autom4te automake makeinfo
|
||||
bison yacc flex lex help2man
|
||||
|
||||
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
|
||||
'g' are ignored when checking the name.
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "missing $scriptversion (GNU Automake)"
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: unknown '$1' option"
|
||||
echo 1>&2 "Try '$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# Run the given program, remember its exit status.
|
||||
"$@"; st=$?
|
||||
|
||||
# If it succeeded, we are done.
|
||||
test $st -eq 0 && exit 0
|
||||
|
||||
# Also exit now if we it failed (or wasn't found), and '--version' was
|
||||
# passed; such an option is passed most likely to detect whether the
|
||||
# program is present and works.
|
||||
case $2 in --version|--help) exit $st;; esac
|
||||
|
||||
# Exit code 63 means version mismatch. This often happens when the user
|
||||
# tries to use an ancient version of a tool on a file that requires a
|
||||
# minimum version.
|
||||
if test $st -eq 63; then
|
||||
msg="probably too old"
|
||||
elif test $st -eq 127; then
|
||||
# Program was missing.
|
||||
msg="missing on your system"
|
||||
else
|
||||
# Program was found and executed, but failed. Give up.
|
||||
exit $st
|
||||
fi
|
||||
|
||||
perl_URL=http://www.perl.org/
|
||||
flex_URL=http://flex.sourceforge.net/
|
||||
gnu_software_URL=http://www.gnu.org/software
|
||||
|
||||
program_details ()
|
||||
{
|
||||
case $1 in
|
||||
aclocal|automake)
|
||||
echo "The '$1' program is part of the GNU Automake package:"
|
||||
echo "<$gnu_software_URL/automake>"
|
||||
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
|
||||
echo "<$gnu_software_URL/autoconf>"
|
||||
echo "<$gnu_software_URL/m4/>"
|
||||
echo "<$perl_URL>"
|
||||
;;
|
||||
autoconf|autom4te|autoheader)
|
||||
echo "The '$1' program is part of the GNU Autoconf package:"
|
||||
echo "<$gnu_software_URL/autoconf/>"
|
||||
echo "It also requires GNU m4 and Perl in order to run:"
|
||||
echo "<$gnu_software_URL/m4/>"
|
||||
echo "<$perl_URL>"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
give_advice ()
|
||||
{
|
||||
# Normalize program name to check for.
|
||||
normalized_program=`echo "$1" | sed '
|
||||
s/^gnu-//; t
|
||||
s/^gnu//; t
|
||||
s/^g//; t'`
|
||||
|
||||
printf '%s\n' "'$1' is $msg."
|
||||
|
||||
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
|
||||
case $normalized_program in
|
||||
autoconf*)
|
||||
echo "You should only need it if you modified 'configure.ac',"
|
||||
echo "or m4 files included by it."
|
||||
program_details 'autoconf'
|
||||
;;
|
||||
autoheader*)
|
||||
echo "You should only need it if you modified 'acconfig.h' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'autoheader'
|
||||
;;
|
||||
automake*)
|
||||
echo "You should only need it if you modified 'Makefile.am' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'automake'
|
||||
;;
|
||||
aclocal*)
|
||||
echo "You should only need it if you modified 'acinclude.m4' or"
|
||||
echo "$configure_deps."
|
||||
program_details 'aclocal'
|
||||
;;
|
||||
autom4te*)
|
||||
echo "You might have modified some maintainer files that require"
|
||||
echo "the 'autom4te' program to be rebuilt."
|
||||
program_details 'autom4te'
|
||||
;;
|
||||
bison*|yacc*)
|
||||
echo "You should only need it if you modified a '.y' file."
|
||||
echo "You may want to install the GNU Bison package:"
|
||||
echo "<$gnu_software_URL/bison/>"
|
||||
;;
|
||||
lex*|flex*)
|
||||
echo "You should only need it if you modified a '.l' file."
|
||||
echo "You may want to install the Fast Lexical Analyzer package:"
|
||||
echo "<$flex_URL>"
|
||||
;;
|
||||
help2man*)
|
||||
echo "You should only need it if you modified a dependency" \
|
||||
"of a man page."
|
||||
echo "You may want to install the GNU Help2man package:"
|
||||
echo "<$gnu_software_URL/help2man/>"
|
||||
;;
|
||||
makeinfo*)
|
||||
echo "You should only need it if you modified a '.texi' file, or"
|
||||
echo "any other file indirectly affecting the aspect of the manual."
|
||||
echo "You might want to install the Texinfo package:"
|
||||
echo "<$gnu_software_URL/texinfo/>"
|
||||
echo "The spurious makeinfo call might also be the consequence of"
|
||||
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
|
||||
echo "want to install GNU make:"
|
||||
echo "<$gnu_software_URL/make/>"
|
||||
;;
|
||||
*)
|
||||
echo "You might have modified some files without having the proper"
|
||||
echo "tools for further handling them. Check the 'README' file, it"
|
||||
echo "often tells you about the needed prerequisites for installing"
|
||||
echo "this package. You may also peek at any GNU archive site, in"
|
||||
echo "case some other package contains this missing '$1' program."
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
give_advice "$1" | sed -e '1s/^/WARNING: /' \
|
||||
-e '2,$s/^/ /' >&2
|
||||
|
||||
# Propagate the correct exit status (expected to be 127 for a program
|
||||
# not found, 63 for a program that failed due to version mismatch).
|
||||
exit $st
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
@ -1,148 +0,0 @@
|
||||
#! /bin/sh
|
||||
# test-driver - basic testsuite driver script.
|
||||
|
||||
scriptversion=2013-07-13.22; # UTC
|
||||
|
||||
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
# Make unconditional expansion of undefined variables an error. This
|
||||
# helps a lot in preventing typo-related bugs.
|
||||
set -u
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$0: $*" >&2
|
||||
print_usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
print_usage ()
|
||||
{
|
||||
cat <<END
|
||||
Usage:
|
||||
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--]
|
||||
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
|
||||
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||
END
|
||||
}
|
||||
|
||||
test_name= # Used for reporting.
|
||||
log_file= # Where to save the output of the test script.
|
||||
trs_file= # Where to save the metadata of the test run.
|
||||
expect_failure=no
|
||||
color_tests=no
|
||||
enable_hard_errors=yes
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) print_usage; exit $?;;
|
||||
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||
--test-name) test_name=$2; shift;;
|
||||
--log-file) log_file=$2; shift;;
|
||||
--trs-file) trs_file=$2; shift;;
|
||||
--color-tests) color_tests=$2; shift;;
|
||||
--expect-failure) expect_failure=$2; shift;;
|
||||
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||
--) shift; break;;
|
||||
-*) usage_error "invalid option: '$1'";;
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
missing_opts=
|
||||
test x"$test_name" = x && missing_opts="$missing_opts --test-name"
|
||||
test x"$log_file" = x && missing_opts="$missing_opts --log-file"
|
||||
test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
|
||||
if test x"$missing_opts" != x; then
|
||||
usage_error "the following mandatory options are missing:$missing_opts"
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
usage_error "missing argument"
|
||||
fi
|
||||
|
||||
if test $color_tests = yes; then
|
||||
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||
red='[0;31m' # Red.
|
||||
grn='[0;32m' # Green.
|
||||
lgn='[1;32m' # Light green.
|
||||
blu='[1;34m' # Blue.
|
||||
mgn='[0;35m' # Magenta.
|
||||
std='[m' # No color.
|
||||
else
|
||||
red= grn= lgn= blu= mgn= std=
|
||||
fi
|
||||
|
||||
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||
trap "st=129; $do_exit" 1
|
||||
trap "st=130; $do_exit" 2
|
||||
trap "st=141; $do_exit" 13
|
||||
trap "st=143; $do_exit" 15
|
||||
|
||||
# Test script is run here.
|
||||
"$@" >$log_file 2>&1
|
||||
estatus=$?
|
||||
|
||||
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||
tweaked_estatus=1
|
||||
else
|
||||
tweaked_estatus=$estatus
|
||||
fi
|
||||
|
||||
case $tweaked_estatus:$expect_failure in
|
||||
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||
esac
|
||||
|
||||
# Report the test outcome and exit status in the logs, so that one can
|
||||
# know whether the test passed or failed simply by looking at the '.log'
|
||||
# file, without the need of also peaking into the corresponding '.trs'
|
||||
# file (automake bug#11814).
|
||||
echo "$res $test_name (exit status: $estatus)" >>$log_file
|
||||
|
||||
# Report outcome to console.
|
||||
echo "${col}${res}${std}: $test_name"
|
||||
|
||||
# Register the test result, and other relevant metadata.
|
||||
echo ":test-result: $res" > $trs_file
|
||||
echo ":global-test-result: $res" >> $trs_file
|
||||
echo ":recheck: $recheck" >> $trs_file
|
||||
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
510
toolkit/crashreporter/google-breakpad/configure
vendored
510
toolkit/crashreporter/google-breakpad/configure
vendored
@ -626,6 +626,10 @@ ac_subst_vars='am__EXEEXT_FALSE
|
||||
am__EXEEXT_TRUE
|
||||
LTLIBOBJS
|
||||
LIBOBJS
|
||||
TESTS_AS_ROOT_FALSE
|
||||
TESTS_AS_ROOT_TRUE
|
||||
RUST_DEMANGLE_LIBS
|
||||
RUST_DEMANGLE_CFLAGS
|
||||
SELFTEST_FALSE
|
||||
SELFTEST_TRUE
|
||||
GTEST_LIBS
|
||||
@ -646,6 +650,7 @@ ANDROID_HOST_FALSE
|
||||
ANDROID_HOST_TRUE
|
||||
LINUX_HOST_FALSE
|
||||
LINUX_HOST_TRUE
|
||||
WARN_CXXFLAGS
|
||||
HAVE_CXX11
|
||||
PTHREAD_CFLAGS
|
||||
PTHREAD_LIBS
|
||||
@ -653,8 +658,6 @@ PTHREAD_CC
|
||||
ax_pthread_config
|
||||
EGREP
|
||||
GREP
|
||||
GCC_FALSE
|
||||
GCC_TRUE
|
||||
RANLIB
|
||||
am__fastdepCXX_FALSE
|
||||
am__fastdepCXX_TRUE
|
||||
@ -775,6 +778,8 @@ enable_processor
|
||||
enable_tools
|
||||
enable_system_test_libs
|
||||
enable_selftest
|
||||
with_rust_demangle
|
||||
with_tests_as_root
|
||||
'
|
||||
ac_precious_vars='build_alias
|
||||
host_alias
|
||||
@ -795,7 +800,9 @@ GMOCK_CFLAGS
|
||||
GMOCK_LIBS
|
||||
GTEST_CONFIG
|
||||
GTEST_CFLAGS
|
||||
GTEST_LIBS'
|
||||
GTEST_LIBS
|
||||
RUST_DEMANGLE_CFLAGS
|
||||
RUST_DEMANGLE_LIBS'
|
||||
|
||||
|
||||
# Initialize some variables set by options.
|
||||
@ -1433,6 +1440,17 @@ Optional Features:
|
||||
--enable-selftest Run extra tests with "make check" (may conflict with
|
||||
optimizations) (default is no)
|
||||
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
--with-rust-demangle=/path/to/rust-demangle-capi
|
||||
Link against the rust-demangle library to demangle
|
||||
Rust language symbols during symbol dumping (default
|
||||
is no) Pass the path to the crate root.
|
||||
--with-tests-as-root Run the tests as root. Use this on platforms like
|
||||
travis-ci.org that require root privileges to use
|
||||
ptrace (default is no)
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command
|
||||
CFLAGS C compiler flags
|
||||
@ -1456,6 +1474,10 @@ Some influential environment variables:
|
||||
GTEST_CFLAGS
|
||||
Compiler flags for gtest
|
||||
GTEST_LIBS Linker flags for gtest
|
||||
RUST_DEMANGLE_CFLAGS
|
||||
Compiler flags for rust-demangle
|
||||
RUST_DEMANGLE_LIBS
|
||||
Linker flags for rust-demangle
|
||||
|
||||
Use these variables to override the choices made by `configure' or to help
|
||||
it to find libraries and programs with nonstandard names/locations.
|
||||
@ -1859,6 +1881,73 @@ $as_echo "$ac_res" >&6; }
|
||||
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
|
||||
|
||||
} # ac_fn_c_check_header_compile
|
||||
|
||||
# ac_fn_c_check_func LINENO FUNC VAR
|
||||
# ----------------------------------
|
||||
# Tests whether FUNC exists, setting the cache variable VAR accordingly
|
||||
ac_fn_c_check_func ()
|
||||
{
|
||||
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
|
||||
$as_echo_n "checking for $2... " >&6; }
|
||||
if eval \${$3+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
|
||||
For example, HP-UX 11i <limits.h> declares gettimeofday. */
|
||||
#define $2 innocuous_$2
|
||||
|
||||
/* System header to define __stub macros and hopefully few prototypes,
|
||||
which can conflict with char $2 (); below.
|
||||
Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
|
||||
<limits.h> exists even on freestanding compilers. */
|
||||
|
||||
#ifdef __STDC__
|
||||
# include <limits.h>
|
||||
#else
|
||||
# include <assert.h>
|
||||
#endif
|
||||
|
||||
#undef $2
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char $2 ();
|
||||
/* The GNU C library defines this for functions which it implements
|
||||
to always fail with ENOSYS. Some functions are actually named
|
||||
something starting with __ and the normal name is an alias. */
|
||||
#if defined __stub_$2 || defined __stub___$2
|
||||
choke me
|
||||
#endif
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return $2 ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
eval "$3=yes"
|
||||
else
|
||||
eval "$3=no"
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
fi
|
||||
eval ac_res=\$$3
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
$as_echo "$ac_res" >&6; }
|
||||
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
|
||||
|
||||
} # ac_fn_c_check_func
|
||||
cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
@ -5720,14 +5809,6 @@ else
|
||||
RANLIB="$ac_cv_prog_RANLIB"
|
||||
fi
|
||||
|
||||
if test "$GCC" = yes; then
|
||||
GCC_TRUE=
|
||||
GCC_FALSE='#'
|
||||
else
|
||||
GCC_TRUE='#'
|
||||
GCC_FALSE=
|
||||
fi
|
||||
# let the Makefile know if we're gcc
|
||||
|
||||
# Check whether --enable-m32 was given.
|
||||
if test "${enable_m32+set}" = set; then :
|
||||
@ -6192,104 +6273,6 @@ rm -rf conftest*
|
||||
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro figures out how to build C programs using POSIX threads. It
|
||||
# sets the PTHREAD_LIBS output variable to the threads library and linker
|
||||
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
|
||||
# flags that are needed. (The user can also force certain compiler
|
||||
# flags/libs to be tested by setting these environment variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these flags,
|
||||
# but also link it with them as well. e.g. you should link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threads programs, you may wish to use these
|
||||
# variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
|
||||
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
#
|
||||
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
|
||||
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
|
||||
# is not found. If ACTION-IF-FOUND is not specified, the default action
|
||||
# will define HAVE_PTHREAD.
|
||||
#
|
||||
# Please let the authors know if this macro fails on any platform, or if
|
||||
# you have any other suggestions or comments. This macro was based on work
|
||||
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
|
||||
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
|
||||
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
|
||||
# grateful for the helpful feedback of numerous users.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 6
|
||||
|
||||
# This is what autoupdate's m4 run will expand. It fires
|
||||
# the warning (with _au_warn_XXX), outputs it into the
|
||||
# updated configure.ac (with AC_DIAGNOSE), and then outputs
|
||||
# the replacement expansion.
|
||||
|
||||
|
||||
# This is an auxiliary macro that is also run when
|
||||
# autoupdate runs m4. It simply calls m4_warning, but
|
||||
# we need a wrapper so that each warning is emitted only
|
||||
# once. We break the quoting in m4_warning's argument in
|
||||
# order to expand this macro's arguments, not AU_DEFUN's.
|
||||
|
||||
|
||||
# Finally, this is the expansion that is picked up by
|
||||
# autoconf. It tells the user to run autoupdate, and
|
||||
# then outputs the replacement expansion. We do not care
|
||||
# about autoupdate's warning because that contains
|
||||
# information on what to do *after* running autoupdate.
|
||||
|
||||
|
||||
|
||||
|
||||
@ -6662,79 +6645,30 @@ fi
|
||||
done
|
||||
|
||||
|
||||
for ac_header in a.out.h
|
||||
for ac_header in a.out.h sys/random.h
|
||||
do :
|
||||
ac_fn_c_check_header_mongrel "$LINENO" "a.out.h" "ac_cv_header_a_out_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_a_out_h" = xyes; then :
|
||||
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
|
||||
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
|
||||
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_A_OUT_H 1
|
||||
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
for ac_func in arc4random getrandom
|
||||
do :
|
||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
|
||||
if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
|
||||
_ACEOF
|
||||
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXXFLAGS to
|
||||
# enable support. VERSION may be '11' (for the C++11 standard) or '14'
|
||||
# (for the C++14 standard).
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
# -std=c++11). If neither is specified, you get whatever works, with
|
||||
# preference for an extended mode.
|
||||
#
|
||||
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||
# indicates that baseline support for the specified C++ standard is
|
||||
# required and that the macro should error out if no mode with that
|
||||
# support is found. If specified 'optional', then configuration proceeds
|
||||
# regardless, after defining HAVE_CXX${VERSION} if and only if a
|
||||
# supporting mode is found.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
|
||||
@ -7397,6 +7331,176 @@ $as_echo "#define HAVE_CXX11 1" >>confdefs.h
|
||||
fi
|
||||
|
||||
|
||||
WARN_CXXFLAGS=
|
||||
ac_ext=cpp
|
||||
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Werror=unknown-warning-option" >&5
|
||||
$as_echo_n "checking whether C++ compiler accepts -Werror=unknown-warning-option... " >&6; }
|
||||
if ${ax_cv_check_cxxflags___Werror_unknown_warning_option+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
ax_check_save_flags=$CXXFLAGS
|
||||
CXXFLAGS="$CXXFLAGS -Werror=unknown-warning-option"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
ax_cv_check_cxxflags___Werror_unknown_warning_option=yes
|
||||
else
|
||||
ax_cv_check_cxxflags___Werror_unknown_warning_option=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
CXXFLAGS=$ax_check_save_flags
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Werror_unknown_warning_option" >&5
|
||||
$as_echo "$ax_cv_check_cxxflags___Werror_unknown_warning_option" >&6; }
|
||||
if test "x$ax_cv_check_cxxflags___Werror_unknown_warning_option" = xyes; then :
|
||||
|
||||
ax_compiler_flags_test="-Werror=unknown-warning-option"
|
||||
|
||||
else
|
||||
|
||||
ax_compiler_flags_test=""
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for flag in -Wmissing-braces -Wnon-virtual-dtor -Woverloaded-virtual -Wreorder -Wsign-compare -Wunused-local-typedefs -Wunused-variable -Wvla ; do
|
||||
as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_${ax_compiler_flags_test}_$flag" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts $flag" >&5
|
||||
$as_echo_n "checking whether C++ compiler accepts $flag... " >&6; }
|
||||
if eval \${$as_CACHEVAR+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
ax_check_save_flags=$CXXFLAGS
|
||||
CXXFLAGS="$CXXFLAGS ${ax_compiler_flags_test} $flag"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
eval "$as_CACHEVAR=yes"
|
||||
else
|
||||
eval "$as_CACHEVAR=no"
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
CXXFLAGS=$ax_check_save_flags
|
||||
fi
|
||||
eval ac_res=\$$as_CACHEVAR
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
$as_echo "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then :
|
||||
|
||||
if ${WARN_CXXFLAGS+:} false; then :
|
||||
|
||||
case " $WARN_CXXFLAGS " in #(
|
||||
*" $flag "*) :
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS already contains \$flag"; } >&5
|
||||
(: WARN_CXXFLAGS already contains $flag) 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; } ;; #(
|
||||
*) :
|
||||
|
||||
as_fn_append WARN_CXXFLAGS " $flag"
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS=\"\$WARN_CXXFLAGS\""; } >&5
|
||||
(: WARN_CXXFLAGS="$WARN_CXXFLAGS") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }
|
||||
;;
|
||||
esac
|
||||
|
||||
else
|
||||
|
||||
WARN_CXXFLAGS=$flag
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS=\"\$WARN_CXXFLAGS\""; } >&5
|
||||
(: WARN_CXXFLAGS="$WARN_CXXFLAGS") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }
|
||||
|
||||
fi
|
||||
|
||||
else
|
||||
:
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
as_fn_append WARN_CXXFLAGS " -Werror"
|
||||
ac_ext=c
|
||||
ac_cpp='$CPP $CPPFLAGS'
|
||||
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||
ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
|
||||
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for O_CLOEXEC defined in fcntl.h" >&5
|
||||
$as_echo_n "checking for O_CLOEXEC defined in fcntl.h... " >&6; }
|
||||
if ${ac_cv_defined_O_CLOEXEC_fcntl_h+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <fcntl.h>
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
#ifdef O_CLOEXEC
|
||||
int ok;
|
||||
#else
|
||||
choke me
|
||||
#endif
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_compile "$LINENO"; then :
|
||||
ac_cv_defined_O_CLOEXEC_fcntl_h=yes
|
||||
else
|
||||
ac_cv_defined_O_CLOEXEC_fcntl_h=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined_O_CLOEXEC_fcntl_h" >&5
|
||||
$as_echo "$ac_cv_defined_O_CLOEXEC_fcntl_h" >&6; }
|
||||
if test $ac_cv_defined_O_CLOEXEC_fcntl_h != "no"; then :
|
||||
|
||||
else
|
||||
|
||||
$as_echo "#define O_CLOEXEC 0" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
@ -7747,6 +7851,58 @@ else
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Check whether --with-rust-demangle was given.
|
||||
if test "${with_rust_demangle+set}" = set; then :
|
||||
withval=$with_rust_demangle; case "${withval}" in
|
||||
yes)
|
||||
as_fn_error $? "You must pass the path to the rust-demangle-capi crate for --with-rust-demangle" "$LINENO" 5
|
||||
;;
|
||||
no)
|
||||
rust_demangle=false
|
||||
;;
|
||||
*)
|
||||
if ! test -f "${withval}/Cargo.toml"; then
|
||||
as_fn_error $? "You must pass the path to the rust-demangle-capi crate for --with-rust-demangle" "$LINENO" 5
|
||||
fi
|
||||
RUST_DEMANGLE_CFLAGS="-DHAVE_RUST_DEMANGLE -I${withval}/target/include"
|
||||
RUST_DEMANGLE_LIBS="-L${withval}/target/release -lrust_demangle -lpthread -ldl"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
rust_demangle=false
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Check whether --with-tests-as-root was given.
|
||||
if test "${with_tests_as_root+set}" = set; then :
|
||||
withval=$with_tests_as_root; case "${withval}" in
|
||||
yes)
|
||||
tests_as_root=true
|
||||
;;
|
||||
no)
|
||||
tests_as_root=false
|
||||
;;
|
||||
*)
|
||||
as_fn_error $? "--with-tests-as-root can only be \"yes\" or \"no\"" "$LINENO" 5
|
||||
;;
|
||||
esac
|
||||
else
|
||||
tests_as_root=false
|
||||
fi
|
||||
|
||||
if test x$tests_as_root = xtrue; then
|
||||
TESTS_AS_ROOT_TRUE=
|
||||
TESTS_AS_ROOT_FALSE='#'
|
||||
else
|
||||
TESTS_AS_ROOT_TRUE='#'
|
||||
TESTS_AS_ROOT_FALSE=
|
||||
fi
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files breakpad.pc breakpad-client.pc Makefile"
|
||||
|
||||
|
||||
@ -7898,10 +8054,6 @@ if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
|
||||
as_fn_error $? "conditional \"am__fastdepCXX\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${GCC_TRUE}" && test -z "${GCC_FALSE}"; then
|
||||
as_fn_error $? "conditional \"GCC\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${LINUX_HOST_TRUE}" && test -z "${LINUX_HOST_FALSE}"; then
|
||||
as_fn_error $? "conditional \"LINUX_HOST\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
@ -7930,6 +8082,10 @@ if test -z "${SELFTEST_TRUE}" && test -z "${SELFTEST_FALSE}"; then
|
||||
as_fn_error $? "conditional \"SELFTEST\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${TESTS_AS_ROOT_TRUE}" && test -z "${TESTS_AS_ROOT_FALSE}"; then
|
||||
as_fn_error $? "conditional \"TESTS_AS_ROOT\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
|
||||
: "${CONFIG_STATUS=./config.status}"
|
||||
ac_write_fail=0
|
||||
|
@ -28,7 +28,7 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
AC_PREREQ(2.57)
|
||||
AC_PREREQ(2.64)
|
||||
|
||||
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
|
||||
dnl Sanity check: the argument is just a file that should exist.
|
||||
@ -48,7 +48,6 @@ AM_PROG_CC_C_O
|
||||
AC_PROG_CPP
|
||||
AC_PROG_CXX
|
||||
AC_PROG_RANLIB
|
||||
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
|
||||
|
||||
dnl This must come before all the feature tests below.
|
||||
AC_ARG_ENABLE(m32,
|
||||
@ -72,13 +71,40 @@ AC_ARG_ENABLE(m32,
|
||||
|
||||
AC_HEADER_STDC
|
||||
AC_SYS_LARGEFILE
|
||||
m4_include(m4/ax_pthread.m4)
|
||||
AX_PTHREAD
|
||||
AC_CHECK_HEADERS([a.out.h])
|
||||
AC_CHECK_HEADERS([a.out.h sys/random.h])
|
||||
AC_CHECK_FUNCS([arc4random getrandom])
|
||||
|
||||
m4_include(m4/ax_cxx_compile_stdcxx.m4)
|
||||
AX_CXX_COMPILE_STDCXX(11, noext, mandatory)
|
||||
|
||||
dnl Test supported warning flags.
|
||||
WARN_CXXFLAGS=
|
||||
dnl This warning flag is used by clang. Its default behavior is to warn when
|
||||
dnl given an unknown flag rather than error out.
|
||||
AC_LANG_PUSH([C++])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
|
||||
ax_compiler_flags_test="-Werror=unknown-warning-option"
|
||||
],[
|
||||
ax_compiler_flags_test=""
|
||||
])
|
||||
AX_APPEND_COMPILE_FLAGS(m4_flatten([
|
||||
-Wmissing-braces
|
||||
-Wnon-virtual-dtor
|
||||
-Woverloaded-virtual
|
||||
-Wreorder
|
||||
-Wsign-compare
|
||||
-Wunused-local-typedefs
|
||||
-Wunused-variable
|
||||
-Wvla
|
||||
]), [WARN_CXXFLAGS], [${ax_compiler_flags_test}])
|
||||
AS_VAR_APPEND([WARN_CXXFLAGS], " -Werror")
|
||||
AC_LANG_POP([C++])
|
||||
AC_SUBST([WARN_CXXFLAGS])
|
||||
|
||||
dnl Test support for O_CLOEXEC
|
||||
AX_CHECK_DEFINE([fcntl.h], [O_CLOEXEC], [],
|
||||
[AC_DEFINE([O_CLOEXEC], [0], [Fallback definition for old systems])])
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
@ -195,6 +221,50 @@ AC_ARG_ENABLE(selftest,
|
||||
[selftest=false])
|
||||
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
|
||||
|
||||
AC_ARG_WITH(rust-demangle,
|
||||
AS_HELP_STRING([--with-rust-demangle=/path/to/rust-demangle-capi],
|
||||
[Link against the rust-demangle library]
|
||||
[to demangle Rust language symbols during]
|
||||
[symbol dumping (default is no)]
|
||||
[Pass the path to the crate root.]),
|
||||
[case "${withval}" in
|
||||
yes)
|
||||
AC_MSG_ERROR(You must pass the path to the rust-demangle-capi crate for --with-rust-demangle)
|
||||
;;
|
||||
no)
|
||||
rust_demangle=false
|
||||
;;
|
||||
*)
|
||||
if ! test -f "${withval}/Cargo.toml"; then
|
||||
AC_MSG_ERROR(You must pass the path to the rust-demangle-capi crate for --with-rust-demangle)
|
||||
fi
|
||||
RUST_DEMANGLE_CFLAGS="-DHAVE_RUST_DEMANGLE -I${withval}/target/include"
|
||||
RUST_DEMANGLE_LIBS="-L${withval}/target/release -lrust_demangle -lpthread -ldl"
|
||||
;;
|
||||
esac],
|
||||
[rust_demangle=false])
|
||||
AC_ARG_VAR([RUST_DEMANGLE_CFLAGS], [Compiler flags for rust-demangle])
|
||||
AC_ARG_VAR([RUST_DEMANGLE_LIBS], [Linker flags for rust-demangle])
|
||||
|
||||
AC_ARG_WITH(tests-as-root,
|
||||
AS_HELP_STRING([--with-tests-as-root],
|
||||
[Run the tests as root. Use this on platforms]
|
||||
[like travis-ci.org that require root privileges]
|
||||
[to use ptrace (default is no)]),
|
||||
[case "${withval}" in
|
||||
yes)
|
||||
tests_as_root=true
|
||||
;;
|
||||
no)
|
||||
tests_as_root=false
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(--with-tests-as-root can only be "yes" or "no")
|
||||
;;
|
||||
esac],
|
||||
[tests_as_root=false])
|
||||
AM_CONDITIONAL(TESTS_AS_ROOT, test x$tests_as_root = xtrue)
|
||||
|
||||
AC_CONFIG_FILES(m4_flatten([
|
||||
breakpad.pc
|
||||
breakpad-client.pc
|
||||
|
43
toolkit/crashreporter/google-breakpad/default.xml
Normal file
43
toolkit/crashreporter/google-breakpad/default.xml
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- AUTOGENERATED BY deps-to-manifest.py; DO NOT EDIT -->
|
||||
<manifest>
|
||||
|
||||
<default revision='refs/heads/master'
|
||||
remote='chromium'
|
||||
sync-c='true'
|
||||
sync-j='8' />
|
||||
|
||||
<remote name='github'
|
||||
fetch='https://github.com/'
|
||||
review='' />
|
||||
|
||||
<remote name='chromium'
|
||||
fetch='https://chromium.googlesource.com/'
|
||||
review='https://chromium-review.googlesource.com' />
|
||||
|
||||
<project path='src'
|
||||
name='breakpad/breakpad'
|
||||
revision='refs/heads/master'
|
||||
remote='chromium' />
|
||||
|
||||
<project path='src/src/tools/gyp'
|
||||
name='external/gyp/'
|
||||
revision='324dd166b7c0b39d513026fa52d6280ac6d56770'
|
||||
remote='chromium' />
|
||||
|
||||
<project path='src/src/testing'
|
||||
name='google/googletest.git'
|
||||
revision='refs/tags/release-1.8.0'
|
||||
remote='github' />
|
||||
|
||||
<project path='src/src/third_party/lss'
|
||||
name='linux-syscall-support/'
|
||||
revision='e6527b0cd469e3ff5764785dadcb39bf7d787154'
|
||||
remote='chromium' />
|
||||
|
||||
<project path='src/src/third_party/protobuf/protobuf'
|
||||
name='google/protobuf.git'
|
||||
revision='cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac'
|
||||
remote='github' />
|
||||
|
||||
</manifest>
|
@ -1,283 +0,0 @@
|
||||
# ===========================================================================
|
||||
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro figures out how to build C programs using POSIX threads. It
|
||||
# sets the PTHREAD_LIBS output variable to the threads library and linker
|
||||
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
|
||||
# flags that are needed. (The user can also force certain compiler
|
||||
# flags/libs to be tested by setting these environment variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these flags,
|
||||
# but also link it with them as well. e.g. you should link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threads programs, you may wish to use these
|
||||
# variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
|
||||
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
#
|
||||
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
|
||||
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
|
||||
# is not found. If ACTION-IF-FOUND is not specified, the default action
|
||||
# will define HAVE_PTHREAD.
|
||||
#
|
||||
# Please let the authors know if this macro fails on any platform, or if
|
||||
# you have any other suggestions or comments. This macro was based on work
|
||||
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
|
||||
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
|
||||
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
|
||||
# grateful for the helpful feedback of numerous users.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 6
|
||||
|
||||
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
|
||||
AC_DEFUN([AX_PTHREAD], [
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
AC_LANG_SAVE
|
||||
AC_LANG_C
|
||||
ax_pthread_ok=no
|
||||
|
||||
# We used to check for pthread.h first, but this fails if pthread.h
|
||||
# requires special compiler flags (e.g. on True64 or Sequent).
|
||||
# It gets checked for in the link test anyway.
|
||||
|
||||
# First of all, check if the user has set any of the PTHREAD_LIBS,
|
||||
# etcetera environment variables, and if threads linking works using
|
||||
# them:
|
||||
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
|
||||
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
|
||||
AC_MSG_RESULT($ax_pthread_ok)
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
fi
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
fi
|
||||
|
||||
# We must check for the threads library under a number of different
|
||||
# names; the ordering is very important because some systems
|
||||
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
|
||||
# libraries is broken (non-POSIX).
|
||||
|
||||
# Create a list of thread flags to try. Items starting with a "-" are
|
||||
# C compiler flags, and other items are library names, except for "none"
|
||||
# which indicates that we try without any flags at all, and "pthread-config"
|
||||
# which is a program returning the flags for the Pth emulation library.
|
||||
|
||||
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
|
||||
|
||||
# The ordering *is* (sometimes) important. Some notes on the
|
||||
# individual items follow:
|
||||
|
||||
# pthreads: AIX (must check this before -lpthread)
|
||||
# none: in case threads are in libc; should be tried before -Kthread and
|
||||
# other compiler flags to prevent continual compiler warnings
|
||||
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
|
||||
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
|
||||
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
|
||||
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
|
||||
# -pthreads: Solaris/gcc
|
||||
# -mthreads: Mingw32/gcc, Lynx/gcc
|
||||
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
|
||||
# doesn't hurt to check since this sometimes defines pthreads too;
|
||||
# also defines -D_REENTRANT)
|
||||
# ... -mt is also the pthreads flag for HP/aCC
|
||||
# pthread: Linux, etcetera
|
||||
# --thread-safe: KAI C++
|
||||
# pthread-config: use pthread-config program (for GNU Pth library)
|
||||
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*solaris*)
|
||||
|
||||
# On Solaris (at least, for some versions), libc contains stubbed
|
||||
# (non-functional) versions of the pthreads routines, so link-based
|
||||
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
|
||||
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
|
||||
# a function called by this macro, so we could check for that, but
|
||||
# who knows whether they'll stub that too in a future libc.) So,
|
||||
# we'll just look for -pthreads and -lpthread first:
|
||||
|
||||
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
|
||||
;;
|
||||
|
||||
*-darwin*)
|
||||
acx_pthread_flags="-pthread $acx_pthread_flags"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test x"$ax_pthread_ok" = xno; then
|
||||
for flag in $ax_pthread_flags; do
|
||||
|
||||
case $flag in
|
||||
none)
|
||||
AC_MSG_CHECKING([whether pthreads work without any flags])
|
||||
;;
|
||||
|
||||
-*)
|
||||
AC_MSG_CHECKING([whether pthreads work with $flag])
|
||||
PTHREAD_CFLAGS="$flag"
|
||||
;;
|
||||
|
||||
pthread-config)
|
||||
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
|
||||
if test x"$ax_pthread_config" = xno; then continue; fi
|
||||
PTHREAD_CFLAGS="`pthread-config --cflags`"
|
||||
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_CHECKING([for the pthreads library -l$flag])
|
||||
PTHREAD_LIBS="-l$flag"
|
||||
;;
|
||||
esac
|
||||
|
||||
save_LIBS="$LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Check for various functions. We must include pthread.h,
|
||||
# since some functions may be macros. (On the Sequent, we
|
||||
# need a special flag -Kthread to make this header compile.)
|
||||
# We check for pthread_join because it is in -lpthread on IRIX
|
||||
# while pthread_create is in libc. We check for pthread_attr_init
|
||||
# due to DEC craziness with -lpthreads. We check for
|
||||
# pthread_cleanup_push because it is one of the few pthread
|
||||
# functions on Solaris that doesn't have a non-functional libc stub.
|
||||
# We try pthread_create on general principles.
|
||||
AC_TRY_LINK([#include <pthread.h>
|
||||
static void routine(void* a) {a=0;}
|
||||
static void* start_routine(void* a) {return a;}],
|
||||
[pthread_t th; pthread_attr_t attr;
|
||||
pthread_join(th, 0);
|
||||
pthread_attr_init(&attr);
|
||||
pthread_cleanup_push(routine, 0);
|
||||
pthread_create(&th,0,start_routine,0);
|
||||
pthread_cleanup_pop(0); ],
|
||||
[ax_pthread_ok=yes])
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
AC_MSG_RESULT($ax_pthread_ok)
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
break;
|
||||
fi
|
||||
|
||||
PTHREAD_LIBS=""
|
||||
PTHREAD_CFLAGS=""
|
||||
done
|
||||
fi
|
||||
|
||||
# Various other checks:
|
||||
if test "x$ax_pthread_ok" = xyes; then
|
||||
save_LIBS="$LIBS"
|
||||
LIBS="$PTHREAD_LIBS $LIBS"
|
||||
save_CFLAGS="$CFLAGS"
|
||||
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
|
||||
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
|
||||
AC_MSG_CHECKING([for joinable pthread attribute])
|
||||
attr_name=unknown
|
||||
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
|
||||
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
|
||||
[attr_name=$attr; break])
|
||||
done
|
||||
AC_MSG_RESULT($attr_name)
|
||||
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
|
||||
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
|
||||
[Define to necessary symbol if this constant
|
||||
uses a non-standard name on your system.])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([if more special flags are required for pthreads])
|
||||
flag=no
|
||||
case "${host_cpu}-${host_os}" in
|
||||
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
|
||||
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
|
||||
esac
|
||||
AC_MSG_RESULT(${flag})
|
||||
if test "x$flag" != xno; then
|
||||
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
|
||||
fi
|
||||
|
||||
LIBS="$save_LIBS"
|
||||
CFLAGS="$save_CFLAGS"
|
||||
|
||||
# More AIX lossage: must compile with xlc_r or cc_r
|
||||
if test x"$GCC" != xyes; then
|
||||
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
|
||||
else
|
||||
PTHREAD_CC=$CC
|
||||
fi
|
||||
else
|
||||
PTHREAD_CC="$CC"
|
||||
fi
|
||||
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PTHREAD_CFLAGS)
|
||||
AC_SUBST(PTHREAD_CC)
|
||||
|
||||
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
|
||||
if test x"$ax_pthread_ok" = xyes; then
|
||||
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
|
||||
:
|
||||
else
|
||||
ax_pthread_ok=no
|
||||
$2
|
||||
fi
|
||||
AC_LANG_RESTORE
|
||||
])dnl AX_PTHREAD
|
7377
toolkit/crashreporter/google-breakpad/m4/libtool.m4
vendored
7377
toolkit/crashreporter/google-breakpad/m4/libtool.m4
vendored
File diff suppressed because it is too large
Load Diff
@ -1,368 +0,0 @@
|
||||
# Helper functions for option handling. -*- Autoconf -*-
|
||||
#
|
||||
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
|
||||
# Written by Gary V. Vaughan, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 6 ltoptions.m4
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
|
||||
|
||||
|
||||
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
|
||||
# ------------------------------------------
|
||||
m4_define([_LT_MANGLE_OPTION],
|
||||
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
|
||||
|
||||
|
||||
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
|
||||
# ---------------------------------------
|
||||
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
|
||||
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
|
||||
# saved as a flag.
|
||||
m4_define([_LT_SET_OPTION],
|
||||
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
|
||||
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
|
||||
_LT_MANGLE_DEFUN([$1], [$2]),
|
||||
[m4_warning([Unknown $1 option `$2'])])[]dnl
|
||||
])
|
||||
|
||||
|
||||
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
|
||||
# ------------------------------------------------------------
|
||||
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
|
||||
m4_define([_LT_IF_OPTION],
|
||||
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
|
||||
|
||||
|
||||
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
|
||||
# -------------------------------------------------------
|
||||
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
|
||||
# are set.
|
||||
m4_define([_LT_UNLESS_OPTIONS],
|
||||
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
|
||||
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
|
||||
[m4_define([$0_found])])])[]dnl
|
||||
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
|
||||
])[]dnl
|
||||
])
|
||||
|
||||
|
||||
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
|
||||
# ----------------------------------------
|
||||
# OPTION-LIST is a space-separated list of Libtool options associated
|
||||
# with MACRO-NAME. If any OPTION has a matching handler declared with
|
||||
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
|
||||
# the unknown option and exit.
|
||||
m4_defun([_LT_SET_OPTIONS],
|
||||
[# Set options
|
||||
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
|
||||
[_LT_SET_OPTION([$1], _LT_Option)])
|
||||
|
||||
m4_if([$1],[LT_INIT],[
|
||||
dnl
|
||||
dnl Simply set some default values (i.e off) if boolean options were not
|
||||
dnl specified:
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
|
||||
])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
|
||||
])
|
||||
dnl
|
||||
dnl If no reference was made to various pairs of opposing options, then
|
||||
dnl we run the default mode handler for the pair. For example, if neither
|
||||
dnl `shared' nor `disable-shared' was passed, we enable building of shared
|
||||
dnl archives by default:
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
|
||||
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
|
||||
[_LT_ENABLE_FAST_INSTALL])
|
||||
])
|
||||
])# _LT_SET_OPTIONS
|
||||
|
||||
|
||||
## --------------------------------- ##
|
||||
## Macros to handle LT_INIT options. ##
|
||||
## --------------------------------- ##
|
||||
|
||||
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
|
||||
# -----------------------------------------
|
||||
m4_define([_LT_MANGLE_DEFUN],
|
||||
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
|
||||
|
||||
|
||||
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
|
||||
# -----------------------------------------------
|
||||
m4_define([LT_OPTION_DEFINE],
|
||||
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
|
||||
])# LT_OPTION_DEFINE
|
||||
|
||||
|
||||
# dlopen
|
||||
# ------
|
||||
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
|
||||
])
|
||||
|
||||
AU_DEFUN([AC_LIBTOOL_DLOPEN],
|
||||
[_LT_SET_OPTION([LT_INIT], [dlopen])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the `dlopen' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
|
||||
|
||||
|
||||
# win32-dll
|
||||
# ---------
|
||||
# Declare package support for building win32 dll's.
|
||||
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
|
||||
[enable_win32_dll=yes
|
||||
|
||||
case $host in
|
||||
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
|
||||
AC_CHECK_TOOL(AS, as, false)
|
||||
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
|
||||
AC_CHECK_TOOL(OBJDUMP, objdump, false)
|
||||
;;
|
||||
esac
|
||||
|
||||
test -z "$AS" && AS=as
|
||||
_LT_DECL([], [AS], [0], [Assembler program])dnl
|
||||
|
||||
test -z "$DLLTOOL" && DLLTOOL=dlltool
|
||||
_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
|
||||
|
||||
test -z "$OBJDUMP" && OBJDUMP=objdump
|
||||
_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
|
||||
])# win32-dll
|
||||
|
||||
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
|
||||
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
|
||||
_LT_SET_OPTION([LT_INIT], [win32-dll])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the `win32-dll' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
|
||||
|
||||
|
||||
# _LT_ENABLE_SHARED([DEFAULT])
|
||||
# ----------------------------
|
||||
# implement the --enable-shared flag, and supports the `shared' and
|
||||
# `disable-shared' LT_INIT options.
|
||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
||||
m4_define([_LT_ENABLE_SHARED],
|
||||
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([shared],
|
||||
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
|
||||
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_shared=yes ;;
|
||||
no) enable_shared=no ;;
|
||||
*)
|
||||
enable_shared=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
||||
for pkg in $enableval; do
|
||||
IFS="$lt_save_ifs"
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_shared=yes
|
||||
fi
|
||||
done
|
||||
IFS="$lt_save_ifs"
|
||||
;;
|
||||
esac],
|
||||
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
|
||||
|
||||
_LT_DECL([build_libtool_libs], [enable_shared], [0],
|
||||
[Whether or not to build shared libraries])
|
||||
])# _LT_ENABLE_SHARED
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
|
||||
|
||||
# Old names:
|
||||
AC_DEFUN([AC_ENABLE_SHARED],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
|
||||
])
|
||||
|
||||
AC_DEFUN([AC_DISABLE_SHARED],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-shared])
|
||||
])
|
||||
|
||||
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
|
||||
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
|
||||
|
||||
|
||||
|
||||
# _LT_ENABLE_STATIC([DEFAULT])
|
||||
# ----------------------------
|
||||
# implement the --enable-static flag, and support the `static' and
|
||||
# `disable-static' LT_INIT options.
|
||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
||||
m4_define([_LT_ENABLE_STATIC],
|
||||
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([static],
|
||||
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
|
||||
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_static=yes ;;
|
||||
no) enable_static=no ;;
|
||||
*)
|
||||
enable_static=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
||||
for pkg in $enableval; do
|
||||
IFS="$lt_save_ifs"
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_static=yes
|
||||
fi
|
||||
done
|
||||
IFS="$lt_save_ifs"
|
||||
;;
|
||||
esac],
|
||||
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
|
||||
|
||||
_LT_DECL([build_old_libs], [enable_static], [0],
|
||||
[Whether or not to build static libraries])
|
||||
])# _LT_ENABLE_STATIC
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
|
||||
|
||||
# Old names:
|
||||
AC_DEFUN([AC_ENABLE_STATIC],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
|
||||
])
|
||||
|
||||
AC_DEFUN([AC_DISABLE_STATIC],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-static])
|
||||
])
|
||||
|
||||
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
|
||||
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
|
||||
|
||||
|
||||
|
||||
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
|
||||
# ----------------------------------
|
||||
# implement the --enable-fast-install flag, and support the `fast-install'
|
||||
# and `disable-fast-install' LT_INIT options.
|
||||
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
|
||||
m4_define([_LT_ENABLE_FAST_INSTALL],
|
||||
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
|
||||
AC_ARG_ENABLE([fast-install],
|
||||
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
|
||||
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
|
||||
[p=${PACKAGE-default}
|
||||
case $enableval in
|
||||
yes) enable_fast_install=yes ;;
|
||||
no) enable_fast_install=no ;;
|
||||
*)
|
||||
enable_fast_install=no
|
||||
# Look at the argument we got. We use all the common list separators.
|
||||
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
|
||||
for pkg in $enableval; do
|
||||
IFS="$lt_save_ifs"
|
||||
if test "X$pkg" = "X$p"; then
|
||||
enable_fast_install=yes
|
||||
fi
|
||||
done
|
||||
IFS="$lt_save_ifs"
|
||||
;;
|
||||
esac],
|
||||
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
|
||||
|
||||
_LT_DECL([fast_install], [enable_fast_install], [0],
|
||||
[Whether or not to optimize for fast installation])dnl
|
||||
])# _LT_ENABLE_FAST_INSTALL
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
|
||||
|
||||
# Old names:
|
||||
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
|
||||
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
|
||||
the `fast-install' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
|
||||
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
|
||||
the `disable-fast-install' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
|
||||
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
|
||||
|
||||
|
||||
# _LT_WITH_PIC([MODE])
|
||||
# --------------------
|
||||
# implement the --with-pic flag, and support the `pic-only' and `no-pic'
|
||||
# LT_INIT options.
|
||||
# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
|
||||
m4_define([_LT_WITH_PIC],
|
||||
[AC_ARG_WITH([pic],
|
||||
[AS_HELP_STRING([--with-pic],
|
||||
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
|
||||
[pic_mode="$withval"],
|
||||
[pic_mode=default])
|
||||
|
||||
test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
|
||||
|
||||
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
|
||||
])# _LT_WITH_PIC
|
||||
|
||||
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
|
||||
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
|
||||
|
||||
# Old name:
|
||||
AU_DEFUN([AC_LIBTOOL_PICMODE],
|
||||
[_LT_SET_OPTION([LT_INIT], [pic-only])
|
||||
AC_DIAGNOSE([obsolete],
|
||||
[$0: Remove this warning and the call to _LT_SET_OPTION when you
|
||||
put the `pic-only' option into LT_INIT's first parameter.])
|
||||
])
|
||||
|
||||
dnl aclocal-1.4 backwards compatibility:
|
||||
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
|
||||
|
||||
## ----------------- ##
|
||||
## LTDL_INIT Options ##
|
||||
## ----------------- ##
|
||||
|
||||
m4_define([_LTDL_MODE], [])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
|
||||
[m4_define([_LTDL_MODE], [nonrecursive])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
|
||||
[m4_define([_LTDL_MODE], [recursive])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
|
||||
[m4_define([_LTDL_MODE], [subproject])])
|
||||
|
||||
m4_define([_LTDL_TYPE], [])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [installable],
|
||||
[m4_define([_LTDL_TYPE], [installable])])
|
||||
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
|
||||
[m4_define([_LTDL_TYPE], [convenience])])
|
123
toolkit/crashreporter/google-breakpad/m4/ltsugar.m4
vendored
123
toolkit/crashreporter/google-breakpad/m4/ltsugar.m4
vendored
@ -1,123 +0,0 @@
|
||||
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
|
||||
#
|
||||
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
|
||||
# Written by Gary V. Vaughan, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 6 ltsugar.m4
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
|
||||
|
||||
|
||||
# lt_join(SEP, ARG1, [ARG2...])
|
||||
# -----------------------------
|
||||
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
|
||||
# associated separator.
|
||||
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
|
||||
# versions in m4sugar had bugs.
|
||||
m4_define([lt_join],
|
||||
[m4_if([$#], [1], [],
|
||||
[$#], [2], [[$2]],
|
||||
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
|
||||
m4_define([_lt_join],
|
||||
[m4_if([$#$2], [2], [],
|
||||
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
|
||||
|
||||
|
||||
# lt_car(LIST)
|
||||
# lt_cdr(LIST)
|
||||
# ------------
|
||||
# Manipulate m4 lists.
|
||||
# These macros are necessary as long as will still need to support
|
||||
# Autoconf-2.59 which quotes differently.
|
||||
m4_define([lt_car], [[$1]])
|
||||
m4_define([lt_cdr],
|
||||
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
|
||||
[$#], 1, [],
|
||||
[m4_dquote(m4_shift($@))])])
|
||||
m4_define([lt_unquote], $1)
|
||||
|
||||
|
||||
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
|
||||
# ------------------------------------------
|
||||
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
|
||||
# Note that neither SEPARATOR nor STRING are expanded; they are appended
|
||||
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
|
||||
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
|
||||
# than defined and empty).
|
||||
#
|
||||
# This macro is needed until we can rely on Autoconf 2.62, since earlier
|
||||
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
|
||||
m4_define([lt_append],
|
||||
[m4_define([$1],
|
||||
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
|
||||
|
||||
|
||||
|
||||
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
|
||||
# ----------------------------------------------------------
|
||||
# Produce a SEP delimited list of all paired combinations of elements of
|
||||
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
|
||||
# has the form PREFIXmINFIXSUFFIXn.
|
||||
# Needed until we can rely on m4_combine added in Autoconf 2.62.
|
||||
m4_define([lt_combine],
|
||||
[m4_if(m4_eval([$# > 3]), [1],
|
||||
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
|
||||
[[m4_foreach([_Lt_prefix], [$2],
|
||||
[m4_foreach([_Lt_suffix],
|
||||
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
|
||||
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
|
||||
|
||||
|
||||
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
|
||||
# -----------------------------------------------------------------------
|
||||
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
|
||||
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
|
||||
m4_define([lt_if_append_uniq],
|
||||
[m4_ifdef([$1],
|
||||
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
|
||||
[lt_append([$1], [$2], [$3])$4],
|
||||
[$5])],
|
||||
[lt_append([$1], [$2], [$3])$4])])
|
||||
|
||||
|
||||
# lt_dict_add(DICT, KEY, VALUE)
|
||||
# -----------------------------
|
||||
m4_define([lt_dict_add],
|
||||
[m4_define([$1($2)], [$3])])
|
||||
|
||||
|
||||
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
|
||||
# --------------------------------------------
|
||||
m4_define([lt_dict_add_subkey],
|
||||
[m4_define([$1($2:$3)], [$4])])
|
||||
|
||||
|
||||
# lt_dict_fetch(DICT, KEY, [SUBKEY])
|
||||
# ----------------------------------
|
||||
m4_define([lt_dict_fetch],
|
||||
[m4_ifval([$3],
|
||||
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
|
||||
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
|
||||
|
||||
|
||||
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
|
||||
# -----------------------------------------------------------------
|
||||
m4_define([lt_if_dict_fetch],
|
||||
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
|
||||
[$5],
|
||||
[$6])])
|
||||
|
||||
|
||||
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
|
||||
# --------------------------------------------------------------
|
||||
m4_define([lt_dict_filter],
|
||||
[m4_if([$5], [], [],
|
||||
[lt_join(m4_quote(m4_default([$4], [[, ]])),
|
||||
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
|
||||
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
|
||||
])
|
@ -1,23 +0,0 @@
|
||||
# ltversion.m4 -- version numbers -*- Autoconf -*-
|
||||
#
|
||||
# Copyright (C) 2004 Free Software Foundation, Inc.
|
||||
# Written by Scott James Remnant, 2004
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# Generated from ltversion.in.
|
||||
|
||||
# serial 3017 ltversion.m4
|
||||
# This file is part of GNU Libtool
|
||||
|
||||
m4_define([LT_PACKAGE_VERSION], [2.2.6b])
|
||||
m4_define([LT_PACKAGE_REVISION], [1.3017])
|
||||
|
||||
AC_DEFUN([LTVERSION_VERSION],
|
||||
[macro_version='2.2.6b'
|
||||
macro_revision='1.3017'
|
||||
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
|
||||
_LT_DECL(, macro_revision, 0)
|
||||
])
|
@ -1,92 +0,0 @@
|
||||
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
|
||||
#
|
||||
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
|
||||
# Written by Scott James Remnant, 2004.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation gives
|
||||
# unlimited permission to copy and/or distribute it, with or without
|
||||
# modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 4 lt~obsolete.m4
|
||||
|
||||
# These exist entirely to fool aclocal when bootstrapping libtool.
|
||||
#
|
||||
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
|
||||
# which have later been changed to m4_define as they aren't part of the
|
||||
# exported API, or moved to Autoconf or Automake where they belong.
|
||||
#
|
||||
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
|
||||
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
|
||||
# using a macro with the same name in our local m4/libtool.m4 it'll
|
||||
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
|
||||
# and doesn't know about Autoconf macros at all.)
|
||||
#
|
||||
# So we provide this file, which has a silly filename so it's always
|
||||
# included after everything else. This provides aclocal with the
|
||||
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
|
||||
# because those macros already exist, or will be overwritten later.
|
||||
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
|
||||
#
|
||||
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
|
||||
# Yes, that means every name once taken will need to remain here until
|
||||
# we give up compatibility with versions before 1.7, at which point
|
||||
# we need to keep only those names which we still refer to.
|
||||
|
||||
# This is to help aclocal find these macros, as it can't see m4_define.
|
||||
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
|
||||
|
||||
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
|
||||
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
|
||||
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
|
||||
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
|
||||
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
|
||||
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
|
||||
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
|
||||
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
|
||||
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
|
||||
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
|
||||
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
|
||||
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
|
||||
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
|
||||
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
|
||||
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
|
||||
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
|
||||
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
|
||||
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
|
||||
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
|
||||
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
|
||||
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
|
||||
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
|
||||
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
|
||||
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
|
||||
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
|
||||
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
|
||||
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
|
||||
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
|
||||
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
|
||||
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
|
||||
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
|
||||
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
|
||||
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
|
||||
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
|
||||
m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
|
||||
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
|
||||
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
|
||||
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])
|
@ -894,7 +894,7 @@
|
||||
],
|
||||
'msvs_cygwin_dirs': ['<(DEPTH)/third_party/cygwin'],
|
||||
'msvs_disabled_warnings': [
|
||||
4100, 4127, 4396, 4503, 4512, 4819, 4995, 4702
|
||||
4091, 4100, 4127, 4366, 4396, 4503, 4512, 4819, 4995, 4702
|
||||
],
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
|
@ -32,15 +32,15 @@
|
||||
'target_name': 'gtest',
|
||||
'type': 'static_library',
|
||||
'sources': [
|
||||
'../testing/gtest/src/gtest-all.cc',
|
||||
'../testing/googletest/src/gtest-all.cc',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../testing/gtest',
|
||||
'../testing/gtest/include',
|
||||
'../testing/googletest',
|
||||
'../testing/googletest/include',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../testing/gtest/include',
|
||||
'../testing/googletest/include',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -51,7 +51,7 @@
|
||||
'gtest',
|
||||
],
|
||||
'sources': [
|
||||
'gtest/src/gtest_main.cc',
|
||||
'../testing/googletest/src/gtest_main.cc',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -61,15 +61,15 @@
|
||||
'gtest',
|
||||
],
|
||||
'sources': [
|
||||
'../testing/src/gmock-all.cc',
|
||||
'../testing/googlemock/src/gmock-all.cc',
|
||||
],
|
||||
'include_dirs': [
|
||||
'../testing',
|
||||
'../testing/include',
|
||||
'../testing/googlemock',
|
||||
'../testing/googlemock/include',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'../testing/include',
|
||||
'../testing/googlemock/include',
|
||||
],
|
||||
},
|
||||
'export_dependent_settings': [
|
||||
@ -83,7 +83,7 @@
|
||||
'gmock',
|
||||
],
|
||||
'sources': [
|
||||
'../testing/src/gmock_main.cc',
|
||||
'../testing/googlemock/src/gmock_main.cc',
|
||||
],
|
||||
},
|
||||
],
|
@ -230,16 +230,13 @@ breakpad_getcontext:
|
||||
#elif defined(__mips__)
|
||||
|
||||
// This implementation is inspired by implementation of getcontext in glibc.
|
||||
#include <asm-mips/asm.h>
|
||||
#include <asm-mips/regdef.h>
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
#include <asm/asm.h>
|
||||
#include <asm/regdef.h>
|
||||
#include <asm/fpregdef.h>
|
||||
#else
|
||||
#include <machine/asm.h>
|
||||
#include <machine/regdef.h>
|
||||
#include <asm-mips/fpregdef.h>
|
||||
#endif
|
||||
|
||||
// from asm/asm.h
|
||||
// from asm-mips/asm.h
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
#define ALSZ 7
|
||||
#define ALMASK ~7
|
||||
|
@ -0,0 +1,9 @@
|
||||
# asm-mips
|
||||
|
||||
The files in this directory are almost direct copies from Android NDK r12, with
|
||||
the exception of changing the include guards to Breakpad ones. They are copied
|
||||
from the MIPS asm/ directory, but are meant to be used as replacements for both
|
||||
asm/ and machine/ includes since the files in each are largely duplicates.
|
||||
|
||||
Some MIPS asm/ and all machine/ headers were removed in the move to unified NDK
|
||||
headers, so Breakpad fails to compile on newer NDK versions without these files.
|
@ -0,0 +1,270 @@
|
||||
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H
|
||||
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H
|
||||
|
||||
#if defined(__has_include_next) && __has_include_next(<asm/asm.h>)
|
||||
#include_next <asm/asm.h>
|
||||
#else
|
||||
|
||||
/****************************************************************************
|
||||
****************************************************************************
|
||||
***
|
||||
*** This header was automatically generated from a Linux kernel header
|
||||
*** of the same name, to make information necessary for userspace to
|
||||
*** call into the kernel available to libc. It contains only constants,
|
||||
*** structures, and macros generated from the original header, and thus,
|
||||
*** contains no copyrightable information.
|
||||
***
|
||||
*** To edit the content of this header, modify the corresponding
|
||||
*** source file (e.g. under external/kernel-headers/original/) then
|
||||
*** run bionic/libc/kernel/tools/update_all.py
|
||||
***
|
||||
*** Any manual change here will be lost the next time this script will
|
||||
*** be run. You've been warned!
|
||||
***
|
||||
****************************************************************************
|
||||
****************************************************************************/
|
||||
|
||||
#include <asm/sgidefs.h>
|
||||
#ifndef CAT
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#ifdef __STDC__
|
||||
#define __CAT(str1, str2) str1##str2
|
||||
#else
|
||||
#define __CAT(str1, str2) str1 str2
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#define CAT(str1, str2) __CAT(str1, str2)
|
||||
#endif
|
||||
#ifdef __PIC__
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define CPRESTORE(register) .cprestore register
|
||||
#define CPADD(register) .cpadd register
|
||||
#define CPLOAD(register) .cpload register
|
||||
#else
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define CPRESTORE(register)
|
||||
#define CPADD(register)
|
||||
#define CPLOAD(register)
|
||||
#endif
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LEAF(symbol) .globl symbol; .align 2; .type symbol, @function; .ent symbol, 0; symbol: .frame sp, 0, ra
|
||||
#define NESTED(symbol, framesize, rpc) .globl symbol; .align 2; .type symbol, @function; .ent symbol, 0; symbol: .frame sp, framesize, rpc
|
||||
#define END(function) .end function; .size function, .-function
|
||||
#define EXPORT(symbol) .globl symbol; symbol:
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define FEXPORT(symbol) .globl symbol; .type symbol, @function; symbol:
|
||||
#define ABS(symbol,value) .globl symbol; symbol = value
|
||||
#define PANIC(msg) .set push; .set reorder; PTR_LA a0, 8f; jal panic; 9: b 9b; .set pop; TEXT(msg)
|
||||
#define PRINT(string)
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define TEXT(msg) .pushsection .data; 8: .asciiz msg; .popsection;
|
||||
#define TTABLE(string) .pushsection .text; .word 1f; .popsection .pushsection .data; 1: .asciiz string; .popsection
|
||||
#define PREF(hint, addr)
|
||||
#define PREFX(hint, addr)
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#if _MIPS_ISA == _MIPS_ISA_MIPS1
|
||||
#define MOVN(rd, rs, rt) .set push; .set reorder; beqz rt, 9f; move rd, rs; .set pop; 9:
|
||||
#define MOVZ(rd, rs, rt) .set push; .set reorder; bnez rt, 9f; move rd, rs; .set pop; 9:
|
||||
#endif
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#if _MIPS_ISA == _MIPS_ISA_MIPS2 || _MIPS_ISA == _MIPS_ISA_MIPS3
|
||||
#define MOVN(rd, rs, rt) .set push; .set noreorder; bnezl rt, 9f; move rd, rs; .set pop; 9:
|
||||
#define MOVZ(rd, rs, rt) .set push; .set noreorder; beqzl rt, 9f; move rd, rs; .set pop; 9:
|
||||
#endif
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#if _MIPS_ISA == _MIPS_ISA_MIPS4 || _MIPS_ISA == _MIPS_ISA_MIPS5 || _MIPS_ISA == _MIPS_ISA_MIPS32 || _MIPS_ISA == _MIPS_ISA_MIPS64
|
||||
#define MOVN(rd, rs, rt) movn rd, rs, rt
|
||||
#define MOVZ(rd, rs, rt) movz rd, rs, rt
|
||||
#endif
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
#define ALSZ 7
|
||||
#define ALMASK ~7
|
||||
#endif
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
#define ALSZ 15
|
||||
#define ALMASK ~15
|
||||
#endif
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#ifdef __mips64
|
||||
#define SZREG 8
|
||||
#else
|
||||
#define SZREG 4
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
#define REG_S sw
|
||||
#define REG_L lw
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define REG_SUBU subu
|
||||
#define REG_ADDU addu
|
||||
#endif
|
||||
#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define REG_S sd
|
||||
#define REG_L ld
|
||||
#define REG_SUBU dsubu
|
||||
#define REG_ADDU daddu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#if _MIPS_SZINT == 32
|
||||
#define INT_ADD add
|
||||
#define INT_ADDU addu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define INT_ADDI addi
|
||||
#define INT_ADDIU addiu
|
||||
#define INT_SUB sub
|
||||
#define INT_SUBU subu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define INT_L lw
|
||||
#define INT_S sw
|
||||
#define INT_SLL sll
|
||||
#define INT_SLLV sllv
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define INT_SRL srl
|
||||
#define INT_SRLV srlv
|
||||
#define INT_SRA sra
|
||||
#define INT_SRAV srav
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#if _MIPS_SZINT == 64
|
||||
#define INT_ADD dadd
|
||||
#define INT_ADDU daddu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define INT_ADDI daddi
|
||||
#define INT_ADDIU daddiu
|
||||
#define INT_SUB dsub
|
||||
#define INT_SUBU dsubu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define INT_L ld
|
||||
#define INT_S sd
|
||||
#define INT_SLL dsll
|
||||
#define INT_SLLV dsllv
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define INT_SRL dsrl
|
||||
#define INT_SRLV dsrlv
|
||||
#define INT_SRA dsra
|
||||
#define INT_SRAV dsrav
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#if _MIPS_SZLONG == 32
|
||||
#define LONG_ADD add
|
||||
#define LONG_ADDU addu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LONG_ADDI addi
|
||||
#define LONG_ADDIU addiu
|
||||
#define LONG_SUB sub
|
||||
#define LONG_SUBU subu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LONG_L lw
|
||||
#define LONG_S sw
|
||||
#define LONG_SLL sll
|
||||
#define LONG_SLLV sllv
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LONG_SRL srl
|
||||
#define LONG_SRLV srlv
|
||||
#define LONG_SRA sra
|
||||
#define LONG_SRAV srav
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LONG .word
|
||||
#define LONGSIZE 4
|
||||
#define LONGMASK 3
|
||||
#define LONGLOG 2
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#if _MIPS_SZLONG == 64
|
||||
#define LONG_ADD dadd
|
||||
#define LONG_ADDU daddu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LONG_ADDI daddi
|
||||
#define LONG_ADDIU daddiu
|
||||
#define LONG_SUB dsub
|
||||
#define LONG_SUBU dsubu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LONG_L ld
|
||||
#define LONG_S sd
|
||||
#define LONG_SLL dsll
|
||||
#define LONG_SLLV dsllv
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LONG_SRL dsrl
|
||||
#define LONG_SRLV dsrlv
|
||||
#define LONG_SRA dsra
|
||||
#define LONG_SRAV dsrav
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define LONG .dword
|
||||
#define LONGSIZE 8
|
||||
#define LONGMASK 7
|
||||
#define LONGLOG 3
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#if _MIPS_SZPTR == 32
|
||||
#define PTR_ADD add
|
||||
#define PTR_ADDU addu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_ADDI addi
|
||||
#define PTR_ADDIU addiu
|
||||
#define PTR_SUB sub
|
||||
#define PTR_SUBU subu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_L lw
|
||||
#define PTR_S sw
|
||||
#define PTR_LA la
|
||||
#define PTR_LI li
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_SLL sll
|
||||
#define PTR_SLLV sllv
|
||||
#define PTR_SRL srl
|
||||
#define PTR_SRLV srlv
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_SRA sra
|
||||
#define PTR_SRAV srav
|
||||
#define PTR_SCALESHIFT 2
|
||||
#define PTR .word
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTRSIZE 4
|
||||
#define PTRLOG 2
|
||||
#endif
|
||||
#if _MIPS_SZPTR == 64
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_ADD dadd
|
||||
#define PTR_ADDU daddu
|
||||
#define PTR_ADDI daddi
|
||||
#define PTR_ADDIU daddiu
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_SUB dsub
|
||||
#define PTR_SUBU dsubu
|
||||
#define PTR_L ld
|
||||
#define PTR_S sd
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_LA dla
|
||||
#define PTR_LI dli
|
||||
#define PTR_SLL dsll
|
||||
#define PTR_SLLV dsllv
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_SRL dsrl
|
||||
#define PTR_SRLV dsrlv
|
||||
#define PTR_SRA dsra
|
||||
#define PTR_SRAV dsrav
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define PTR_SCALESHIFT 3
|
||||
#define PTR .dword
|
||||
#define PTRSIZE 8
|
||||
#define PTRLOG 3
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
#define MFC0 mfc0
|
||||
#define MTC0 mtc0
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#if _MIPS_SIM == _MIPS_SIM_NABI32 || _MIPS_SIM == _MIPS_SIM_ABI64
|
||||
#define MFC0 dmfc0
|
||||
#define MTC0 dmtc0
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#define SSNOP sll zero, zero, 1
|
||||
#define R10KCBARRIER(addr)
|
||||
#endif // defined(__has_include_next) && __has_include_next(<asm/asm.h>)
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
@ -0,0 +1,117 @@
|
||||
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H
|
||||
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H
|
||||
|
||||
#if defined(__has_include_next) && __has_include_next(<asm/fpregdef.h>)
|
||||
#include_next <asm/fpregdef.h>
|
||||
#else
|
||||
|
||||
/****************************************************************************
|
||||
****************************************************************************
|
||||
***
|
||||
*** This header was automatically generated from a Linux kernel header
|
||||
*** of the same name, to make information necessary for userspace to
|
||||
*** call into the kernel available to libc. It contains only constants,
|
||||
*** structures, and macros generated from the original header, and thus,
|
||||
*** contains no copyrightable information.
|
||||
***
|
||||
*** To edit the content of this header, modify the corresponding
|
||||
*** source file (e.g. under external/kernel-headers/original/) then
|
||||
*** run bionic/libc/kernel/tools/update_all.py
|
||||
***
|
||||
*** Any manual change here will be lost the next time this script will
|
||||
*** be run. You've been warned!
|
||||
***
|
||||
****************************************************************************
|
||||
****************************************************************************/
|
||||
|
||||
#include <asm/sgidefs.h>
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fv0 $f0
|
||||
#define fv0f $f1
|
||||
#define fv1 $f2
|
||||
#define fv1f $f3
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fa0 $f12
|
||||
#define fa0f $f13
|
||||
#define fa1 $f14
|
||||
#define fa1f $f15
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define ft0 $f4
|
||||
#define ft0f $f5
|
||||
#define ft1 $f6
|
||||
#define ft1f $f7
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define ft2 $f8
|
||||
#define ft2f $f9
|
||||
#define ft3 $f10
|
||||
#define ft3f $f11
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define ft4 $f16
|
||||
#define ft4f $f17
|
||||
#define ft5 $f18
|
||||
#define ft5f $f19
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fs0 $f20
|
||||
#define fs0f $f21
|
||||
#define fs1 $f22
|
||||
#define fs1f $f23
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fs2 $f24
|
||||
#define fs2f $f25
|
||||
#define fs3 $f26
|
||||
#define fs3f $f27
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fs4 $f28
|
||||
#define fs4f $f29
|
||||
#define fs5 $f30
|
||||
#define fs5f $f31
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fcr31 $31
|
||||
#endif
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
|
||||
#define fv0 $f0
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fv1 $f2
|
||||
#define fa0 $f12
|
||||
#define fa1 $f13
|
||||
#define fa2 $f14
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fa3 $f15
|
||||
#define fa4 $f16
|
||||
#define fa5 $f17
|
||||
#define fa6 $f18
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fa7 $f19
|
||||
#define ft0 $f4
|
||||
#define ft1 $f5
|
||||
#define ft2 $f6
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define ft3 $f7
|
||||
#define ft4 $f8
|
||||
#define ft5 $f9
|
||||
#define ft6 $f10
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define ft7 $f11
|
||||
#define ft8 $f20
|
||||
#define ft9 $f21
|
||||
#define ft10 $f22
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define ft11 $f23
|
||||
#define ft12 $f1
|
||||
#define ft13 $f3
|
||||
#define fs0 $f24
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fs1 $f25
|
||||
#define fs2 $f26
|
||||
#define fs3 $f27
|
||||
#define fs4 $f28
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define fs5 $f29
|
||||
#define fs6 $f30
|
||||
#define fs7 $f31
|
||||
#define fcr31 $31
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#endif
|
||||
#endif // defined(__has_include_next) && __has_include_next(<asm/fpregdef.h>)
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H
|
@ -0,0 +1,125 @@
|
||||
#ifndef GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H
|
||||
#define GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H
|
||||
|
||||
#if defined(__has_include_next) && __has_include_next(<asm/regdef.h>)
|
||||
#include_next <asm/regdef.h>
|
||||
#else
|
||||
|
||||
/****************************************************************************
|
||||
****************************************************************************
|
||||
***
|
||||
*** This header was automatically generated from a Linux kernel header
|
||||
*** of the same name, to make information necessary for userspace to
|
||||
*** call into the kernel available to libc. It contains only constants,
|
||||
*** structures, and macros generated from the original header, and thus,
|
||||
*** contains no copyrightable information.
|
||||
***
|
||||
*** To edit the content of this header, modify the corresponding
|
||||
*** source file (e.g. under external/kernel-headers/original/) then
|
||||
*** run bionic/libc/kernel/tools/update_all.py
|
||||
***
|
||||
*** Any manual change here will be lost the next time this script will
|
||||
*** be run. You've been warned!
|
||||
***
|
||||
****************************************************************************
|
||||
****************************************************************************/
|
||||
|
||||
#include <asm/sgidefs.h>
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI32
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define zero $0
|
||||
#define AT $1
|
||||
#define v0 $2
|
||||
#define v1 $3
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define a0 $4
|
||||
#define a1 $5
|
||||
#define a2 $6
|
||||
#define a3 $7
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define t0 $8
|
||||
#define t1 $9
|
||||
#define t2 $10
|
||||
#define t3 $11
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define t4 $12
|
||||
#define t5 $13
|
||||
#define t6 $14
|
||||
#define t7 $15
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define s0 $16
|
||||
#define s1 $17
|
||||
#define s2 $18
|
||||
#define s3 $19
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define s4 $20
|
||||
#define s5 $21
|
||||
#define s6 $22
|
||||
#define s7 $23
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define t8 $24
|
||||
#define t9 $25
|
||||
#define jp $25
|
||||
#define k0 $26
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define k1 $27
|
||||
#define gp $28
|
||||
#define sp $29
|
||||
#define fp $30
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define s8 $30
|
||||
#define ra $31
|
||||
#endif
|
||||
#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define zero $0
|
||||
#define AT $at
|
||||
#define v0 $2
|
||||
#define v1 $3
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define a0 $4
|
||||
#define a1 $5
|
||||
#define a2 $6
|
||||
#define a3 $7
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define a4 $8
|
||||
#define ta0 $8
|
||||
#define a5 $9
|
||||
#define ta1 $9
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define a6 $10
|
||||
#define ta2 $10
|
||||
#define a7 $11
|
||||
#define ta3 $11
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define t0 $12
|
||||
#define t1 $13
|
||||
#define t2 $14
|
||||
#define t3 $15
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define s0 $16
|
||||
#define s1 $17
|
||||
#define s2 $18
|
||||
#define s3 $19
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define s4 $20
|
||||
#define s5 $21
|
||||
#define s6 $22
|
||||
#define s7 $23
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define t8 $24
|
||||
#define t9 $25
|
||||
#define jp $25
|
||||
#define k0 $26
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define k1 $27
|
||||
#define gp $28
|
||||
#define sp $29
|
||||
#define fp $30
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
||||
#define s8 $30
|
||||
#define ra $31
|
||||
#endif
|
||||
#endif // defined(__has_include_next) && __has_include_next(<asm/regdef.h>)
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H
|
||||
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
|
@ -49,7 +49,6 @@
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#if defined(ANDROID) && ANDROID_VERSION <= 20
|
||||
struct r_debug {
|
||||
int r_version;
|
||||
struct link_map* r_map;
|
||||
@ -68,7 +67,6 @@ struct link_map {
|
||||
struct link_map* l_next;
|
||||
struct link_map* l_prev;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
@ -38,6 +38,10 @@
|
||||
// TODO(primiano): remove these changes after Chromium has stably rolled to
|
||||
// an NDK with the appropriate fixes. https://crbug.com/358831
|
||||
|
||||
// With traditional headers, <sys/user.h> forgot to do this. Unified headers get
|
||||
// it right.
|
||||
#include <sys/types.h>
|
||||
|
||||
#include_next <sys/user.h>
|
||||
|
||||
#include <android/api-level.h>
|
||||
|
@ -50,7 +50,6 @@
|
||||
'android/breakpad_getcontext.S',
|
||||
'android/include/elf.h',
|
||||
'android/include/link.h',
|
||||
'android/include/sgidefs.h',
|
||||
'android/include/stab.h',
|
||||
'android/include/sys/procfs.h',
|
||||
'android/include/sys/signal.h',
|
||||
@ -121,6 +120,8 @@
|
||||
'linux/safe_readlink.h',
|
||||
'linux/synth_elf.cc',
|
||||
'linux/synth_elf.h',
|
||||
'long_string_dictionary.cc',
|
||||
'long_string_dictionary.h',
|
||||
'mac/arch_utilities.cc',
|
||||
'mac/arch_utilities.h',
|
||||
'mac/bootstrap_compat.cc',
|
||||
@ -220,6 +221,7 @@
|
||||
'linux/tests/auto_testfile.h',
|
||||
'linux/tests/crash_generator.cc',
|
||||
'linux/tests/crash_generator.h',
|
||||
'long_string_dictionary_unittest.cc',
|
||||
'mac/macho_reader_unittest.cc',
|
||||
'memory_allocator_unittest.cc',
|
||||
'memory_range_unittest.cc',
|
||||
@ -238,9 +240,9 @@
|
||||
],
|
||||
'dependencies': [
|
||||
'common',
|
||||
'../build/testing.gypi:gmock_main',
|
||||
'../build/testing.gypi:gmock',
|
||||
'../build/testing.gypi:gtest',
|
||||
'../build/testing.gyp:gmock_main',
|
||||
'../build/testing.gyp:gmock',
|
||||
'../build/testing.gyp:gtest',
|
||||
],
|
||||
'libraries': [
|
||||
'-ldl',
|
||||
|
@ -46,7 +46,9 @@ CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
|
||||
unsigned return_address_register,
|
||||
uint8_t version,
|
||||
const string &augmentation,
|
||||
bool dwarf64) {
|
||||
bool dwarf64,
|
||||
uint8_t address_size,
|
||||
uint8_t segment_size) {
|
||||
assert(!entry_length_);
|
||||
entry_length_ = new PendingLength();
|
||||
in_fde_ = false;
|
||||
@ -63,6 +65,10 @@ CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
|
||||
}
|
||||
D8(version);
|
||||
AppendCString(augmentation);
|
||||
if (version >= 4) {
|
||||
D8(address_size);
|
||||
D8(segment_size);
|
||||
}
|
||||
ULEB128(code_alignment_factor);
|
||||
LEB128(data_alignment_factor);
|
||||
if (version == 1)
|
||||
|
@ -138,7 +138,9 @@ class CFISection: public Section {
|
||||
unsigned return_address_register,
|
||||
uint8_t version = 3,
|
||||
const string &augmentation = "",
|
||||
bool dwarf64 = false);
|
||||
bool dwarf64 = false,
|
||||
uint8_t address_size = 8,
|
||||
uint8_t segment_size = 0);
|
||||
|
||||
// Append a Frame Description Entry header to this section with the
|
||||
// given values. If dwarf64 is true, use the 64-bit DWARF initial
|
||||
|
@ -232,7 +232,7 @@ enum DwarfAttribute {
|
||||
DW_AT_call_column = 0x57,
|
||||
DW_AT_call_file = 0x58,
|
||||
DW_AT_call_line = 0x59,
|
||||
// DWARF 4 values.
|
||||
// DWARF 4
|
||||
DW_AT_linkage_name = 0x6e,
|
||||
// SGI/MIPS extensions.
|
||||
DW_AT_MIPS_fde = 0x2001,
|
||||
@ -501,7 +501,7 @@ enum DwarfOpcode {
|
||||
DW_OP_call_frame_cfa =0x9c,
|
||||
DW_OP_bit_piece =0x9d,
|
||||
DW_OP_lo_user =0xe0,
|
||||
DW_OP_hi_user =0xff,
|
||||
DW_OP_hi_user =0xff,
|
||||
// GNU extensions
|
||||
DW_OP_GNU_push_tls_address =0xe0,
|
||||
// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
|
||||
@ -544,6 +544,8 @@ enum DwarfLanguage
|
||||
DW_LANG_ObjC_plus_plus =0x0011,
|
||||
DW_LANG_UPC =0x0012,
|
||||
DW_LANG_D =0x0013,
|
||||
DW_LANG_Rust =0x001c,
|
||||
DW_LANG_Swift =0x001e,
|
||||
// Implementation-defined language code range.
|
||||
DW_LANG_lo_user = 0x8000,
|
||||
DW_LANG_hi_user = 0xffff,
|
||||
@ -670,7 +672,7 @@ enum DwarfPointerEncoding
|
||||
// encoding (except DW_EH_PE_aligned), and indicates that the
|
||||
// encoded value represents the address at which the true address
|
||||
// is stored, not the true address itself.
|
||||
DW_EH_PE_indirect = 0x80
|
||||
DW_EH_PE_indirect = 0x80
|
||||
};
|
||||
|
||||
} // namespace dwarf2reader
|
||||
|
@ -942,6 +942,13 @@ void LineInfo::ReadHeader() {
|
||||
header_.min_insn_length = reader_->ReadOneByte(lineptr);
|
||||
lineptr += 1;
|
||||
|
||||
if (header_.version >= 4) {
|
||||
__attribute__((unused)) uint8 max_ops_per_insn =
|
||||
reader_->ReadOneByte(lineptr);
|
||||
++lineptr;
|
||||
assert(max_ops_per_insn == 1);
|
||||
}
|
||||
|
||||
header_.default_is_stmt = reader_->ReadOneByte(lineptr);
|
||||
lineptr += 1;
|
||||
|
||||
@ -1258,12 +1265,12 @@ class CallFrameInfo::Rule {
|
||||
public:
|
||||
virtual ~Rule() { }
|
||||
|
||||
// Tell HANDLER that, at ADDRESS in the program, REGISTER can be
|
||||
// recovered using this rule. If REGISTER is kCFARegister, then this rule
|
||||
// describes how to compute the canonical frame address. Return what the
|
||||
// HANDLER member function returned.
|
||||
// Tell HANDLER that, at ADDRESS in the program, REG can be recovered using
|
||||
// this rule. If REG is kCFARegister, then this rule describes how to compute
|
||||
// the canonical frame address. Return what the HANDLER member function
|
||||
// returned.
|
||||
virtual bool Handle(Handler *handler,
|
||||
uint64 address, int register) const = 0;
|
||||
uint64 address, int reg) const = 0;
|
||||
|
||||
// Equality on rules. We use these to decide which rules we need
|
||||
// to report after a DW_CFA_restore_state instruction.
|
||||
@ -2253,11 +2260,11 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
cursor++;
|
||||
|
||||
// If we don't recognize the version, we can't parse any more fields of the
|
||||
// CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
|
||||
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
|
||||
// CIE. For DWARF CFI, we handle versions 1 through 4 (there was never a
|
||||
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 4 as well;
|
||||
// the difference between those versions seems to be the same as for
|
||||
// .debug_frame.
|
||||
if (cie->version < 1 || cie->version > 3) {
|
||||
if (cie->version < 1 || cie->version > 4) {
|
||||
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
||||
return false;
|
||||
}
|
||||
@ -2287,16 +2294,36 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
}
|
||||
}
|
||||
|
||||
if (cie->version >= 4) {
|
||||
uint8_t address_size = *cursor++;
|
||||
if (address_size != 8) {
|
||||
// TODO(scottmg): Only supporting x64 for now.
|
||||
reporter_->UnexpectedAddressSize(cie->offset, address_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t segment_size = *cursor++;
|
||||
if (segment_size != 0) {
|
||||
// TODO(scottmg): Only supporting x64 for now.
|
||||
// I would have perhaps expected 4 here, but LLVM emits a 0, near
|
||||
// http://llvm.org/docs/doxygen/html/MCDwarf_8cpp_source.html#l00606. As
|
||||
// we are not using the value, only succeed for now if it's the expected
|
||||
// 0.
|
||||
reporter_->UnexpectedSegmentSize(cie->offset, segment_size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the code alignment factor.
|
||||
cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
|
||||
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
|
||||
cursor += len;
|
||||
|
||||
|
||||
// Parse the data alignment factor.
|
||||
cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
|
||||
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
|
||||
cursor += len;
|
||||
|
||||
|
||||
// Parse the return address register. This is a ubyte in version 1, and
|
||||
// a ULEB128 in version 3.
|
||||
if (cie->version == 1) {
|
||||
@ -2407,7 +2434,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CallFrameInfo::ReadFDEFields(FDE *fde) {
|
||||
const uint8_t *cursor = fde->fields;
|
||||
size_t size;
|
||||
@ -2648,6 +2675,22 @@ void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) {
|
||||
filename_.c_str(), offset, section_.c_str(), cie_offset);
|
||||
}
|
||||
|
||||
void CallFrameInfo::Reporter::UnexpectedAddressSize(uint64 offset,
|
||||
uint8_t address_size) {
|
||||
fprintf(stderr,
|
||||
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||
" CIE specifies unexpected address size: %d\n",
|
||||
filename_.c_str(), offset, section_.c_str(), address_size);
|
||||
}
|
||||
|
||||
void CallFrameInfo::Reporter::UnexpectedSegmentSize(uint64 offset,
|
||||
uint8_t segment_size) {
|
||||
fprintf(stderr,
|
||||
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||
" CIE specifies unexpected segment size: %d\n",
|
||||
filename_.c_str(), offset, section_.c_str(), segment_size);
|
||||
}
|
||||
|
||||
void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
|
||||
fprintf(stderr,
|
||||
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||
|
@ -1227,6 +1227,14 @@ class CallFrameInfo::Reporter {
|
||||
// there is not a CIE.
|
||||
virtual void BadCIEId(uint64 offset, uint64 cie_offset);
|
||||
|
||||
// The FDE at OFFSET refers to a CIE with an address size we don't know how
|
||||
// to handle.
|
||||
virtual void UnexpectedAddressSize(uint64 offset, uint8_t address_size);
|
||||
|
||||
// The FDE at OFFSET refers to a CIE with an segment descriptor size we
|
||||
// don't know how to handle.
|
||||
virtual void UnexpectedSegmentSize(uint64 offset, uint8_t segment_size);
|
||||
|
||||
// The FDE at OFFSET refers to a CIE with version number VERSION,
|
||||
// which we don't recognize. We cannot parse DWARF CFI if it uses
|
||||
// a version number we don't recognize.
|
||||
|
@ -126,6 +126,8 @@ class MockCallFrameErrorReporter: public CallFrameInfo::Reporter {
|
||||
MOCK_METHOD1(EarlyEHTerminator, void(uint64));
|
||||
MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64));
|
||||
MOCK_METHOD2(BadCIEId, void(uint64, uint64));
|
||||
MOCK_METHOD2(UnexpectedAddressSize, void(uint64, uint8_t));
|
||||
MOCK_METHOD2(UnexpectedSegmentSize, void(uint64, uint8_t));
|
||||
MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version));
|
||||
MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &));
|
||||
MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8));
|
||||
@ -605,6 +607,91 @@ TEST_F(CFI, CIEVersion3ReturnColumn) {
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
||||
TEST_F(CFI, CIEVersion4AdditionalFields) {
|
||||
CFISection section(kBigEndian, 4);
|
||||
Label cie;
|
||||
section
|
||||
.Mark(&cie)
|
||||
// CIE version 4 with expected address and segment size.
|
||||
.CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 4, "", true, 8, 0)
|
||||
.FinishEntry()
|
||||
// FDE, citing that CIE.
|
||||
.FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
|
||||
.FinishEntry();
|
||||
|
||||
PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section);
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 4, "", 0x89))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(handler, End()).WillOnce(Return(true));
|
||||
}
|
||||
|
||||
string contents;
|
||||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
||||
TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedAddressSize) {
|
||||
CFISection section(kBigEndian, 4);
|
||||
Label cie;
|
||||
|
||||
section
|
||||
.Mark(&cie)
|
||||
// Unexpected address size.
|
||||
.CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 4, "", true, 3, 0)
|
||||
.FinishEntry()
|
||||
// FDE, citing that CIE.
|
||||
.FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
|
||||
.FinishEntry();
|
||||
|
||||
PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedAddress", section);
|
||||
|
||||
EXPECT_CALL(reporter, UnexpectedAddressSize(_, 3))
|
||||
.WillOnce(Return());
|
||||
|
||||
string contents;
|
||||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(8);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
||||
TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedSegmentSize) {
|
||||
CFISection section(kBigEndian, 4);
|
||||
Label cie;
|
||||
|
||||
section
|
||||
.Mark(&cie)
|
||||
.CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 4, "", true, 8, 7)
|
||||
.FinishEntry()
|
||||
.FDEHeader(cie, 0x7bf0fda0, 0xcbcd28d8)
|
||||
.FinishEntry();
|
||||
|
||||
PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedSegment", section);
|
||||
|
||||
EXPECT_CALL(reporter, UnexpectedSegmentSize(_, 7))
|
||||
.WillOnce(Return());
|
||||
|
||||
string contents;
|
||||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(8);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
||||
struct CFIInsnFixture: public CFIFixture {
|
||||
CFIInsnFixture() : CFIFixture() {
|
||||
data_factor = 0xb6f;
|
||||
|
@ -19,8 +19,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/dwarf/types.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::pair;
|
||||
|
||||
|
@ -195,7 +195,7 @@ void DwarfCFIToModule::Record(Module::Address address, int reg,
|
||||
|
||||
// Place the name in our global set of strings, and then use the string
|
||||
// from the set. Even though the assignment looks like a copy, all the
|
||||
// major std::string implementations use reference counting internally,
|
||||
// major string implementations use reference counting internally,
|
||||
// so the effect is to have all our data structures share copies of rules
|
||||
// whenever possible. Since register names are drawn from a
|
||||
// vector<string>, register names are already shared.
|
||||
|
@ -181,7 +181,7 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
|
||||
|
||||
// The names of the return address and canonical frame address. Putting
|
||||
// these here instead of using string literals allows us to share their
|
||||
// texts in reference-counted std::string implementations (all the
|
||||
// texts in reference-counted string implementations (all the
|
||||
// popular ones). Many, many rules cite these strings.
|
||||
string cfa_name_, ra_name_;
|
||||
|
||||
@ -189,7 +189,7 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
|
||||
// our data structures, insert it into this set, and then use the string
|
||||
// from the set.
|
||||
//
|
||||
// Because std::string uses reference counting internally, simply using
|
||||
// Because string uses reference counting internally, simply using
|
||||
// strings from this set, even if passed by value, assigned, or held
|
||||
// directly in structures and containers (map<string, ...>, for example),
|
||||
// causes those strings to share a single instance of each distinct piece
|
||||
|
@ -39,9 +39,6 @@
|
||||
#include "common/dwarf_cu_to_module.h"
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@ -264,7 +261,7 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
|
||||
uint64 offset_;
|
||||
|
||||
// Place the name in the global set of strings. Even though this looks
|
||||
// like a copy, all the major std::string implementations use reference
|
||||
// like a copy, all the major string implementations use reference
|
||||
// counting internally, so the effect is to have all the data structures
|
||||
// share copies of strings whenever possible.
|
||||
// FIXME: Should this return something like a string_ref to avoid the
|
||||
@ -354,22 +351,23 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
||||
case dwarf2reader::DW_AT_name:
|
||||
name_attribute_ = AddStringToPool(data);
|
||||
break;
|
||||
case dwarf2reader::DW_AT_linkage_name:
|
||||
case dwarf2reader::DW_AT_MIPS_linkage_name: {
|
||||
char* demangled = NULL;
|
||||
int status = -1;
|
||||
#if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle.
|
||||
demangled = abi::__cxa_demangle(data.c_str(), NULL, NULL, &status);
|
||||
#endif
|
||||
if (status != 0) {
|
||||
cu_context_->reporter->DemangleError(data, status);
|
||||
demangled_name_ = "";
|
||||
raw_name_ = AddStringToPool(data);
|
||||
break;
|
||||
}
|
||||
if (demangled) {
|
||||
demangled_name_ = AddStringToPool(demangled);
|
||||
free(reinterpret_cast<void*>(demangled));
|
||||
case dwarf2reader::DW_AT_MIPS_linkage_name:
|
||||
case dwarf2reader::DW_AT_linkage_name: {
|
||||
string demangled;
|
||||
Language::DemangleResult result =
|
||||
cu_context_->language->DemangleName(data, &demangled);
|
||||
switch (result) {
|
||||
case Language::kDemangleSuccess:
|
||||
demangled_name_ = AddStringToPool(demangled);
|
||||
break;
|
||||
|
||||
case Language::kDemangleFailure:
|
||||
cu_context_->reporter->DemangleError(data);
|
||||
// fallthrough
|
||||
case Language::kDontDemangle:
|
||||
demangled_name_.clear();
|
||||
raw_name_ = AddStringToPool(data);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -684,11 +682,10 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
|
||||
filename_.c_str(), offset);
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::DemangleError(
|
||||
const string &input, int error) {
|
||||
void DwarfCUToModule::WarningReporter::DemangleError(const string &input) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: failed to demangle %s with error %d\n",
|
||||
filename_.c_str(), input.c_str(), error);
|
||||
fprintf(stderr, "%s: warning: failed to demangle %s\n",
|
||||
filename_.c_str(), input.c_str());
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UnhandledInterCUReference(
|
||||
@ -769,6 +766,7 @@ dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
|
||||
case dwarf2reader::DW_TAG_class_type:
|
||||
case dwarf2reader::DW_TAG_structure_type:
|
||||
case dwarf2reader::DW_TAG_union_type:
|
||||
case dwarf2reader::DW_TAG_module:
|
||||
return new NamedScopeHandler(cu_context_.get(), child_context_.get(),
|
||||
offset);
|
||||
default:
|
||||
@ -782,6 +780,14 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
|
||||
cu_context_->language = Language::Java;
|
||||
break;
|
||||
|
||||
case dwarf2reader::DW_LANG_Swift:
|
||||
cu_context_->language = Language::Swift;
|
||||
break;
|
||||
|
||||
case dwarf2reader::DW_LANG_Rust:
|
||||
cu_context_->language = Language::Rust;
|
||||
break;
|
||||
|
||||
// DWARF has no generic language code for assembly language; this is
|
||||
// what the GNU toolchain uses.
|
||||
case dwarf2reader::DW_LANG_Mips_Assembler:
|
||||
|
@ -202,7 +202,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||
virtual void UnnamedFunction(uint64 offset);
|
||||
|
||||
// __cxa_demangle() failed to demangle INPUT.
|
||||
virtual void DemangleError(const string &input, int error);
|
||||
virtual void DemangleError(const string &input);
|
||||
|
||||
// The DW_FORM_ref_addr at OFFSET to TARGET was not handled because
|
||||
// FilePrivate did not retain the inter-CU specification data.
|
||||
|
@ -83,7 +83,7 @@ class MockWarningReporter: public DwarfCUToModule::WarningReporter {
|
||||
MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function));
|
||||
MOCK_METHOD1(UncoveredLine, void(const Module::Line &line));
|
||||
MOCK_METHOD1(UnnamedFunction, void(uint64 offset));
|
||||
MOCK_METHOD2(DemangleError, void(const string &input, int error));
|
||||
MOCK_METHOD1(DemangleError, void(const string &input));
|
||||
MOCK_METHOD2(UnhandledInterCUReference, void(uint64 offset, uint64 target));
|
||||
};
|
||||
|
||||
@ -1205,6 +1205,7 @@ TEST_F(Specifications, Function) {
|
||||
}
|
||||
|
||||
TEST_F(Specifications, MangledName) {
|
||||
// Language defaults to C++, so no need to set it here.
|
||||
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
|
||||
|
||||
StartCU();
|
||||
@ -1221,6 +1222,53 @@ TEST_F(Specifications, MangledName) {
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
}
|
||||
|
||||
TEST_F(Specifications, MangledNameSwift) {
|
||||
// Swift mangled names should pass through untouched.
|
||||
SetLanguage(dwarf2reader::DW_LANG_Swift);
|
||||
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
|
||||
StartCU();
|
||||
const string kName = "_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si";
|
||||
DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
|
||||
dwarf2reader::DW_TAG_subprogram, "declaration-name",
|
||||
kName);
|
||||
DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
|
||||
0xcd3c51b946fb1eeeLL, "",
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
root_handler_.Finish();
|
||||
|
||||
TestFunctionCount(1);
|
||||
TestFunction(0, kName,
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
}
|
||||
|
||||
TEST_F(Specifications, MangledNameRust) {
|
||||
SetLanguage(dwarf2reader::DW_LANG_Rust);
|
||||
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
|
||||
|
||||
StartCU();
|
||||
const string kName = "_ZN14rustc_demangle8demangle17h373defa94bffacdeE";
|
||||
DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
|
||||
dwarf2reader::DW_TAG_subprogram, "declaration-name",
|
||||
kName);
|
||||
DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
|
||||
0xcd3c51b946fb1eeeLL, "",
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
root_handler_.Finish();
|
||||
|
||||
TestFunctionCount(1);
|
||||
TestFunction(0,
|
||||
#ifndef HAVE_RUST_DEMANGLE
|
||||
// Rust mangled names should pass through untouched if not
|
||||
// using rust-demangle.
|
||||
kName,
|
||||
#else
|
||||
// If rust-demangle is available this should be properly
|
||||
// demangled.
|
||||
"rustc_demangle::demangle",
|
||||
#endif
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
}
|
||||
|
||||
TEST_F(Specifications, MemberFunction) {
|
||||
PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691);
|
||||
|
||||
|
@ -34,18 +34,70 @@
|
||||
|
||||
#include "common/language.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_RUST_DEMANGLE)
|
||||
#include <rust_demangle.h>
|
||||
#endif
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
|
||||
string MakeQualifiedNameWithSeparator(const string& parent_name,
|
||||
const char* separator,
|
||||
const string& name) {
|
||||
if (parent_name.empty()) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return parent_name + separator + name;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// C++ language-specific operations.
|
||||
class CPPLanguage: public Language {
|
||||
public:
|
||||
CPPLanguage() {}
|
||||
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
if (parent_name.empty())
|
||||
return name;
|
||||
else
|
||||
return parent_name + "::" + name;
|
||||
return MakeQualifiedNameWithSeparator(parent_name, "::", name);
|
||||
}
|
||||
|
||||
virtual DemangleResult DemangleName(const string& mangled,
|
||||
string* demangled) const {
|
||||
#if defined(__ANDROID__)
|
||||
// Android NDK doesn't provide abi::__cxa_demangle.
|
||||
demangled->clear();
|
||||
return kDontDemangle;
|
||||
#else
|
||||
int status;
|
||||
char* demangled_c =
|
||||
abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
|
||||
|
||||
DemangleResult result;
|
||||
if (status == 0) {
|
||||
result = kDemangleSuccess;
|
||||
demangled->assign(demangled_c);
|
||||
} else {
|
||||
result = kDemangleFailure;
|
||||
demangled->clear();
|
||||
}
|
||||
|
||||
if (demangled_c) {
|
||||
free(reinterpret_cast<void*>(demangled_c));
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@ -54,19 +106,79 @@ CPPLanguage CPPLanguageSingleton;
|
||||
// Java language-specific operations.
|
||||
class JavaLanguage: public Language {
|
||||
public:
|
||||
JavaLanguage() {}
|
||||
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
if (parent_name.empty())
|
||||
return name;
|
||||
else
|
||||
return parent_name + "." + name;
|
||||
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
|
||||
}
|
||||
};
|
||||
|
||||
JavaLanguage JavaLanguageSingleton;
|
||||
|
||||
// Swift language-specific operations.
|
||||
class SwiftLanguage: public Language {
|
||||
public:
|
||||
SwiftLanguage() {}
|
||||
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
|
||||
}
|
||||
|
||||
virtual DemangleResult DemangleName(const string& mangled,
|
||||
string* demangled) const {
|
||||
// There is no programmatic interface to a Swift demangler. Pass through the
|
||||
// mangled form because it encodes more information than the qualified name
|
||||
// that would have been built by MakeQualifiedName(). The output can be
|
||||
// post-processed by xcrun swift-demangle to transform mangled Swift names
|
||||
// into something more readable.
|
||||
demangled->assign(mangled);
|
||||
return kDemangleSuccess;
|
||||
}
|
||||
};
|
||||
|
||||
SwiftLanguage SwiftLanguageSingleton;
|
||||
|
||||
// Rust language-specific operations.
|
||||
class RustLanguage: public Language {
|
||||
public:
|
||||
RustLanguage() {}
|
||||
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
|
||||
}
|
||||
|
||||
virtual DemangleResult DemangleName(const string& mangled,
|
||||
string* demangled) const {
|
||||
// Rust names use GCC C++ name mangling, but demangling them with
|
||||
// abi_demangle doesn't produce stellar results due to them having
|
||||
// another layer of encoding.
|
||||
// If callers provide rustc-demangle, use that.
|
||||
#if defined(HAVE_RUST_DEMANGLE)
|
||||
char* rust_demangled = rust_demangle(mangled.c_str());
|
||||
if (rust_demangled == nullptr) {
|
||||
return kDemangleFailure;
|
||||
}
|
||||
demangled->assign(rust_demangled);
|
||||
free_rust_demangled_name(rust_demangled);
|
||||
#else
|
||||
// Otherwise, pass through the mangled name so callers can demangle
|
||||
// after the fact.
|
||||
demangled->assign(mangled);
|
||||
#endif
|
||||
return kDemangleSuccess;
|
||||
}
|
||||
};
|
||||
|
||||
RustLanguage RustLanguageSingleton;
|
||||
|
||||
// Assembler language-specific operations.
|
||||
class AssemblerLanguage: public Language {
|
||||
public:
|
||||
AssemblerLanguage() {}
|
||||
|
||||
bool HasFunctions() const { return false; }
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
@ -78,6 +190,8 @@ AssemblerLanguage AssemblerLanguageSingleton;
|
||||
|
||||
const Language * const Language::CPlusPlus = &CPPLanguageSingleton;
|
||||
const Language * const Language::Java = &JavaLanguageSingleton;
|
||||
const Language * const Language::Swift = &SwiftLanguageSingleton;
|
||||
const Language * const Language::Rust = &RustLanguageSingleton;
|
||||
const Language * const Language::Assembler = &AssemblerLanguageSingleton;
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -77,9 +77,26 @@ class Language {
|
||||
virtual string MakeQualifiedName (const string &parent_name,
|
||||
const string &name) const = 0;
|
||||
|
||||
enum DemangleResult {
|
||||
// Demangling was not performed because it’s not appropriate to attempt.
|
||||
kDontDemangle = -1,
|
||||
|
||||
kDemangleSuccess,
|
||||
kDemangleFailure,
|
||||
};
|
||||
|
||||
// Wraps abi::__cxa_demangle() or similar for languages where appropriate.
|
||||
virtual DemangleResult DemangleName(const string& mangled,
|
||||
string* demangled) const {
|
||||
demangled->clear();
|
||||
return kDontDemangle;
|
||||
}
|
||||
|
||||
// Instances for specific languages.
|
||||
static const Language * const CPlusPlus,
|
||||
* const Java,
|
||||
* const Swift,
|
||||
* const Rust,
|
||||
* const Assembler;
|
||||
};
|
||||
|
||||
|
@ -67,6 +67,7 @@
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/memory_allocator.h"
|
||||
#include "common/module.h"
|
||||
#include "common/path_helper.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#ifndef NO_STABS_SUPPORT
|
||||
#include "common/stabs_reader.h"
|
||||
@ -107,15 +108,6 @@ using google_breakpad::wasteful_vector;
|
||||
#define EM_AARCH64 183
|
||||
#endif
|
||||
|
||||
// Define SHT_ANDROID_REL and SHT_ANDROID_RELA if not defined by the host.
|
||||
// Sections with this type contain Android packed relocations.
|
||||
#ifndef SHT_ANDROID_REL
|
||||
#define SHT_ANDROID_REL (SHT_LOOS + 1)
|
||||
#endif
|
||||
#ifndef SHT_ANDROID_RELA
|
||||
#define SHT_ANDROID_RELA (SHT_LOOS + 2)
|
||||
#endif
|
||||
|
||||
//
|
||||
// FDWrapper
|
||||
//
|
||||
@ -662,7 +654,6 @@ bool LoadSymbols(const string& obj_file,
|
||||
typedef typename ElfClass::Addr Addr;
|
||||
typedef typename ElfClass::Phdr Phdr;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
typedef typename ElfClass::Word Word;
|
||||
|
||||
Addr loading_addr = GetLoadingAddress<ElfClass>(
|
||||
GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
|
||||
@ -670,8 +661,6 @@ bool LoadSymbols(const string& obj_file,
|
||||
module->SetLoadAddress(loading_addr);
|
||||
info->set_loading_addr(loading_addr, obj_file);
|
||||
|
||||
Word debug_section_type =
|
||||
elf_header->e_machine == EM_MIPS ? SHT_MIPS_DWARF : SHT_PROGBITS;
|
||||
const Shdr* sections =
|
||||
GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
|
||||
const Shdr* section_names = sections + elf_header->e_shstrndx;
|
||||
@ -681,28 +670,6 @@ bool LoadSymbols(const string& obj_file,
|
||||
bool found_debug_info_section = false;
|
||||
bool found_usable_info = false;
|
||||
|
||||
// Reject files that contain Android packed relocations. The pre-packed
|
||||
// version of the file should be symbolized; the packed version is only
|
||||
// intended for use on the target system.
|
||||
if (FindElfSectionByName<ElfClass>(".rel.dyn", SHT_ANDROID_REL,
|
||||
sections, names,
|
||||
names_end, elf_header->e_shnum)) {
|
||||
fprintf(stderr, "%s: file contains a \".rel.dyn\" section "
|
||||
"with type SHT_ANDROID_REL\n", obj_file.c_str());
|
||||
fprintf(stderr, "Files containing Android packed relocations "
|
||||
"may not be symbolized.\n");
|
||||
return false;
|
||||
}
|
||||
if (FindElfSectionByName<ElfClass>(".rela.dyn", SHT_ANDROID_RELA,
|
||||
sections, names,
|
||||
names_end, elf_header->e_shnum)) {
|
||||
fprintf(stderr, "%s: file contains a \".rela.dyn\" section "
|
||||
"with type SHT_ANDROID_RELA\n", obj_file.c_str());
|
||||
fprintf(stderr, "Files containing Android packed relocations "
|
||||
"may not be symbolized.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.symbol_data != ONLY_CFI) {
|
||||
#ifndef NO_STABS_SUPPORT
|
||||
// Look for STABS debugging information, and load it if present.
|
||||
@ -727,9 +694,19 @@ bool LoadSymbols(const string& obj_file,
|
||||
|
||||
// Look for DWARF debugging information, and load it if present.
|
||||
const Shdr* dwarf_section =
|
||||
FindElfSectionByName<ElfClass>(".debug_info", debug_section_type,
|
||||
FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
|
||||
// .debug_info section type is SHT_PROGBITS for mips on pnacl toolchains,
|
||||
// but MIPS_DWARF for regular gnu toolchains, so both need to be checked
|
||||
if (elf_header->e_machine == EM_MIPS && !dwarf_section) {
|
||||
dwarf_section =
|
||||
FindElfSectionByName<ElfClass>(".debug_info", SHT_MIPS_DWARF,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
}
|
||||
|
||||
if (dwarf_section) {
|
||||
found_debug_info_section = true;
|
||||
found_usable_info = true;
|
||||
@ -804,9 +781,19 @@ bool LoadSymbols(const string& obj_file,
|
||||
// Dwarf Call Frame Information (CFI) is actually independent from
|
||||
// the other DWARF debugging information, and can be used alone.
|
||||
const Shdr* dwarf_cfi_section =
|
||||
FindElfSectionByName<ElfClass>(".debug_frame", debug_section_type,
|
||||
FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
|
||||
// .debug_frame section type is SHT_PROGBITS for mips on pnacl toolchains,
|
||||
// but MIPS_DWARF for regular gnu toolchains, so both need to be checked
|
||||
if (elf_header->e_machine == EM_MIPS && !dwarf_cfi_section) {
|
||||
dwarf_cfi_section =
|
||||
FindElfSectionByName<ElfClass>(".debug_frame", SHT_MIPS_DWARF,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
}
|
||||
|
||||
if (dwarf_cfi_section) {
|
||||
// Ignore the return value of this function; even without call frame
|
||||
// information, the other debugging information could be perfectly
|
||||
@ -935,16 +922,6 @@ const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
|
||||
}
|
||||
}
|
||||
|
||||
// Return the non-directory portion of FILENAME: the portion after the
|
||||
// last slash, or the whole filename if there are no slashes.
|
||||
string BaseFileName(const string &filename) {
|
||||
// Lots of copies! basename's behavior is less than ideal.
|
||||
char* c_filename = strdup(filename.c_str());
|
||||
string base = basename(c_filename);
|
||||
free(c_filename);
|
||||
return base;
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
bool SanitizeDebugFile(const typename ElfClass::Ehdr* debug_elf_header,
|
||||
const string& debuglink_file,
|
||||
@ -995,7 +972,7 @@ bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
|
||||
return false;
|
||||
}
|
||||
|
||||
string name = BaseFileName(obj_filename);
|
||||
string name = google_breakpad::BaseName(obj_filename);
|
||||
string os = "Linux";
|
||||
// Add an extra "0" at the end. PDB files on Windows have an 'age'
|
||||
// number appended to the end of the file identifier; this isn't
|
||||
|
@ -78,14 +78,12 @@ void FindElfClassSection(const char *elf_base,
|
||||
template<typename ElfClass>
|
||||
void FindElfClassSegment(const char *elf_base,
|
||||
typename ElfClass::Word segment_type,
|
||||
const void **segment_start,
|
||||
size_t *segment_size) {
|
||||
wasteful_vector<ElfSegment> *segments) {
|
||||
typedef typename ElfClass::Ehdr Ehdr;
|
||||
typedef typename ElfClass::Phdr Phdr;
|
||||
|
||||
assert(elf_base);
|
||||
assert(segment_start);
|
||||
assert(segment_size);
|
||||
assert(segments);
|
||||
|
||||
assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
|
||||
|
||||
@ -97,9 +95,10 @@ void FindElfClassSegment(const char *elf_base,
|
||||
|
||||
for (int i = 0; i < elf_header->e_phnum; ++i) {
|
||||
if (phdrs[i].p_type == segment_type) {
|
||||
*segment_start = elf_base + phdrs[i].p_offset;
|
||||
*segment_size = phdrs[i].p_filesz;
|
||||
return;
|
||||
ElfSegment seg = {};
|
||||
seg.start = elf_base + phdrs[i].p_offset;
|
||||
seg.size = phdrs[i].p_filesz;
|
||||
segments->push_back(seg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -122,8 +121,7 @@ bool FindElfSection(const void *elf_mapped_base,
|
||||
const char *section_name,
|
||||
uint32_t section_type,
|
||||
const void **section_start,
|
||||
size_t *section_size,
|
||||
int *elfclass) {
|
||||
size_t *section_size) {
|
||||
assert(elf_mapped_base);
|
||||
assert(section_start);
|
||||
assert(section_size);
|
||||
@ -135,10 +133,6 @@ bool FindElfSection(const void *elf_mapped_base,
|
||||
return false;
|
||||
|
||||
int cls = ElfClass(elf_mapped_base);
|
||||
if (elfclass) {
|
||||
*elfclass = cls;
|
||||
}
|
||||
|
||||
const char* elf_base =
|
||||
static_cast<const char*>(elf_mapped_base);
|
||||
|
||||
@ -155,37 +149,25 @@ bool FindElfSection(const void *elf_mapped_base,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindElfSegment(const void *elf_mapped_base,
|
||||
uint32_t segment_type,
|
||||
const void **segment_start,
|
||||
size_t *segment_size,
|
||||
int *elfclass) {
|
||||
bool FindElfSegments(const void* elf_mapped_base,
|
||||
uint32_t segment_type,
|
||||
wasteful_vector<ElfSegment>* segments) {
|
||||
assert(elf_mapped_base);
|
||||
assert(segment_start);
|
||||
assert(segment_size);
|
||||
|
||||
*segment_start = NULL;
|
||||
*segment_size = 0;
|
||||
assert(segments);
|
||||
|
||||
if (!IsValidElf(elf_mapped_base))
|
||||
return false;
|
||||
|
||||
int cls = ElfClass(elf_mapped_base);
|
||||
if (elfclass) {
|
||||
*elfclass = cls;
|
||||
}
|
||||
|
||||
const char* elf_base =
|
||||
static_cast<const char*>(elf_mapped_base);
|
||||
|
||||
if (cls == ELFCLASS32) {
|
||||
FindElfClassSegment<ElfClass32>(elf_base, segment_type,
|
||||
segment_start, segment_size);
|
||||
return *segment_start != NULL;
|
||||
FindElfClassSegment<ElfClass32>(elf_base, segment_type, segments);
|
||||
return true;
|
||||
} else if (cls == ELFCLASS64) {
|
||||
FindElfClassSegment<ElfClass64>(elf_base, segment_type,
|
||||
segment_start, segment_size);
|
||||
return *segment_start != NULL;
|
||||
FindElfClassSegment<ElfClass64>(elf_base, segment_type, segments);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -37,6 +37,8 @@
|
||||
#include <link.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common/memory_allocator.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Traits classes so consumers can write templatized code to deal
|
||||
@ -81,14 +83,12 @@ int ElfClass(const void* elf_base);
|
||||
// Attempt to find a section named |section_name| of type |section_type|
|
||||
// in the ELF binary data at |elf_mapped_base|. On success, returns true
|
||||
// and sets |*section_start| to point to the start of the section data,
|
||||
// and |*section_size| to the size of the section's data. If |elfclass|
|
||||
// is not NULL, set |*elfclass| to the ELF file class.
|
||||
// and |*section_size| to the size of the section's data.
|
||||
bool FindElfSection(const void *elf_mapped_base,
|
||||
const char *section_name,
|
||||
uint32_t section_type,
|
||||
const void **section_start,
|
||||
size_t *section_size,
|
||||
int *elfclass);
|
||||
size_t *section_size);
|
||||
|
||||
// Internal helper method, exposed for convenience for callers
|
||||
// that already have more info.
|
||||
@ -101,16 +101,17 @@ FindElfSectionByName(const char* name,
|
||||
const char* names_end,
|
||||
int nsection);
|
||||
|
||||
// Attempt to find the first segment of type |segment_type| in the ELF
|
||||
// binary data at |elf_mapped_base|. On success, returns true and sets
|
||||
// |*segment_start| to point to the start of the segment data, and
|
||||
// and |*segment_size| to the size of the segment's data. If |elfclass|
|
||||
// is not NULL, set |*elfclass| to the ELF file class.
|
||||
bool FindElfSegment(const void *elf_mapped_base,
|
||||
uint32_t segment_type,
|
||||
const void **segment_start,
|
||||
size_t *segment_size,
|
||||
int *elfclass);
|
||||
struct ElfSegment {
|
||||
const void* start;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
// Attempt to find all segments of type |segment_type| in the ELF
|
||||
// binary data at |elf_mapped_base|. On success, returns true and fills
|
||||
// |*segments| with a list of segments of the given type.
|
||||
bool FindElfSegments(const void* elf_mapped_base,
|
||||
uint32_t segment_type,
|
||||
wasteful_vector<ElfSegment>* segments);
|
||||
|
||||
// Convert an offset from an Elf header into a pointer to the mapped
|
||||
// address in the current process. Takes an extra template parameter
|
||||
|
@ -61,10 +61,11 @@ FileID::FileID(const char* path) : path_(path) {}
|
||||
// These functions are also used inside the crashed process, so be safe
|
||||
// and use the syscall/libc wrappers instead of direct syscalls or libc.
|
||||
|
||||
template<typename ElfClass>
|
||||
static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
|
||||
wasteful_vector<uint8_t>& identifier) {
|
||||
typedef typename ElfClass::Nhdr Nhdr;
|
||||
static_assert(sizeof(ElfClass32::Nhdr) == sizeof(ElfClass64::Nhdr),
|
||||
"Elf32_Nhdr and Elf64_Nhdr should be the same");
|
||||
typedef typename ElfClass32::Nhdr Nhdr;
|
||||
|
||||
const void* section_end = reinterpret_cast<const char*>(section) + length;
|
||||
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
|
||||
@ -94,24 +95,22 @@ static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
|
||||
// and copy it into |identifier|.
|
||||
static bool FindElfBuildIDNote(const void* elf_mapped_base,
|
||||
wasteful_vector<uint8_t>& identifier) {
|
||||
void* note_section;
|
||||
size_t note_size;
|
||||
int elfclass;
|
||||
if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
|
||||
(const void**)¬e_section, ¬e_size, &elfclass) ||
|
||||
note_size == 0) &&
|
||||
(!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
|
||||
(const void**)¬e_section, ¬e_size, &elfclass) ||
|
||||
note_size == 0)) {
|
||||
return false;
|
||||
PageAllocator allocator;
|
||||
// lld normally creates 2 PT_NOTEs, gold normally creates 1.
|
||||
auto_wasteful_vector<ElfSegment, 2> segs(&allocator);
|
||||
if (FindElfSegments(elf_mapped_base, PT_NOTE, &segs)) {
|
||||
for (ElfSegment& seg : segs) {
|
||||
if (ElfClassBuildIDNoteIdentifier(seg.start, seg.size, identifier)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (elfclass == ELFCLASS32) {
|
||||
return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size,
|
||||
identifier);
|
||||
} else if (elfclass == ELFCLASS64) {
|
||||
return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size,
|
||||
identifier);
|
||||
void* note_section;
|
||||
size_t note_size;
|
||||
if (FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
|
||||
(const void**)¬e_section, ¬e_size)) {
|
||||
return ElfClassBuildIDNoteIdentifier(note_section, note_size, identifier);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -126,7 +125,7 @@ static bool HashElfTextSection(const void* elf_mapped_base,
|
||||
void* text_section;
|
||||
size_t text_size;
|
||||
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
|
||||
(const void**)&text_section, &text_size, NULL) ||
|
||||
(const void**)&text_section, &text_size) ||
|
||||
text_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "common/memory_allocator.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
@ -70,16 +71,16 @@ class FileID {
|
||||
// Convert the |identifier| data to a string. The string will
|
||||
// be formatted as a UUID in all uppercase without dashes.
|
||||
// (e.g., 22F065BBFC9C49F780FE26A7CEBD7BCE).
|
||||
static std::string ConvertIdentifierToUUIDString(
|
||||
static string ConvertIdentifierToUUIDString(
|
||||
const wasteful_vector<uint8_t>& identifier);
|
||||
|
||||
// Convert the entire |identifier| data to a hex string.
|
||||
static std::string ConvertIdentifierToString(
|
||||
static string ConvertIdentifierToString(
|
||||
const wasteful_vector<uint8_t>& identifier);
|
||||
|
||||
private:
|
||||
// Storage for the path specified
|
||||
std::string path_;
|
||||
string path_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -278,6 +278,40 @@ TYPED_TEST(FileIDTest, BuildIDPH) {
|
||||
EXPECT_EQ(expected_identifier_string, identifier_string);
|
||||
}
|
||||
|
||||
TYPED_TEST(FileIDTest, BuildIDMultiplePH) {
|
||||
const uint8_t kExpectedIdentifierBytes[] =
|
||||
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13};
|
||||
const string expected_identifier_string =
|
||||
this->get_file_id(kExpectedIdentifierBytes);
|
||||
|
||||
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
elf.AddSection(".text", text, SHT_PROGBITS);
|
||||
Notes notes1(kLittleEndian);
|
||||
notes1.AddNote(0, "Linux",
|
||||
reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4);
|
||||
Notes notes2(kLittleEndian);
|
||||
notes2.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
|
||||
sizeof(kExpectedIdentifierBytes));
|
||||
int note1_idx = elf.AddSection(".note1", notes1, SHT_NOTE);
|
||||
int note2_idx = elf.AddSection(".note2", notes2, SHT_NOTE);
|
||||
elf.AddSegment(note1_idx, note1_idx, PT_NOTE);
|
||||
elf.AddSegment(note2_idx, note2_idx, PT_NOTE);
|
||||
elf.Finish();
|
||||
this->GetElfContents(elf);
|
||||
|
||||
id_vector identifier(this->make_vector());
|
||||
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
|
||||
identifier));
|
||||
EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
|
||||
|
||||
string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
|
||||
EXPECT_EQ(expected_identifier_string, identifier_string);
|
||||
}
|
||||
|
||||
// Test to make sure two files with different text sections produce
|
||||
// different hashes when not using a build id.
|
||||
TYPED_TEST(FileIDTest, UniqueHashes) {
|
||||
|
@ -27,15 +27,26 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/guid_creator.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(HAVE_SYS_RANDOM_H)
|
||||
#include <sys/random.h>
|
||||
#endif
|
||||
|
||||
//
|
||||
// GUIDGenerator
|
||||
//
|
||||
@ -61,28 +72,101 @@ class GUIDGenerator {
|
||||
}
|
||||
|
||||
static bool CreateGUID(GUID *guid) {
|
||||
InitOnce();
|
||||
guid->data1 = random();
|
||||
guid->data2 = (uint16_t)(random());
|
||||
guid->data3 = (uint16_t)(random());
|
||||
UInt32ToBytes(&guid->data4[0], random());
|
||||
UInt32ToBytes(&guid->data4[4], random());
|
||||
#if defined(HAVE_ARC4RANDOM) // Android, BSD, ...
|
||||
CreateGuidFromArc4Random(guid);
|
||||
#else // Linux
|
||||
bool success = false;
|
||||
|
||||
#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM)
|
||||
success = CreateGUIDFromGetrandom(guid);
|
||||
#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM
|
||||
if (!success) {
|
||||
success = CreateGUIDFromDevUrandom(guid);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
CreateGUIDFromRand(guid);
|
||||
success = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Put in the version according to RFC 4122.
|
||||
guid->data3 &= 0x0fff;
|
||||
guid->data3 |= 0x4000;
|
||||
|
||||
// Put in the variant according to RFC 4122.
|
||||
guid->data4[0] &= 0x3f;
|
||||
guid->data4[0] |= 0x80;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef HAVE_ARC4RANDOM
|
||||
static void CreateGuidFromArc4Random(GUID *guid) {
|
||||
char *buf = reinterpret_cast<char *>(guid);
|
||||
|
||||
for (size_t i = 0; i < sizeof(GUID); i += sizeof(uint32_t)) {
|
||||
uint32_t random_data = arc4random();
|
||||
|
||||
memcpy(buf + i, &random_data, sizeof(uint32_t));
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void InitOnce() {
|
||||
pthread_once(&once_control, &InitOnceImpl);
|
||||
}
|
||||
|
||||
static void InitOnceImpl() {
|
||||
srandom(time(NULL));
|
||||
// time(NULL) is a very poor seed, so lacking anything better mix an
|
||||
// address into it. We drop the four rightmost bits as they're likely to
|
||||
// be 0 on almost all architectures.
|
||||
srand(time(NULL) | ((uintptr_t)&once_control >> 4));
|
||||
}
|
||||
|
||||
static pthread_once_t once_control;
|
||||
|
||||
#if defined(HAVE_SYS_RANDOM_H) && defined(HAVE_GETRANDOM)
|
||||
static bool CreateGUIDFromGetrandom(GUID *guid) {
|
||||
char *buf = reinterpret_cast<char *>(guid);
|
||||
int read_bytes = getrandom(buf, sizeof(GUID), GRND_NONBLOCK);
|
||||
|
||||
return (read_bytes == static_cast<int>(sizeof(GUID)));
|
||||
}
|
||||
#endif // HAVE_SYS_RANDOM_H && HAVE_GETRANDOM
|
||||
|
||||
// Populate the GUID using random bytes read from /dev/urandom, returns false
|
||||
// if the GUID wasn't fully populated with random data.
|
||||
static bool CreateGUIDFromDevUrandom(GUID *guid) {
|
||||
char *buf = reinterpret_cast<char *>(guid);
|
||||
int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
|
||||
|
||||
if (fd == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t read_bytes = HANDLE_EINTR(read(fd, buf, sizeof(GUID)));
|
||||
close(fd);
|
||||
|
||||
return (read_bytes == static_cast<ssize_t>(sizeof(GUID)));
|
||||
}
|
||||
|
||||
// Populate the GUID using a stream of random bytes obtained from rand().
|
||||
static void CreateGUIDFromRand(GUID *guid) {
|
||||
char *buf = reinterpret_cast<char *>(guid);
|
||||
|
||||
InitOnce();
|
||||
|
||||
for (size_t i = 0; i < sizeof(GUID); i++) {
|
||||
buf[i] = rand();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef HAVE_ARC4RANDOM
|
||||
pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
|
||||
#endif
|
||||
|
||||
bool CreateGUID(GUID *guid) {
|
||||
return GUIDGenerator::CreateGUID(guid);
|
||||
|
@ -202,7 +202,7 @@ bool CrashGenerator::CreateChildCrash(
|
||||
// On Android the signal sometimes doesn't seem to get sent even though
|
||||
// tkill returns '0'. Retry a couple of times if the signal doesn't get
|
||||
// through on the first go:
|
||||
// https://code.google.com/p/google-breakpad/issues/detail?id=579
|
||||
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=579
|
||||
#if defined(__ANDROID__)
|
||||
const int kRetries = 60;
|
||||
const unsigned int kSleepTimeInSeconds = 1;
|
||||
|
@ -0,0 +1,178 @@
|
||||
// Copyright (c) 2017, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "common/long_string_dictionary.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "common/simple_string_dictionary.h"
|
||||
|
||||
#define arraysize(f) (sizeof(f) / sizeof(*f))
|
||||
|
||||
namespace {
|
||||
// Suffixes for segment keys.
|
||||
const char* const kSuffixes[] = {"__1", "__2", "__3", "__4", "__5", "__6",
|
||||
"__7", "__8", "__9", "__10"};
|
||||
#if !defined(NDEBUG)
|
||||
// The maximum suffix string length.
|
||||
const size_t kMaxSuffixLength = 4;
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
void LongStringDictionary::SetKeyValue(const char* key, const char* value) {
|
||||
assert(key);
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
RemoveKey(key);
|
||||
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Key must not be an empty string.
|
||||
assert(key[0] != '\0');
|
||||
if (key[0] == '\0')
|
||||
return;
|
||||
|
||||
// If the value is not valid for segmentation, forwards the key and the value
|
||||
// to SetKeyValue of SimpleStringDictionary and returns.
|
||||
size_t value_length = strlen(value);
|
||||
if (value_length <= (value_size - 1)) {
|
||||
SimpleStringDictionary::SetKeyValue(key, value);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t key_length = strlen(key);
|
||||
assert(key_length + kMaxSuffixLength <= (key_size - 1));
|
||||
|
||||
char segment_key[key_size];
|
||||
char segment_value[value_size];
|
||||
|
||||
strcpy(segment_key, key);
|
||||
|
||||
const char* remain_value = value;
|
||||
size_t remain_value_length = strlen(value);
|
||||
|
||||
for (unsigned long i = 0; i < arraysize(kSuffixes); i++) {
|
||||
if (remain_value_length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
strcpy(segment_key + key_length, kSuffixes[i]);
|
||||
|
||||
size_t segment_value_length =
|
||||
std::min(remain_value_length, value_size - 1);
|
||||
|
||||
strncpy(segment_value, remain_value, segment_value_length);
|
||||
segment_value[segment_value_length] = '\0';
|
||||
|
||||
remain_value += segment_value_length;
|
||||
remain_value_length -= segment_value_length;
|
||||
|
||||
SimpleStringDictionary::SetKeyValue(segment_key, segment_value);
|
||||
}
|
||||
}
|
||||
|
||||
bool LongStringDictionary::RemoveKey(const char* key) {
|
||||
assert(key);
|
||||
if (!key)
|
||||
return false;
|
||||
|
||||
if (SimpleStringDictionary::RemoveKey(key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t key_length = strlen(key);
|
||||
assert(key_length + kMaxSuffixLength <= (key_size - 1));
|
||||
|
||||
char segment_key[key_size];
|
||||
strcpy(segment_key, key);
|
||||
|
||||
unsigned long i = 0;
|
||||
for (; i < arraysize(kSuffixes); i++) {
|
||||
strcpy(segment_key + key_length, kSuffixes[i]);
|
||||
if (!SimpleStringDictionary::RemoveKey(segment_key)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i != 0;
|
||||
}
|
||||
|
||||
const string LongStringDictionary::GetValueForKey(const char* key) const {
|
||||
assert(key);
|
||||
if (!key)
|
||||
return "";
|
||||
|
||||
// Key must not be an empty string.
|
||||
assert(key[0] != '\0');
|
||||
if (key[0] == '\0')
|
||||
return "";
|
||||
|
||||
const char* value = SimpleStringDictionary::GetValueForKey(key);
|
||||
if (value)
|
||||
return string(value);
|
||||
|
||||
size_t key_length = strlen(key);
|
||||
assert(key_length + kMaxSuffixLength <= (key_size - 1));
|
||||
|
||||
bool found_segment = false;
|
||||
char segment_key[key_size];
|
||||
string return_value;
|
||||
|
||||
strcpy(segment_key, key);
|
||||
for (unsigned long i = 0; i < arraysize(kSuffixes); i++) {
|
||||
strcpy(segment_key + key_length, kSuffixes[i]);
|
||||
|
||||
const char* segment_value =
|
||||
SimpleStringDictionary::GetValueForKey(segment_key);
|
||||
|
||||
if (segment_value != NULL) {
|
||||
found_segment = true;
|
||||
return_value.append(segment_value);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_segment) {
|
||||
return return_value;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -0,0 +1,87 @@
|
||||
// Copyright (c) 2017, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef COMMON_LONG_STRING_DICTIONARY_H_
|
||||
#define COMMON_LONG_STRING_DICTIONARY_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/simple_string_dictionary.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
// key_size is the maxium size that |key| can take in
|
||||
// SimpleStringDictionary which is defined in simple_string_dictionary.h.
|
||||
//
|
||||
// value_size is the maxium size that |value| can take in
|
||||
// SimpleStringDictionary which is defined in simple_string_dictionary.h.
|
||||
//
|
||||
// LongStringDictionary is a subclass of SimpleStringDictionary which supports
|
||||
// longer values to be stored in the dictionary. The maximum length supported is
|
||||
// (value_size - 1) * 10.
|
||||
//
|
||||
// For example, LongStringDictionary will store long value with key 'abc' into
|
||||
// segment values with segment keys 'abc__1', 'abc__2', 'abc__3', ...
|
||||
//
|
||||
// Clients must avoid using the same suffixes as their key's suffix when
|
||||
// LongStringDictionary is used.
|
||||
class LongStringDictionary : public SimpleStringDictionary {
|
||||
public:
|
||||
// Stores |value| into |key|, or segment values into segment keys. The maxium
|
||||
// number of segments is 10. If |value| can not be stored in 10 segments, it
|
||||
// will be truncated. Replacing the existing value if |key| is already present
|
||||
// and replacing the existing segment values if segment keys are already
|
||||
// present.
|
||||
//
|
||||
// |key| must not be NULL. If the |value| need to be divided into segments,
|
||||
// the lengh of |key| must be smaller enough so that lengths of segment keys
|
||||
// which are key with suffixes are all samller than (key_size - 1). Currently,
|
||||
// the max length of suffixes are 4.
|
||||
//
|
||||
// If |value| is NULL, the key and its corresponding segment keys are removed
|
||||
// from the map. If there is no more space in the map, then the operation
|
||||
// silently fails.
|
||||
void SetKeyValue(const char* key, const char* value);
|
||||
|
||||
// Given |key|, removes any associated value or associated segment values.
|
||||
// |key| must not be NULL. If the key is not found, searchs its segment keys
|
||||
// and removes corresponding segment values if found.
|
||||
bool RemoveKey(const char* key);
|
||||
|
||||
// Given |key|, returns its corresponding |value|. |key| must not be NULL. If
|
||||
// the key is found, its corresponding |value| is returned.
|
||||
//
|
||||
// If no corresponding |value| is found, segment keys of the given |key| will
|
||||
// be used to search for corresponding segment values. If segment values
|
||||
// exist, assembled value from them is returned. If no segment value exists,
|
||||
// NULL is returned.
|
||||
const std::string GetValueForKey(const char* key) const;
|
||||
};
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LONG_STRING_DICTIONARY_H_
|
@ -0,0 +1,301 @@
|
||||
// Copyright (c) 2017, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/long_string_dictionary.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
TEST(LongStringDictionary, LongStringDictionary) {
|
||||
// Make a new dictionary
|
||||
LongStringDictionary dict;
|
||||
|
||||
// Set three distinct values on three keys
|
||||
dict.SetKeyValue("key1", "value1");
|
||||
dict.SetKeyValue("key2", "value2");
|
||||
dict.SetKeyValue("key3", "value3");
|
||||
|
||||
EXPECT_EQ("value1", dict.GetValueForKey("key1"));
|
||||
EXPECT_EQ("value2", dict.GetValueForKey("key2"));
|
||||
EXPECT_EQ("value3", dict.GetValueForKey("key3"));
|
||||
EXPECT_EQ(3u, dict.GetCount());
|
||||
// try an unknown key
|
||||
EXPECT_EQ("", dict.GetValueForKey("key4"));
|
||||
|
||||
// Remove a key
|
||||
dict.RemoveKey("key3");
|
||||
|
||||
// Now make sure it's not there anymore
|
||||
EXPECT_EQ("", dict.GetValueForKey("key3"));
|
||||
|
||||
// Remove by setting value to NULL
|
||||
dict.SetKeyValue("key2", NULL);
|
||||
|
||||
// Now make sure it's not there anymore
|
||||
EXPECT_EQ("", dict.GetValueForKey("key2"));
|
||||
}
|
||||
|
||||
// Add a bunch of values to the dictionary, remove some entries in the middle,
|
||||
// and then add more.
|
||||
TEST(LongStringDictionary, Iterator) {
|
||||
LongStringDictionary* dict = new LongStringDictionary();
|
||||
ASSERT_TRUE(dict);
|
||||
|
||||
char key[LongStringDictionary::key_size];
|
||||
char value[LongStringDictionary::value_size];
|
||||
|
||||
const int kDictionaryCapacity = LongStringDictionary::num_entries;
|
||||
const int kPartitionIndex = kDictionaryCapacity - 5;
|
||||
|
||||
// We assume at least this size in the tests below
|
||||
ASSERT_GE(kDictionaryCapacity, 64);
|
||||
|
||||
// We'll keep track of the number of key/value pairs we think should
|
||||
// be in the dictionary
|
||||
int expectedDictionarySize = 0;
|
||||
|
||||
// Set a bunch of key/value pairs like key0/value0, key1/value1, ...
|
||||
for (int i = 0; i < kPartitionIndex; ++i) {
|
||||
sprintf(key, "key%d", i);
|
||||
sprintf(value, "value%d", i);
|
||||
dict->SetKeyValue(key, value);
|
||||
}
|
||||
expectedDictionarySize = kPartitionIndex;
|
||||
|
||||
// set a couple of the keys twice (with the same value) - should be nop
|
||||
dict->SetKeyValue("key2", "value2");
|
||||
dict->SetKeyValue("key4", "value4");
|
||||
dict->SetKeyValue("key15", "value15");
|
||||
|
||||
// Remove some random elements in the middle
|
||||
dict->RemoveKey("key7");
|
||||
dict->RemoveKey("key18");
|
||||
dict->RemoveKey("key23");
|
||||
dict->RemoveKey("key31");
|
||||
expectedDictionarySize -= 4; // we just removed four key/value pairs
|
||||
|
||||
// Set some more key/value pairs like key59/value59, key60/value60, ...
|
||||
for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) {
|
||||
sprintf(key, "key%d", i);
|
||||
sprintf(value, "value%d", i);
|
||||
dict->SetKeyValue(key, value);
|
||||
}
|
||||
expectedDictionarySize += kDictionaryCapacity - kPartitionIndex;
|
||||
|
||||
// Now create an iterator on the dictionary
|
||||
SimpleStringDictionary::Iterator iter(*dict);
|
||||
|
||||
// We then verify that it iterates through exactly the number of
|
||||
// key/value pairs we expect, and that they match one-for-one with what we
|
||||
// would expect. The ordering of the iteration does not matter...
|
||||
|
||||
// used to keep track of number of occurrences found for key/value pairs
|
||||
int count[kDictionaryCapacity];
|
||||
memset(count, 0, sizeof(count));
|
||||
|
||||
int totalCount = 0;
|
||||
|
||||
const SimpleStringDictionary::Entry* entry;
|
||||
while ((entry = iter.Next())) {
|
||||
totalCount++;
|
||||
|
||||
// Extract keyNumber from a string of the form key<keyNumber>
|
||||
int keyNumber;
|
||||
sscanf(entry->key, "key%d", &keyNumber);
|
||||
|
||||
// Extract valueNumber from a string of the form value<valueNumber>
|
||||
int valueNumber;
|
||||
sscanf(entry->value, "value%d", &valueNumber);
|
||||
|
||||
// The value number should equal the key number since that's how we set them
|
||||
EXPECT_EQ(keyNumber, valueNumber);
|
||||
|
||||
// Key and value numbers should be in proper range:
|
||||
// 0 <= keyNumber < kDictionaryCapacity
|
||||
bool isKeyInGoodRange = (keyNumber >= 0 && keyNumber < kDictionaryCapacity);
|
||||
bool isValueInGoodRange =
|
||||
(valueNumber >= 0 && valueNumber < kDictionaryCapacity);
|
||||
EXPECT_TRUE(isKeyInGoodRange);
|
||||
EXPECT_TRUE(isValueInGoodRange);
|
||||
|
||||
if (isKeyInGoodRange && isValueInGoodRange) {
|
||||
++count[keyNumber];
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure each of the key/value pairs showed up exactly one time, except
|
||||
// for the ones which we removed.
|
||||
for (size_t i = 0; i < kDictionaryCapacity; ++i) {
|
||||
// Skip over key7, key18, key23, and key31, since we removed them
|
||||
if (!(i == 7 || i == 18 || i == 23 || i == 31)) {
|
||||
EXPECT_EQ(count[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the number of iterations matches the expected dictionary size.
|
||||
EXPECT_EQ(totalCount, expectedDictionarySize);
|
||||
}
|
||||
|
||||
TEST(LongStringDictionary, AddRemove) {
|
||||
LongStringDictionary dict;
|
||||
dict.SetKeyValue("rob", "ert");
|
||||
dict.SetKeyValue("mike", "pink");
|
||||
dict.SetKeyValue("mark", "allays");
|
||||
|
||||
EXPECT_EQ(3u, dict.GetCount());
|
||||
EXPECT_EQ("ert", dict.GetValueForKey("rob"));
|
||||
EXPECT_EQ("pink", dict.GetValueForKey("mike"));
|
||||
EXPECT_EQ("allays", dict.GetValueForKey("mark"));
|
||||
|
||||
dict.RemoveKey("mike");
|
||||
|
||||
EXPECT_EQ(2u, dict.GetCount());
|
||||
EXPECT_EQ("", dict.GetValueForKey("mike"));
|
||||
|
||||
dict.SetKeyValue("mark", "mal");
|
||||
EXPECT_EQ(2u, dict.GetCount());
|
||||
EXPECT_EQ("mal", dict.GetValueForKey("mark"));
|
||||
|
||||
dict.RemoveKey("mark");
|
||||
EXPECT_EQ(1u, dict.GetCount());
|
||||
EXPECT_EQ("", dict.GetValueForKey("mark"));
|
||||
}
|
||||
|
||||
TEST(LongStringDictionary, AddRemoveLongValue) {
|
||||
LongStringDictionary dict;
|
||||
|
||||
string long_value = string(256, 'x');
|
||||
dict.SetKeyValue("rob", long_value.c_str());
|
||||
|
||||
EXPECT_EQ(2u, dict.GetCount());
|
||||
|
||||
string long_value_part_1 = string(255, 'x');
|
||||
|
||||
EXPECT_EQ(long_value_part_1, dict.GetValueForKey("rob__1"));
|
||||
EXPECT_EQ("x", dict.GetValueForKey("rob__2"));
|
||||
|
||||
EXPECT_EQ(long_value, dict.GetValueForKey("rob"));
|
||||
|
||||
dict.RemoveKey("rob");
|
||||
EXPECT_EQ(0u, dict.GetCount());
|
||||
}
|
||||
|
||||
TEST(LongStringDictionary, AddRemoveSuperLongValue) {
|
||||
LongStringDictionary dict;
|
||||
|
||||
string long_value = string(255 * 10, 'x');
|
||||
dict.SetKeyValue("rob", long_value.c_str());
|
||||
|
||||
EXPECT_EQ(10u, dict.GetCount());
|
||||
|
||||
string long_value_part = string(255, 'x');
|
||||
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10"));
|
||||
EXPECT_EQ(10u, dict.GetCount());
|
||||
|
||||
EXPECT_EQ(long_value, dict.GetValueForKey("rob"));
|
||||
|
||||
dict.RemoveKey("rob");
|
||||
EXPECT_EQ(0u, dict.GetCount());
|
||||
}
|
||||
|
||||
TEST(LongStringDictionary, TruncateSuperLongValue) {
|
||||
LongStringDictionary dict;
|
||||
|
||||
string long_value = string(255 * 11, 'x');
|
||||
dict.SetKeyValue("rob", long_value.c_str());
|
||||
|
||||
EXPECT_EQ(10u, dict.GetCount());
|
||||
|
||||
string long_value_part = string(255, 'x');
|
||||
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__1"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__2"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__3"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__4"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__5"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__6"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__7"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__8"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__9"));
|
||||
EXPECT_EQ(long_value_part, dict.GetValueForKey("rob__10"));
|
||||
EXPECT_EQ(10u, dict.GetCount());
|
||||
|
||||
string expected_long_value = string(255 * 10, 'x');
|
||||
EXPECT_EQ(expected_long_value, dict.GetValueForKey("rob"));
|
||||
|
||||
dict.RemoveKey("rob");
|
||||
EXPECT_EQ(0u, dict.GetCount());
|
||||
}
|
||||
|
||||
TEST(LongStringDictionary, OverrideLongValue) {
|
||||
LongStringDictionary dict;
|
||||
|
||||
string long_value = string(255 * 10, 'x');
|
||||
dict.SetKeyValue("rob", long_value.c_str());
|
||||
|
||||
EXPECT_EQ(10u, dict.GetCount());
|
||||
EXPECT_EQ(long_value, dict.GetValueForKey("rob"));
|
||||
|
||||
dict.SetKeyValue("rob", "short_value");
|
||||
|
||||
EXPECT_EQ(1u, dict.GetCount());
|
||||
EXPECT_EQ("short_value", dict.GetValueForKey("rob"));
|
||||
}
|
||||
|
||||
TEST(LongStringDictionary, OverrideShortValue) {
|
||||
LongStringDictionary dict;
|
||||
|
||||
dict.SetKeyValue("rob", "short_value");
|
||||
|
||||
EXPECT_EQ(1u, dict.GetCount());
|
||||
EXPECT_EQ("short_value", dict.GetValueForKey("rob"));
|
||||
|
||||
string long_value = string(255 * 10, 'x');
|
||||
dict.SetKeyValue("rob", long_value.c_str());
|
||||
|
||||
EXPECT_EQ(10u, dict.GetCount());
|
||||
EXPECT_EQ(long_value, dict.GetValueForKey("rob"));
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -117,7 +117,7 @@
|
||||
// developer level errors. This implementation simply macros to NSLog/NSAssert.
|
||||
// It is not intended to be a general logging/reporting system.
|
||||
//
|
||||
// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert
|
||||
// Please see https://github.com/google/google-toolbox-for-mac/wiki/DevLogNAssert
|
||||
// for a little more background on the usage of these macros.
|
||||
//
|
||||
// _GTMDevLog log some error/problem in debug builds
|
||||
|
@ -49,13 +49,13 @@ static NSString *PercentEncodeNSString(NSString *key) {
|
||||
|
||||
// As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has
|
||||
// been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements
|
||||
// it using -[NSURLSession dataTaskWithRequest:completionHandler:] when using
|
||||
// those SDKs.
|
||||
// it using -[NSURLSession dataTaskWithRequest:completionHandler:] which is
|
||||
// available on iOS 7+.
|
||||
static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
||||
NSURLResponse **out_response,
|
||||
NSError **out_error) {
|
||||
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_9_0) && \
|
||||
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0) || \
|
||||
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \
|
||||
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) || \
|
||||
(defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
|
||||
defined(MAC_OS_X_VERSION_10_11) && \
|
||||
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
|
||||
@ -223,9 +223,7 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
||||
|
||||
// Add any files to the message
|
||||
NSArray *fileNames = [files_ allKeys];
|
||||
count = [fileNames count];
|
||||
for (NSInteger i = 0; i < count; ++i) {
|
||||
NSString *name = [fileNames objectAtIndex:i];
|
||||
for (NSString *name in fileNames) {
|
||||
id fileOrData = [files_ objectForKey:name];
|
||||
NSData *fileData;
|
||||
|
||||
|
@ -111,6 +111,7 @@ namespace {
|
||||
enum Architecture {
|
||||
kArch_i386 = 0,
|
||||
kArch_x86_64,
|
||||
kArch_x86_64h,
|
||||
kArch_arm,
|
||||
kArch_arm64,
|
||||
kArch_ppc,
|
||||
@ -135,6 +136,13 @@ const NXArchInfo kKnownArchitectures[] = {
|
||||
NX_LittleEndian,
|
||||
"Intel x86-64"
|
||||
},
|
||||
{
|
||||
"x86_64h",
|
||||
CPU_TYPE_X86_64,
|
||||
CPU_SUBTYPE_X86_64_H,
|
||||
NX_LittleEndian,
|
||||
"Intel x86-64h Haswell"
|
||||
},
|
||||
{
|
||||
"arm",
|
||||
CPU_TYPE_ARM,
|
||||
@ -189,23 +197,35 @@ const NXArchInfo *NXGetArchInfoFromName(const char *name) {
|
||||
|
||||
const NXArchInfo *NXGetArchInfoFromCpuType(cpu_type_t cputype,
|
||||
cpu_subtype_t cpusubtype) {
|
||||
const NXArchInfo *candidate = NULL;
|
||||
for (int arch = 0; arch < kNumArchitectures; ++arch) {
|
||||
if (kKnownArchitectures[arch].cputype == cputype) {
|
||||
return &kKnownArchitectures[arch];
|
||||
if (kKnownArchitectures[arch].cpusubtype == cpusubtype) {
|
||||
return &kKnownArchitectures[arch];
|
||||
}
|
||||
if (!candidate) {
|
||||
candidate = &kKnownArchitectures[arch];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return candidate;
|
||||
}
|
||||
|
||||
struct fat_arch *NXFindBestFatArch(cpu_type_t cputype,
|
||||
cpu_subtype_t cpusubtype,
|
||||
struct fat_arch *fat_archs,
|
||||
uint32_t nfat_archs) {
|
||||
struct fat_arch *candidate = NULL;
|
||||
for (uint32_t f = 0; f < nfat_archs; ++f) {
|
||||
if (fat_archs[f].cputype == cputype) {
|
||||
return &fat_archs[f];
|
||||
if (fat_archs[f].cpusubtype == cpusubtype) {
|
||||
return &fat_archs[f];
|
||||
}
|
||||
if (!candidate) {
|
||||
candidate = &fat_archs[f];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return candidate;
|
||||
}
|
||||
#endif // !__APPLE__
|
||||
|
@ -31,14 +31,13 @@
|
||||
|
||||
// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dump_syms.mm: Create a symbol file for use with minidumps
|
||||
// dump_syms.cc: Create a symbol file for use with minidumps
|
||||
|
||||
#include "common/mac/dump_syms.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <mach-o/arch.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <stdint.h>
|
||||
@ -60,6 +59,7 @@
|
||||
#include "common/mac/arch_utilities.h"
|
||||
#include "common/mac/macho_reader.h"
|
||||
#include "common/module.h"
|
||||
#include "common/path_helper.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/stabs_reader.h"
|
||||
#include "common/stabs_to_module.h"
|
||||
@ -370,8 +370,7 @@ bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) {
|
||||
}
|
||||
|
||||
// Compute a module name, to appear in the MODULE record.
|
||||
string module_name = object_filename_;
|
||||
module_name = basename(&module_name[0]);
|
||||
string module_name = google_breakpad::BaseName(object_filename_);
|
||||
|
||||
// Choose an identifier string, to appear in the MODULE record.
|
||||
string identifier = Identifier();
|
||||
|
@ -1114,7 +1114,7 @@ TEST_F(LoadCommand, SegmentBE32) {
|
||||
Return(true)));
|
||||
EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
|
||||
|
||||
EXPECT_EQ(false, actual_segment.bits_64);
|
||||
EXPECT_FALSE(actual_segment.bits_64);
|
||||
EXPECT_EQ("froon", actual_segment.name);
|
||||
EXPECT_EQ(0x1891139cU, actual_segment.vmaddr);
|
||||
EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
|
||||
@ -1151,7 +1151,7 @@ TEST_F(LoadCommand, SegmentLE32) {
|
||||
Return(true)));
|
||||
EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
|
||||
|
||||
EXPECT_EQ(false, actual_segment.bits_64);
|
||||
EXPECT_FALSE(actual_segment.bits_64);
|
||||
EXPECT_EQ("sixteenprecisely", actual_segment.name);
|
||||
EXPECT_EQ(0x4b877866U, actual_segment.vmaddr);
|
||||
EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user