mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-12-23 16:25:37 +00:00
Breakpad implementation for ios.
To be noted: 1) All is done in process, as multi-process is not allowed on ios. 2) Dump are saved when a crash occures but are not automatically send to the server. 3) Breakpad.h contains function to check if a dump must be uploaded, and to upload a dump. 4) The code is copy pasting a log of Breakpad implementation for Mac OS. It might be possible to do some refactoring. Review URL: http://breakpad.appspot.com/309003 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@868 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
e694156d72
commit
375928a0a6
201
src/client/ios/Breakpad.h
Normal file
201
src/client/ios/Breakpad.h
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
// Copyright (c) 2011, 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.
|
||||||
|
|
||||||
|
// Framework to provide a simple C API to crash reporting for
|
||||||
|
// applications. By default, if any machine-level exception (e.g.,
|
||||||
|
// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef
|
||||||
|
// object as follows:
|
||||||
|
//
|
||||||
|
// 1. Create a minidump file (see Breakpad for details)
|
||||||
|
// 2. Create a config file.
|
||||||
|
//
|
||||||
|
// These files can then be uploaded to a server.
|
||||||
|
|
||||||
|
typedef void *BreakpadRef;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
#include <client/mac/Framework/BreakpadDefines.h>
|
||||||
|
|
||||||
|
// Optional user-defined function to decide if we should handle this crash or
|
||||||
|
// forward it along.
|
||||||
|
// Return true if you want Breakpad to handle it.
|
||||||
|
// Return false if you want Breakpad to skip it
|
||||||
|
// The exception handler always returns false, as if SEND_AND_EXIT were false
|
||||||
|
// (which means the next exception handler will take the exception)
|
||||||
|
typedef bool (*BreakpadFilterCallback)(int exception_type,
|
||||||
|
int exception_code,
|
||||||
|
mach_port_t crashing_thread,
|
||||||
|
void *context);
|
||||||
|
|
||||||
|
// Create a new BreakpadRef object and install it as an exception
|
||||||
|
// handler. The |parameters| will typically be the contents of your
|
||||||
|
// bundle's Info.plist.
|
||||||
|
//
|
||||||
|
// You can also specify these additional keys for customizable behavior:
|
||||||
|
// Key: Value:
|
||||||
|
// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct")
|
||||||
|
// This one is used as the key to identify
|
||||||
|
// the product when uploading. Falls back to
|
||||||
|
// CFBundleName if not specified.
|
||||||
|
// REQUIRED
|
||||||
|
//
|
||||||
|
// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty
|
||||||
|
// name for the product when the crash_sender
|
||||||
|
// pops up UI for the user. Falls back first to
|
||||||
|
// CFBundleDisplayName and then to
|
||||||
|
// BREAKPAD_PRODUCT if not specified.
|
||||||
|
//
|
||||||
|
// BREAKPAD_VERSION Product version (e.g., 1.2.3), used
|
||||||
|
// as metadata for crash report. Falls back to
|
||||||
|
// CFBundleVersion if not specified.
|
||||||
|
// REQUIRED
|
||||||
|
//
|
||||||
|
// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has
|
||||||
|
// been created that you can send to <vendor>")
|
||||||
|
//
|
||||||
|
// BREAKPAD_URL URL destination for reporting
|
||||||
|
// REQUIRED
|
||||||
|
//
|
||||||
|
// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps
|
||||||
|
// in. By default, we use
|
||||||
|
// ~/Library/Cache/Breakpad/<BREAKPAD_PRODUCT>
|
||||||
|
// The path you specify here is tilde-expanded.
|
||||||
|
//
|
||||||
|
// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to
|
||||||
|
// rewrite the upload parameters for a specific
|
||||||
|
// server type. The currently valid values are
|
||||||
|
// 'socorro' or 'google'. If you want to add
|
||||||
|
// other types, see the function in
|
||||||
|
// crash_report_sender.m that maps parameters to
|
||||||
|
// URL parameters. Defaults to 'google'.
|
||||||
|
//
|
||||||
|
// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static
|
||||||
|
// parameters that are uploaded to the
|
||||||
|
// server. The parameters are sent as
|
||||||
|
// is to the crash server. Their
|
||||||
|
// content isn't added to the minidump
|
||||||
|
// but pass as URL parameters when
|
||||||
|
// uploading theminidump to the crash
|
||||||
|
// server.
|
||||||
|
//=============================================================================
|
||||||
|
// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are
|
||||||
|
// required to have non-NULL values. By default, the BREAKPAD_PRODUCT
|
||||||
|
// will be the CFBundleName and the BREAKPAD_VERSION will be the
|
||||||
|
// CFBundleVersion when these keys are present in the bundle's
|
||||||
|
// Info.plist, which is usually passed in to BreakpadCreate() as an
|
||||||
|
// NSDictionary (you could also pass in another dictionary that had
|
||||||
|
// the same keys configured). If the BREAKPAD_PRODUCT or
|
||||||
|
// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will
|
||||||
|
// fail. You have been warned.
|
||||||
|
//
|
||||||
|
// If you are running in a debugger, Breakpad will not install, unless the
|
||||||
|
// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero.
|
||||||
|
//
|
||||||
|
//=============================================================================
|
||||||
|
// The following are NOT user-supplied but are documented here for
|
||||||
|
// completeness. They are calculated by Breakpad during initialization &
|
||||||
|
// crash-dump generation, or entered in by the user.
|
||||||
|
//
|
||||||
|
// BREAKPAD_PROCESS_START_TIME The time the process started.
|
||||||
|
//
|
||||||
|
// BREAKPAD_PROCESS_CRASH_TIME The time the process crashed.
|
||||||
|
//
|
||||||
|
// BREAKPAD_PROCESS_UP_TIME The total time the process has been
|
||||||
|
// running. This parameter is not set
|
||||||
|
// until the crash-dump-generation phase.
|
||||||
|
//
|
||||||
|
// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad
|
||||||
|
// internally, because Breakpad uses
|
||||||
|
// the same dictionary internally to
|
||||||
|
// track both its internal
|
||||||
|
// configuration parameters and
|
||||||
|
// parameters meant to be uploaded
|
||||||
|
// to the server. This string is
|
||||||
|
// used internally by Breakpad to
|
||||||
|
// prefix user-supplied parameter
|
||||||
|
// names so those can be sent to the
|
||||||
|
// server without leaking Breakpad's
|
||||||
|
// internal values.
|
||||||
|
|
||||||
|
// Returns a new BreakpadRef object on success, NULL otherwise.
|
||||||
|
BreakpadRef BreakpadCreate(NSDictionary *parameters);
|
||||||
|
|
||||||
|
// Uninstall and release the data associated with |ref|.
|
||||||
|
void BreakpadRelease(BreakpadRef ref);
|
||||||
|
|
||||||
|
// User defined key and value string storage. Generally this is used
|
||||||
|
// to configure Breakpad's internal operation, such as whether the
|
||||||
|
// crash_sender should prompt the user, or the filesystem location for
|
||||||
|
// the minidump file. See Breakpad.h for some parameters that can be
|
||||||
|
// set. Anything longer than 255 bytes will be truncated. Note that
|
||||||
|
// the string is converted to UTF8 before truncation, so any multibyte
|
||||||
|
// character that straddles the 255(256 - 1 for terminator) byte limit
|
||||||
|
// will be mangled.
|
||||||
|
//
|
||||||
|
// A maximum number of 64 key/value pairs are supported. An assert()
|
||||||
|
// will fire if more than this number are set. Unfortunately, right
|
||||||
|
// now, the same dictionary is used for both Breakpad's parameters AND
|
||||||
|
// the Upload parameters.
|
||||||
|
//
|
||||||
|
// TODO (nealsid): Investigate how necessary this is if we don't
|
||||||
|
// automatically upload parameters to the server anymore.
|
||||||
|
// TODO (nealsid): separate server parameter dictionary from the
|
||||||
|
// dictionary used to configure Breakpad, and document limits for each
|
||||||
|
// independently.
|
||||||
|
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value);
|
||||||
|
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key);
|
||||||
|
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key);
|
||||||
|
|
||||||
|
// You can use this method to specify parameters that will be uploaded
|
||||||
|
// to the crash server. They will be automatically encoded as
|
||||||
|
// necessary. Note that as mentioned above there are limits on both
|
||||||
|
// the number of keys and their length.
|
||||||
|
void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key,
|
||||||
|
NSString *value);
|
||||||
|
|
||||||
|
// This method will remove a previously-added parameter from the
|
||||||
|
// upload parameter set.
|
||||||
|
void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key);
|
||||||
|
|
||||||
|
// Method to handle uploading data to the server
|
||||||
|
|
||||||
|
// Returns if there is some report to send to the server.
|
||||||
|
bool BreakpadHasCrashReportToUpload(BreakpadRef ref);
|
||||||
|
|
||||||
|
// Upload next report to the server.
|
||||||
|
void BreakpadUploadNextReport(BreakpadRef ref);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
683
src/client/ios/Breakpad.mm
Normal file
683
src/client/ios/Breakpad.mm
Normal file
|
@ -0,0 +1,683 @@
|
||||||
|
// Copyright (c) 2011, 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.
|
||||||
|
|
||||||
|
#define VERBOSE 0
|
||||||
|
|
||||||
|
#if VERBOSE
|
||||||
|
static bool gDebugLog = true;
|
||||||
|
#else
|
||||||
|
static bool gDebugLog = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEBUGLOG if (gDebugLog) fprintf
|
||||||
|
#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER"
|
||||||
|
|
||||||
|
#import "common/mac/SimpleStringDictionary.h"
|
||||||
|
|
||||||
|
#import "client/mac/crash_generation/ConfigFile.h"
|
||||||
|
#import "client/mac/sender/uploader.h"
|
||||||
|
#import "client/mac/handler/exception_handler.h"
|
||||||
|
#import "client/ios/Breakpad.h"
|
||||||
|
#import "client/mac/handler/protected_memory_allocator.h"
|
||||||
|
|
||||||
|
#import <sys/stat.h>
|
||||||
|
#import <sys/sysctl.h>
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
using google_breakpad::ConfigFile;
|
||||||
|
using google_breakpad::EnsureDirectoryPathExists;
|
||||||
|
using google_breakpad::KeyValueEntry;
|
||||||
|
using google_breakpad::SimpleStringDictionary;
|
||||||
|
using google_breakpad::SimpleStringDictionaryIterator;
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// We want any memory allocations which are used by breakpad during the
|
||||||
|
// exception handling process (after a crash has happened) to be read-only
|
||||||
|
// to prevent them from being smashed before a crash occurs. Unfortunately
|
||||||
|
// we cannot protect against smashes to our exception handling thread's
|
||||||
|
// stack.
|
||||||
|
//
|
||||||
|
// NOTE: Any memory allocations which are not used during the exception
|
||||||
|
// handling process may be allocated in the normal ways.
|
||||||
|
//
|
||||||
|
// The ProtectedMemoryAllocator class provides an Allocate() method which
|
||||||
|
// we'll using in conjunction with placement operator new() to control
|
||||||
|
// allocation of C++ objects. Note that we don't use operator delete()
|
||||||
|
// but instead call the objects destructor directly: object->~ClassName();
|
||||||
|
//
|
||||||
|
ProtectedMemoryAllocator *gMasterAllocator = NULL;
|
||||||
|
ProtectedMemoryAllocator *gKeyValueAllocator = NULL;
|
||||||
|
ProtectedMemoryAllocator *gBreakpadAllocator = NULL;
|
||||||
|
|
||||||
|
// Mutex for thread-safe access to the key/value dictionary used by breakpad.
|
||||||
|
// It's a global instead of an instance variable of Breakpad
|
||||||
|
// since it can't live in a protected memory area.
|
||||||
|
pthread_mutex_t gDictionaryMutex;
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// Stack-based object for thread-safe access to a memory-protected region.
|
||||||
|
// It's assumed that normally the memory block (allocated by the allocator)
|
||||||
|
// is protected (read-only). Creating a stack-based instance of
|
||||||
|
// ProtectedMemoryLocker will unprotect this block after taking the lock.
|
||||||
|
// Its destructor will first re-protect the memory then release the lock.
|
||||||
|
class ProtectedMemoryLocker {
|
||||||
|
public:
|
||||||
|
// allocator may be NULL, in which case no Protect() or Unprotect() calls
|
||||||
|
// will be made, but a lock will still be taken
|
||||||
|
ProtectedMemoryLocker(pthread_mutex_t *mutex,
|
||||||
|
ProtectedMemoryAllocator *allocator)
|
||||||
|
: mutex_(mutex), allocator_(allocator) {
|
||||||
|
// Lock the mutex
|
||||||
|
assert(pthread_mutex_lock(mutex_) == 0);
|
||||||
|
|
||||||
|
// Unprotect the memory
|
||||||
|
if (allocator_ ) {
|
||||||
|
allocator_->Unprotect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~ProtectedMemoryLocker() {
|
||||||
|
// First protect the memory
|
||||||
|
if (allocator_) {
|
||||||
|
allocator_->Protect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then unlock the mutex
|
||||||
|
assert(pthread_mutex_unlock(mutex_) == 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Keep anybody from ever creating one of these things not on the stack.
|
||||||
|
ProtectedMemoryLocker() { }
|
||||||
|
ProtectedMemoryLocker(const ProtectedMemoryLocker&);
|
||||||
|
ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&);
|
||||||
|
|
||||||
|
pthread_mutex_t *mutex_;
|
||||||
|
ProtectedMemoryAllocator *allocator_;
|
||||||
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
class Breakpad {
|
||||||
|
public:
|
||||||
|
// factory method
|
||||||
|
static Breakpad *Create(NSDictionary *parameters) {
|
||||||
|
// Allocate from our special allocation pool
|
||||||
|
Breakpad *breakpad =
|
||||||
|
new (gBreakpadAllocator->Allocate(sizeof(Breakpad)))
|
||||||
|
Breakpad();
|
||||||
|
|
||||||
|
if (!breakpad)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!breakpad->Initialize(parameters)) {
|
||||||
|
// Don't use operator delete() here since we allocated from special pool
|
||||||
|
breakpad->~Breakpad();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return breakpad;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Breakpad();
|
||||||
|
|
||||||
|
void SetKeyValue(NSString *key, NSString *value);
|
||||||
|
NSString *KeyValue(NSString *key);
|
||||||
|
void RemoveKeyValue(NSString *key);
|
||||||
|
NSString *NextCrashReportToUpload();
|
||||||
|
void UploadNextReport();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Breakpad()
|
||||||
|
: handler_(NULL),
|
||||||
|
config_params_(NULL) {}
|
||||||
|
|
||||||
|
bool Initialize(NSDictionary *parameters);
|
||||||
|
|
||||||
|
bool ExtractParameters(NSDictionary *parameters);
|
||||||
|
|
||||||
|
// Dispatches to HandleMinidump()
|
||||||
|
static bool HandleMinidumpCallback(const char *dump_dir,
|
||||||
|
const char *minidump_id,
|
||||||
|
void *context, bool succeeded);
|
||||||
|
|
||||||
|
bool HandleMinidump(const char *dump_dir,
|
||||||
|
const char *minidump_id);
|
||||||
|
|
||||||
|
// Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's
|
||||||
|
// MachineExceptions.h, we have to explicitly name the handler.
|
||||||
|
google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG)
|
||||||
|
|
||||||
|
SimpleStringDictionary *config_params_; // Create parameters (STRONG)
|
||||||
|
|
||||||
|
ConfigFile config_file_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark Helper functions
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
static BOOL IsDebuggerActive() {
|
||||||
|
BOOL result = NO;
|
||||||
|
NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
|
||||||
|
// We check both defaults and the environment variable here
|
||||||
|
|
||||||
|
BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER];
|
||||||
|
|
||||||
|
if (!ignoreDebugger) {
|
||||||
|
char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER);
|
||||||
|
ignoreDebugger =
|
||||||
|
(ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignoreDebugger) {
|
||||||
|
pid_t pid = getpid();
|
||||||
|
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
|
||||||
|
int mibSize = sizeof(mib) / sizeof(int);
|
||||||
|
size_t actualSize;
|
||||||
|
|
||||||
|
if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) {
|
||||||
|
struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize);
|
||||||
|
|
||||||
|
if (info) {
|
||||||
|
// This comes from looking at the Darwin xnu Kernel
|
||||||
|
if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0)
|
||||||
|
result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO;
|
||||||
|
|
||||||
|
free(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
bool Breakpad::HandleMinidumpCallback(const char *dump_dir,
|
||||||
|
const char *minidump_id,
|
||||||
|
void *context, bool succeeded) {
|
||||||
|
Breakpad *breakpad = (Breakpad *)context;
|
||||||
|
|
||||||
|
// If our context is damaged or something, just return false to indicate that
|
||||||
|
// the handler should continue without us.
|
||||||
|
if (!breakpad || !succeeded)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return breakpad->HandleMinidump(dump_dir, minidump_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
#pragma mark -
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
bool Breakpad::Initialize(NSDictionary *parameters) {
|
||||||
|
// Initialize
|
||||||
|
config_params_ = NULL;
|
||||||
|
handler_ = NULL;
|
||||||
|
|
||||||
|
// Gather any user specified parameters
|
||||||
|
if (!ExtractParameters(parameters)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for debugger
|
||||||
|
if (IsDebuggerActive()) {
|
||||||
|
DEBUGLOG(stderr, "Debugger is active: Not installing handler\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the handler (allocating it in our special protected pool)
|
||||||
|
handler_ =
|
||||||
|
new (gBreakpadAllocator->Allocate(
|
||||||
|
sizeof(google_breakpad::ExceptionHandler)))
|
||||||
|
google_breakpad::ExceptionHandler(
|
||||||
|
config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY),
|
||||||
|
0, &HandleMinidumpCallback, this, true, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
Breakpad::~Breakpad() {
|
||||||
|
// Note that we don't use operator delete() on these pointers,
|
||||||
|
// since they were allocated by ProtectedMemoryAllocator objects.
|
||||||
|
//
|
||||||
|
if (config_params_) {
|
||||||
|
config_params_->~SimpleStringDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler_)
|
||||||
|
handler_->~ExceptionHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
||||||
|
NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
|
||||||
|
NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE];
|
||||||
|
NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
|
||||||
|
NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT];
|
||||||
|
NSString *version = [parameters objectForKey:@BREAKPAD_VERSION];
|
||||||
|
NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL];
|
||||||
|
NSString *vendor =
|
||||||
|
[parameters objectForKey:@BREAKPAD_VENDOR];
|
||||||
|
NSString *dumpSubdirectory =
|
||||||
|
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY];
|
||||||
|
|
||||||
|
NSDictionary *serverParameters =
|
||||||
|
[parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT];
|
||||||
|
|
||||||
|
if (!product)
|
||||||
|
product = [parameters objectForKey:@"CFBundleName"];
|
||||||
|
|
||||||
|
if (!display) {
|
||||||
|
display = [parameters objectForKey:@"CFBundleDisplayName"];
|
||||||
|
if (!display) {
|
||||||
|
display = product;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!version)
|
||||||
|
version = [parameters objectForKey:@"CFBundleVersion"];
|
||||||
|
|
||||||
|
if (!vendor) {
|
||||||
|
vendor = @"Vendor not specified";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dumpSubdirectory) {
|
||||||
|
NSString *cachePath =
|
||||||
|
[NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
|
||||||
|
NSUserDomainMask,
|
||||||
|
YES)
|
||||||
|
objectAtIndex:0];
|
||||||
|
dumpSubdirectory =
|
||||||
|
[cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory];
|
||||||
|
|
||||||
|
EnsureDirectoryPathExists(dumpSubdirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The product, version, and URL are required values.
|
||||||
|
if (![product length]) {
|
||||||
|
DEBUGLOG(stderr, "Missing required product key.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![version length]) {
|
||||||
|
DEBUGLOG(stderr, "Missing required version key.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (![urlStr length]) {
|
||||||
|
DEBUGLOG(stderr, "Missing required URL key.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
config_params_ =
|
||||||
|
new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) )
|
||||||
|
SimpleStringDictionary();
|
||||||
|
|
||||||
|
SimpleStringDictionary &dictionary = *config_params_;
|
||||||
|
|
||||||
|
dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]);
|
||||||
|
dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]);
|
||||||
|
dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]);
|
||||||
|
dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]);
|
||||||
|
dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]);
|
||||||
|
dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]);
|
||||||
|
dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY,
|
||||||
|
[dumpSubdirectory UTF8String]);
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
char timeStartedString[32];
|
||||||
|
sprintf(timeStartedString, "%zd", tv.tv_sec);
|
||||||
|
dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString);
|
||||||
|
|
||||||
|
if (serverParameters) {
|
||||||
|
// For each key-value pair, call BreakpadAddUploadParameter()
|
||||||
|
NSEnumerator *keyEnumerator = [serverParameters keyEnumerator];
|
||||||
|
NSString *aParameter;
|
||||||
|
while ((aParameter = [keyEnumerator nextObject])) {
|
||||||
|
BreakpadAddUploadParameter(this, aParameter,
|
||||||
|
[serverParameters objectForKey:aParameter]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
void Breakpad::SetKeyValue(NSString *key, NSString *value) {
|
||||||
|
// We allow nil values. This is the same as removing the keyvalue.
|
||||||
|
if (!config_params_ || !key)
|
||||||
|
return;
|
||||||
|
|
||||||
|
config_params_->SetKeyValue([key UTF8String], [value UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
NSString *Breakpad::KeyValue(NSString *key) {
|
||||||
|
if (!config_params_ || !key)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
const char *value = config_params_->GetValueForKey([key UTF8String]);
|
||||||
|
return value ? [NSString stringWithUTF8String:value] : nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
void Breakpad::RemoveKeyValue(NSString *key) {
|
||||||
|
if (!config_params_ || !key) return;
|
||||||
|
|
||||||
|
config_params_->RemoveKey([key UTF8String]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
NSString *Breakpad::NextCrashReportToUpload() {
|
||||||
|
NSString *directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY);
|
||||||
|
if (!directory)
|
||||||
|
return nil;
|
||||||
|
NSArray *dirContents = [[NSFileManager defaultManager]
|
||||||
|
contentsOfDirectoryAtPath:directory error:nil];
|
||||||
|
NSArray *configs = [dirContents filteredArrayUsingPredicate:[NSPredicate
|
||||||
|
predicateWithFormat:@"self BEGINSWITH 'Config-'"]];
|
||||||
|
NSString *config = [configs lastObject];
|
||||||
|
if (!config)
|
||||||
|
return nil;
|
||||||
|
return [NSString stringWithFormat:@"%@/%@", directory, config];
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
void Breakpad::UploadNextReport() {
|
||||||
|
NSString* configFile = NextCrashReportToUpload();
|
||||||
|
if (configFile) {
|
||||||
|
Uploader* uploader = [[Uploader alloc]
|
||||||
|
initWithConfigFile:[configFile UTF8String]];
|
||||||
|
if (uploader)
|
||||||
|
[uploader report];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
bool Breakpad::HandleMinidump(const char *dump_dir,
|
||||||
|
const char *minidump_id) {
|
||||||
|
DEBUGLOG(stderr, "Breakpad: a minidump has been created.\n");
|
||||||
|
|
||||||
|
config_file_.WriteFile(dump_dir,
|
||||||
|
config_params_,
|
||||||
|
dump_dir,
|
||||||
|
minidump_id);
|
||||||
|
|
||||||
|
// Return true here to indicate that we've processed things as much as we
|
||||||
|
// want.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
#pragma mark -
|
||||||
|
#pragma mark Public API
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
BreakpadRef BreakpadCreate(NSDictionary *parameters) {
|
||||||
|
try {
|
||||||
|
// This is confusing. Our two main allocators for breakpad memory are:
|
||||||
|
// - gKeyValueAllocator for the key/value memory
|
||||||
|
// - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other
|
||||||
|
// breakpad allocations which are accessed at exception handling time.
|
||||||
|
//
|
||||||
|
// But in order to avoid these two allocators themselves from being smashed,
|
||||||
|
// we'll protect them as well by allocating them with gMasterAllocator.
|
||||||
|
//
|
||||||
|
// gMasterAllocator itself will NOT be protected, but this doesn't matter,
|
||||||
|
// since once it does its allocations and locks the memory, smashes to
|
||||||
|
// itself don't affect anything we care about.
|
||||||
|
gMasterAllocator =
|
||||||
|
new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2);
|
||||||
|
|
||||||
|
gKeyValueAllocator =
|
||||||
|
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
|
||||||
|
ProtectedMemoryAllocator(sizeof(SimpleStringDictionary));
|
||||||
|
|
||||||
|
// Create a mutex for use in accessing the SimpleStringDictionary
|
||||||
|
int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL);
|
||||||
|
if (mutexResult == 0) {
|
||||||
|
|
||||||
|
// With the current compiler, gBreakpadAllocator is allocating 1444 bytes.
|
||||||
|
// Let's round up to the nearest page size.
|
||||||
|
//
|
||||||
|
int breakpad_pool_size = 4096;
|
||||||
|
|
||||||
|
/*
|
||||||
|
sizeof(Breakpad)
|
||||||
|
+ sizeof(google_breakpad::ExceptionHandler)
|
||||||
|
+ sizeof( STUFF ALLOCATED INSIDE ExceptionHandler )
|
||||||
|
*/
|
||||||
|
|
||||||
|
gBreakpadAllocator =
|
||||||
|
new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator)))
|
||||||
|
ProtectedMemoryAllocator(breakpad_pool_size);
|
||||||
|
|
||||||
|
// Stack-based autorelease pool for Breakpad::Create() obj-c code.
|
||||||
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
Breakpad *breakpad = Breakpad::Create(parameters);
|
||||||
|
|
||||||
|
if (breakpad) {
|
||||||
|
// Make read-only to protect against memory smashers
|
||||||
|
gMasterAllocator->Protect();
|
||||||
|
gKeyValueAllocator->Protect();
|
||||||
|
gBreakpadAllocator->Protect();
|
||||||
|
// Can uncomment this line to figure out how much space was actually
|
||||||
|
// allocated using this allocator
|
||||||
|
// printf("gBreakpadAllocator allocated size = %d\n",
|
||||||
|
// gBreakpadAllocator->GetAllocatedSize() );
|
||||||
|
[pool release];
|
||||||
|
return (BreakpadRef)breakpad;
|
||||||
|
}
|
||||||
|
|
||||||
|
[pool release];
|
||||||
|
}
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadCreate() : error\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gKeyValueAllocator) {
|
||||||
|
gKeyValueAllocator->~ProtectedMemoryAllocator();
|
||||||
|
gKeyValueAllocator = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gBreakpadAllocator) {
|
||||||
|
gBreakpadAllocator->~ProtectedMemoryAllocator();
|
||||||
|
gBreakpadAllocator = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete gMasterAllocator;
|
||||||
|
gMasterAllocator = NULL;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
void BreakpadRelease(BreakpadRef ref) {
|
||||||
|
try {
|
||||||
|
Breakpad *breakpad = (Breakpad *)ref;
|
||||||
|
|
||||||
|
if (gMasterAllocator) {
|
||||||
|
gMasterAllocator->Unprotect();
|
||||||
|
gKeyValueAllocator->Unprotect();
|
||||||
|
gBreakpadAllocator->Unprotect();
|
||||||
|
|
||||||
|
breakpad->~Breakpad();
|
||||||
|
|
||||||
|
// Unfortunately, it's not possible to deallocate this stuff
|
||||||
|
// because the exception handling thread is still finishing up
|
||||||
|
// asynchronously at this point... OK, it could be done with
|
||||||
|
// locks, etc. But since BreakpadRelease() should usually only
|
||||||
|
// be called right before the process exits, it's not worth
|
||||||
|
// deallocating this stuff.
|
||||||
|
#if 0
|
||||||
|
gKeyValueAllocator->~ProtectedMemoryAllocator();
|
||||||
|
gBreakpadAllocator->~ProtectedMemoryAllocator();
|
||||||
|
delete gMasterAllocator;
|
||||||
|
|
||||||
|
gMasterAllocator = NULL;
|
||||||
|
gKeyValueAllocator = NULL;
|
||||||
|
gBreakpadAllocator = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&gDictionaryMutex);
|
||||||
|
}
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadRelease() : error\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) {
|
||||||
|
try {
|
||||||
|
// Not called at exception time
|
||||||
|
Breakpad *breakpad = (Breakpad *)ref;
|
||||||
|
|
||||||
|
if (breakpad && key && gKeyValueAllocator) {
|
||||||
|
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||||
|
|
||||||
|
breakpad->SetKeyValue(key, value);
|
||||||
|
}
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpadAddUploadParameter(BreakpadRef ref,
|
||||||
|
NSString *key,
|
||||||
|
NSString *value) {
|
||||||
|
// The only difference, internally, between an upload parameter and
|
||||||
|
// a key value one that is set with BreakpadSetKeyValue is that we
|
||||||
|
// prepend the keyname with a special prefix. This informs the
|
||||||
|
// crash sender that the parameter should be sent along with the
|
||||||
|
// POST of the crash dump upload.
|
||||||
|
try {
|
||||||
|
Breakpad *breakpad = (Breakpad *)ref;
|
||||||
|
|
||||||
|
if (breakpad && key && gKeyValueAllocator) {
|
||||||
|
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||||
|
|
||||||
|
NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX
|
||||||
|
stringByAppendingString:key];
|
||||||
|
breakpad->SetKeyValue(prefixedKey, value);
|
||||||
|
}
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadSetKeyValue() : error\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BreakpadRemoveUploadParameter(BreakpadRef ref,
|
||||||
|
NSString *key) {
|
||||||
|
try {
|
||||||
|
// Not called at exception time
|
||||||
|
Breakpad *breakpad = (Breakpad *)ref;
|
||||||
|
|
||||||
|
if (breakpad && key && gKeyValueAllocator) {
|
||||||
|
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||||
|
|
||||||
|
NSString *prefixedKey = [NSString stringWithFormat:@"%@%@",
|
||||||
|
@BREAKPAD_SERVER_PARAMETER_PREFIX, key];
|
||||||
|
breakpad->RemoveKeyValue(prefixedKey);
|
||||||
|
}
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//=============================================================================
|
||||||
|
NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) {
|
||||||
|
NSString *value = nil;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Not called at exception time
|
||||||
|
Breakpad *breakpad = (Breakpad *)ref;
|
||||||
|
|
||||||
|
if (!breakpad || !key || !gKeyValueAllocator)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||||
|
|
||||||
|
value = breakpad->KeyValue(key);
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadKeyValue() : error\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) {
|
||||||
|
try {
|
||||||
|
// Not called at exception time
|
||||||
|
Breakpad *breakpad = (Breakpad *)ref;
|
||||||
|
|
||||||
|
if (breakpad && key && gKeyValueAllocator) {
|
||||||
|
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator);
|
||||||
|
|
||||||
|
breakpad->RemoveKeyValue(key);
|
||||||
|
}
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadRemoveKeyValue() : error\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
bool BreakpadHasCrashReportToUpload(BreakpadRef ref) {
|
||||||
|
try {
|
||||||
|
// Not called at exception time
|
||||||
|
Breakpad *breakpad = (Breakpad *)ref;
|
||||||
|
|
||||||
|
if (breakpad) {
|
||||||
|
return breakpad->NextCrashReportToUpload() != 0;
|
||||||
|
}
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadHasCrashReportToUpload() : error\n");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
void BreakpadUploadNextReport(BreakpadRef ref) {
|
||||||
|
try {
|
||||||
|
// Not called at exception time
|
||||||
|
Breakpad *breakpad = (Breakpad *)ref;
|
||||||
|
|
||||||
|
if (breakpad) {
|
||||||
|
breakpad->UploadNextReport();
|
||||||
|
}
|
||||||
|
} catch(...) { // don't let exceptions leave this C API
|
||||||
|
fprintf(stderr, "BreakpadUploadNextReport() : error\n");
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,8 +27,10 @@
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#import <fcntl.h>
|
||||||
#import <pwd.h>
|
#import <pwd.h>
|
||||||
#import <sys/stat.h>
|
#import <sys/stat.h>
|
||||||
|
#include <TargetConditionals.h>
|
||||||
#import <unistd.h>
|
#import <unistd.h>
|
||||||
|
|
||||||
#import <SystemConfiguration/SystemConfiguration.h>
|
#import <SystemConfiguration/SystemConfiguration.h>
|
||||||
|
@ -241,6 +243,9 @@ NSString *const kDefaultServerType = @"google";
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
- (BOOL)readLogFileData {
|
- (BOOL)readLogFileData {
|
||||||
|
#if TARGET_OS_IPHONE
|
||||||
|
return NO;
|
||||||
|
#else
|
||||||
unsigned int logFileCounter = 0;
|
unsigned int logFileCounter = 0;
|
||||||
|
|
||||||
NSString *logPath;
|
NSString *logPath;
|
||||||
|
@ -331,6 +336,7 @@ NSString *const kDefaultServerType = @"google";
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
return YES;
|
return YES;
|
||||||
|
#endif // TARGET_OS_IPHONE
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
|
Loading…
Reference in a new issue