commit
97cee1b6b0
@ -1,55 +0,0 @@ |
|||||||
/*
|
|
||||||
* UBImportVirtualPrinter.h |
|
||||||
* |
|
||||||
* Created on: 18 mars 2009 |
|
||||||
* Author: Julien |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef UBIMPORTVIRTUALPRINTER_H_ |
|
||||||
#define UBIMPORTVIRTUALPRINTER_H_ |
|
||||||
|
|
||||||
#include <QtGui> |
|
||||||
|
|
||||||
#include "UBImportAdaptor.h" |
|
||||||
|
|
||||||
#include "document/UBDocumentProxy.h" |
|
||||||
|
|
||||||
/**
|
|
||||||
* This class import ini file that are generated by Uniboard Printer. |
|
||||||
*/ |
|
||||||
class UBImportVirtualPrinter: public UBImportAdaptor |
|
||||||
{ |
|
||||||
Q_OBJECT; |
|
||||||
|
|
||||||
public: |
|
||||||
UBImportVirtualPrinter(QObject *parent = 0); |
|
||||||
virtual ~UBImportVirtualPrinter(); |
|
||||||
|
|
||||||
// this is the name of the default printer that must be reset after importing from virtual printer.
|
|
||||||
// this allow other import adaptor to backup default printer, then set Uniboard printer as default printer and launch the
|
|
||||||
// print. Then when uniboard will import the file from virtual printer, it will reset the default printer to this one.
|
|
||||||
static QString sOriginalDefaultPrintername; |
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The document expecting to be merged with incoming data |
|
||||||
*/ |
|
||||||
static QPointer<UBDocumentProxy> pendingDocument; |
|
||||||
|
|
||||||
virtual QStringList supportedExtentions(); |
|
||||||
virtual QString importFileFilter(); |
|
||||||
virtual UBDocumentProxy* importFile(const QFile& pFile, const QString& pGroup); |
|
||||||
|
|
||||||
virtual bool addFileToDocument(UBDocumentProxy* pDocument, const QFile& pFile); |
|
||||||
|
|
||||||
|
|
||||||
private: |
|
||||||
|
|
||||||
QString pdfFileName(const QFile& pFile); |
|
||||||
QStringList emfFileNames(const QFile& pFile); |
|
||||||
|
|
||||||
void cleanUp(const QFile& pFile, const QString& pPdfFileName, QStringList pEmfFilenames); |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
#endif /* UBIMPORTVIRTUALPRINTER_H_ */ |
|
@ -1,13 +0,0 @@ |
|||||||
|
|
||||||
#ifndef UBPOWERPOINTAPPLICATION_H_ |
|
||||||
#define UBPOWERPOINTAPPLICATION_H_ |
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) |
|
||||||
#include "UBPowerPointApplication_win.h" |
|
||||||
#elif defined(Q_OS_MAC) |
|
||||||
#include "UBPowerPointApplication_mac.h" |
|
||||||
#else |
|
||||||
//TODO Linux
|
|
||||||
#endif |
|
||||||
|
|
||||||
#endif /* UBPOWERPOINTAPPLICATION_H_ */ |
|
@ -1,28 +0,0 @@ |
|||||||
|
|
||||||
#ifndef UBPOWERPOINTAPPLICATIONMAC_H_ |
|
||||||
#define UBPOWERPOINTAPPLICATIONMAC_H_ |
|
||||||
|
|
||||||
#include <QtCore> |
|
||||||
#include "UBImportAdaptor.h" |
|
||||||
|
|
||||||
class UBPowerPointApplication : public UBImportAdaptor |
|
||||||
{ |
|
||||||
Q_OBJECT |
|
||||||
|
|
||||||
public: |
|
||||||
UBPowerPointApplication(QObject* parent = 0); |
|
||||||
virtual ~UBPowerPointApplication(); |
|
||||||
|
|
||||||
virtual QStringList supportedExtentions(); |
|
||||||
virtual QString importFileFilter(); |
|
||||||
virtual UBDocumentProxy* importFile(const QFile& pFile, const QString& pGroup); |
|
||||||
|
|
||||||
virtual bool addFileToDocument(UBDocumentProxy* pDocument, const QFile& pFile); |
|
||||||
|
|
||||||
private: |
|
||||||
bool generatePdfFromPptFile(const QString& pptFile, const QString& pOutputFile); |
|
||||||
|
|
||||||
|
|
||||||
}; |
|
||||||
|
|
||||||
#endif /* UBPOWERPOINTAPPLICATIONMAC_H_ */ |
|
@ -1,227 +0,0 @@ |
|||||||
|
|
||||||
#include "UBPowerPointApplication_mac.h" |
|
||||||
|
|
||||||
#include "core/UBApplication.h" |
|
||||||
#include "core/UBDocumentManager.h" |
|
||||||
#include "frameworks/UBPlatformUtils.h" |
|
||||||
#include "frameworks/UBFileSystemUtils.h" |
|
||||||
|
|
||||||
#import <Foundation/NSAutoreleasePool.h> |
|
||||||
#import <Carbon/Carbon.h> |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
UBPowerPointApplication::UBPowerPointApplication(QObject* parent) |
|
||||||
: UBImportAdaptor(parent) |
|
||||||
{ |
|
||||||
// NOOP |
|
||||||
} |
|
||||||
|
|
||||||
UBPowerPointApplication::~UBPowerPointApplication() |
|
||||||
{ |
|
||||||
// NOOP |
|
||||||
} |
|
||||||
|
|
||||||
class AppleScriptThread : public QThread |
|
||||||
{ |
|
||||||
public: |
|
||||||
AppleScriptThread(NSAppleScript *appleScript, QObject *parent = 0) |
|
||||||
: QThread(parent) |
|
||||||
, mAppleScript(appleScript) |
|
||||||
, mError(nil) |
|
||||||
{ |
|
||||||
// NOOP |
|
||||||
} |
|
||||||
|
|
||||||
~AppleScriptThread() |
|
||||||
{ |
|
||||||
if (mError) |
|
||||||
{ |
|
||||||
[mError release]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void run() |
|
||||||
{ |
|
||||||
NSDictionary *error = nil; |
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
||||||
[mAppleScript executeAndReturnError:&error]; |
|
||||||
mError = error ? [[NSDictionary alloc] initWithDictionary:error] : nil; |
|
||||||
[pool release]; |
|
||||||
} |
|
||||||
|
|
||||||
NSDictionary* error() |
|
||||||
{ |
|
||||||
return mError; |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
NSAppleScript *mAppleScript; |
|
||||||
NSDictionary *mError; |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
NSString* escapePath(const QString &filePath) |
|
||||||
{ |
|
||||||
QString escapedFilePath(filePath); |
|
||||||
escapedFilePath.replace("\"", "\\\""); |
|
||||||
QByteArray pathRepresentation = QFile::encodeName(escapedFilePath); |
|
||||||
return [[NSFileManager defaultManager] stringWithFileSystemRepresentation:pathRepresentation.constData() length:pathRepresentation.length()]; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool UBPowerPointApplication::generatePdfFromPptFile(const QString& pptFile, const QString& pOutputFile) |
|
||||||
{ |
|
||||||
bool result = true; |
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
||||||
QResource scriptResource(":/PowerPointImport.applescript"); |
|
||||||
NSString *script = [[NSString alloc] initWithBytes:scriptResource.data() length:scriptResource.size() encoding:NSUTF8StringEncoding]; |
|
||||||
QFileInfo pptFileInfo(pptFile); |
|
||||||
NSString *scriptSource = [NSString stringWithFormat:script, escapePath(pptFileInfo.fileName()), escapePath(pptFile), escapePath(pOutputFile)]; |
|
||||||
[script release]; |
|
||||||
NSAppleScript *pdfExportScript = [[NSAppleScript alloc] initWithSource:scriptSource]; |
|
||||||
AppleScriptThread appleScriptThread(pdfExportScript); |
|
||||||
appleScriptThread.start(); |
|
||||||
while (appleScriptThread.isRunning()) |
|
||||||
{ |
|
||||||
qApp->processEvents(); |
|
||||||
} |
|
||||||
if (appleScriptThread.error()) |
|
||||||
{ |
|
||||||
const char* errorDescription = [[appleScriptThread.error() description] UTF8String]; |
|
||||||
qWarning() << "PowerPoint import error:" << QString::fromUtf8(errorDescription, strlen(errorDescription)); |
|
||||||
result = false; |
|
||||||
} |
|
||||||
[pdfExportScript release]; |
|
||||||
[pool drain]; |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
QStringList UBPowerPointApplication::supportedExtentions() |
|
||||||
{ |
|
||||||
QStringList result; |
|
||||||
CFURLRef powerPointURL = NULL; |
|
||||||
|
|
||||||
if (LSFindApplicationForInfo(kLSUnknownCreator, CFSTR("com.microsoft.Powerpoint"), NULL, NULL, &powerPointURL) == noErr) |
|
||||||
{ |
|
||||||
CFBundleRef powerPointBundle = CFBundleCreate(kCFAllocatorDefault, powerPointURL); |
|
||||||
if (powerPointBundle) |
|
||||||
{ |
|
||||||
CFStringRef buildNumber = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(powerPointBundle, CFSTR("MicrosoftBuildNumber")); |
|
||||||
if (buildNumber && (CFGetTypeID(buildNumber) == CFStringGetTypeID())) |
|
||||||
{ |
|
||||||
int buildValue = CFStringGetIntValue(buildNumber); |
|
||||||
if (buildValue >= 80409) |
|
||||||
{ |
|
||||||
// PowerPoint 2008 |
|
||||||
result << "ppt" << "pptx" << "pptm" << "pps" << "ppsx" << "ppsm"; |
|
||||||
} |
|
||||||
/* |
|
||||||
else if (buildValue >= `Office 2004 MicrosoftBuildNumber`) |
|
||||||
{ |
|
||||||
result << "ppt" << "pptm" << "pps" << "ppsm"; |
|
||||||
} |
|
||||||
*/ |
|
||||||
else |
|
||||||
{ |
|
||||||
qWarning("Unsupported Microsoft PowerPoint version: %d", buildValue); |
|
||||||
} |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
qWarning("Invalid PowerPoint MicrosoftBuildNumber"); |
|
||||||
} |
|
||||||
CFRelease(powerPointBundle); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
qWarning("Microsoft PowerPoint bundle was not found"); |
|
||||||
} |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
qWarning("Microsoft PowerPoint was not found"); |
|
||||||
} |
|
||||||
|
|
||||||
if (powerPointURL) |
|
||||||
{ |
|
||||||
CFRelease(powerPointURL); |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
QString UBPowerPointApplication::importFileFilter() |
|
||||||
{ |
|
||||||
QStringList extentions = supportedExtentions(); |
|
||||||
if (extentions.count() > 0) |
|
||||||
{ |
|
||||||
QString filter = "PowerPoint ("; |
|
||||||
foreach (const QString ext, extentions) |
|
||||||
{ |
|
||||||
filter.append("*."); |
|
||||||
filter.append(ext); |
|
||||||
filter.append(" "); |
|
||||||
} |
|
||||||
filter = filter.trimmed(); |
|
||||||
filter.append(")"); |
|
||||||
return filter; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
return 0; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
UBDocumentProxy* UBPowerPointApplication::importFile(const QFile& pFile, const QString& pGroup) |
|
||||||
{ |
|
||||||
UBApplication::showMessage(tr("Converting PowerPoint file ..."), true); |
|
||||||
UBDocumentProxy* result = 0; |
|
||||||
|
|
||||||
QString tempDir = UBFileSystemUtils::createTempDir(); |
|
||||||
QFileInfo sourceFileInfo(pFile); |
|
||||||
QString tempFile = tempDir + "/" + sourceFileInfo.baseName() + ".pdf"; |
|
||||||
|
|
||||||
if (generatePdfFromPptFile(pFile.fileName(), tempFile)) |
|
||||||
{ |
|
||||||
UBApplication::showMessage(tr("PowerPoint import successful.")); |
|
||||||
QFile tmp(tempFile); |
|
||||||
result = UBDocumentManager::documentManager()->importFile(tmp, pGroup); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
UBApplication::showMessage(tr("PowerPoint import failed.")); |
|
||||||
} |
|
||||||
|
|
||||||
UBFileSystemUtils::deleteDir(tempDir); |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool UBPowerPointApplication::addFileToDocument(UBDocumentProxy* pDocument, const QFile& pFile) |
|
||||||
{ |
|
||||||
UBApplication::showMessage(tr("Converting PowerPoint file ..."), true); |
|
||||||
bool result = false; |
|
||||||
|
|
||||||
QString tempDir = UBFileSystemUtils::createTempDir(); |
|
||||||
QFileInfo sourceFileInfo(pFile); |
|
||||||
QString tempFile = tempDir + "/" + sourceFileInfo.baseName() + ".pdf"; |
|
||||||
|
|
||||||
if (generatePdfFromPptFile(pFile.fileName(), tempFile)) |
|
||||||
{ |
|
||||||
UBApplication::showMessage(tr("PowerPoint import successful.")); |
|
||||||
QFile tmp(tempFile); |
|
||||||
result = UBDocumentManager::documentManager()->addFileToDocument(pDocument, tmp); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
UBApplication::showMessage(tr("PowerPoint import failed.")); |
|
||||||
} |
|
||||||
|
|
||||||
UBFileSystemUtils::deleteDir(tempDir); |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
@ -1,269 +0,0 @@ |
|||||||
/*
|
|
||||||
* UBPowerPointApplication.cpp |
|
||||||
* |
|
||||||
* Created on: Dec 4, 2008 |
|
||||||
* Author: Luc |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "UBPowerPointApplication_win.h" |
|
||||||
|
|
||||||
#include "frameworks/UBFileSystemUtils.h" |
|
||||||
|
|
||||||
#include "core/UBApplication.h" |
|
||||||
#include "core/UBDocumentManager.h" |
|
||||||
#include "core/UBPersistenceManager.h" |
|
||||||
|
|
||||||
#include "UBImportVirtualPrinter.h" |
|
||||||
#include "msppt.h" |
|
||||||
#include "mso.h" |
|
||||||
#include "windows.h" |
|
||||||
|
|
||||||
UBPowerPointApplication::UBPowerPointApplication(QObject* parent) |
|
||||||
: UBImportAdaptor(parent) |
|
||||||
, mInit(false) |
|
||||||
, mHasException(false) |
|
||||||
, mSupportPpt(false) |
|
||||||
, mSupportPptX(false) |
|
||||||
{ |
|
||||||
// NOOP
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
UBPowerPointApplication::~UBPowerPointApplication() |
|
||||||
{ |
|
||||||
// NOOP
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
void UBPowerPointApplication::init() |
|
||||||
{ |
|
||||||
PowerPoint::Application ppt; |
|
||||||
qDebug() << "PPT version :" << ppt.Version().toFloat(); |
|
||||||
mSupportPpt = !ppt.isNull(); |
|
||||||
mSupportPptX = ppt.Version().toFloat() >= 12; |
|
||||||
mInit = true; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool UBPowerPointApplication::isPowerPointInstalled() |
|
||||||
{ |
|
||||||
if (!mInit) |
|
||||||
{ |
|
||||||
init(); |
|
||||||
} |
|
||||||
return mSupportPpt; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool UBPowerPointApplication::supportPptx() |
|
||||||
{ |
|
||||||
if (!mInit) |
|
||||||
{ |
|
||||||
init(); |
|
||||||
} |
|
||||||
|
|
||||||
return mSupportPptX; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool UBPowerPointApplication::generatePdfFromPptFile(const QString& pptFile, const QString& outputDir) |
|
||||||
{ |
|
||||||
Q_UNUSED(pptFile); |
|
||||||
Q_UNUSED(outputDir); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool UBPowerPointApplication::generateImagesFromPptFile(const QString& pptFile, const QString& outputDir, const QString& imageFormat, const QSize& imageSize) |
|
||||||
{ |
|
||||||
mHasException = false; |
|
||||||
|
|
||||||
PowerPoint::Application ppt; |
|
||||||
|
|
||||||
connect(&ppt, SIGNAL(exception ( int , const QString & , const QString & , const QString & )) |
|
||||||
, this, SLOT(exception ( int , const QString & , const QString & , const QString & ))); |
|
||||||
|
|
||||||
if (ppt.isNull()) |
|
||||||
return false; |
|
||||||
|
|
||||||
ppt.Activate(); |
|
||||||
ppt.SetWindowState(PowerPoint::ppWindowMinimized); |
|
||||||
|
|
||||||
UBApplication::processEvents(); |
|
||||||
|
|
||||||
int previouslyOpenPresentations = ppt.Presentations()->Count(); |
|
||||||
|
|
||||||
PowerPoint::Presentation *presentation = |
|
||||||
ppt.Presentations()->Open(QDir::toNativeSeparators(pptFile)); |
|
||||||
|
|
||||||
int currentOpenPresentations = ppt.Presentations()->Count(); |
|
||||||
|
|
||||||
if(!presentation) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (ppt.Version().toFloat() >= 12) // PPT 2007 is broken with high res exports : https://trac.assembla.com/uniboard/ticket/297#comment:16
|
|
||||||
{ |
|
||||||
presentation->Export(QDir::toNativeSeparators(outputDir), imageFormat); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
presentation->Export(QDir::toNativeSeparators(outputDir), imageFormat, imageSize.width(), imageSize.height()); |
|
||||||
} |
|
||||||
|
|
||||||
if(mHasException) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (currentOpenPresentations != previouslyOpenPresentations) |
|
||||||
presentation->Close(); |
|
||||||
|
|
||||||
if (ppt.Presentations()->Count() == 0) |
|
||||||
ppt.Quit(); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
QStringList UBPowerPointApplication::supportedExtentions() |
|
||||||
{ |
|
||||||
QStringList result; |
|
||||||
if (UBPowerPointApplication::isPowerPointInstalled()) |
|
||||||
{ |
|
||||||
result << QStringList("ppt") << "pps"; |
|
||||||
if (UBPowerPointApplication::supportPptx()) |
|
||||||
{ |
|
||||||
result << "pptx" << "pptm" << "ppsx" << "ppsm"; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
QString UBPowerPointApplication::importFileFilter() |
|
||||||
{ |
|
||||||
if (UBPowerPointApplication::isPowerPointInstalled()) |
|
||||||
{ |
|
||||||
if (UBPowerPointApplication::supportPptx()) |
|
||||||
{ |
|
||||||
return "PowerPoint (*.ppt *.pptx *.pptm *.pps *.ppsx *.ppsm)"; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
return "PowerPoint (*.ppt *.pps)"; |
|
||||||
} |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
return ""; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
UBDocumentProxy* UBPowerPointApplication::importFile(const QFile& pFile, const QString& pGroup) |
|
||||||
{ |
|
||||||
|
|
||||||
UBDocumentProxy* document = 0; |
|
||||||
// print by changing default printer and use shell execute to print
|
|
||||||
LPTSTR wDefaultPrinterName = new TCHAR[255]; |
|
||||||
LPTSTR virtualPrinter = new TCHAR[255]; |
|
||||||
|
|
||||||
int i = QString("Uniboard").toWCharArray(virtualPrinter); // TODO UB 4.x make me configurable ....
|
|
||||||
virtualPrinter[i] = 0; |
|
||||||
DWORD bufferSize = 1000; |
|
||||||
GetDefaultPrinter(wDefaultPrinterName, &bufferSize); |
|
||||||
UBImportVirtualPrinter::sOriginalDefaultPrintername = QString::fromWCharArray(wDefaultPrinterName); |
|
||||||
|
|
||||||
if (!SetDefaultPrinter(virtualPrinter)) |
|
||||||
{ |
|
||||||
QMessageBox msgBox(0); |
|
||||||
msgBox.setText(tr("Uniboard printer is not installed. Import will be done in jpg format.")); |
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok); |
|
||||||
msgBox.setDefaultButton(QMessageBox::Ok); |
|
||||||
msgBox.exec(); |
|
||||||
QString tempLocation = UBFileSystemUtils::createTempDir(); |
|
||||||
QDir tempDir(tempLocation); |
|
||||||
|
|
||||||
bool pptSuccess = generateImagesFromPptFile(pFile.fileName(), tempLocation, "jpg", QSize(3000, 2250)); |
|
||||||
|
|
||||||
if (pptSuccess) |
|
||||||
{ |
|
||||||
document = UBDocumentManager::documentManager()->importDir(tempDir, pGroup); |
|
||||||
|
|
||||||
if (document) |
|
||||||
{ |
|
||||||
UBFileSystemUtils::deleteDir(tempLocation); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
document = UBPersistenceManager::persistenceManager()->createDocument(pGroup); |
|
||||||
|
|
||||||
QFileInfo sourceFileInfo(pFile); |
|
||||||
|
|
||||||
document->setMetaData(UBSettings::documentName, sourceFileInfo.baseName()); |
|
||||||
UBPersistenceManager::persistenceManager()->persistDocumentMetadata(document); |
|
||||||
|
|
||||||
UBImportVirtualPrinter::pendingDocument = document; |
|
||||||
|
|
||||||
int result = (int)ShellExecute(NULL, QString("print").utf16() , pFile.fileName().utf16(), NULL, NULL, SW_HIDE); |
|
||||||
|
|
||||||
qDebug() << "PPT shellexec print result" << result; |
|
||||||
} |
|
||||||
|
|
||||||
delete[] wDefaultPrinterName; |
|
||||||
delete[] virtualPrinter; |
|
||||||
|
|
||||||
return document; |
|
||||||
} |
|
||||||
|
|
||||||
bool UBPowerPointApplication::addFileToDocument(UBDocumentProxy* pDocument, const QFile& pFile) |
|
||||||
{ |
|
||||||
bool result = false; |
|
||||||
// print by changing default printer and use shell execute to print
|
|
||||||
LPTSTR wDefaultPrinterName = new TCHAR[255]; |
|
||||||
LPTSTR virtualPrinter = new TCHAR[255]; |
|
||||||
|
|
||||||
int i = QString("Uniboard").toWCharArray(virtualPrinter); // TODO UB 4.x make me configurable ....
|
|
||||||
virtualPrinter[i] = 0; |
|
||||||
DWORD bufferSize = 1000; |
|
||||||
GetDefaultPrinter(wDefaultPrinterName, &bufferSize); |
|
||||||
UBImportVirtualPrinter::sOriginalDefaultPrintername = QString::fromWCharArray(wDefaultPrinterName); |
|
||||||
|
|
||||||
if (!SetDefaultPrinter(virtualPrinter)) |
|
||||||
{ |
|
||||||
QMessageBox msgBox(0); |
|
||||||
msgBox.setText(tr("Uniboard printer is not installed. Import will be done in jpg format.")); |
|
||||||
msgBox.setStandardButtons(QMessageBox::Ok); |
|
||||||
msgBox.setDefaultButton(QMessageBox::Ok); |
|
||||||
msgBox.exec(); |
|
||||||
QString tempLocation = UBFileSystemUtils::createTempDir(); |
|
||||||
QDir tempDir(tempLocation); |
|
||||||
|
|
||||||
bool pptSuccess = generateImagesFromPptFile(pFile.fileName(), tempLocation, "jpg", QSize(3000, 2250)); |
|
||||||
|
|
||||||
if (pptSuccess) |
|
||||||
{ |
|
||||||
if (UBDocumentManager::documentManager()->addImageDirToDocument(tempDir, pDocument)) |
|
||||||
{ |
|
||||||
UBFileSystemUtils::deleteDir(tempLocation); |
|
||||||
result = true; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
UBImportVirtualPrinter::pendingDocument = pDocument; |
|
||||||
result = ShellExecute(NULL, QString("print").utf16() , pFile.fileName().utf16(), NULL, NULL, SW_HIDE); |
|
||||||
} |
|
||||||
|
|
||||||
delete[] wDefaultPrinterName; |
|
||||||
delete[] virtualPrinter; |
|
||||||
return result; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
void UBPowerPointApplication::exception ( int code, const QString & source, const QString & desc, const QString & help ) |
|
||||||
{ |
|
||||||
Q_UNUSED(help); |
|
||||||
mHasException = true; |
|
||||||
qCritical() << source << desc << code; |
|
||||||
} |
|
||||||
|
|
@ -1,50 +0,0 @@ |
|||||||
/*
|
|
||||||
* UBPowerPointApplication.h |
|
||||||
* |
|
||||||
* Created on: Dec 4, 2008 |
|
||||||
* Author: Luc |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef UBPOWERPOINTAPPLICATIONWIN_H_ |
|
||||||
#define UBPOWERPOINTAPPLICATIONWIN_H_ |
|
||||||
|
|
||||||
#include <QtCore> |
|
||||||
#include "UBImportAdaptor.h" |
|
||||||
|
|
||||||
class UBPowerPointApplication : public UBImportAdaptor { |
|
||||||
|
|
||||||
Q_OBJECT; |
|
||||||
|
|
||||||
public: |
|
||||||
UBPowerPointApplication(QObject* parent = 0); |
|
||||||
virtual ~UBPowerPointApplication(); |
|
||||||
|
|
||||||
bool isPowerPointInstalled(); |
|
||||||
|
|
||||||
bool supportPptx(); |
|
||||||
|
|
||||||
virtual QStringList supportedExtentions(); |
|
||||||
virtual QString importFileFilter(); |
|
||||||
virtual UBDocumentProxy* importFile(const QFile& pFile, const QString& pGroup); |
|
||||||
|
|
||||||
virtual bool addFileToDocument(UBDocumentProxy* pDocument, const QFile& pFile); |
|
||||||
|
|
||||||
private: |
|
||||||
|
|
||||||
bool generateImagesFromPptFile(const QString& pptFile, const QString&outputDir, const QString& imageFormat, const QSize& imageSize); |
|
||||||
bool generatePdfFromPptFile(const QString& pptFile, const QString& pOutputFile); |
|
||||||
bool catchAndProcessFile(); |
|
||||||
|
|
||||||
void init(); |
|
||||||
bool mInit; |
|
||||||
bool mHasException; |
|
||||||
bool mSupportPpt; |
|
||||||
bool mSupportPptX; |
|
||||||
|
|
||||||
|
|
||||||
private slots: |
|
||||||
void exception ( int code, const QString & source, const QString & desc, const QString & help); |
|
||||||
|
|
||||||
}; |
|
||||||
|
|
||||||
#endif /* UBPOWERPOINTAPPLICATIONWIN_H_ */ |
|
@ -1 +0,0 @@ |
|||||||
opensource@google.com |
|
@ -1,28 +0,0 @@ |
|||||||
Copyright (c) 2006, 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. |
|
@ -1,44 +0,0 @@ |
|||||||
# Copyright (c) 2010, 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. |
|
||||||
|
|
||||||
# We only use this file to ease the steps of generating projects after |
|
||||||
# syncing, if we use gclient. All dependencies are svn:externals instead. |
|
||||||
# If you're not using gclient, you need to run the gyp python script to |
|
||||||
# generate the projects. |
|
||||||
# This can be done by the following command (assuming current directory): |
|
||||||
# src\tools\gyp\gyp.bat src\client\windows\breakpad_client.gyp |
|
||||||
hooks = [ |
|
||||||
{ |
|
||||||
# A change to a .gyp, .gypi, or to GYP itself should run the generator. |
|
||||||
"pattern": ".", |
|
||||||
"action": ["python", |
|
||||||
"src/src/tools/gyp/gyp", |
|
||||||
"src/src/client/windows/breakpad_client.gyp"], |
|
||||||
}, |
|
||||||
] |
|
@ -1,234 +0,0 @@ |
|||||||
Installation Instructions |
|
||||||
************************* |
|
||||||
|
|
||||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, |
|
||||||
2006 Free Software Foundation, Inc. |
|
||||||
|
|
||||||
This file is free documentation; the Free Software Foundation gives |
|
||||||
unlimited permission to copy, distribute and modify it. |
|
||||||
|
|
||||||
Basic Installation |
|
||||||
================== |
|
||||||
|
|
||||||
Briefly, the shell commands `./configure; make; make install' should |
|
||||||
configure, build, and install this package. The following |
|
||||||
more-detailed instructions are generic; see the `README' file for |
|
||||||
instructions specific to this package. |
|
||||||
|
|
||||||
The `configure' shell script attempts to guess correct values for |
|
||||||
various system-dependent variables used during compilation. It uses |
|
||||||
those values to create a `Makefile' in each directory of the package. |
|
||||||
It may also create one or more `.h' files containing system-dependent |
|
||||||
definitions. Finally, it creates a shell script `config.status' that |
|
||||||
you can run in the future to recreate the current configuration, and a |
|
||||||
file `config.log' containing compiler output (useful mainly for |
|
||||||
debugging `configure'). |
|
||||||
|
|
||||||
It can also use an optional file (typically called `config.cache' |
|
||||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves |
|
||||||
the results of its tests to speed up reconfiguring. Caching is |
|
||||||
disabled by default to prevent problems with accidental use of stale |
|
||||||
cache files. |
|
||||||
|
|
||||||
If you need to do unusual things to compile the package, please try |
|
||||||
to figure out how `configure' could check whether to do them, and mail |
|
||||||
diffs or instructions to the address given in the `README' so they can |
|
||||||
be considered for the next release. If you are using the cache, and at |
|
||||||
some point `config.cache' contains results you don't want to keep, you |
|
||||||
may remove or edit it. |
|
||||||
|
|
||||||
The file `configure.ac' (or `configure.in') is used to create |
|
||||||
`configure' by a program called `autoconf'. You need `configure.ac' if |
|
||||||
you want to change it or regenerate `configure' using a newer version |
|
||||||
of `autoconf'. |
|
||||||
|
|
||||||
The simplest way to compile this package is: |
|
||||||
|
|
||||||
1. `cd' to the directory containing the package's source code and type |
|
||||||
`./configure' to configure the package for your system. |
|
||||||
|
|
||||||
Running `configure' might take a while. While running, it prints |
|
||||||
some messages telling which features it is checking for. |
|
||||||
|
|
||||||
2. Type `make' to compile the package. |
|
||||||
|
|
||||||
3. Optionally, type `make check' to run any self-tests that come with |
|
||||||
the package. |
|
||||||
|
|
||||||
4. Type `make install' to install the programs and any data files and |
|
||||||
documentation. |
|
||||||
|
|
||||||
5. You can remove the program binaries and object files from the |
|
||||||
source code directory by typing `make clean'. To also remove the |
|
||||||
files that `configure' created (so you can compile the package for |
|
||||||
a different kind of computer), type `make distclean'. There is |
|
||||||
also a `make maintainer-clean' target, but that is intended mainly |
|
||||||
for the package's developers. If you use it, you may have to get |
|
||||||
all sorts of other programs in order to regenerate files that came |
|
||||||
with the distribution. |
|
||||||
|
|
||||||
Compilers and Options |
|
||||||
===================== |
|
||||||
|
|
||||||
Some systems require unusual options for compilation or linking that the |
|
||||||
`configure' script does not know about. Run `./configure --help' for |
|
||||||
details on some of the pertinent environment variables. |
|
||||||
|
|
||||||
You can give `configure' initial values for configuration parameters |
|
||||||
by setting variables in the command line or in the environment. Here |
|
||||||
is an example: |
|
||||||
|
|
||||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix |
|
||||||
|
|
||||||
*Note Defining Variables::, for more details. |
|
||||||
|
|
||||||
Compiling For Multiple Architectures |
|
||||||
==================================== |
|
||||||
|
|
||||||
You can compile the package for more than one kind of computer at the |
|
||||||
same time, by placing the object files for each architecture in their |
|
||||||
own directory. To do this, you can use GNU `make'. `cd' to the |
|
||||||
directory where you want the object files and executables to go and run |
|
||||||
the `configure' script. `configure' automatically checks for the |
|
||||||
source code in the directory that `configure' is in and in `..'. |
|
||||||
|
|
||||||
With a non-GNU `make', it is safer to compile the package for one |
|
||||||
architecture at a time in the source code directory. After you have |
|
||||||
installed the package for one architecture, use `make distclean' before |
|
||||||
reconfiguring for another architecture. |
|
||||||
|
|
||||||
Installation Names |
|
||||||
================== |
|
||||||
|
|
||||||
By default, `make install' installs the package's commands under |
|
||||||
`/usr/local/bin', include files under `/usr/local/include', etc. You |
|
||||||
can specify an installation prefix other than `/usr/local' by giving |
|
||||||
`configure' the option `--prefix=PREFIX'. |
|
||||||
|
|
||||||
You can specify separate installation prefixes for |
|
||||||
architecture-specific files and architecture-independent files. If you |
|
||||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses |
|
||||||
PREFIX as the prefix for installing programs and libraries. |
|
||||||
Documentation and other data files still use the regular prefix. |
|
||||||
|
|
||||||
In addition, if you use an unusual directory layout you can give |
|
||||||
options like `--bindir=DIR' to specify different values for particular |
|
||||||
kinds of files. Run `configure --help' for a list of the directories |
|
||||||
you can set and what kinds of files go in them. |
|
||||||
|
|
||||||
If the package supports it, you can cause programs to be installed |
|
||||||
with an extra prefix or suffix on their names by giving `configure' the |
|
||||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. |
|
||||||
|
|
||||||
Optional Features |
|
||||||
================= |
|
||||||
|
|
||||||
Some packages pay attention to `--enable-FEATURE' options to |
|
||||||
`configure', where FEATURE indicates an optional part of the package. |
|
||||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE |
|
||||||
is something like `gnu-as' or `x' (for the X Window System). The |
|
||||||
`README' should mention any `--enable-' and `--with-' options that the |
|
||||||
package recognizes. |
|
||||||
|
|
||||||
For packages that use the X Window System, `configure' can usually |
|
||||||
find the X include and library files automatically, but if it doesn't, |
|
||||||
you can use the `configure' options `--x-includes=DIR' and |
|
||||||
`--x-libraries=DIR' to specify their locations. |
|
||||||
|
|
||||||
Specifying the System Type |
|
||||||
========================== |
|
||||||
|
|
||||||
There may be some features `configure' cannot figure out automatically, |
|
||||||
but needs to determine by the type of machine the package will run on. |
|
||||||
Usually, assuming the package is built to be run on the _same_ |
|
||||||
architectures, `configure' can figure that out, but if it prints a |
|
||||||
message saying it cannot guess the machine type, give it the |
|
||||||
`--build=TYPE' option. TYPE can either be a short name for the system |
|
||||||
type, such as `sun4', or a canonical name which has the form: |
|
||||||
|
|
||||||
CPU-COMPANY-SYSTEM |
|
||||||
|
|
||||||
where SYSTEM can have one of these forms: |
|
||||||
|
|
||||||
OS KERNEL-OS |
|
||||||
|
|
||||||
See the file `config.sub' for the possible values of each field. If |
|
||||||
`config.sub' isn't included in this package, then this package doesn't |
|
||||||
need to know the machine type. |
|
||||||
|
|
||||||
If you are _building_ compiler tools for cross-compiling, you should |
|
||||||
use the option `--target=TYPE' to select the type of system they will |
|
||||||
produce code for. |
|
||||||
|
|
||||||
If you want to _use_ a cross compiler, that generates code for a |
|
||||||
platform different from the build platform, you should specify the |
|
||||||
"host" platform (i.e., that on which the generated programs will |
|
||||||
eventually be run) with `--host=TYPE'. |
|
||||||
|
|
||||||
Sharing Defaults |
|
||||||
================ |
|
||||||
|
|
||||||
If you want to set default values for `configure' scripts to share, you |
|
||||||
can create a site shell script called `config.site' that gives default |
|
||||||
values for variables like `CC', `cache_file', and `prefix'. |
|
||||||
`configure' looks for `PREFIX/share/config.site' if it exists, then |
|
||||||
`PREFIX/etc/config.site' if it exists. Or, you can set the |
|
||||||
`CONFIG_SITE' environment variable to the location of the site script. |
|
||||||
A warning: not all `configure' scripts look for a site script. |
|
||||||
|
|
||||||
Defining Variables |
|
||||||
================== |
|
||||||
|
|
||||||
Variables not defined in a site shell script can be set in the |
|
||||||
environment passed to `configure'. However, some packages may run |
|
||||||
configure again during the build, and the customized values of these |
|
||||||
variables may be lost. In order to avoid this problem, you should set |
|
||||||
them in the `configure' command line, using `VAR=value'. For example: |
|
||||||
|
|
||||||
./configure CC=/usr/local2/bin/gcc |
|
||||||
|
|
||||||
causes the specified `gcc' to be used as the C compiler (unless it is |
|
||||||
overridden in the site shell script). |
|
||||||
|
|
||||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to |
|
||||||
an Autoconf bug. Until the bug is fixed you can use this workaround: |
|
||||||
|
|
||||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash |
|
||||||
|
|
||||||
`configure' Invocation |
|
||||||
====================== |
|
||||||
|
|
||||||
`configure' recognizes the following options to control how it operates. |
|
||||||
|
|
||||||
`--help' |
|
||||||
`-h' |
|
||||||
Print a summary of the options to `configure', and exit. |
|
||||||
|
|
||||||
`--version' |
|
||||||
`-V' |
|
||||||
Print the version of Autoconf used to generate the `configure' |
|
||||||
script, and exit. |
|
||||||
|
|
||||||
`--cache-file=FILE' |
|
||||||
Enable the cache: use and save the results of the tests in FILE, |
|
||||||
traditionally `config.cache'. FILE defaults to `/dev/null' to |
|
||||||
disable caching. |
|
||||||
|
|
||||||
`--config-cache' |
|
||||||
`-C' |
|
||||||
Alias for `--cache-file=config.cache'. |
|
||||||
|
|
||||||
`--quiet' |
|
||||||
`--silent' |
|
||||||
`-q' |
|
||||||
Do not print messages saying which checks are being made. To |
|
||||||
suppress all normal output, redirect it to `/dev/null' (any error |
|
||||||
messages will still be shown). |
|
||||||
|
|
||||||
`--srcdir=DIR' |
|
||||||
Look for the package's source code in directory DIR. Usually |
|
||||||
`configure' can determine that directory automatically. |
|
||||||
|
|
||||||
`configure' also accepts some other, not widely useful, options. Run |
|
||||||
`configure --help' for more details. |
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,43 +0,0 @@ |
|||||||
Breakpad is a set of client and server components which implement a |
|
||||||
crash-reporting system. |
|
||||||
|
|
||||||
|
|
||||||
----- |
|
||||||
Getting started in 32-bit mode (from trunk) |
|
||||||
Configure: CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure |
|
||||||
Build: make |
|
||||||
Test: make check |
|
||||||
Install: make install |
|
||||||
|
|
||||||
If you need to reconfigure your build be sure to run "make distclean" first. |
|
||||||
|
|
||||||
|
|
||||||
----- |
|
||||||
To request change review: |
|
||||||
0. Get access to a read-write copy of source. |
|
||||||
Owners at http://code.google.com/p/google-breakpad/ are able to grant |
|
||||||
this access. |
|
||||||
|
|
||||||
1. Check out a read-write copy of source using instructions at |
|
||||||
http://code.google.com/p/google-breakpad/source/checkout |
|
||||||
|
|
||||||
2. Make changes. Build and test your changes. |
|
||||||
For core code like processor use methods above. |
|
||||||
For linux/mac/windows, there are test targets in each project file. |
|
||||||
|
|
||||||
3. Download http://codereview.appspot.com/static/upload.py |
|
||||||
|
|
||||||
4. Run upload.py from the 'src' directory: |
|
||||||
upload.py --server=breakpad.appspot.com |
|
||||||
|
|
||||||
You will be prompted for credential and a description. |
|
||||||
|
|
||||||
5. At http://breakpad.appspot.com you'll find your issue listed; click on it, |
|
||||||
and select Publish+Mail, and enter in the code reviewer and CC |
|
||||||
google-breakpad-dev@googlegroups.com |
|
||||||
|
|
||||||
6. When applying code review feedback, specify the '-i' option when running |
|
||||||
upload.py again and pass the issue number so it updates the existing issue, |
|
||||||
rather than creating a new one. |
|
||||||
Be sure to rerun upload.py from the same directory as you did for previous |
|
||||||
uploads to allow for proper diff calculations. |
|
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@ |
|||||||
/usr/share/automake-1.11/compile |
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,584 +0,0 @@ |
|||||||
#! /bin/sh |
|
||||||
# depcomp - compile a program generating dependencies as side-effects |
|
||||||
|
|
||||||
scriptversion=2006-10-15.18 |
|
||||||
|
|
||||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 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, write to the Free Software |
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
||||||
# 02110-1301, USA. |
|
||||||
|
|
||||||
# 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 outputing dependencies. |
|
||||||
libtool Whether libtool is used (yes/no). |
|
||||||
|
|
||||||
Report bugs to <bug-automake@gnu.org>. |
|
||||||
EOF |
|
||||||
exit $? |
|
||||||
;; |
|
||||||
-v | --v*) |
|
||||||
echo "depcomp $scriptversion" |
|
||||||
exit $? |
|
||||||
;; |
|
||||||
esac |
|
||||||
|
|
||||||
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" |
|
||||||
|
|
||||||
# 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 |
|
||||||
|
|
||||||
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 -eq 0; then : |
|
||||||
else |
|
||||||
rm -f "$tmpdepfile" |
|
||||||
exit $stat |
|
||||||
fi |
|
||||||
mv "$tmpdepfile" "$depfile" |
|
||||||
;; |
|
||||||
|
|
||||||
gcc) |
|
||||||
## 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). |
|
||||||
## - 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 -eq 0; then : |
|
||||||
else |
|
||||||
rm -f "$tmpdepfile" |
|
||||||
exit $stat |
|
||||||
fi |
|
||||||
rm -f "$depfile" |
|
||||||
echo "$object : \\" > "$depfile" |
|
||||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz |
|
||||||
## 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. |
|
||||||
tr ' ' ' |
|
||||||
' < "$tmpdepfile" | |
|
||||||
## 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. |
|
||||||
## Some versions of the HPUX 10.20 sed can't process this invocation |
|
||||||
## correctly. Breaking it into two sed invocations is a workaround. |
|
||||||
sed -e 's/^\\$//' -e '/^$/d' -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 -eq 0; then : |
|
||||||
else |
|
||||||
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 ' ' ' |
|
||||||
' < "$tmpdepfile" \ |
|
||||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ |
|
||||||
tr ' |
|
||||||
' ' ' >> $depfile |
|
||||||
echo >> $depfile |
|
||||||
|
|
||||||
# The second pass generates a dummy entry for each header file. |
|
||||||
tr ' ' ' |
|
||||||
' < "$tmpdepfile" \ |
|
||||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ |
|
||||||
>> $depfile |
|
||||||
else |
|
||||||
# The sourcefile does not contain any dependencies, so just |
|
||||||
# store a dummy comment line, to avoid errors with the Makefile |
|
||||||
# "include basename.Plo" scheme. |
|
||||||
echo "#dummy" > "$depfile" |
|
||||||
fi |
|
||||||
rm -f "$tmpdepfile" |
|
||||||
;; |
|
||||||
|
|
||||||
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. |
|
||||||
stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` |
|
||||||
tmpdepfile="$stripped.u" |
|
||||||
if test "$libtool" = yes; then |
|
||||||
"$@" -Wc,-M |
|
||||||
else |
|
||||||
"$@" -M |
|
||||||
fi |
|
||||||
stat=$? |
|
||||||
|
|
||||||
if test -f "$tmpdepfile"; then : |
|
||||||
else |
|
||||||
stripped=`echo "$stripped" | sed 's,^.*/,,'` |
|
||||||
tmpdepfile="$stripped.u" |
|
||||||
fi |
|
||||||
|
|
||||||
if test $stat -eq 0; then : |
|
||||||
else |
|
||||||
rm -f "$tmpdepfile" |
|
||||||
exit $stat |
|
||||||
fi |
|
||||||
|
|
||||||
if test -f "$tmpdepfile"; then |
|
||||||
outname="$stripped.o" |
|
||||||
# Each line is of the form `foo.o: dependent.h'. |
|
||||||
# Do two passes, one to just change these to |
|
||||||
# `$object: dependent.h' and one to simply `dependent.h:'. |
|
||||||
sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" |
|
||||||
sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" |
|
||||||
else |
|
||||||
# The sourcefile does not contain any dependencies, so just |
|
||||||
# store a dummy comment line, to avoid errors with the Makefile |
|
||||||
# "include basename.Plo" scheme. |
|
||||||
echo "#dummy" > "$depfile" |
|
||||||
fi |
|
||||||
rm -f "$tmpdepfile" |
|
||||||
;; |
|
||||||
|
|
||||||
icc) |
|
||||||
# Intel's C compiler understands `-MD -MF file'. However on |
|
||||||
# icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c |
|
||||||
# ICC 7.0 will fill foo.d with something like |
|
||||||
# foo.o: sub/foo.c |
|
||||||
# foo.o: sub/foo.h |
|
||||||
# which is wrong. We want: |
|
||||||
# sub/foo.o: sub/foo.c |
|
||||||
# sub/foo.o: sub/foo.h |
|
||||||
# sub/foo.c: |
|
||||||
# sub/foo.h: |
|
||||||
# ICC 7.1 will output |
|
||||||
# foo.o: sub/foo.c sub/foo.h |
|
||||||
# and will wrap long lines using \ : |
|
||||||
# foo.o: sub/foo.c ... \ |
|
||||||
# sub/foo.h ... \ |
|
||||||
# ... |
|
||||||
|
|
||||||
"$@" -MD -MF "$tmpdepfile" |
|
||||||
stat=$? |
|
||||||
if test $stat -eq 0; then : |
|
||||||
else |
|
||||||
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. |
|
||||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` |
|
||||||
test "x$dir" = "x$object" && dir= |
|
||||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` |
|
||||||
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 -eq 0; then : |
|
||||||
else |
|
||||||
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,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" |
|
||||||
# Add `dependent.h:' lines. |
|
||||||
sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile" |
|
||||||
else |
|
||||||
echo "#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. |
|
||||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` |
|
||||||
test "x$dir" = "x$object" && dir= |
|
||||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` |
|
||||||
|
|
||||||
if test "$libtool" = yes; then |
|
||||||
# With Tru64 cc, shared objects can also be used to make a |
|
||||||
# static library. This mechanism is used in libtool 1.4 series to |
|
||||||
# handle both shared and static libraries in a single compilation. |
|
||||||
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. |
|
||||||
# |
|
||||||
# With libtool 1.5 this exception was removed, and libtool now |
|
||||||
# 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.libs/$base.lo.d # libtool 1.4 |
|
||||||
tmpdepfile2=$dir$base.o.d # libtool 1.5 |
|
||||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 |
|
||||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 |
|
||||||
"$@" -Wc,-MD |
|
||||||
else |
|
||||||
tmpdepfile1=$dir$base.o.d |
|
||||||
tmpdepfile2=$dir$base.d |
|
||||||
tmpdepfile3=$dir$base.d |
|
||||||
tmpdepfile4=$dir$base.d |
|
||||||
"$@" -MD |
|
||||||
fi |
|
||||||
|
|
||||||
stat=$? |
|
||||||
if test $stat -eq 0; then : |
|
||||||
else |
|
||||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" |
|
||||||
exit $stat |
|
||||||
fi |
|
||||||
|
|
||||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" |
|
||||||
do |
|
||||||
test -f "$tmpdepfile" && break |
|
||||||
done |
|
||||||
if test -f "$tmpdepfile"; then |
|
||||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" |
|
||||||
# That's a tab and a space in the []. |
|
||||||
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" |
|
||||||
else |
|
||||||
echo "#dummy" > "$depfile" |
|
||||||
fi |
|
||||||
rm -f "$tmpdepfile" |
|
||||||
;; |
|
||||||
|
|
||||||
#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 $1 != '--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:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" |
|
||||||
rm -f "$depfile" |
|
||||||
cat < "$tmpdepfile" > "$depfile" |
|
||||||
tr ' ' ' |
|
||||||
' < "$tmpdepfile" | \ |
|
||||||
## Some versions of the HPUX 10.20 sed can't process this invocation |
|
||||||
## correctly. Breaking it into two sed invocations is a workaround. |
|
||||||
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 $1 != '--mode=compile'; do |
|
||||||
shift |
|
||||||
done |
|
||||||
shift |
|
||||||
fi |
|
||||||
# X makedepend |
|
||||||
shift |
|
||||||
cleared=no |
|
||||||
for arg in "$@"; do |
|
||||||
case $cleared in |
|
||||||
no) |
|
||||||
set ""; shift |
|
||||||
cleared=yes ;; |
|
||||||
esac |
|
||||||
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. |
|
||||||
-*|$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" |
|
||||||
cat < "$tmpdepfile" > "$depfile" |
|
||||||
sed '1,2d' "$tmpdepfile" | tr ' ' ' |
|
||||||
' | \ |
|
||||||
## Some versions of the HPUX 10.20 sed can't process this invocation |
|
||||||
## correctly. Breaking it into two sed invocations is a workaround. |
|
||||||
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 $1 != '--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, regardless of -o, |
|
||||||
# because we must use -o when running libtool. |
|
||||||
"$@" || exit $? |
|
||||||
IFS=" " |
|
||||||
for arg |
|
||||||
do |
|
||||||
case "$arg" in |
|
||||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") |
|
||||||
set fnord "$@" |
|
||||||
shift |
|
||||||
shift |
|
||||||
;; |
|
||||||
*) |
|
||||||
set fnord "$@" "$arg" |
|
||||||
shift |
|
||||||
shift |
|
||||||
;; |
|
||||||
esac |
|
||||||
done |
|
||||||
"$@" -E | |
|
||||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" |
|
||||||
rm -f "$depfile" |
|
||||||
echo "$object : \\" > "$depfile" |
|
||||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" |
|
||||||
echo " " >> "$depfile" |
|
||||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" |
|
||||||
rm -f "$tmpdepfile" |
|
||||||
;; |
|
||||||
|
|
||||||
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-end: "$" |
|
||||||
# End: |
|
@ -1,507 +0,0 @@ |
|||||||
#!/bin/sh |
|
||||||
# install - install a program, script, or datafile |
|
||||||
|
|
||||||
scriptversion=2006-10-14.15 |
|
||||||
|
|
||||||
# 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. |
|
||||||
|
|
||||||
nl=' |
|
||||||
' |
|
||||||
IFS=" "" $nl" |
|
||||||
|
|
||||||
# set DOITPROG to echo to test this script |
|
||||||
|
|
||||||
# Don't use :- since 4.3BSD and earlier shells don't like it. |
|
||||||
doit="${DOITPROG-}" |
|
||||||
if test -z "$doit"; then |
|
||||||
doit_exec=exec |
|
||||||
else |
|
||||||
doit_exec=$doit |
|
||||||
fi |
|
||||||
|
|
||||||
# Put in absolute file names if you don't have them in your path; |
|
||||||
# or use environment vars. |
|
||||||
|
|
||||||
mvprog="${MVPROG-mv}" |
|
||||||
cpprog="${CPPROG-cp}" |
|
||||||
chmodprog="${CHMODPROG-chmod}" |
|
||||||
chownprog="${CHOWNPROG-chown}" |
|
||||||
chgrpprog="${CHGRPPROG-chgrp}" |
|
||||||
stripprog="${STRIPPROG-strip}" |
|
||||||
rmprog="${RMPROG-rm}" |
|
||||||
mkdirprog="${MKDIRPROG-mkdir}" |
|
||||||
|
|
||||||
posix_glob= |
|
||||||
posix_mkdir= |
|
||||||
|
|
||||||
# Desired mode of installed file. |
|
||||||
mode=0755 |
|
||||||
|
|
||||||
chmodcmd=$chmodprog |
|
||||||
chowncmd= |
|
||||||
chgrpcmd= |
|
||||||
stripcmd= |
|
||||||
rmcmd="$rmprog -f" |
|
||||||
mvcmd="$mvprog" |
|
||||||
src= |
|
||||||
dst= |
|
||||||
dir_arg= |
|
||||||
dstarg= |
|
||||||
no_target_directory= |
|
||||||
|
|
||||||
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: |
|
||||||
-c (ignored) |
|
||||||
-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. |
|
||||||
--help display this help and exit. |
|
||||||
--version display version info and exit. |
|
||||||
|
|
||||||
Environment variables override the default commands: |
|
||||||
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG |
|
||||||
" |
|
||||||
|
|
||||||
while test $# -ne 0; do |
|
||||||
case $1 in |
|
||||||
-c) shift |
|
||||||
continue;; |
|
||||||
|
|
||||||
-d) dir_arg=true |
|
||||||
shift |
|
||||||
continue;; |
|
||||||
|
|
||||||
-g) chgrpcmd="$chgrpprog $2" |
|
||||||
shift |
|
||||||
shift |
|
||||||
continue;; |
|
||||||
|
|
||||||
--help) echo "$usage"; exit $?;; |
|
||||||
|
|
||||||
-m) mode=$2 |
|
||||||
shift |
|
||||||
shift |
|
||||||
case $mode in |
|
||||||
*' '* | *' '* | *' |
|
||||||
'* | *'*'* | *'?'* | *'['*) |
|
||||||
echo "$0: invalid mode: $mode" >&2 |
|
||||||
exit 1;; |
|
||||||
esac |
|
||||||
continue;; |
|
||||||
|
|
||||||
-o) chowncmd="$chownprog $2" |
|
||||||
shift |
|
||||||
shift |
|
||||||
continue;; |
|
||||||
|
|
||||||
-s) stripcmd=$stripprog |
|
||||||
shift |
|
||||||
continue;; |
|
||||||
|
|
||||||
-t) dstarg=$2 |
|
||||||
shift |
|
||||||
shift |
|
||||||
continue;; |
|
||||||
|
|
||||||
-T) no_target_directory=true |
|
||||||
shift |
|
||||||
continue;; |
|
||||||
|
|
||||||
--version) echo "$0 $scriptversion"; exit $?;; |
|
||||||
|
|
||||||
--) shift |
|
||||||
break;; |
|
||||||
|
|
||||||
-*) echo "$0: invalid option: $1" >&2 |
|
||||||
exit 1;; |
|
||||||
|
|
||||||
*) break;; |
|
||||||
esac |
|
||||||
done |
|
||||||
|
|
||||||
if test $# -ne 0 && test -z "$dir_arg$dstarg"; 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 "$dstarg"; then |
|
||||||
# $@ is not empty: it contains at least $arg. |
|
||||||
set fnord "$@" "$dstarg" |
|
||||||
shift # fnord |
|
||||||
fi |
|
||||||
shift # arg |
|
||||||
dstarg=$arg |
|
||||||
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 |
|
||||||
trap '(exit $?); exit' 1 2 13 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 starting with `-'. |
|
||||||
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 "$dstarg"; then |
|
||||||
echo "$0: no destination specified." >&2 |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
|
|
||||||
dst=$dstarg |
|
||||||
# Protect names starting with `-'. |
|
||||||
case $dst in |
|
||||||
-*) dst=./$dst ;; |
|
||||||
esac |
|
||||||
|
|
||||||
# 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 -n "$no_target_directory"; then |
|
||||||
echo "$0: $dstarg: Is a directory" >&2 |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
dstdir=$dst |
|
||||||
dst=$dstdir/`basename "$src"` |
|
||||||
dstdir_status=0 |
|
||||||
else |
|
||||||
# Prefer dirname, but fall back on a substitute if dirname fails. |
|
||||||
dstdir=` |
|
||||||
(dirname "$dst") 2>/dev/null || |
|
||||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ |
|
||||||
X"$dst" : 'X\(//\)[^/]' \| \ |
|
||||||
X"$dst" : 'X\(//\)$' \| \ |
|
||||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null || |
|
||||||
echo X"$dst" | |
|
||||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ |
|
||||||
s//\1/ |
|
||||||
q |
|
||||||
} |
|
||||||
/^X\(\/\/\)[^/].*/{ |
|
||||||
s//\1/ |
|
||||||
q |
|
||||||
} |
|
||||||
/^X\(\/\/\)$/{ |
|
||||||
s//\1/ |
|
||||||
q |
|
||||||
} |
|
||||||
/^X\(\/\).*/{ |
|
||||||
s//\1/ |
|
||||||
q |
|
||||||
} |
|
||||||
s/.*/./; q' |
|
||||||
` |
|
||||||
|
|
||||||
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-writeable 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 |
|
||||||
|
|
||||||
case $posix_glob in |
|
||||||
'') |
|
||||||
if (set -f) 2>/dev/null; then |
|
||||||
posix_glob=true |
|
||||||
else |
|
||||||
posix_glob=false |
|
||||||
fi ;; |
|
||||||
esac |
|
||||||
|
|
||||||
oIFS=$IFS |
|
||||||
IFS=/ |
|
||||||
$posix_glob && set -f |
|
||||||
set fnord $dstdir |
|
||||||
shift |
|
||||||
$posix_glob && set +f |
|
||||||
IFS=$oIFS |
|
||||||
|
|
||||||
prefixes= |
|
||||||
|
|
||||||
for d |
|
||||||
do |
|
||||||
test -z "$d" && 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"; } && |
|
||||||
|
|
||||||
# Now 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. |
|
||||||
{ |
|
||||||
if test -f "$dst"; then |
|
||||||
$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 |
|
||||||
} |
|
||||||
else |
|
||||||
: |
|
||||||
fi |
|
||||||
} && |
|
||||||
|
|
||||||
# Now rename the file to the real destination. |
|
||||||
$doit $mvcmd "$dsttmp" "$dst" |
|
||||||
} |
|
||||||
} || 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-end: "$" |
|
||||||
# End: |
|
File diff suppressed because it is too large
Load Diff
@ -1,367 +0,0 @@ |
|||||||
#! /bin/sh |
|
||||||
# Common stub for a few missing GNU programs while installing. |
|
||||||
|
|
||||||
scriptversion=2006-05-10.23 |
|
||||||
|
|
||||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006 |
|
||||||
# Free Software Foundation, Inc. |
|
||||||
# Originally 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, write to the Free Software |
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
||||||
# 02110-1301, USA. |
|
||||||
|
|
||||||
# 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 |
|
||||||
|
|
||||||
run=: |
|
||||||
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' |
|
||||||
sed_minuso='s/.* -o \([^ ]*\).*/\1/p' |
|
||||||
|
|
||||||
# In the cases where this matters, `missing' is being run in the |
|
||||||
# srcdir already. |
|
||||||
if test -f configure.ac; then |
|
||||||
configure_ac=configure.ac |
|
||||||
else |
|
||||||
configure_ac=configure.in |
|
||||||
fi |
|
||||||
|
|
||||||
msg="missing on your system" |
|
||||||
|
|
||||||
case $1 in |
|
||||||
--run) |
|
||||||
# Try to run requested program, and just exit if it succeeds. |
|
||||||
run= |
|
||||||
shift |
|
||||||
"$@" && exit 0 |
|
||||||
# Exit code 63 means version mismatch. This often happens |
|
||||||
# when the user try to use an ancient version of a tool on |
|
||||||
# a file that requires a minimum version. In this case we |
|
||||||
# we should proceed has if the program had been absent, or |
|
||||||
# if --run hadn't been passed. |
|
||||||
if test $? = 63; then |
|
||||||
run=: |
|
||||||
msg="probably too old" |
|
||||||
fi |
|
||||||
;; |
|
||||||
|
|
||||||
-h|--h|--he|--hel|--help) |
|
||||||
echo "\ |
|
||||||
$0 [OPTION]... PROGRAM [ARGUMENT]... |
|
||||||
|
|
||||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an |
|
||||||
error status if there is no known handling for PROGRAM. |
|
||||||
|
|
||||||
Options: |
|
||||||
-h, --help display this help and exit |
|
||||||
-v, --version output version information and exit |
|
||||||
--run try to run the given command, and emulate it if it fails |
|
||||||
|
|
||||||
Supported PROGRAM values: |
|
||||||
aclocal touch file \`aclocal.m4' |
|
||||||
autoconf touch file \`configure' |
|
||||||
autoheader touch file \`config.h.in' |
|
||||||
autom4te touch the output file, or create a stub one |
|
||||||
automake touch all \`Makefile.in' files |
|
||||||
bison create \`y.tab.[ch]', if possible, from existing .[ch] |
|
||||||
flex create \`lex.yy.c', if possible, from existing .c |
|
||||||
help2man touch the output file |
|
||||||
lex create \`lex.yy.c', if possible, from existing .c |
|
||||||
makeinfo touch the output file |
|
||||||
tar try tar, gnutar, gtar, then tar without non-portable flags |
|
||||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch] |
|
||||||
|
|
||||||
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 |
|
||||||
|
|
||||||
# Now exit if we have it, but it failed. Also exit now if we |
|
||||||
# don't have it and --version was passed (most likely to detect |
|
||||||
# the program). |
|
||||||
case $1 in |
|
||||||
lex|yacc) |
|
||||||
# Not GNU programs, they don't have --version. |
|
||||||
;; |
|
||||||
|
|
||||||
tar) |
|
||||||
if test -n "$run"; then |
|
||||||
echo 1>&2 "ERROR: \`tar' requires --run" |
|
||||||
exit 1 |
|
||||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
;; |
|
||||||
|
|
||||||
*) |
|
||||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then |
|
||||||
# We have it, but it failed. |
|
||||||
exit 1 |
|
||||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then |
|
||||||
# Could not run --version or --help. This is probably someone |
|
||||||
# running `$TOOL --version' or `$TOOL --help' to check whether |
|
||||||
# $TOOL exists and not knowing $TOOL uses missing. |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
;; |
|
||||||
esac |
|
||||||
|
|
||||||
# If it does not exist, or fails to run (possibly an outdated version), |
|
||||||
# try to emulate it. |
|
||||||
case $1 in |
|
||||||
aclocal*) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is $msg. You should only need it if |
|
||||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want |
|
||||||
to install the \`Automake' and \`Perl' packages. Grab them from |
|
||||||
any GNU archive site." |
|
||||||
touch aclocal.m4 |
|
||||||
;; |
|
||||||
|
|
||||||
autoconf) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is $msg. You should only need it if |
|
||||||
you modified \`${configure_ac}'. You might want to install the |
|
||||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU |
|
||||||
archive site." |
|
||||||
touch configure |
|
||||||
;; |
|
||||||
|
|
||||||
autoheader) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is $msg. You should only need it if |
|
||||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want |
|
||||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them |
|
||||||
from any GNU archive site." |
|
||||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` |
|
||||||
test -z "$files" && files="config.h" |
|
||||||
touch_files= |
|
||||||
for f in $files; do |
|
||||||
case $f in |
|
||||||
*:*) touch_files="$touch_files "`echo "$f" | |
|
||||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;; |
|
||||||
*) touch_files="$touch_files $f.in";; |
|
||||||
esac |
|
||||||
done |
|
||||||
touch $touch_files |
|
||||||
;; |
|
||||||
|
|
||||||
automake*) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is $msg. You should only need it if |
|
||||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. |
|
||||||
You might want to install the \`Automake' and \`Perl' packages. |
|
||||||
Grab them from any GNU archive site." |
|
||||||
find . -type f -name Makefile.am -print | |
|
||||||
sed 's/\.am$/.in/' | |
|
||||||
while read f; do touch "$f"; done |
|
||||||
;; |
|
||||||
|
|
||||||
autom4te) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is needed, but is $msg. |
|
||||||
You might have modified some files without having the |
|
||||||
proper tools for further handling them. |
|
||||||
You can get \`$1' as part of \`Autoconf' from any GNU |
|
||||||
archive site." |
|
||||||
|
|
||||||
file=`echo "$*" | sed -n "$sed_output"` |
|
||||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` |
|
||||||
if test -f "$file"; then |
|
||||||
touch $file |
|
||||||
else |
|
||||||
test -z "$file" || exec >$file |
|
||||||
echo "#! /bin/sh" |
|
||||||
echo "# Created by GNU Automake missing as a replacement of" |
|
||||||
echo "# $ $@" |
|
||||||
echo "exit 0" |
|
||||||
chmod +x $file |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
;; |
|
||||||
|
|
||||||
bison|yacc) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' $msg. You should only need it if |
|
||||||
you modified a \`.y' file. You may need the \`Bison' package |
|
||||||
in order for those modifications to take effect. You can get |
|
||||||
\`Bison' from any GNU archive site." |
|
||||||
rm -f y.tab.c y.tab.h |
|
||||||
if test $# -ne 1; then |
|
||||||
eval LASTARG="\${$#}" |
|
||||||
case $LASTARG in |
|
||||||
*.y) |
|
||||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` |
|
||||||
if test -f "$SRCFILE"; then |
|
||||||
cp "$SRCFILE" y.tab.c |
|
||||||
fi |
|
||||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` |
|
||||||
if test -f "$SRCFILE"; then |
|
||||||
cp "$SRCFILE" y.tab.h |
|
||||||
fi |
|
||||||
;; |
|
||||||
esac |
|
||||||
fi |
|
||||||
if test ! -f y.tab.h; then |
|
||||||
echo >y.tab.h |
|
||||||
fi |
|
||||||
if test ! -f y.tab.c; then |
|
||||||
echo 'main() { return 0; }' >y.tab.c |
|
||||||
fi |
|
||||||
;; |
|
||||||
|
|
||||||
lex|flex) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is $msg. You should only need it if |
|
||||||
you modified a \`.l' file. You may need the \`Flex' package |
|
||||||
in order for those modifications to take effect. You can get |
|
||||||
\`Flex' from any GNU archive site." |
|
||||||
rm -f lex.yy.c |
|
||||||
if test $# -ne 1; then |
|
||||||
eval LASTARG="\${$#}" |
|
||||||
case $LASTARG in |
|
||||||
*.l) |
|
||||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` |
|
||||||
if test -f "$SRCFILE"; then |
|
||||||
cp "$SRCFILE" lex.yy.c |
|
||||||
fi |
|
||||||
;; |
|
||||||
esac |
|
||||||
fi |
|
||||||
if test ! -f lex.yy.c; then |
|
||||||
echo 'main() { return 0; }' >lex.yy.c |
|
||||||
fi |
|
||||||
;; |
|
||||||
|
|
||||||
help2man) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is $msg. You should only need it if |
|
||||||
you modified a dependency of a manual page. You may need the |
|
||||||
\`Help2man' package in order for those modifications to take |
|
||||||
effect. You can get \`Help2man' from any GNU archive site." |
|
||||||
|
|
||||||
file=`echo "$*" | sed -n "$sed_output"` |
|
||||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` |
|
||||||
if test -f "$file"; then |
|
||||||
touch $file |
|
||||||
else |
|
||||||
test -z "$file" || exec >$file |
|
||||||
echo ".ab help2man is required to generate this page" |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
;; |
|
||||||
|
|
||||||
makeinfo) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is $msg. You should only need it if |
|
||||||
you modified a \`.texi' or \`.texinfo' file, or any other file |
|
||||||
indirectly affecting the aspect of the manual. The spurious |
|
||||||
call might also be the consequence of using a buggy \`make' (AIX, |
|
||||||
DU, IRIX). You might want to install the \`Texinfo' package or |
|
||||||
the \`GNU make' package. Grab either from any GNU archive site." |
|
||||||
# The file to touch is that specified with -o ... |
|
||||||
file=`echo "$*" | sed -n "$sed_output"` |
|
||||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` |
|
||||||
if test -z "$file"; then |
|
||||||
# ... or it is the one specified with @setfilename ... |
|
||||||
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` |
|
||||||
file=`sed -n ' |
|
||||||
/^@setfilename/{ |
|
||||||
s/.* \([^ ]*\) *$/\1/ |
|
||||||
p |
|
||||||
q |
|
||||||
}' $infile` |
|
||||||
# ... or it is derived from the source name (dir/f.texi becomes f.info) |
|
||||||
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info |
|
||||||
fi |
|
||||||
# If the file does not exist, the user really needs makeinfo; |
|
||||||
# let's fail without touching anything. |
|
||||||
test -f $file || exit 1 |
|
||||||
touch $file |
|
||||||
;; |
|
||||||
|
|
||||||
tar) |
|
||||||
shift |
|
||||||
|
|
||||||
# We have already tried tar in the generic part. |
|
||||||
# Look for gnutar/gtar before invocation to avoid ugly error |
|
||||||
# messages. |
|
||||||
if (gnutar --version > /dev/null 2>&1); then |
|
||||||
gnutar "$@" && exit 0 |
|
||||||
fi |
|
||||||
if (gtar --version > /dev/null 2>&1); then |
|
||||||
gtar "$@" && exit 0 |
|
||||||
fi |
|
||||||
firstarg="$1" |
|
||||||
if shift; then |
|
||||||
case $firstarg in |
|
||||||
*o*) |
|
||||||
firstarg=`echo "$firstarg" | sed s/o//` |
|
||||||
tar "$firstarg" "$@" && exit 0 |
|
||||||
;; |
|
||||||
esac |
|
||||||
case $firstarg in |
|
||||||
*h*) |
|
||||||
firstarg=`echo "$firstarg" | sed s/h//` |
|
||||||
tar "$firstarg" "$@" && exit 0 |
|
||||||
;; |
|
||||||
esac |
|
||||||
fi |
|
||||||
|
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: I can't seem to be able to run \`tar' with the given arguments. |
|
||||||
You may want to install GNU tar or Free paxutils, or check the |
|
||||||
command line arguments." |
|
||||||
exit 1 |
|
||||||
;; |
|
||||||
|
|
||||||
*) |
|
||||||
echo 1>&2 "\ |
|
||||||
WARNING: \`$1' is needed, and is $msg. |
|
||||||
You might have modified some files without having the |
|
||||||
proper tools for further handling them. Check the \`README' file, |
|
||||||
it often tells you about the needed prerequisites for installing |
|
||||||
this package. You may also peek at any GNU archive site, in case |
|
||||||
some other package would contain this missing \`$1' program." |
|
||||||
exit 1 |
|
||||||
;; |
|
||||||
esac |
|
||||||
|
|
||||||
exit 0 |
|
||||||
|
|
||||||
# Local variables: |
|
||||||
# eval: (add-hook 'write-file-hooks 'time-stamp) |
|
||||||
# time-stamp-start: "scriptversion=" |
|
||||||
# time-stamp-format: "%:y-%02m-%02d.%02H" |
|
||||||
# time-stamp-end: "$" |
|
||||||
# End: |
|
@ -1,5 +0,0 @@ |
|||||||
# This file is used by gcl to get repository specific information. |
|
||||||
CODE_REVIEW_SERVER: breakpad.appspot.com |
|
||||||
CC_LIST: google-breakpad-dev@googlegroups.com |
|
||||||
TRY_ON_UPLOAD: False |
|
||||||
VIEW_VC: http://code.google.com/p/google-breakpad/source/detail?r= |
|
File diff suppressed because it is too large
Load Diff
@ -1,150 +0,0 @@ |
|||||||
# Copyright (c) 2006, 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. |
|
||||||
|
|
||||||
|
|
||||||
AC_PREREQ(2.57) |
|
||||||
|
|
||||||
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com) |
|
||||||
dnl Sanity check: the argument is just a file that should exist. |
|
||||||
AC_CONFIG_SRCDIR(README) |
|
||||||
AC_CONFIG_AUX_DIR(autotools) |
|
||||||
AC_CONFIG_MACRO_DIR([m4]) |
|
||||||
AC_CANONICAL_HOST |
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1) |
|
||||||
AM_CONFIG_HEADER(src/config.h) |
|
||||||
|
|
||||||
AC_PROG_CC |
|
||||||
AM_PROG_CC_C_O |
|
||||||
AC_PROG_CPP |
|
||||||
AC_PROG_CXX |
|
||||||
AC_PROG_RANLIB |
|
||||||
|
|
||||||
AC_HEADER_STDC |
|
||||||
m4_include(m4/ax_pthread.m4) |
|
||||||
AX_PTHREAD |
|
||||||
AC_CHECK_HEADERS([a.out.h]) |
|
||||||
|
|
||||||
# Only build Linux client libs when compiling for Linux |
|
||||||
case $host in |
|
||||||
*-*-linux* | *-android* ) |
|
||||||
LINUX_HOST=true |
|
||||||
;; |
|
||||||
esac |
|
||||||
AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue) |
|
||||||
|
|
||||||
AC_ARG_ENABLE(m32, |
|
||||||
AS_HELP_STRING([--enable-m32], |
|
||||||
[Compile/build with -m32] |
|
||||||
[(default is no)]), |
|
||||||
[case "${enableval}" in |
|
||||||
yes) |
|
||||||
CFLAGS="${CFLAGS} -m32" |
|
||||||
CXXFLAGS="${CXXFLAGS} -m32" |
|
||||||
usem32=true |
|
||||||
;; |
|
||||||
no) |
|
||||||
usem32=false |
|
||||||
;; |
|
||||||
*) |
|
||||||
AC_MSG_ERROR(bad value ${enableval} for --enable-m32) |
|
||||||
;; |
|
||||||
esac], |
|
||||||
[usem32=false]) |
|
||||||
|
|
||||||
AC_ARG_ENABLE(processor, |
|
||||||
AS_HELP_STRING([--disable-processor], |
|
||||||
[Don't build processor library] |
|
||||||
[(default is no)]), |
|
||||||
[case "${enableval}" in |
|
||||||
yes) |
|
||||||
disable_processor=false |
|
||||||
;; |
|
||||||
no) |
|
||||||
disable_processor=true |
|
||||||
;; |
|
||||||
*) |
|
||||||
AC_MSG_ERROR(bad value ${enableval} for --disable-processor) |
|
||||||
;; |
|
||||||
esac], |
|
||||||
[disable_processor=false]) |
|
||||||
AM_CONDITIONAL(DISABLE_PROCESSOR, test x$disable_processor = xtrue) |
|
||||||
|
|
||||||
AC_ARG_ENABLE(tools, |
|
||||||
AS_HELP_STRING([--disable-tools], |
|
||||||
[Don't build tool binaries] |
|
||||||
[(default is no)]), |
|
||||||
[case "${enableval}" in |
|
||||||
yes) |
|
||||||
disable_tools=false |
|
||||||
;; |
|
||||||
no) |
|
||||||
disable_tools=true |
|
||||||
;; |
|
||||||
*) |
|
||||||
AC_MSG_ERROR(bad value ${enableval} for --disable-tools) |
|
||||||
;; |
|
||||||
esac], |
|
||||||
[disable_tools=false]) |
|
||||||
AM_CONDITIONAL(DISABLE_TOOLS, test x$disable_tools = xtrue) |
|
||||||
|
|
||||||
if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools = xtrue; then |
|
||||||
AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!]) |
|
||||||
fi |
|
||||||
|
|
||||||
AC_CHECK_MEMBER(struct sockaddr.sa_len, |
|
||||||
[AC_DEFINE([GET_SA_LEN(X)],[(((struct sockaddr*)&(X))->sa_len)], |
|
||||||
[actual length of specific struct sockaddr])], |
|
||||||
[AC_DEFINE([GET_SA_LEN(X)], |
|
||||||
[(((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : \ |
|
||||||
((struct sockaddr*)&(X))->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr))], |
|
||||||
[actual length of specific struct sockaddr])], |
|
||||||
[#include <sys/socket.h>]) |
|
||||||
|
|
||||||
AC_ARG_ENABLE(selftest, |
|
||||||
AS_HELP_STRING([--enable-selftest], |
|
||||||
[Run extra tests with "make check" ] |
|
||||||
[(may conflict with optimizations) ] |
|
||||||
[(default is no)]), |
|
||||||
[case "${enableval}" in |
|
||||||
yes) |
|
||||||
selftest=true |
|
||||||
;; |
|
||||||
no) |
|
||||||
selftest=false |
|
||||||
;; |
|
||||||
*) |
|
||||||
AC_MSG_ERROR(bad value ${enableval} for --enable-selftest) |
|
||||||
;; |
|
||||||
esac], |
|
||||||
[selftest=false]) |
|
||||||
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue) |
|
||||||
|
|
||||||
AC_CONFIG_FILES([Makefile]) |
|
||||||
AC_OUTPUT |
|
@ -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 |
|
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])]) |
|
@ -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])]) |
|
@ -1,36 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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 BREAKPAD_GOOGLETEST_INCLUDES_H__ |
|
||||||
#define BREAKPAD_GOOGLETEST_INCLUDES_H__ |
|
||||||
|
|
||||||
#include "testing/gtest/include/gtest/gtest.h" |
|
||||||
#include "testing/include/gmock/gmock.h" |
|
||||||
|
|
||||||
#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__
|
|
@ -1,44 +0,0 @@ |
|||||||
// Copyright (c) 2010, 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.
|
|
||||||
|
|
||||||
// The Android NDK doesn't have link.h. Fortunately, the only thing
|
|
||||||
// that Breakpad uses from it is the ElfW macro, so define it here.
|
|
||||||
|
|
||||||
#ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_ |
|
||||||
#define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_ |
|
||||||
|
|
||||||
#include <sys/exec_elf.h> |
|
||||||
|
|
||||||
#ifndef ElfW |
|
||||||
#define ElfW(type) _ElfW (Elf, ELFSIZE, type) |
|
||||||
#define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) |
|
||||||
#define _ElfW_1(e,w,t) e##w##t |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif // GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_LINK_H_
|
|
@ -1,77 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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.
|
|
||||||
|
|
||||||
// Android runs a fairly new Linux kernel, so signal info is there,
|
|
||||||
// but the NDK doesn't have the structs defined, so define
|
|
||||||
// them here.
|
|
||||||
// Adapted from platform-linux.cc in V8
|
|
||||||
|
|
||||||
#ifndef GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_ |
|
||||||
#define GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_ |
|
||||||
|
|
||||||
#include <signal.h> |
|
||||||
|
|
||||||
#if !defined(__GLIBC__) && (defined(__arm__) || defined(__thumb__)) |
|
||||||
|
|
||||||
struct sigcontext { |
|
||||||
uint32_t trap_no; |
|
||||||
uint32_t error_code; |
|
||||||
uint32_t oldmask; |
|
||||||
uint32_t arm_r0; |
|
||||||
uint32_t arm_r1; |
|
||||||
uint32_t arm_r2; |
|
||||||
uint32_t arm_r3; |
|
||||||
uint32_t arm_r4; |
|
||||||
uint32_t arm_r5; |
|
||||||
uint32_t arm_r6; |
|
||||||
uint32_t arm_r7; |
|
||||||
uint32_t arm_r8; |
|
||||||
uint32_t arm_r9; |
|
||||||
uint32_t arm_r10; |
|
||||||
uint32_t arm_fp; |
|
||||||
uint32_t arm_ip; |
|
||||||
uint32_t arm_sp; |
|
||||||
uint32_t arm_lr; |
|
||||||
uint32_t arm_pc; |
|
||||||
uint32_t arm_cpsr; |
|
||||||
uint32_t fault_address; |
|
||||||
}; |
|
||||||
typedef uint32_t __sigset_t; |
|
||||||
typedef struct sigcontext mcontext_t; |
|
||||||
typedef struct ucontext { |
|
||||||
uint32_t uc_flags; |
|
||||||
struct ucontext* uc_link; |
|
||||||
stack_t uc_stack; |
|
||||||
mcontext_t uc_mcontext; |
|
||||||
__sigset_t uc_sigmask; |
|
||||||
} ucontext_t; |
|
||||||
|
|
||||||
#endif |
|
||||||
|
|
||||||
#endif // GOOGLE_BREAKPAD_CLIENT_LINUX_ANDROID_UCONTEXT_H_
|
|
@ -1,44 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ |
|
||||||
#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
class CrashGenerationServer; |
|
||||||
|
|
||||||
struct ClientInfo { |
|
||||||
CrashGenerationServer* crash_server_; |
|
||||||
pid_t pid_; |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
|
|
@ -1,89 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 <stdio.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <sys/types.h> |
|
||||||
|
|
||||||
#include <algorithm> |
|
||||||
|
|
||||||
#include "client/linux/crash_generation/crash_generation_client.h" |
|
||||||
#include "common/linux/eintr_wrapper.h" |
|
||||||
#include "common/linux/linux_libc_support.h" |
|
||||||
#include "third_party/lss/linux_syscall_support.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
bool |
|
||||||
CrashGenerationClient::RequestDump(const void* blob, size_t blob_size) |
|
||||||
{ |
|
||||||
int fds[2]; |
|
||||||
sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds); |
|
||||||
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); |
|
||||||
|
|
||||||
struct kernel_msghdr msg; |
|
||||||
my_memset(&msg, 0, sizeof(struct kernel_msghdr)); |
|
||||||
struct kernel_iovec iov[1]; |
|
||||||
iov[0].iov_base = const_cast<void*>(blob); |
|
||||||
iov[0].iov_len = blob_size; |
|
||||||
|
|
||||||
msg.msg_iov = iov; |
|
||||||
msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); |
|
||||||
char cmsg[kControlMsgSize]; |
|
||||||
my_memset(cmsg, 0, kControlMsgSize); |
|
||||||
msg.msg_control = cmsg; |
|
||||||
msg.msg_controllen = sizeof(cmsg); |
|
||||||
|
|
||||||
struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); |
|
||||||
hdr->cmsg_level = SOL_SOCKET; |
|
||||||
hdr->cmsg_type = SCM_RIGHTS; |
|
||||||
hdr->cmsg_len = CMSG_LEN(sizeof(int)); |
|
||||||
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr)); |
|
||||||
*p = fds[1]; |
|
||||||
|
|
||||||
HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); |
|
||||||
sys_close(fds[1]); |
|
||||||
|
|
||||||
// wait for an ACK from the server
|
|
||||||
char b; |
|
||||||
HANDLE_EINTR(sys_read(fds[0], &b, 1)); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
//static
|
|
||||||
CrashGenerationClient* |
|
||||||
CrashGenerationClient::TryCreate(int server_fd) |
|
||||||
{ |
|
||||||
if (0 > server_fd) |
|
||||||
return NULL; |
|
||||||
return new CrashGenerationClient(server_fd); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,69 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ |
|
||||||
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ |
|
||||||
|
|
||||||
#include <stddef.h> |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
class CrashGenerationClient { |
|
||||||
public: |
|
||||||
~CrashGenerationClient() |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
// Request the crash server to generate a dump. |blob| is a hack,
|
|
||||||
// see exception_handler.h and minidump_writer.h
|
|
||||||
//
|
|
||||||
// Return true if the dump was successful; false otherwise.
|
|
||||||
bool RequestDump(const void* blob, size_t blob_size); |
|
||||||
|
|
||||||
// Return a new CrashGenerationClient if |server_fd| is valid and
|
|
||||||
// connects to a CrashGenerationServer. Otherwise, return NULL.
|
|
||||||
// The returned CrashGenerationClient* is owned by the caller of
|
|
||||||
// this function.
|
|
||||||
static CrashGenerationClient* TryCreate(int server_fd); |
|
||||||
|
|
||||||
private: |
|
||||||
CrashGenerationClient(int server_fd) : server_fd_(server_fd) |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
int server_fd_; |
|
||||||
|
|
||||||
// prevent copy construction and assignment
|
|
||||||
CrashGenerationClient(const CrashGenerationClient&); |
|
||||||
CrashGenerationClient& operator=(const CrashGenerationClient&); |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
|
@ -1,468 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 <assert.h> |
|
||||||
#include <dirent.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <limits.h> |
|
||||||
#include <poll.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <sys/stat.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include "client/linux/crash_generation/crash_generation_server.h" |
|
||||||
#include "client/linux/crash_generation/client_info.h" |
|
||||||
#include "client/linux/handler/exception_handler.h" |
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h" |
|
||||||
#include "common/linux/eintr_wrapper.h" |
|
||||||
#include "common/linux/guid_creator.h" |
|
||||||
|
|
||||||
static const char kCommandQuit = 'x'; |
|
||||||
|
|
||||||
static bool |
|
||||||
GetInodeForFileDescriptor(ino_t* inode_out, int fd) |
|
||||||
{ |
|
||||||
assert(inode_out); |
|
||||||
|
|
||||||
struct stat buf; |
|
||||||
if (fstat(fd, &buf) < 0) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (!S_ISSOCK(buf.st_mode)) |
|
||||||
return false; |
|
||||||
|
|
||||||
*inode_out = buf.st_ino; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// expected prefix of the target of the /proc/self/fd/%d link for a socket
|
|
||||||
static const char kSocketLinkPrefix[] = "socket:["; |
|
||||||
|
|
||||||
// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
|
|
||||||
// socket.
|
|
||||||
// inode_out: (output) set to the inode number on success
|
|
||||||
// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
|
|
||||||
static bool |
|
||||||
GetInodeForProcPath(ino_t* inode_out, const char* path) |
|
||||||
{ |
|
||||||
assert(inode_out); |
|
||||||
assert(path); |
|
||||||
|
|
||||||
char buf[256]; |
|
||||||
const ssize_t n = readlink(path, buf, sizeof(buf) - 1); |
|
||||||
if (n == -1) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
buf[n] = 0; |
|
||||||
|
|
||||||
if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
char* endptr; |
|
||||||
const u_int64_t inode_ul = |
|
||||||
strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); |
|
||||||
if (*endptr != ']') |
|
||||||
return false; |
|
||||||
|
|
||||||
if (inode_ul == ULLONG_MAX) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
*inode_out = inode_ul; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
static bool |
|
||||||
FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) |
|
||||||
{ |
|
||||||
assert(pid_out); |
|
||||||
bool already_found = false; |
|
||||||
|
|
||||||
DIR* proc = opendir("/proc"); |
|
||||||
if (!proc) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
std::vector<pid_t> pids; |
|
||||||
|
|
||||||
struct dirent* dent; |
|
||||||
while ((dent = readdir(proc))) { |
|
||||||
char* endptr; |
|
||||||
const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); |
|
||||||
if (pid_ul == ULONG_MAX || '\0' != *endptr) |
|
||||||
continue; |
|
||||||
pids.push_back(pid_ul); |
|
||||||
} |
|
||||||
closedir(proc); |
|
||||||
|
|
||||||
for (std::vector<pid_t>::const_iterator |
|
||||||
i = pids.begin(); i != pids.end(); ++i) { |
|
||||||
const pid_t current_pid = *i; |
|
||||||
char buf[256]; |
|
||||||
snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); |
|
||||||
DIR* fd = opendir(buf); |
|
||||||
if (!fd) |
|
||||||
continue; |
|
||||||
|
|
||||||
while ((dent = readdir(fd))) { |
|
||||||
if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, |
|
||||||
dent->d_name) >= static_cast<int>(sizeof(buf))) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
ino_t fd_inode; |
|
||||||
if (GetInodeForProcPath(&fd_inode, buf) |
|
||||||
&& fd_inode == socket_inode) { |
|
||||||
if (already_found) { |
|
||||||
closedir(fd); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
already_found = true; |
|
||||||
*pid_out = current_pid; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
closedir(fd); |
|
||||||
} |
|
||||||
|
|
||||||
return already_found; |
|
||||||
} |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
CrashGenerationServer::CrashGenerationServer( |
|
||||||
const int listen_fd, |
|
||||||
OnClientDumpRequestCallback dump_callback, |
|
||||||
void* dump_context, |
|
||||||
OnClientExitingCallback exit_callback, |
|
||||||
void* exit_context, |
|
||||||
bool generate_dumps, |
|
||||||
const std::string* dump_path) : |
|
||||||
server_fd_(listen_fd), |
|
||||||
dump_callback_(dump_callback), |
|
||||||
dump_context_(dump_context), |
|
||||||
exit_callback_(exit_callback), |
|
||||||
exit_context_(exit_context), |
|
||||||
generate_dumps_(generate_dumps), |
|
||||||
started_(false) |
|
||||||
{ |
|
||||||
if (dump_path) |
|
||||||
dump_dir_ = *dump_path; |
|
||||||
else |
|
||||||
dump_dir_ = "/tmp"; |
|
||||||
} |
|
||||||
|
|
||||||
CrashGenerationServer::~CrashGenerationServer() |
|
||||||
{ |
|
||||||
if (started_) |
|
||||||
Stop(); |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
CrashGenerationServer::Start() |
|
||||||
{ |
|
||||||
if (started_ || 0 > server_fd_) |
|
||||||
return false; |
|
||||||
|
|
||||||
int control_pipe[2]; |
|
||||||
if (pipe(control_pipe)) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC)) |
|
||||||
return false; |
|
||||||
if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC)) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK)) |
|
||||||
return false; |
|
||||||
|
|
||||||
control_pipe_in_ = control_pipe[0]; |
|
||||||
control_pipe_out_ = control_pipe[1]; |
|
||||||
|
|
||||||
if (pthread_create(&thread_, NULL, |
|
||||||
ThreadMain, reinterpret_cast<void*>(this))) |
|
||||||
return false; |
|
||||||
|
|
||||||
started_ = true; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
CrashGenerationServer::Stop() |
|
||||||
{ |
|
||||||
assert(pthread_self() != thread_); |
|
||||||
|
|
||||||
if (!started_) |
|
||||||
return; |
|
||||||
|
|
||||||
HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1)); |
|
||||||
|
|
||||||
void* dummy; |
|
||||||
pthread_join(thread_, &dummy); |
|
||||||
|
|
||||||
started_ = false; |
|
||||||
} |
|
||||||
|
|
||||||
//static
|
|
||||||
bool |
|
||||||
CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd) |
|
||||||
{ |
|
||||||
int fds[2]; |
|
||||||
|
|
||||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) |
|
||||||
return false; |
|
||||||
|
|
||||||
static const int on = 1; |
|
||||||
// Enable passcred on the server end of the socket
|
|
||||||
if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (fcntl(fds[1], F_SETFL, O_NONBLOCK)) |
|
||||||
return false; |
|
||||||
if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) |
|
||||||
return false; |
|
||||||
|
|
||||||
*client_fd = fds[0]; |
|
||||||
*server_fd = fds[1]; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// The following methods/functions execute on the server thread
|
|
||||||
|
|
||||||
void |
|
||||||
CrashGenerationServer::Run() |
|
||||||
{ |
|
||||||
struct pollfd pollfds[2]; |
|
||||||
memset(&pollfds, 0, sizeof(pollfds)); |
|
||||||
|
|
||||||
pollfds[0].fd = server_fd_; |
|
||||||
pollfds[0].events = POLLIN; |
|
||||||
|
|
||||||
pollfds[1].fd = control_pipe_in_; |
|
||||||
pollfds[1].events = POLLIN; |
|
||||||
|
|
||||||
while (true) { |
|
||||||
// infinite timeout
|
|
||||||
int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); |
|
||||||
if (-1 == nevents) { |
|
||||||
if (EINTR == errno) { |
|
||||||
continue; |
|
||||||
} else { |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (pollfds[0].revents && !ClientEvent(pollfds[0].revents)) |
|
||||||
return; |
|
||||||
|
|
||||||
if (pollfds[1].revents && !ControlEvent(pollfds[1].revents)) |
|
||||||
return; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
CrashGenerationServer::ClientEvent(short revents) |
|
||||||
{ |
|
||||||
if (POLLHUP & revents) |
|
||||||
return false; |
|
||||||
assert(POLLIN & revents); |
|
||||||
|
|
||||||
// A process has crashed and has signaled us by writing a datagram
|
|
||||||
// to the death signal socket. The datagram contains the crash context needed
|
|
||||||
// for writing the minidump as well as a file descriptor and a credentials
|
|
||||||
// block so that they can't lie about their pid.
|
|
||||||
|
|
||||||
// The length of the control message:
|
|
||||||
static const unsigned kControlMsgSize = |
|
||||||
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); |
|
||||||
// The length of the regular payload:
|
|
||||||
static const unsigned kCrashContextSize = |
|
||||||
sizeof(google_breakpad::ExceptionHandler::CrashContext); |
|
||||||
|
|
||||||
struct msghdr msg = {0}; |
|
||||||
struct iovec iov[1]; |
|
||||||
char crash_context[kCrashContextSize]; |
|
||||||
char control[kControlMsgSize]; |
|
||||||
const ssize_t expected_msg_size = sizeof(crash_context); |
|
||||||
|
|
||||||
iov[0].iov_base = crash_context; |
|
||||||
iov[0].iov_len = sizeof(crash_context); |
|
||||||
msg.msg_iov = iov; |
|
||||||
msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); |
|
||||||
msg.msg_control = control; |
|
||||||
msg.msg_controllen = kControlMsgSize; |
|
||||||
|
|
||||||
const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0)); |
|
||||||
if (msg_size != expected_msg_size) |
|
||||||
return true; |
|
||||||
|
|
||||||
if (msg.msg_controllen != kControlMsgSize || |
|
||||||
msg.msg_flags & ~MSG_TRUNC) |
|
||||||
return true; |
|
||||||
|
|
||||||
// Walk the control payload and extract the file descriptor and validated pid.
|
|
||||||
pid_t crashing_pid = -1; |
|
||||||
int signal_fd = -1; |
|
||||||
for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; |
|
||||||
hdr = CMSG_NXTHDR(&msg, hdr)) { |
|
||||||
if (hdr->cmsg_level != SOL_SOCKET) |
|
||||||
continue; |
|
||||||
if (hdr->cmsg_type == SCM_RIGHTS) { |
|
||||||
const unsigned len = hdr->cmsg_len - |
|
||||||
(((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); |
|
||||||
assert(len % sizeof(int) == 0u); |
|
||||||
const unsigned num_fds = len / sizeof(int); |
|
||||||
if (num_fds > 1 || num_fds == 0) { |
|
||||||
// A nasty process could try and send us too many descriptors and
|
|
||||||
// force a leak.
|
|
||||||
for (unsigned i = 0; i < num_fds; ++i) |
|
||||||
HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i])); |
|
||||||
return true; |
|
||||||
} else { |
|
||||||
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0]; |
|
||||||
} |
|
||||||
} else if (hdr->cmsg_type == SCM_CREDENTIALS) { |
|
||||||
const struct ucred *cred = |
|
||||||
reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); |
|
||||||
crashing_pid = cred->pid; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (crashing_pid == -1 || signal_fd == -1) { |
|
||||||
if (signal_fd) |
|
||||||
HANDLE_EINTR(close(signal_fd)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// Kernel bug workaround (broken in 2.6.30 at least):
|
|
||||||
// The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID
|
|
||||||
// namespaces. Thus |crashing_pid| might be garbage from our point of view.
|
|
||||||
// In the future we can remove this workaround, but we have to wait a couple
|
|
||||||
// of years to be sure that it's worked its way out into the world.
|
|
||||||
|
|
||||||
ino_t inode_number; |
|
||||||
if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) { |
|
||||||
HANDLE_EINTR(close(signal_fd)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { |
|
||||||
HANDLE_EINTR(close(signal_fd)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
std::string minidump_filename; |
|
||||||
if (!MakeMinidumpFilename(minidump_filename)) |
|
||||||
return true; |
|
||||||
|
|
||||||
if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), |
|
||||||
crashing_pid, crash_context, |
|
||||||
kCrashContextSize)) { |
|
||||||
HANDLE_EINTR(close(signal_fd)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
if (dump_callback_) { |
|
||||||
ClientInfo info; |
|
||||||
|
|
||||||
info.crash_server_ = this; |
|
||||||
info.pid_ = crashing_pid; |
|
||||||
|
|
||||||
dump_callback_(dump_context_, &info, &minidump_filename); |
|
||||||
} |
|
||||||
|
|
||||||
// Send the done signal to the process: it can exit now.
|
|
||||||
memset(&msg, 0, sizeof(msg)); |
|
||||||
struct iovec done_iov; |
|
||||||
done_iov.iov_base = const_cast<char*>("\x42"); |
|
||||||
done_iov.iov_len = 1; |
|
||||||
msg.msg_iov = &done_iov; |
|
||||||
msg.msg_iovlen = 1; |
|
||||||
|
|
||||||
HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); |
|
||||||
HANDLE_EINTR(close(signal_fd)); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
CrashGenerationServer::ControlEvent(short revents) |
|
||||||
{ |
|
||||||
if (POLLHUP & revents) |
|
||||||
return false; |
|
||||||
assert(POLLIN & revents); |
|
||||||
|
|
||||||
char command; |
|
||||||
if (read(control_pipe_in_, &command, 1)) |
|
||||||
return false; |
|
||||||
|
|
||||||
switch (command) { |
|
||||||
case kCommandQuit: |
|
||||||
return false; |
|
||||||
default: |
|
||||||
assert(0); |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
CrashGenerationServer::MakeMinidumpFilename(std::string& outFilename) |
|
||||||
{ |
|
||||||
GUID guid; |
|
||||||
char guidString[kGUIDStringLength+1]; |
|
||||||
|
|
||||||
if (!(CreateGUID(&guid) |
|
||||||
&& GUIDToString(&guid, guidString, sizeof(guidString)))) |
|
||||||
return false; |
|
||||||
|
|
||||||
char path[PATH_MAX]; |
|
||||||
snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString); |
|
||||||
|
|
||||||
outFilename = path; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// static
|
|
||||||
void* |
|
||||||
CrashGenerationServer::ThreadMain(void *arg) |
|
||||||
{ |
|
||||||
reinterpret_cast<CrashGenerationServer*>(arg)->Run(); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,133 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ |
|
||||||
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ |
|
||||||
|
|
||||||
#include <pthread.h> |
|
||||||
|
|
||||||
#include <string> |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
class ClientInfo; |
|
||||||
|
|
||||||
class CrashGenerationServer { |
|
||||||
public: |
|
||||||
// WARNING: callbacks may be invoked on a different thread
|
|
||||||
// than that which creates the CrashGenerationServer. They must
|
|
||||||
// be thread safe.
|
|
||||||
typedef void (*OnClientDumpRequestCallback)(void* context, |
|
||||||
const ClientInfo* client_info, |
|
||||||
const std::string* file_path); |
|
||||||
|
|
||||||
typedef void (*OnClientExitingCallback)(void* context, |
|
||||||
const ClientInfo* client_info); |
|
||||||
|
|
||||||
// Create an instance with the given parameters.
|
|
||||||
//
|
|
||||||
// Parameter listen_fd: The server fd created by CreateReportChannel().
|
|
||||||
// Parameter dump_callback: Callback for a client crash dump request.
|
|
||||||
// Parameter dump_context: Context for client crash dump request callback.
|
|
||||||
// Parameter exit_callback: Callback for client process exit.
|
|
||||||
// Parameter exit_context: Context for client exit callback.
|
|
||||||
// Parameter generate_dumps: Whether to automatically generate dumps.
|
|
||||||
// Client code of this class might want to generate dumps explicitly
|
|
||||||
// in the crash dump request callback. In that case, false can be
|
|
||||||
// passed for this parameter.
|
|
||||||
// Parameter dump_path: Path for generating dumps; required only if true is
|
|
||||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
|
||||||
CrashGenerationServer(const int listen_fd, |
|
||||||
OnClientDumpRequestCallback dump_callback, |
|
||||||
void* dump_context, |
|
||||||
OnClientExitingCallback exit_callback, |
|
||||||
void* exit_context, |
|
||||||
bool generate_dumps, |
|
||||||
const std::string* dump_path); |
|
||||||
|
|
||||||
~CrashGenerationServer(); |
|
||||||
|
|
||||||
// Perform initialization steps needed to start listening to clients.
|
|
||||||
//
|
|
||||||
// Return true if initialization is successful; false otherwise.
|
|
||||||
bool Start(); |
|
||||||
|
|
||||||
// Stop the server.
|
|
||||||
void Stop(); |
|
||||||
|
|
||||||
// Create a "channel" that can be used by clients to report crashes
|
|
||||||
// to a CrashGenerationServer. |*server_fd| should be passed to
|
|
||||||
// this class's constructor, and |*client_fd| should be passed to
|
|
||||||
// the ExceptionHandler constructor in the client process.
|
|
||||||
static bool CreateReportChannel(int* server_fd, int* client_fd); |
|
||||||
|
|
||||||
private: |
|
||||||
// Run the server's event loop
|
|
||||||
void Run(); |
|
||||||
|
|
||||||
// Invoked when an child process (client) event occurs
|
|
||||||
// Returning true => "keep running", false => "exit loop"
|
|
||||||
bool ClientEvent(short revents); |
|
||||||
|
|
||||||
// Invoked when the controlling thread (main) event occurs
|
|
||||||
// Returning true => "keep running", false => "exit loop"
|
|
||||||
bool ControlEvent(short revents); |
|
||||||
|
|
||||||
// Return a unique filename at which a minidump can be written
|
|
||||||
bool MakeMinidumpFilename(std::string& outFilename); |
|
||||||
|
|
||||||
// Trampoline to |Run()|
|
|
||||||
static void* ThreadMain(void* arg); |
|
||||||
|
|
||||||
int server_fd_; |
|
||||||
|
|
||||||
OnClientDumpRequestCallback dump_callback_; |
|
||||||
void* dump_context_; |
|
||||||
|
|
||||||
OnClientExitingCallback exit_callback_; |
|
||||||
void* exit_context_; |
|
||||||
|
|
||||||
bool generate_dumps_; |
|
||||||
|
|
||||||
std::string dump_dir_; |
|
||||||
|
|
||||||
bool started_; |
|
||||||
|
|
||||||
pthread_t thread_; |
|
||||||
int control_pipe_in_; |
|
||||||
int control_pipe_out_; |
|
||||||
|
|
||||||
// disable these
|
|
||||||
CrashGenerationServer(const CrashGenerationServer&); |
|
||||||
CrashGenerationServer& operator=(const CrashGenerationServer&); |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
|
@ -1,3 +0,0 @@ |
|||||||
MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so |
|
||||||
PUBLIC 400 0 __kernel_vsyscall |
|
||||||
STACK WIN 4 400 100 1 1 0 0 0 0 0 1 |
|
@ -1,3 +0,0 @@ |
|||||||
MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so |
|
||||||
PUBLIC 400 0 __kernel_vsyscall |
|
||||||
STACK WIN 4 400 200 3 3 0 0 0 0 0 1 |
|
@ -1,512 +0,0 @@ |
|||||||
// Copyright (c) 2010 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.
|
|
||||||
|
|
||||||
// The ExceptionHandler object installs signal handlers for a number of
|
|
||||||
// signals. We rely on the signal handler running on the thread which crashed
|
|
||||||
// in order to identify it. This is true of the synchronous signals (SEGV etc),
|
|
||||||
// but not true of ABRT. Thus, if you send ABRT to yourself in a program which
|
|
||||||
// uses ExceptionHandler, you need to use tgkill to direct it to the current
|
|
||||||
// thread.
|
|
||||||
//
|
|
||||||
// The signal flow looks like this:
|
|
||||||
//
|
|
||||||
// SignalHandler (uses a global stack of ExceptionHandler objects to find
|
|
||||||
// | one to handle the signal. If the first rejects it, try
|
|
||||||
// | the second etc...)
|
|
||||||
// V
|
|
||||||
// HandleSignal ----------------------------| (clones a new process which
|
|
||||||
// | | shares an address space with
|
|
||||||
// (wait for cloned | the crashed process. This
|
|
||||||
// process) | allows us to ptrace the crashed
|
|
||||||
// | | process)
|
|
||||||
// V V
|
|
||||||
// (set signal handler to ThreadEntry (static function to bounce
|
|
||||||
// SIG_DFL and rethrow, | back into the object)
|
|
||||||
// killing the crashed |
|
|
||||||
// process) V
|
|
||||||
// DoDump (writes minidump)
|
|
||||||
// |
|
|
||||||
// V
|
|
||||||
// sys_exit
|
|
||||||
//
|
|
||||||
|
|
||||||
// This code is a little fragmented. Different functions of the ExceptionHandler
|
|
||||||
// class run in a number of different contexts. Some of them run in a normal
|
|
||||||
// context and are easy to code, others run in a compromised context and the
|
|
||||||
// restrictions at the top of minidump_writer.cc apply: no libc and use the
|
|
||||||
// alternative malloc. Each function should have comment above it detailing the
|
|
||||||
// context which it runs in.
|
|
||||||
|
|
||||||
#include "client/linux/handler/exception_handler.h" |
|
||||||
|
|
||||||
#include <errno.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <linux/limits.h> |
|
||||||
#include <sched.h> |
|
||||||
#include <signal.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <sys/mman.h> |
|
||||||
#include <sys/prctl.h> |
|
||||||
#if !defined(__ANDROID__) |
|
||||||
#include <sys/signal.h> |
|
||||||
#endif |
|
||||||
#include <sys/syscall.h> |
|
||||||
#if !defined(__ANDROID__) |
|
||||||
#include <sys/ucontext.h> |
|
||||||
#include <sys/user.h> |
|
||||||
#endif |
|
||||||
#include <sys/wait.h> |
|
||||||
#if !defined(__ANDROID__) |
|
||||||
#include <ucontext.h> |
|
||||||
#endif |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
#include <algorithm> |
|
||||||
#include <utility> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include "common/linux/linux_libc_support.h" |
|
||||||
#include "common/memory.h" |
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h" |
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h" |
|
||||||
#include "common/linux/guid_creator.h" |
|
||||||
#include "common/linux/eintr_wrapper.h" |
|
||||||
#include "third_party/lss/linux_syscall_support.h" |
|
||||||
|
|
||||||
#ifndef PR_SET_PTRACER |
|
||||||
#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 { |
|
||||||
|
|
||||||
// The list of signals which we consider to be crashes. The default action for
|
|
||||||
// all these signals must be Core (see man 7 signal) because we rethrow the
|
|
||||||
// signal after handling it and expect that it'll be fatal.
|
|
||||||
static const int kExceptionSignals[] = { |
|
||||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1 |
|
||||||
}; |
|
||||||
|
|
||||||
// We can stack multiple exception handlers. In that case, this is the global
|
|
||||||
// which holds the stack.
|
|
||||||
std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL; |
|
||||||
unsigned ExceptionHandler::handler_stack_index_ = 0; |
|
||||||
pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = |
|
||||||
PTHREAD_MUTEX_INITIALIZER; |
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
|
||||||
ExceptionHandler::ExceptionHandler(const std::string &dump_path, |
|
||||||
FilterCallback filter, |
|
||||||
MinidumpCallback callback, |
|
||||||
void *callback_context, |
|
||||||
bool install_handler) |
|
||||||
: filter_(filter), |
|
||||||
callback_(callback), |
|
||||||
callback_context_(callback_context), |
|
||||||
handler_installed_(install_handler) |
|
||||||
{ |
|
||||||
Init(dump_path, -1); |
|
||||||
} |
|
||||||
|
|
||||||
ExceptionHandler::ExceptionHandler(const std::string &dump_path, |
|
||||||
FilterCallback filter, |
|
||||||
MinidumpCallback callback, |
|
||||||
void* callback_context, |
|
||||||
bool install_handler, |
|
||||||
const int server_fd) |
|
||||||
: filter_(filter), |
|
||||||
callback_(callback), |
|
||||||
callback_context_(callback_context), |
|
||||||
handler_installed_(install_handler) |
|
||||||
{ |
|
||||||
Init(dump_path, server_fd); |
|
||||||
} |
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
|
||||||
ExceptionHandler::~ExceptionHandler() { |
|
||||||
UninstallHandlers(); |
|
||||||
} |
|
||||||
|
|
||||||
void ExceptionHandler::Init(const std::string &dump_path, |
|
||||||
const int server_fd) |
|
||||||
{ |
|
||||||
crash_handler_ = NULL; |
|
||||||
if (0 <= server_fd) |
|
||||||
crash_generation_client_ |
|
||||||
.reset(CrashGenerationClient::TryCreate(server_fd)); |
|
||||||
|
|
||||||
if (handler_installed_) |
|
||||||
InstallHandlers(); |
|
||||||
|
|
||||||
if (!IsOutOfProcess()) |
|
||||||
set_dump_path(dump_path); |
|
||||||
|
|
||||||
pthread_mutex_lock(&handler_stack_mutex_); |
|
||||||
if (handler_stack_ == NULL) |
|
||||||
handler_stack_ = new std::vector<ExceptionHandler *>; |
|
||||||
handler_stack_->push_back(this); |
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_); |
|
||||||
} |
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
|
||||||
bool ExceptionHandler::InstallHandlers() { |
|
||||||
// We run the signal handlers on an alternative stack because we might have
|
|
||||||
// crashed because of a stack overflow.
|
|
||||||
|
|
||||||
// We use this value rather than SIGSTKSZ because we would end up overrunning
|
|
||||||
// such a small stack.
|
|
||||||
static const unsigned kSigStackSize = 8192; |
|
||||||
|
|
||||||
signal_stack = malloc(kSigStackSize); |
|
||||||
stack_t stack; |
|
||||||
memset(&stack, 0, sizeof(stack)); |
|
||||||
stack.ss_sp = signal_stack; |
|
||||||
stack.ss_size = kSigStackSize; |
|
||||||
|
|
||||||
if (sys_sigaltstack(&stack, NULL) == -1) |
|
||||||
return false; |
|
||||||
|
|
||||||
struct sigaction sa; |
|
||||||
memset(&sa, 0, sizeof(sa)); |
|
||||||
sigemptyset(&sa.sa_mask); |
|
||||||
|
|
||||||
// mask all exception signals when we're handling one of them.
|
|
||||||
for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) |
|
||||||
sigaddset(&sa.sa_mask, kExceptionSignals[i]); |
|
||||||
|
|
||||||
sa.sa_sigaction = SignalHandler; |
|
||||||
sa.sa_flags = SA_ONSTACK | SA_SIGINFO; |
|
||||||
|
|
||||||
for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) { |
|
||||||
struct sigaction* old = new struct sigaction; |
|
||||||
if (sigaction(kExceptionSignals[i], &sa, old) == -1) |
|
||||||
return false; |
|
||||||
old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old)); |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
|
||||||
void ExceptionHandler::UninstallHandlers() { |
|
||||||
for (unsigned i = 0; i < old_handlers_.size(); ++i) { |
|
||||||
struct sigaction *action = |
|
||||||
reinterpret_cast<struct sigaction*>(old_handlers_[i].second); |
|
||||||
sigaction(old_handlers_[i].first, action, NULL); |
|
||||||
delete action; |
|
||||||
} |
|
||||||
pthread_mutex_lock(&handler_stack_mutex_); |
|
||||||
std::vector<ExceptionHandler*>::iterator handler = |
|
||||||
std::find(handler_stack_->begin(), handler_stack_->end(), this); |
|
||||||
handler_stack_->erase(handler); |
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_); |
|
||||||
old_handlers_.clear(); |
|
||||||
} |
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
|
||||||
void ExceptionHandler::UpdateNextID() { |
|
||||||
GUID guid; |
|
||||||
char guid_str[kGUIDStringLength + 1]; |
|
||||||
if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) { |
|
||||||
next_minidump_id_ = guid_str; |
|
||||||
next_minidump_id_c_ = next_minidump_id_.c_str(); |
|
||||||
|
|
||||||
char minidump_path[PATH_MAX]; |
|
||||||
snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp", |
|
||||||
dump_path_c_, |
|
||||||
guid_str); |
|
||||||
|
|
||||||
next_minidump_path_ = minidump_path; |
|
||||||
next_minidump_path_c_ = next_minidump_path_.c_str(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
|
|
||||||
// crash_handler_ = callback;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
// Runs on the crashing thread.
|
|
||||||
// static
|
|
||||||
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { |
|
||||||
// All the exception signals are blocked at this point.
|
|
||||||
pthread_mutex_lock(&handler_stack_mutex_); |
|
||||||
|
|
||||||
if (!handler_stack_->size()) { |
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
for (int i = handler_stack_->size() - 1; i >= 0; --i) { |
|
||||||
if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) { |
|
||||||
// successfully handled: We are in an invalid state since an exception
|
|
||||||
// signal has been delivered. We don't call the exit handlers because
|
|
||||||
// they could end up corrupting on-disk state.
|
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_); |
|
||||||
|
|
||||||
if (info->si_pid) { |
|
||||||
// This signal was triggered by somebody sending us the signal with kill().
|
|
||||||
// In order to retrigger it, we have to queue a new signal by calling
|
|
||||||
// kill() ourselves.
|
|
||||||
if (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.
|
|
||||||
_exit(1); |
|
||||||
} |
|
||||||
} else { |
|
||||||
// This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV).
|
|
||||||
// No need to reissue the signal. It will automatically trigger again,
|
|
||||||
// when we return from the signal handler.
|
|
||||||
} |
|
||||||
|
|
||||||
// As soon as we return from the signal handler, our signal will become
|
|
||||||
// unmasked. At that time, we will get terminated with the same signal that
|
|
||||||
// was triggered originally. This allows our parent to know that we crashed.
|
|
||||||
// The default action for all the signals which we catch is Core, so
|
|
||||||
// this is the end of us.
|
|
||||||
signal(sig, SIG_DFL); |
|
||||||
} |
|
||||||
|
|
||||||
struct ThreadArgument { |
|
||||||
pid_t pid; // the crashing process
|
|
||||||
ExceptionHandler* handler; |
|
||||||
const void* context; // a CrashContext structure
|
|
||||||
size_t context_size; |
|
||||||
}; |
|
||||||
|
|
||||||
// This is the entry function for the cloned process. We are in a compromised
|
|
||||||
// context here: see the top of the file.
|
|
||||||
// static
|
|
||||||
int ExceptionHandler::ThreadEntry(void *arg) { |
|
||||||
const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg); |
|
||||||
|
|
||||||
// Block here until the crashing process unblocks us when
|
|
||||||
// we're allowed to use ptrace
|
|
||||||
thread_arg->handler->WaitForContinueSignal(); |
|
||||||
|
|
||||||
return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, |
|
||||||
thread_arg->context_size) == false; |
|
||||||
} |
|
||||||
|
|
||||||
// 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) { |
|
||||||
if (filter_ && !filter_(callback_context_)) |
|
||||||
return false; |
|
||||||
|
|
||||||
// Allow ourselves to be dumped if the signal is trusted.
|
|
||||||
bool signal_trusted = info->si_code > 0; |
|
||||||
bool signal_pid_trusted = info->si_code == SI_USER || |
|
||||||
info->si_code == SI_TKILL; |
|
||||||
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { |
|
||||||
sys_prctl(PR_SET_DUMPABLE, 1); |
|
||||||
} |
|
||||||
CrashContext context; |
|
||||||
memcpy(&context.siginfo, info, sizeof(siginfo_t)); |
|
||||||
memcpy(&context.context, uc, sizeof(struct ucontext)); |
|
||||||
#if !defined(__ARM_EABI__) |
|
||||||
// FP state is not part of user ABI on ARM Linux.
|
|
||||||
struct ucontext *uc_ptr = (struct ucontext*)uc; |
|
||||||
if (uc_ptr->uc_mcontext.fpregs) { |
|
||||||
memcpy(&context.float_state, |
|
||||||
uc_ptr->uc_mcontext.fpregs, |
|
||||||
sizeof(context.float_state)); |
|
||||||
} |
|
||||||
#endif |
|
||||||
context.tid = syscall(__NR_gettid); |
|
||||||
if (crash_handler_ != NULL) { |
|
||||||
if (crash_handler_(&context, sizeof(context), |
|
||||||
callback_context_)) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
return GenerateDump(&context); |
|
||||||
} |
|
||||||
|
|
||||||
// This function may run in a compromised context: see the top of the file.
|
|
||||||
bool ExceptionHandler::GenerateDump(CrashContext *context) { |
|
||||||
if (IsOutOfProcess()) |
|
||||||
return crash_generation_client_->RequestDump(context, sizeof(*context)); |
|
||||||
|
|
||||||
static const unsigned kChildStackSize = 8000; |
|
||||||
PageAllocator allocator; |
|
||||||
uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); |
|
||||||
if (!stack) |
|
||||||
return false; |
|
||||||
// clone() needs the top-most address. (scrub just to be safe)
|
|
||||||
stack += kChildStackSize; |
|
||||||
my_memset(stack - 16, 0, 16); |
|
||||||
|
|
||||||
ThreadArgument thread_arg; |
|
||||||
thread_arg.handler = this; |
|
||||||
thread_arg.pid = getpid(); |
|
||||||
thread_arg.context = context; |
|
||||||
thread_arg.context_size = sizeof(*context); |
|
||||||
|
|
||||||
// We need to explicitly enable ptrace of parent processes on some
|
|
||||||
// kernels, but we need to know the PID of the cloned process before we
|
|
||||||
// can do this. Create a pipe here which we can use to block the
|
|
||||||
// cloned process after creating it, until we have explicitly enabled ptrace
|
|
||||||
if(sys_pipe(fdes) == -1) { |
|
||||||
// Creating the pipe failed. We'll log an error but carry on anyway,
|
|
||||||
// as we'll probably still get a useful crash report. All that will happen
|
|
||||||
// is the write() and read() calls will fail with EBADF
|
|
||||||
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
|
|
||||||
sys_pipe failed:"; |
|
||||||
sys_write(2, no_pipe_msg, sizeof(no_pipe_msg) - 1); |
|
||||||
sys_write(2, strerror(errno), strlen(strerror(errno))); |
|
||||||
sys_write(2, "\n", 1); |
|
||||||
} |
|
||||||
|
|
||||||
const pid_t child = sys_clone( |
|
||||||
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, |
|
||||||
&thread_arg, NULL, NULL, NULL); |
|
||||||
int r, status; |
|
||||||
// Allow the child to ptrace us
|
|
||||||
prctl(PR_SET_PTRACER, child, 0, 0, 0); |
|
||||||
SendContinueSignalToChild(); |
|
||||||
do { |
|
||||||
r = sys_waitpid(child, &status, __WALL); |
|
||||||
} while (r == -1 && errno == EINTR); |
|
||||||
|
|
||||||
sys_close(fdes[0]); |
|
||||||
sys_close(fdes[1]); |
|
||||||
|
|
||||||
if (r == -1) { |
|
||||||
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; |
|
||||||
sys_write(2, msg, sizeof(msg) - 1); |
|
||||||
sys_write(2, strerror(errno), strlen(strerror(errno))); |
|
||||||
sys_write(2, "\n", 1); |
|
||||||
} |
|
||||||
|
|
||||||
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; |
|
||||||
|
|
||||||
if (callback_) |
|
||||||
success = callback_(dump_path_c_, next_minidump_id_c_, |
|
||||||
callback_context_, success); |
|
||||||
|
|
||||||
return success; |
|
||||||
} |
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
void ExceptionHandler::SendContinueSignalToChild() { |
|
||||||
static const char okToContinueMessage = 'a'; |
|
||||||
int r; |
|
||||||
r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); |
|
||||||
if(r == -1) { |
|
||||||
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
|
|
||||||
sys_write failed:"; |
|
||||||
sys_write(2, msg, sizeof(msg) - 1); |
|
||||||
sys_write(2, strerror(errno), strlen(strerror(errno))); |
|
||||||
sys_write(2, "\n", 1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
// Runs on the cloned process.
|
|
||||||
void ExceptionHandler::WaitForContinueSignal() { |
|
||||||
int r; |
|
||||||
char receivedMessage; |
|
||||||
r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); |
|
||||||
if(r == -1) { |
|
||||||
static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
|
|
||||||
sys_read failed:"; |
|
||||||
sys_write(2, msg, sizeof(msg) - 1); |
|
||||||
sys_write(2, strerror(errno), strlen(strerror(errno))); |
|
||||||
sys_write(2, "\n", 1); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// This function runs in a compromised context: see the top of the file.
|
|
||||||
// Runs on the cloned process.
|
|
||||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, |
|
||||||
size_t context_size) { |
|
||||||
return google_breakpad::WriteMinidump(next_minidump_path_c_, |
|
||||||
crashing_process, |
|
||||||
context, |
|
||||||
context_size, |
|
||||||
mapping_list_); |
|
||||||
} |
|
||||||
|
|
||||||
// static
|
|
||||||
bool ExceptionHandler::WriteMinidump(const std::string &dump_path, |
|
||||||
MinidumpCallback callback, |
|
||||||
void* callback_context) { |
|
||||||
ExceptionHandler eh(dump_path, NULL, callback, callback_context, false); |
|
||||||
return eh.WriteMinidump(); |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::WriteMinidump() { |
|
||||||
#if !defined(__ARM_EABI__) |
|
||||||
// Allow ourselves to be dumped.
|
|
||||||
sys_prctl(PR_SET_DUMPABLE, 1); |
|
||||||
|
|
||||||
CrashContext context; |
|
||||||
int getcontext_result = getcontext(&context.context); |
|
||||||
if (getcontext_result) |
|
||||||
return false; |
|
||||||
memcpy(&context.float_state, context.context.uc_mcontext.fpregs, |
|
||||||
sizeof(context.float_state)); |
|
||||||
context.tid = sys_gettid(); |
|
||||||
|
|
||||||
bool success = GenerateDump(&context); |
|
||||||
UpdateNextID(); |
|
||||||
return success; |
|
||||||
#else |
|
||||||
return false; |
|
||||||
#endif // !defined(__ARM_EABI__)
|
|
||||||
} |
|
||||||
|
|
||||||
void ExceptionHandler::AddMappingInfo(const std::string& name, |
|
||||||
const u_int8_t identifier[sizeof(MDGUID)], |
|
||||||
uintptr_t start_address, |
|
||||||
size_t mapping_size, |
|
||||||
size_t file_offset) { |
|
||||||
MappingInfo info; |
|
||||||
info.start_addr = start_address; |
|
||||||
info.size = mapping_size; |
|
||||||
info.offset = file_offset; |
|
||||||
strncpy(info.name, name.c_str(), std::min(name.size(), sizeof(info))); |
|
||||||
|
|
||||||
MappingEntry mapping; |
|
||||||
mapping.first = info; |
|
||||||
memcpy(mapping.second, identifier, sizeof(MDGUID)); |
|
||||||
mapping_list_.push_back(mapping); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,259 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ |
|
||||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ |
|
||||||
|
|
||||||
#include <string> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#include <pthread.h> |
|
||||||
#include <signal.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <stdio.h> |
|
||||||
|
|
||||||
#if defined(__ANDROID__) |
|
||||||
#include "client/linux/android_ucontext.h" |
|
||||||
#endif |
|
||||||
#include "client/linux/crash_generation/crash_generation_client.h" |
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h" |
|
||||||
#include "google_breakpad/common/minidump_format.h" |
|
||||||
#include "processor/scoped_ptr.h" |
|
||||||
|
|
||||||
struct sigaction; |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
class ExceptionHandler; |
|
||||||
|
|
||||||
// ExceptionHandler
|
|
||||||
//
|
|
||||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
|
||||||
// or when WriteMinidump() is called explicitly by your program.
|
|
||||||
//
|
|
||||||
// To have the exception handler write minidumps when an uncaught exception
|
|
||||||
// (crash) occurs, you should create an instance early in the execution
|
|
||||||
// of your program, and keep it around for the entire time you want to
|
|
||||||
// have crash handling active (typically, until shutdown).
|
|
||||||
// (NOTE): There should be only be one this kind of exception handler
|
|
||||||
// object per process.
|
|
||||||
//
|
|
||||||
// If you want to write minidumps without installing the exception handler,
|
|
||||||
// you can create an ExceptionHandler with install_handler set to false,
|
|
||||||
// then call WriteMinidump. You can also use this technique if you want to
|
|
||||||
// use different minidump callbacks for different call sites.
|
|
||||||
//
|
|
||||||
// In either case, a callback function is called when a minidump is written,
|
|
||||||
// which receives the unqiue id of the minidump. The caller can use this
|
|
||||||
// id to collect and write additional application state, and to launch an
|
|
||||||
// external crash-reporting application.
|
|
||||||
//
|
|
||||||
// Caller should try to make the callbacks as crash-friendly as possible,
|
|
||||||
// it should avoid use heap memory allocation as much as possible.
|
|
||||||
class ExceptionHandler { |
|
||||||
public: |
|
||||||
// A callback function to run before Breakpad performs any substantial
|
|
||||||
// processing of an exception. A FilterCallback is called before writing
|
|
||||||
// a minidump. context is the parameter supplied by the user as
|
|
||||||
// callback_context when the handler was created.
|
|
||||||
//
|
|
||||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
|
||||||
// attempting to write a minidump. If a FilterCallback returns false,
|
|
||||||
// Breakpad will immediately report the exception as unhandled without
|
|
||||||
// writing a minidump, allowing another handler the opportunity to handle it.
|
|
||||||
typedef bool (*FilterCallback)(void *context); |
|
||||||
|
|
||||||
// A callback function to run after the minidump has been written.
|
|
||||||
// minidump_id is a unique id for the dump, so the minidump
|
|
||||||
// file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
|
|
||||||
// by the user as callback_context when the handler was created. succeeded
|
|
||||||
// indicates whether a minidump file was successfully written.
|
|
||||||
//
|
|
||||||
// If an exception occurred and the callback returns true, Breakpad will
|
|
||||||
// treat the exception as fully-handled, suppressing any other handlers from
|
|
||||||
// being notified of the exception. If the callback returns false, Breakpad
|
|
||||||
// will treat the exception as unhandled, and allow another handler to handle
|
|
||||||
// it. If there are no other handlers, Breakpad will report the exception to
|
|
||||||
// the system as unhandled, allowing a debugger or native crash dialog the
|
|
||||||
// opportunity to handle the exception. Most callback implementations
|
|
||||||
// should normally return the value of |succeeded|, or when they wish to
|
|
||||||
// not report an exception of handled, false. Callbacks will rarely want to
|
|
||||||
// return true directly (unless |succeeded| is true).
|
|
||||||
typedef bool (*MinidumpCallback)(const char *dump_path, |
|
||||||
const char *minidump_id, |
|
||||||
void *context, |
|
||||||
bool succeeded); |
|
||||||
|
|
||||||
// In certain cases, a user may wish to handle the generation of the minidump
|
|
||||||
// themselves. In this case, they can install a handler callback which is
|
|
||||||
// called when a crash has occurred. If this function returns true, no other
|
|
||||||
// processing of occurs and the process will shortly be crashed. If this
|
|
||||||
// returns false, the normal processing continues.
|
|
||||||
typedef bool (*HandlerCallback)(const void* crash_context, |
|
||||||
size_t crash_context_size, |
|
||||||
void* context); |
|
||||||
|
|
||||||
// Creates a new ExceptionHandler instance to handle writing minidumps.
|
|
||||||
// Before writing a minidump, the optional filter callback will be called.
|
|
||||||
// Its return value determines whether or not Breakpad should write a
|
|
||||||
// minidump. Minidump files will be written to dump_path, and the optional
|
|
||||||
// callback is called after writing the dump file, as described above.
|
|
||||||
// If install_handler is true, then a minidump will be written whenever
|
|
||||||
// an unhandled exception occurs. If it is false, minidumps will only
|
|
||||||
// be written when WriteMinidump is called.
|
|
||||||
ExceptionHandler(const std::string &dump_path, |
|
||||||
FilterCallback filter, MinidumpCallback callback, |
|
||||||
void *callback_context, |
|
||||||
bool install_handler); |
|
||||||
|
|
||||||
// Creates a new ExceptionHandler instance that can attempt to
|
|
||||||
// perform out-of-process dump generation if server_fd is valid. If
|
|
||||||
// server_fd is invalid, in-process dump generation will be
|
|
||||||
// used. See the above ctor for a description of the other
|
|
||||||
// parameters.
|
|
||||||
ExceptionHandler(const std::string& dump_path, |
|
||||||
FilterCallback filter, MinidumpCallback callback, |
|
||||||
void* callback_context, |
|
||||||
bool install_handler, |
|
||||||
const int server_fd); |
|
||||||
|
|
||||||
~ExceptionHandler(); |
|
||||||
|
|
||||||
// Get and set the minidump path.
|
|
||||||
std::string dump_path() const { return dump_path_; } |
|
||||||
void set_dump_path(const std::string &dump_path) { |
|
||||||
dump_path_ = dump_path; |
|
||||||
dump_path_c_ = dump_path_.c_str(); |
|
||||||
UpdateNextID(); |
|
||||||
} |
|
||||||
|
|
||||||
void set_crash_handler(HandlerCallback callback) { |
|
||||||
crash_handler_ = callback; |
|
||||||
} |
|
||||||
|
|
||||||
// Writes a minidump immediately. This can be used to capture the
|
|
||||||
// execution state independently of a crash. Returns true on success.
|
|
||||||
bool WriteMinidump(); |
|
||||||
|
|
||||||
// Convenience form of WriteMinidump which does not require an
|
|
||||||
// ExceptionHandler instance.
|
|
||||||
static bool WriteMinidump(const std::string &dump_path, |
|
||||||
MinidumpCallback callback, |
|
||||||
void *callback_context); |
|
||||||
|
|
||||||
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
|
|
||||||
// blob. It shouldn't be needed in any user code.
|
|
||||||
struct CrashContext { |
|
||||||
siginfo_t siginfo; |
|
||||||
pid_t tid; // the crashing thread.
|
|
||||||
struct ucontext context; |
|
||||||
#if !defined(__ARM_EABI__) |
|
||||||
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
|
|
||||||
struct _libc_fpstate float_state; |
|
||||||
#endif |
|
||||||
}; |
|
||||||
|
|
||||||
// Returns whether out-of-process dump generation is used or not.
|
|
||||||
bool IsOutOfProcess() const { |
|
||||||
return crash_generation_client_.get() != NULL; |
|
||||||
} |
|
||||||
|
|
||||||
// Add information about a memory mapping. This can be used if
|
|
||||||
// a custom library loader is used that maps things in a way
|
|
||||||
// that the linux dumper can't handle by reading the maps file.
|
|
||||||
void AddMappingInfo(const std::string& name, |
|
||||||
const u_int8_t identifier[sizeof(MDGUID)], |
|
||||||
uintptr_t start_address, |
|
||||||
size_t mapping_size, |
|
||||||
size_t file_offset); |
|
||||||
|
|
||||||
private: |
|
||||||
void Init(const std::string &dump_path, |
|
||||||
const int server_fd); |
|
||||||
bool InstallHandlers(); |
|
||||||
void UninstallHandlers(); |
|
||||||
void PreresolveSymbols(); |
|
||||||
bool GenerateDump(CrashContext *context); |
|
||||||
void SendContinueSignalToChild(); |
|
||||||
void WaitForContinueSignal(); |
|
||||||
|
|
||||||
void UpdateNextID(); |
|
||||||
static void SignalHandler(int sig, siginfo_t* info, void* uc); |
|
||||||
bool HandleSignal(int sig, siginfo_t* info, void* uc); |
|
||||||
static int ThreadEntry(void* arg); |
|
||||||
bool DoDump(pid_t crashing_process, const void* context, |
|
||||||
size_t context_size); |
|
||||||
|
|
||||||
const FilterCallback filter_; |
|
||||||
const MinidumpCallback callback_; |
|
||||||
void* const callback_context_; |
|
||||||
|
|
||||||
scoped_ptr<CrashGenerationClient> crash_generation_client_; |
|
||||||
|
|
||||||
std::string dump_path_; |
|
||||||
std::string next_minidump_path_; |
|
||||||
std::string next_minidump_id_; |
|
||||||
|
|
||||||
// Pointers to C-string representations of the above. These are set
|
|
||||||
// when the above are set so we can avoid calling c_str during
|
|
||||||
// an exception.
|
|
||||||
const char* dump_path_c_; |
|
||||||
const char* next_minidump_path_c_; |
|
||||||
const char* next_minidump_id_c_; |
|
||||||
|
|
||||||
const bool handler_installed_; |
|
||||||
void* signal_stack; // the handler stack.
|
|
||||||
HandlerCallback crash_handler_; |
|
||||||
|
|
||||||
// The global exception handler stack. This is need becuase there may exist
|
|
||||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
|
||||||
// registered in this stack.
|
|
||||||
static std::vector<ExceptionHandler*> *handler_stack_; |
|
||||||
// The index of the handler that should handle the next exception.
|
|
||||||
static unsigned handler_stack_index_; |
|
||||||
static pthread_mutex_t handler_stack_mutex_; |
|
||||||
|
|
||||||
// A vector of the old signal handlers.
|
|
||||||
std::vector<std::pair<int, struct sigaction *> > old_handlers_; |
|
||||||
|
|
||||||
// We need to explicitly enable ptrace of parent processes on some
|
|
||||||
// kernels, but we need to know the PID of the cloned process before we
|
|
||||||
// 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]; |
|
||||||
|
|
||||||
// Callers can add extra info about mappings for cases where the
|
|
||||||
// dumper code cannot extract enough information from /proc/<pid>/maps.
|
|
||||||
MappingList mapping_list_; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
|
@ -1,775 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 <stdint.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <signal.h> |
|
||||||
#include <sys/mman.h> |
|
||||||
#include <sys/poll.h> |
|
||||||
#include <sys/socket.h> |
|
||||||
#include <sys/uio.h> |
|
||||||
#include <sys/wait.h> |
|
||||||
|
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include "breakpad_googletest_includes.h" |
|
||||||
#include "client/linux/handler/exception_handler.h" |
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h" |
|
||||||
#include "common/linux/eintr_wrapper.h" |
|
||||||
#include "common/linux/file_id.h" |
|
||||||
#include "common/linux/linux_libc_support.h" |
|
||||||
#include "third_party/lss/linux_syscall_support.h" |
|
||||||
#include "google_breakpad/processor/minidump.h" |
|
||||||
|
|
||||||
using namespace google_breakpad; |
|
||||||
|
|
||||||
#if !defined(__ANDROID__) |
|
||||||
#define TEMPDIR "/tmp" |
|
||||||
#else |
|
||||||
#define TEMPDIR "/data/local/tmp" |
|
||||||
#endif |
|
||||||
|
|
||||||
// Length of a formatted GUID string =
|
|
||||||
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
|
|
||||||
const int kGUIDStringSize = 37; |
|
||||||
|
|
||||||
static void sigchld_handler(int signo) { } |
|
||||||
|
|
||||||
class ExceptionHandlerTest : public ::testing::Test { |
|
||||||
protected: |
|
||||||
void SetUp() { |
|
||||||
// We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN.
|
|
||||||
struct sigaction sa; |
|
||||||
memset(&sa, 0, sizeof(sa)); |
|
||||||
sa.sa_handler = sigchld_handler; |
|
||||||
ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1); |
|
||||||
} |
|
||||||
|
|
||||||
void TearDown() { |
|
||||||
sigaction(SIGCHLD, &old_action, NULL); |
|
||||||
} |
|
||||||
|
|
||||||
struct sigaction old_action; |
|
||||||
}; |
|
||||||
|
|
||||||
TEST(ExceptionHandlerTest, Simple) { |
|
||||||
ExceptionHandler handler(TEMPDIR, NULL, NULL, NULL, true); |
|
||||||
} |
|
||||||
|
|
||||||
static bool DoneCallback(const char* dump_path, |
|
||||||
const char* minidump_id, |
|
||||||
void* context, |
|
||||||
bool succeeded) { |
|
||||||
if (!succeeded) |
|
||||||
return succeeded; |
|
||||||
|
|
||||||
int fd = (intptr_t) context; |
|
||||||
uint32_t len = my_strlen(minidump_id); |
|
||||||
HANDLE_EINTR(sys_write(fd, &len, sizeof(len))); |
|
||||||
HANDLE_EINTR(sys_write(fd, minidump_id, len)); |
|
||||||
sys_close(fd); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
TEST(ExceptionHandlerTest, ChildCrash) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(pipe(fds), -1); |
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[0]); |
|
||||||
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], |
|
||||||
true); |
|
||||||
*reinterpret_cast<volatile int*>(NULL) = 0; |
|
||||||
} |
|
||||||
close(fds[1]); |
|
||||||
|
|
||||||
int status; |
|
||||||
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); |
|
||||||
ASSERT_TRUE(WIFSIGNALED(status)); |
|
||||||
ASSERT_EQ(WTERMSIG(status), SIGSEGV); |
|
||||||
|
|
||||||
struct pollfd pfd; |
|
||||||
memset(&pfd, 0, sizeof(pfd)); |
|
||||||
pfd.fd = fds[0]; |
|
||||||
pfd.events = POLLIN | POLLERR; |
|
||||||
|
|
||||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); |
|
||||||
ASSERT_EQ(r, 1); |
|
||||||
ASSERT_TRUE(pfd.revents & POLLIN); |
|
||||||
|
|
||||||
uint32_t len; |
|
||||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); |
|
||||||
ASSERT_LT(len, (uint32_t)2048); |
|
||||||
char* filename = reinterpret_cast<char*>(malloc(len + 1)); |
|
||||||
ASSERT_EQ(read(fds[0], filename, len), len); |
|
||||||
filename[len] = 0; |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + |
|
||||||
".dmp"; |
|
||||||
|
|
||||||
struct stat st; |
|
||||||
ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); |
|
||||||
ASSERT_GT(st.st_size, 0u); |
|
||||||
unlink(minidump_filename.c_str()); |
|
||||||
} |
|
||||||
|
|
||||||
// Test that memory around the instruction pointer is written
|
|
||||||
// to the dump as a MinidumpMemoryRegion.
|
|
||||||
TEST(ExceptionHandlerTest, InstructionPointerMemory) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(pipe(fds), -1); |
|
||||||
|
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
const u_int32_t kMemorySize = 256; // bytes
|
|
||||||
const int kOffset = kMemorySize / 2; |
|
||||||
// This crashes with SIGILL on x86/x86-64/arm.
|
|
||||||
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; |
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[0]); |
|
||||||
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], |
|
||||||
true); |
|
||||||
// Get some executable memory.
|
|
||||||
char* memory = |
|
||||||
reinterpret_cast<char*>(mmap(NULL, |
|
||||||
kMemorySize, |
|
||||||
PROT_READ | PROT_WRITE | PROT_EXEC, |
|
||||||
MAP_PRIVATE | MAP_ANON, |
|
||||||
-1, |
|
||||||
0)); |
|
||||||
if (!memory) |
|
||||||
exit(0); |
|
||||||
|
|
||||||
// Write some instructions that will crash. Put them in the middle
|
|
||||||
// of the block of memory, because the minidump should contain 128
|
|
||||||
// bytes on either side of the instruction pointer.
|
|
||||||
memcpy(memory + kOffset, instructions, sizeof(instructions)); |
|
||||||
|
|
||||||
// Now execute the instructions, which should crash.
|
|
||||||
typedef void (*void_function)(void); |
|
||||||
void_function memory_function = |
|
||||||
reinterpret_cast<void_function>(memory + kOffset); |
|
||||||
memory_function(); |
|
||||||
} |
|
||||||
close(fds[1]); |
|
||||||
|
|
||||||
int status; |
|
||||||
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); |
|
||||||
ASSERT_TRUE(WIFSIGNALED(status)); |
|
||||||
ASSERT_EQ(WTERMSIG(status), SIGILL); |
|
||||||
|
|
||||||
struct pollfd pfd; |
|
||||||
memset(&pfd, 0, sizeof(pfd)); |
|
||||||
pfd.fd = fds[0]; |
|
||||||
pfd.events = POLLIN | POLLERR; |
|
||||||
|
|
||||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); |
|
||||||
ASSERT_EQ(r, 1); |
|
||||||
ASSERT_TRUE(pfd.revents & POLLIN); |
|
||||||
|
|
||||||
uint32_t len; |
|
||||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); |
|
||||||
ASSERT_LT(len, (uint32_t)2048); |
|
||||||
char* filename = reinterpret_cast<char*>(malloc(len + 1)); |
|
||||||
ASSERT_EQ(read(fds[0], filename, len), len); |
|
||||||
filename[len] = 0; |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + |
|
||||||
".dmp"; |
|
||||||
|
|
||||||
struct stat st; |
|
||||||
ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); |
|
||||||
ASSERT_GT(st.st_size, 0u); |
|
||||||
|
|
||||||
// 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.
|
|
||||||
Minidump minidump(minidump_filename); |
|
||||||
ASSERT_TRUE(minidump.Read()); |
|
||||||
|
|
||||||
MinidumpException* exception = minidump.GetException(); |
|
||||||
MinidumpMemoryList* memory_list = minidump.GetMemoryList(); |
|
||||||
ASSERT_TRUE(exception); |
|
||||||
ASSERT_TRUE(memory_list); |
|
||||||
ASSERT_LT(0, memory_list->region_count()); |
|
||||||
|
|
||||||
MinidumpContext* context = exception->GetContext(); |
|
||||||
ASSERT_TRUE(context); |
|
||||||
|
|
||||||
u_int64_t instruction_pointer; |
|
||||||
switch (context->GetContextCPU()) { |
|
||||||
case MD_CONTEXT_X86: |
|
||||||
instruction_pointer = context->GetContextX86()->eip; |
|
||||||
break; |
|
||||||
case MD_CONTEXT_AMD64: |
|
||||||
instruction_pointer = context->GetContextAMD64()->rip; |
|
||||||
break; |
|
||||||
case MD_CONTEXT_ARM: |
|
||||||
instruction_pointer = context->GetContextARM()->iregs[15]; |
|
||||||
break; |
|
||||||
default: |
|
||||||
FAIL() << "Unknown context CPU: " << context->GetContextCPU(); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
MinidumpMemoryRegion* region = |
|
||||||
memory_list->GetMemoryRegionForAddress(instruction_pointer); |
|
||||||
ASSERT_TRUE(region); |
|
||||||
|
|
||||||
EXPECT_EQ(kMemorySize, region->GetSize()); |
|
||||||
const u_int8_t* bytes = region->GetMemory(); |
|
||||||
ASSERT_TRUE(bytes); |
|
||||||
|
|
||||||
u_int8_t prefix_bytes[kOffset]; |
|
||||||
u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)]; |
|
||||||
memset(prefix_bytes, 0, sizeof(prefix_bytes)); |
|
||||||
memset(suffix_bytes, 0, sizeof(suffix_bytes)); |
|
||||||
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); |
|
||||||
EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); |
|
||||||
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), |
|
||||||
suffix_bytes, sizeof(suffix_bytes)) == 0); |
|
||||||
|
|
||||||
unlink(minidump_filename.c_str()); |
|
||||||
free(filename); |
|
||||||
} |
|
||||||
|
|
||||||
// Test that the memory region around the instruction pointer is
|
|
||||||
// bounded correctly on the low end.
|
|
||||||
TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(pipe(fds), -1); |
|
||||||
|
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
const u_int32_t kMemorySize = 256; // bytes
|
|
||||||
const int kOffset = 0; |
|
||||||
// This crashes with SIGILL on x86/x86-64/arm.
|
|
||||||
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; |
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[0]); |
|
||||||
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], |
|
||||||
true); |
|
||||||
// Get some executable memory.
|
|
||||||
char* memory = |
|
||||||
reinterpret_cast<char*>(mmap(NULL, |
|
||||||
kMemorySize, |
|
||||||
PROT_READ | PROT_WRITE | PROT_EXEC, |
|
||||||
MAP_PRIVATE | MAP_ANON, |
|
||||||
-1, |
|
||||||
0)); |
|
||||||
if (!memory) |
|
||||||
exit(0); |
|
||||||
|
|
||||||
// Write some instructions that will crash. Put them in the middle
|
|
||||||
// of the block of memory, because the minidump should contain 128
|
|
||||||
// bytes on either side of the instruction pointer.
|
|
||||||
memcpy(memory + kOffset, instructions, sizeof(instructions)); |
|
||||||
|
|
||||||
// Now execute the instructions, which should crash.
|
|
||||||
typedef void (*void_function)(void); |
|
||||||
void_function memory_function = |
|
||||||
reinterpret_cast<void_function>(memory + kOffset); |
|
||||||
memory_function(); |
|
||||||
} |
|
||||||
close(fds[1]); |
|
||||||
|
|
||||||
int status; |
|
||||||
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); |
|
||||||
ASSERT_TRUE(WIFSIGNALED(status)); |
|
||||||
ASSERT_EQ(WTERMSIG(status), SIGILL); |
|
||||||
|
|
||||||
struct pollfd pfd; |
|
||||||
memset(&pfd, 0, sizeof(pfd)); |
|
||||||
pfd.fd = fds[0]; |
|
||||||
pfd.events = POLLIN | POLLERR; |
|
||||||
|
|
||||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); |
|
||||||
ASSERT_EQ(r, 1); |
|
||||||
ASSERT_TRUE(pfd.revents & POLLIN); |
|
||||||
|
|
||||||
uint32_t len; |
|
||||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); |
|
||||||
ASSERT_LT(len, (uint32_t)2048); |
|
||||||
char* filename = reinterpret_cast<char*>(malloc(len + 1)); |
|
||||||
ASSERT_EQ(read(fds[0], filename, len), len); |
|
||||||
filename[len] = 0; |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + |
|
||||||
".dmp"; |
|
||||||
|
|
||||||
struct stat st; |
|
||||||
ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); |
|
||||||
ASSERT_GT(st.st_size, 0u); |
|
||||||
|
|
||||||
// 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.
|
|
||||||
Minidump minidump(minidump_filename); |
|
||||||
ASSERT_TRUE(minidump.Read()); |
|
||||||
|
|
||||||
MinidumpException* exception = minidump.GetException(); |
|
||||||
MinidumpMemoryList* memory_list = minidump.GetMemoryList(); |
|
||||||
ASSERT_TRUE(exception); |
|
||||||
ASSERT_TRUE(memory_list); |
|
||||||
ASSERT_LT(0, memory_list->region_count()); |
|
||||||
|
|
||||||
MinidumpContext* context = exception->GetContext(); |
|
||||||
ASSERT_TRUE(context); |
|
||||||
|
|
||||||
u_int64_t instruction_pointer; |
|
||||||
switch (context->GetContextCPU()) { |
|
||||||
case MD_CONTEXT_X86: |
|
||||||
instruction_pointer = context->GetContextX86()->eip; |
|
||||||
break; |
|
||||||
case MD_CONTEXT_AMD64: |
|
||||||
instruction_pointer = context->GetContextAMD64()->rip; |
|
||||||
break; |
|
||||||
case MD_CONTEXT_ARM: |
|
||||||
instruction_pointer = context->GetContextARM()->iregs[15]; |
|
||||||
break; |
|
||||||
default: |
|
||||||
FAIL() << "Unknown context CPU: " << context->GetContextCPU(); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
MinidumpMemoryRegion* region = |
|
||||||
memory_list->GetMemoryRegionForAddress(instruction_pointer); |
|
||||||
ASSERT_TRUE(region); |
|
||||||
|
|
||||||
EXPECT_EQ(kMemorySize / 2, region->GetSize()); |
|
||||||
const u_int8_t* bytes = region->GetMemory(); |
|
||||||
ASSERT_TRUE(bytes); |
|
||||||
|
|
||||||
u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)]; |
|
||||||
memset(suffix_bytes, 0, sizeof(suffix_bytes)); |
|
||||||
EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); |
|
||||||
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), |
|
||||||
suffix_bytes, sizeof(suffix_bytes)) == 0); |
|
||||||
|
|
||||||
unlink(minidump_filename.c_str()); |
|
||||||
free(filename); |
|
||||||
} |
|
||||||
|
|
||||||
// Test that the memory region around the instruction pointer is
|
|
||||||
// bounded correctly on the high end.
|
|
||||||
TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(pipe(fds), -1); |
|
||||||
|
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
// Use 4k here because the OS will hand out a single page even
|
|
||||||
// if a smaller size is requested, and this test wants to
|
|
||||||
// test the upper bound of the memory range.
|
|
||||||
const u_int32_t kMemorySize = 4096; // bytes
|
|
||||||
// This crashes with SIGILL on x86/x86-64/arm.
|
|
||||||
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; |
|
||||||
const int kOffset = kMemorySize - sizeof(instructions); |
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[0]); |
|
||||||
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], |
|
||||||
true); |
|
||||||
// Get some executable memory.
|
|
||||||
char* memory = |
|
||||||
reinterpret_cast<char*>(mmap(NULL, |
|
||||||
kMemorySize, |
|
||||||
PROT_READ | PROT_WRITE | PROT_EXEC, |
|
||||||
MAP_PRIVATE | MAP_ANON, |
|
||||||
-1, |
|
||||||
0)); |
|
||||||
if (!memory) |
|
||||||
exit(0); |
|
||||||
|
|
||||||
// Write some instructions that will crash. Put them in the middle
|
|
||||||
// of the block of memory, because the minidump should contain 128
|
|
||||||
// bytes on either side of the instruction pointer.
|
|
||||||
memcpy(memory + kOffset, instructions, sizeof(instructions)); |
|
||||||
|
|
||||||
// Now execute the instructions, which should crash.
|
|
||||||
typedef void (*void_function)(void); |
|
||||||
void_function memory_function = |
|
||||||
reinterpret_cast<void_function>(memory + kOffset); |
|
||||||
memory_function(); |
|
||||||
} |
|
||||||
close(fds[1]); |
|
||||||
|
|
||||||
int status; |
|
||||||
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); |
|
||||||
ASSERT_TRUE(WIFSIGNALED(status)); |
|
||||||
ASSERT_EQ(WTERMSIG(status), SIGILL); |
|
||||||
|
|
||||||
struct pollfd pfd; |
|
||||||
memset(&pfd, 0, sizeof(pfd)); |
|
||||||
pfd.fd = fds[0]; |
|
||||||
pfd.events = POLLIN | POLLERR; |
|
||||||
|
|
||||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); |
|
||||||
ASSERT_EQ(r, 1); |
|
||||||
ASSERT_TRUE(pfd.revents & POLLIN); |
|
||||||
|
|
||||||
uint32_t len; |
|
||||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); |
|
||||||
ASSERT_LT(len, (uint32_t)2048); |
|
||||||
char* filename = reinterpret_cast<char*>(malloc(len + 1)); |
|
||||||
ASSERT_EQ(read(fds[0], filename, len), len); |
|
||||||
filename[len] = 0; |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + |
|
||||||
".dmp"; |
|
||||||
|
|
||||||
struct stat st; |
|
||||||
ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); |
|
||||||
ASSERT_GT(st.st_size, 0u); |
|
||||||
|
|
||||||
// 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.
|
|
||||||
Minidump minidump(minidump_filename); |
|
||||||
ASSERT_TRUE(minidump.Read()); |
|
||||||
|
|
||||||
MinidumpException* exception = minidump.GetException(); |
|
||||||
MinidumpMemoryList* memory_list = minidump.GetMemoryList(); |
|
||||||
ASSERT_TRUE(exception); |
|
||||||
ASSERT_TRUE(memory_list); |
|
||||||
ASSERT_LT(0, memory_list->region_count()); |
|
||||||
|
|
||||||
MinidumpContext* context = exception->GetContext(); |
|
||||||
ASSERT_TRUE(context); |
|
||||||
|
|
||||||
u_int64_t instruction_pointer; |
|
||||||
switch (context->GetContextCPU()) { |
|
||||||
case MD_CONTEXT_X86: |
|
||||||
instruction_pointer = context->GetContextX86()->eip; |
|
||||||
break; |
|
||||||
case MD_CONTEXT_AMD64: |
|
||||||
instruction_pointer = context->GetContextAMD64()->rip; |
|
||||||
break; |
|
||||||
case MD_CONTEXT_ARM: |
|
||||||
instruction_pointer = context->GetContextARM()->iregs[15]; |
|
||||||
break; |
|
||||||
default: |
|
||||||
FAIL() << "Unknown context CPU: " << context->GetContextCPU(); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
MinidumpMemoryRegion* region = |
|
||||||
memory_list->GetMemoryRegionForAddress(instruction_pointer); |
|
||||||
ASSERT_TRUE(region); |
|
||||||
|
|
||||||
const size_t kPrefixSize = 128; // bytes
|
|
||||||
EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize()); |
|
||||||
const u_int8_t* bytes = region->GetMemory(); |
|
||||||
ASSERT_TRUE(bytes); |
|
||||||
|
|
||||||
u_int8_t prefix_bytes[kPrefixSize]; |
|
||||||
memset(prefix_bytes, 0, sizeof(prefix_bytes)); |
|
||||||
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); |
|
||||||
EXPECT_TRUE(memcmp(bytes + kPrefixSize, |
|
||||||
instructions, sizeof(instructions)) == 0); |
|
||||||
|
|
||||||
unlink(minidump_filename.c_str()); |
|
||||||
free(filename); |
|
||||||
} |
|
||||||
|
|
||||||
// Ensure that an extra memory block doesn't get added when the
|
|
||||||
// instruction pointer is not in mapped memory.
|
|
||||||
TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(pipe(fds), -1); |
|
||||||
|
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[0]); |
|
||||||
ExceptionHandler handler(TEMPDIR, NULL, DoneCallback, (void*) fds[1], |
|
||||||
true); |
|
||||||
// Try calling a NULL pointer.
|
|
||||||
typedef void (*void_function)(void); |
|
||||||
void_function memory_function = |
|
||||||
reinterpret_cast<void_function>(NULL); |
|
||||||
memory_function(); |
|
||||||
} |
|
||||||
close(fds[1]); |
|
||||||
|
|
||||||
int status; |
|
||||||
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); |
|
||||||
ASSERT_TRUE(WIFSIGNALED(status)); |
|
||||||
ASSERT_EQ(WTERMSIG(status), SIGSEGV); |
|
||||||
|
|
||||||
struct pollfd pfd; |
|
||||||
memset(&pfd, 0, sizeof(pfd)); |
|
||||||
pfd.fd = fds[0]; |
|
||||||
pfd.events = POLLIN | POLLERR; |
|
||||||
|
|
||||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); |
|
||||||
ASSERT_EQ(r, 1); |
|
||||||
ASSERT_TRUE(pfd.revents & POLLIN); |
|
||||||
|
|
||||||
uint32_t len; |
|
||||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); |
|
||||||
ASSERT_LT(len, (uint32_t)2048); |
|
||||||
char* filename = reinterpret_cast<char*>(malloc(len + 1)); |
|
||||||
ASSERT_EQ(read(fds[0], filename, len), len); |
|
||||||
filename[len] = 0; |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
const std::string minidump_filename = std::string(TEMPDIR) + "/" + filename + |
|
||||||
".dmp"; |
|
||||||
|
|
||||||
struct stat st; |
|
||||||
ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); |
|
||||||
ASSERT_GT(st.st_size, 0u); |
|
||||||
|
|
||||||
// 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.
|
|
||||||
Minidump minidump(minidump_filename); |
|
||||||
ASSERT_TRUE(minidump.Read()); |
|
||||||
|
|
||||||
MinidumpException* exception = minidump.GetException(); |
|
||||||
MinidumpMemoryList* memory_list = minidump.GetMemoryList(); |
|
||||||
ASSERT_TRUE(exception); |
|
||||||
ASSERT_TRUE(memory_list); |
|
||||||
ASSERT_EQ((unsigned int)1, memory_list->region_count()); |
|
||||||
|
|
||||||
unlink(minidump_filename.c_str()); |
|
||||||
free(filename); |
|
||||||
} |
|
||||||
|
|
||||||
static bool SimpleCallback(const char* dump_path, |
|
||||||
const char* minidump_id, |
|
||||||
void* context, |
|
||||||
bool succeeded) { |
|
||||||
if (!succeeded) |
|
||||||
return succeeded; |
|
||||||
|
|
||||||
string* minidump_file = reinterpret_cast<string*>(context); |
|
||||||
minidump_file->append(dump_path); |
|
||||||
minidump_file->append("/"); |
|
||||||
minidump_file->append(minidump_id); |
|
||||||
minidump_file->append(".dmp"); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// Test that anonymous memory maps can be annotated with names and IDs.
|
|
||||||
TEST(ExceptionHandlerTest, ModuleInfo) { |
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); |
|
||||||
const char* kMemoryName = "a fake module"; |
|
||||||
const u_int8_t kModuleGUID[sizeof(MDGUID)] = { |
|
||||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, |
|
||||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF |
|
||||||
}; |
|
||||||
char module_identifier_buffer[kGUIDStringSize]; |
|
||||||
FileID::ConvertIdentifierToString(kModuleGUID, |
|
||||||
module_identifier_buffer, |
|
||||||
sizeof(module_identifier_buffer)); |
|
||||||
string module_identifier(module_identifier_buffer); |
|
||||||
// Strip out dashes
|
|
||||||
size_t pos; |
|
||||||
while ((pos = module_identifier.find('-')) != string::npos) { |
|
||||||
module_identifier.erase(pos, 1); |
|
||||||
} |
|
||||||
// And append a zero, because module IDs include an "age" field
|
|
||||||
// which is always zero on Linux.
|
|
||||||
module_identifier += "0"; |
|
||||||
|
|
||||||
// Get some memory.
|
|
||||||
char* memory = |
|
||||||
reinterpret_cast<char*>(mmap(NULL, |
|
||||||
kMemorySize, |
|
||||||
PROT_READ | PROT_WRITE, |
|
||||||
MAP_PRIVATE | MAP_ANON, |
|
||||||
-1, |
|
||||||
0)); |
|
||||||
const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory); |
|
||||||
ASSERT_TRUE(memory); |
|
||||||
|
|
||||||
string minidump_filename; |
|
||||||
ExceptionHandler handler(TEMPDIR, NULL, SimpleCallback, |
|
||||||
(void*)&minidump_filename, true); |
|
||||||
// Add info about the anonymous memory mapping.
|
|
||||||
handler.AddMappingInfo(kMemoryName, |
|
||||||
kModuleGUID, |
|
||||||
kMemoryAddress, |
|
||||||
kMemorySize, |
|
||||||
0); |
|
||||||
handler.WriteMinidump(); |
|
||||||
|
|
||||||
// Read the minidump. Load the module list, and ensure that
|
|
||||||
// the mmap'ed |memory| is listed with the given module name
|
|
||||||
// and debug ID.
|
|
||||||
Minidump minidump(minidump_filename); |
|
||||||
ASSERT_TRUE(minidump.Read()); |
|
||||||
|
|
||||||
MinidumpModuleList* module_list = minidump.GetModuleList(); |
|
||||||
ASSERT_TRUE(module_list); |
|
||||||
const MinidumpModule* module = |
|
||||||
module_list->GetModuleForAddress(kMemoryAddress); |
|
||||||
ASSERT_TRUE(module); |
|
||||||
|
|
||||||
EXPECT_EQ(kMemoryAddress, module->base_address()); |
|
||||||
EXPECT_EQ(kMemorySize, module->size()); |
|
||||||
EXPECT_EQ(kMemoryName, module->code_file()); |
|
||||||
EXPECT_EQ(module_identifier, module->debug_identifier()); |
|
||||||
|
|
||||||
unlink(minidump_filename.c_str()); |
|
||||||
} |
|
||||||
|
|
||||||
static const unsigned kControlMsgSize = |
|
||||||
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); |
|
||||||
|
|
||||||
static bool |
|
||||||
CrashHandler(const void* crash_context, size_t crash_context_size, |
|
||||||
void* context) { |
|
||||||
const int fd = (intptr_t) context; |
|
||||||
int fds[2]; |
|
||||||
pipe(fds); |
|
||||||
struct kernel_msghdr msg = {0}; |
|
||||||
struct kernel_iovec iov; |
|
||||||
iov.iov_base = const_cast<void*>(crash_context); |
|
||||||
iov.iov_len = crash_context_size; |
|
||||||
msg.msg_iov = &iov; |
|
||||||
msg.msg_iovlen = 1; |
|
||||||
char cmsg[kControlMsgSize]; |
|
||||||
memset(cmsg, 0, kControlMsgSize); |
|
||||||
msg.msg_control = cmsg; |
|
||||||
msg.msg_controllen = sizeof(cmsg); |
|
||||||
|
|
||||||
struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); |
|
||||||
hdr->cmsg_level = SOL_SOCKET; |
|
||||||
hdr->cmsg_type = SCM_RIGHTS; |
|
||||||
hdr->cmsg_len = CMSG_LEN(sizeof(int)); |
|
||||||
*((int*) CMSG_DATA(hdr)) = fds[1]; |
|
||||||
hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr); |
|
||||||
hdr->cmsg_level = SOL_SOCKET; |
|
||||||
hdr->cmsg_type = SCM_CREDENTIALS; |
|
||||||
hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred)); |
|
||||||
struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); |
|
||||||
cred->uid = getuid(); |
|
||||||
cred->gid = getgid(); |
|
||||||
cred->pid = getpid(); |
|
||||||
|
|
||||||
HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)); |
|
||||||
sys_close(fds[1]); |
|
||||||
|
|
||||||
char b; |
|
||||||
HANDLE_EINTR(sys_read(fds[0], &b, 1)); |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
TEST(ExceptionHandlerTest, ExternalDumper) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); |
|
||||||
static const int on = 1; |
|
||||||
setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); |
|
||||||
setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); |
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[0]); |
|
||||||
ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true); |
|
||||||
handler.set_crash_handler(CrashHandler); |
|
||||||
*reinterpret_cast<volatile int*>(NULL) = 0; |
|
||||||
} |
|
||||||
close(fds[1]); |
|
||||||
struct msghdr msg = {0}; |
|
||||||
struct iovec iov; |
|
||||||
static const unsigned kCrashContextSize = |
|
||||||
sizeof(ExceptionHandler::CrashContext); |
|
||||||
char context[kCrashContextSize]; |
|
||||||
char control[kControlMsgSize]; |
|
||||||
iov.iov_base = context; |
|
||||||
iov.iov_len = kCrashContextSize; |
|
||||||
msg.msg_iov = &iov; |
|
||||||
msg.msg_iovlen = 1; |
|
||||||
msg.msg_control = control; |
|
||||||
msg.msg_controllen = kControlMsgSize; |
|
||||||
|
|
||||||
const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); |
|
||||||
ASSERT_EQ(n, kCrashContextSize); |
|
||||||
ASSERT_EQ(msg.msg_controllen, kControlMsgSize); |
|
||||||
ASSERT_EQ(msg.msg_flags, 0); |
|
||||||
|
|
||||||
pid_t crashing_pid = -1; |
|
||||||
int signal_fd = -1; |
|
||||||
for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; |
|
||||||
hdr = CMSG_NXTHDR(&msg, hdr)) { |
|
||||||
if (hdr->cmsg_level != SOL_SOCKET) |
|
||||||
continue; |
|
||||||
if (hdr->cmsg_type == SCM_RIGHTS) { |
|
||||||
const unsigned len = hdr->cmsg_len - |
|
||||||
(((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); |
|
||||||
ASSERT_EQ(len, sizeof(int)); |
|
||||||
signal_fd = *((int *) CMSG_DATA(hdr)); |
|
||||||
} else if (hdr->cmsg_type == SCM_CREDENTIALS) { |
|
||||||
const struct ucred *cred = |
|
||||||
reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); |
|
||||||
crashing_pid = cred->pid; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
ASSERT_NE(crashing_pid, -1); |
|
||||||
ASSERT_NE(signal_fd, -1); |
|
||||||
|
|
||||||
char templ[] = TEMPDIR "/exception-handler-unittest-XXXXXX"; |
|
||||||
mktemp(templ); |
|
||||||
ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context, |
|
||||||
kCrashContextSize)); |
|
||||||
static const char b = 0; |
|
||||||
HANDLE_EINTR(write(signal_fd, &b, 1)); |
|
||||||
|
|
||||||
int status; |
|
||||||
ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); |
|
||||||
ASSERT_TRUE(WIFSIGNALED(status)); |
|
||||||
ASSERT_EQ(WTERMSIG(status), SIGSEGV); |
|
||||||
|
|
||||||
struct stat st; |
|
||||||
ASSERT_EQ(stat(templ, &st), 0); |
|
||||||
ASSERT_GT(st.st_size, 0u); |
|
||||||
unlink(templ); |
|
||||||
} |
|
@ -1,105 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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 CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ |
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <limits.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <errno.h> |
|
||||||
#include <string.h> |
|
||||||
|
|
||||||
#include "third_party/lss/linux_syscall_support.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
// A class for enumerating a directory without using diropen/readdir or other
|
|
||||||
// functions which may allocate memory.
|
|
||||||
class DirectoryReader { |
|
||||||
public: |
|
||||||
DirectoryReader(int fd) |
|
||||||
: fd_(fd), |
|
||||||
buf_used_(0) { |
|
||||||
} |
|
||||||
|
|
||||||
// Return the next entry from the directory
|
|
||||||
// name: (output) the NUL terminated entry name
|
|
||||||
//
|
|
||||||
// Returns true iff successful (false on EOF).
|
|
||||||
//
|
|
||||||
// After calling this, one must call |PopEntry| otherwise you'll get the same
|
|
||||||
// entry over and over.
|
|
||||||
bool GetNextEntry(const char** name) { |
|
||||||
struct kernel_dirent* const dent = |
|
||||||
reinterpret_cast<kernel_dirent*>(buf_); |
|
||||||
|
|
||||||
if (buf_used_ == 0) { |
|
||||||
// need to read more entries.
|
|
||||||
const int n = sys_getdents(fd_, dent, sizeof(buf_)); |
|
||||||
if (n < 0) { |
|
||||||
return false; |
|
||||||
} else if (n == 0) { |
|
||||||
hit_eof_ = true; |
|
||||||
} else { |
|
||||||
buf_used_ += n; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (buf_used_ == 0 && hit_eof_) |
|
||||||
return false; |
|
||||||
|
|
||||||
assert(buf_used_ > 0); |
|
||||||
|
|
||||||
*name = dent->d_name; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
void PopEntry() { |
|
||||||
if (!buf_used_) |
|
||||||
return; |
|
||||||
|
|
||||||
const struct kernel_dirent* const dent = |
|
||||||
reinterpret_cast<kernel_dirent*>(buf_); |
|
||||||
|
|
||||||
buf_used_ -= dent->d_reclen; |
|
||||||
memmove(buf_, buf_ + dent->d_reclen, buf_used_); |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
const int fd_; |
|
||||||
bool hit_eof_; |
|
||||||
unsigned buf_used_; |
|
||||||
uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
|
|
@ -1,77 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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 <set> |
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include <dirent.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <sys/types.h> |
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/directory_reader.h" |
|
||||||
#include "breakpad_googletest_includes.h" |
|
||||||
|
|
||||||
using namespace google_breakpad; |
|
||||||
|
|
||||||
namespace { |
|
||||||
typedef testing::Test DirectoryReaderTest; |
|
||||||
} |
|
||||||
|
|
||||||
TEST(DirectoryReaderTest, CompareResults) { |
|
||||||
std::set<std::string> dent_set; |
|
||||||
|
|
||||||
DIR *const dir = opendir("/proc/self"); |
|
||||||
ASSERT_TRUE(dir != NULL); |
|
||||||
|
|
||||||
struct dirent* dent; |
|
||||||
while ((dent = readdir(dir))) |
|
||||||
dent_set.insert(dent->d_name); |
|
||||||
|
|
||||||
closedir(dir); |
|
||||||
|
|
||||||
const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY); |
|
||||||
ASSERT_GE(fd, 0); |
|
||||||
|
|
||||||
DirectoryReader dir_reader(fd); |
|
||||||
unsigned seen = 0; |
|
||||||
|
|
||||||
const char* name; |
|
||||||
while (dir_reader.GetNextEntry(&name)) { |
|
||||||
ASSERT_TRUE(dent_set.find(name) != dent_set.end()); |
|
||||||
seen++; |
|
||||||
dir_reader.PopEntry(); |
|
||||||
} |
|
||||||
|
|
||||||
ASSERT_TRUE(dent_set.find("status") != dent_set.end()); |
|
||||||
ASSERT_TRUE(dent_set.find("stat") != dent_set.end()); |
|
||||||
ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end()); |
|
||||||
|
|
||||||
ASSERT_EQ(dent_set.size(), seen); |
|
||||||
close(fd); |
|
||||||
} |
|
@ -1,130 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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 CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ |
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <string.h> |
|
||||||
|
|
||||||
#include "third_party/lss/linux_syscall_support.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
// A class for reading a file, line by line, without using fopen/fgets or other
|
|
||||||
// functions which may allocate memory.
|
|
||||||
class LineReader { |
|
||||||
public: |
|
||||||
LineReader(int fd) |
|
||||||
: fd_(fd), |
|
||||||
hit_eof_(false), |
|
||||||
buf_used_(0) { |
|
||||||
} |
|
||||||
|
|
||||||
// The maximum length of a line.
|
|
||||||
static const size_t kMaxLineLen = 512; |
|
||||||
|
|
||||||
// Return the next line from the file.
|
|
||||||
// line: (output) a pointer to the start of the line. The line is NUL
|
|
||||||
// terminated.
|
|
||||||
// len: (output) the length of the line (not inc the NUL byte)
|
|
||||||
//
|
|
||||||
// Returns true iff successful (false on EOF).
|
|
||||||
//
|
|
||||||
// One must call |PopLine| after this function, otherwise you'll continue to
|
|
||||||
// get the same line over and over.
|
|
||||||
bool GetNextLine(const char **line, unsigned *len) { |
|
||||||
for (;;) { |
|
||||||
if (buf_used_ == 0 && hit_eof_) |
|
||||||
return false; |
|
||||||
|
|
||||||
for (unsigned i = 0; i < buf_used_; ++i) { |
|
||||||
if (buf_[i] == '\n' || buf_[i] == 0) { |
|
||||||
buf_[i] = 0; |
|
||||||
*len = i; |
|
||||||
*line = buf_; |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (buf_used_ == sizeof(buf_)) { |
|
||||||
// we scanned the whole buffer and didn't find an end-of-line marker.
|
|
||||||
// This line is too long to process.
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// We didn't find any end-of-line terminators in the buffer. However, if
|
|
||||||
// this is the last line in the file it might not have one:
|
|
||||||
if (hit_eof_) { |
|
||||||
assert(buf_used_); |
|
||||||
// There's room for the NUL because of the buf_used_ == sizeof(buf_)
|
|
||||||
// check above.
|
|
||||||
buf_[buf_used_] = 0; |
|
||||||
*len = buf_used_; |
|
||||||
buf_used_ += 1; // since we appended the NUL.
|
|
||||||
*line = buf_; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// Otherwise, we should pull in more data from the file
|
|
||||||
const ssize_t n = sys_read(fd_, buf_ + buf_used_, |
|
||||||
sizeof(buf_) - buf_used_); |
|
||||||
if (n < 0) { |
|
||||||
return false; |
|
||||||
} else if (n == 0) { |
|
||||||
hit_eof_ = true; |
|
||||||
} else { |
|
||||||
buf_used_ += n; |
|
||||||
} |
|
||||||
|
|
||||||
// At this point, we have either set the hit_eof_ flag, or we have more
|
|
||||||
// data to process...
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void PopLine(unsigned len) { |
|
||||||
// len doesn't include the NUL byte at the end.
|
|
||||||
|
|
||||||
assert(buf_used_ >= len + 1); |
|
||||||
buf_used_ -= len + 1; |
|
||||||
memmove(buf_, buf_ + len + 1, buf_used_); |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
const int fd_; |
|
||||||
|
|
||||||
bool hit_eof_; |
|
||||||
unsigned buf_used_; |
|
||||||
char buf_[kMaxLineLen]; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
|
|
@ -1,190 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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 <stdlib.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <sys/types.h> |
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/line_reader.h" |
|
||||||
#include "breakpad_googletest_includes.h" |
|
||||||
|
|
||||||
using namespace google_breakpad; |
|
||||||
|
|
||||||
#if !defined(__ANDROID__) |
|
||||||
#define TEMPDIR "/tmp" |
|
||||||
#else |
|
||||||
#define TEMPDIR "/data/local/tmp" |
|
||||||
#endif |
|
||||||
|
|
||||||
static int TemporaryFile() { |
|
||||||
static const char templ[] = TEMPDIR "/line-reader-unittest-XXXXXX"; |
|
||||||
char templ_copy[sizeof(templ)]; |
|
||||||
memcpy(templ_copy, templ, sizeof(templ)); |
|
||||||
const int fd = mkstemp(templ_copy); |
|
||||||
if (fd >= 0) |
|
||||||
unlink(templ_copy); |
|
||||||
|
|
||||||
return fd; |
|
||||||
} |
|
||||||
|
|
||||||
namespace { |
|
||||||
typedef testing::Test LineReaderTest; |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LineReaderTest, EmptyFile) { |
|
||||||
const int fd = TemporaryFile(); |
|
||||||
LineReader reader(fd); |
|
||||||
|
|
||||||
const char *line; |
|
||||||
unsigned len; |
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len)); |
|
||||||
|
|
||||||
close(fd); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LineReaderTest, OneLineTerminated) { |
|
||||||
const int fd = TemporaryFile(); |
|
||||||
write(fd, "a\n", 2); |
|
||||||
lseek(fd, 0, SEEK_SET); |
|
||||||
LineReader reader(fd); |
|
||||||
|
|
||||||
const char *line; |
|
||||||
unsigned int len; |
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len)); |
|
||||||
ASSERT_EQ(len, (unsigned int)1); |
|
||||||
ASSERT_EQ(line[0], 'a'); |
|
||||||
ASSERT_EQ(line[1], 0); |
|
||||||
reader.PopLine(len); |
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len)); |
|
||||||
|
|
||||||
close(fd); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LineReaderTest, OneLine) { |
|
||||||
const int fd = TemporaryFile(); |
|
||||||
write(fd, "a", 1); |
|
||||||
lseek(fd, 0, SEEK_SET); |
|
||||||
LineReader reader(fd); |
|
||||||
|
|
||||||
const char *line; |
|
||||||
unsigned len; |
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len)); |
|
||||||
ASSERT_EQ(len, (unsigned)1); |
|
||||||
ASSERT_EQ(line[0], 'a'); |
|
||||||
ASSERT_EQ(line[1], 0); |
|
||||||
reader.PopLine(len); |
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len)); |
|
||||||
|
|
||||||
close(fd); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LineReaderTest, TwoLinesTerminated) { |
|
||||||
const int fd = TemporaryFile(); |
|
||||||
write(fd, "a\nb\n", 4); |
|
||||||
lseek(fd, 0, SEEK_SET); |
|
||||||
LineReader reader(fd); |
|
||||||
|
|
||||||
const char *line; |
|
||||||
unsigned len; |
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len)); |
|
||||||
ASSERT_EQ(len, (unsigned)1); |
|
||||||
ASSERT_EQ(line[0], 'a'); |
|
||||||
ASSERT_EQ(line[1], 0); |
|
||||||
reader.PopLine(len); |
|
||||||
|
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len)); |
|
||||||
ASSERT_EQ(len, (unsigned)1); |
|
||||||
ASSERT_EQ(line[0], 'b'); |
|
||||||
ASSERT_EQ(line[1], 0); |
|
||||||
reader.PopLine(len); |
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len)); |
|
||||||
|
|
||||||
close(fd); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LineReaderTest, TwoLines) { |
|
||||||
const int fd = TemporaryFile(); |
|
||||||
write(fd, "a\nb", 3); |
|
||||||
lseek(fd, 0, SEEK_SET); |
|
||||||
LineReader reader(fd); |
|
||||||
|
|
||||||
const char *line; |
|
||||||
unsigned len; |
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len)); |
|
||||||
ASSERT_EQ(len, (unsigned)1); |
|
||||||
ASSERT_EQ(line[0], 'a'); |
|
||||||
ASSERT_EQ(line[1], 0); |
|
||||||
reader.PopLine(len); |
|
||||||
|
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len)); |
|
||||||
ASSERT_EQ(len, (unsigned)1); |
|
||||||
ASSERT_EQ(line[0], 'b'); |
|
||||||
ASSERT_EQ(line[1], 0); |
|
||||||
reader.PopLine(len); |
|
||||||
|
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len)); |
|
||||||
|
|
||||||
close(fd); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LineReaderTest, MaxLength) { |
|
||||||
const int fd = TemporaryFile(); |
|
||||||
char l[LineReader::kMaxLineLen - 1]; |
|
||||||
memset(l, 'a', sizeof(l)); |
|
||||||
write(fd, l, sizeof(l)); |
|
||||||
lseek(fd, 0, SEEK_SET); |
|
||||||
LineReader reader(fd); |
|
||||||
|
|
||||||
const char *line; |
|
||||||
unsigned len; |
|
||||||
ASSERT_TRUE(reader.GetNextLine(&line, &len)); |
|
||||||
ASSERT_EQ(len, sizeof(l)); |
|
||||||
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); |
|
||||||
ASSERT_EQ(line[len], 0); |
|
||||||
|
|
||||||
close(fd); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LineReaderTest, TooLong) { |
|
||||||
const int fd = TemporaryFile(); |
|
||||||
char l[LineReader::kMaxLineLen]; |
|
||||||
memset(l, 'a', sizeof(l)); |
|
||||||
write(fd, l, sizeof(l)); |
|
||||||
lseek(fd, 0, SEEK_SET); |
|
||||||
LineReader reader(fd); |
|
||||||
|
|
||||||
const char *line; |
|
||||||
unsigned len; |
|
||||||
ASSERT_FALSE(reader.GetNextLine(&line, &len)); |
|
||||||
|
|
||||||
close(fd); |
|
||||||
} |
|
@ -1,556 +0,0 @@ |
|||||||
// Copyright (c) 2010, 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.
|
|
||||||
|
|
||||||
// This code deals with the mechanics of getting information about a crashed
|
|
||||||
// process. Since this code may run in a compromised address space, the same
|
|
||||||
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
|
|
||||||
// use the alternative allocator.
|
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h" |
|
||||||
|
|
||||||
#include <asm/ptrace.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <errno.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <limits.h> |
|
||||||
#if !defined(__ANDROID__) |
|
||||||
#include <link.h> |
|
||||||
#endif |
|
||||||
#include <stddef.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
#include <sys/ptrace.h> |
|
||||||
#include <sys/wait.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
#include <algorithm> |
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/directory_reader.h" |
|
||||||
#include "client/linux/minidump_writer/line_reader.h" |
|
||||||
#include "common/linux/file_id.h" |
|
||||||
#include "common/linux/linux_libc_support.h" |
|
||||||
#include "third_party/lss/linux_syscall_support.h" |
|
||||||
|
|
||||||
static const char kMappedFileUnsafePrefix[] = "/dev/"; |
|
||||||
static const char kDeletedSuffix[] = " (deleted)"; |
|
||||||
|
|
||||||
// Suspend a thread by attaching to it.
|
|
||||||
static bool SuspendThread(pid_t pid) { |
|
||||||
// This may fail if the thread has just died or debugged.
|
|
||||||
errno = 0; |
|
||||||
if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && |
|
||||||
errno != 0) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
while (sys_waitpid(pid, NULL, __WALL) < 0) { |
|
||||||
if (errno != EINTR) { |
|
||||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
#if defined(__i386) || defined(__x86_64) |
|
||||||
// On x86, the stack pointer is NULL or -1, when executing trusted code in
|
|
||||||
// the seccomp sandbox. Not only does this cause difficulties down the line
|
|
||||||
// when trying to dump the thread's stack, it also results in the minidumps
|
|
||||||
// containing information about the trusted threads. This information is
|
|
||||||
// generally completely meaningless and just pollutes the minidumps.
|
|
||||||
// We thus test the stack pointer and exclude any threads that are part of
|
|
||||||
// the seccomp sandbox's trusted code.
|
|
||||||
user_regs_struct regs; |
|
||||||
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || |
|
||||||
#if defined(__i386) |
|
||||||
!regs.esp |
|
||||||
#elif defined(__x86_64) |
|
||||||
!regs.rsp |
|
||||||
#endif |
|
||||||
) { |
|
||||||
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); |
|
||||||
return false; |
|
||||||
} |
|
||||||
#endif |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// Resume a thread by detaching from it.
|
|
||||||
static bool ResumeThread(pid_t pid) { |
|
||||||
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; |
|
||||||
} |
|
||||||
|
|
||||||
inline static bool IsMappedFileOpenUnsafe( |
|
||||||
const google_breakpad::MappingInfo& mapping) { |
|
||||||
// It is unsafe to attempt to open a mapped file that lives under /dev,
|
|
||||||
// because the semantics of the open may be driver-specific so we'd risk
|
|
||||||
// hanging the crash dumper. And a file in /dev/ almost certainly has no
|
|
||||||
// ELF file identifier anyways.
|
|
||||||
return my_strncmp(mapping.name, |
|
||||||
kMappedFileUnsafePrefix, |
|
||||||
sizeof(kMappedFileUnsafePrefix) - 1) == 0; |
|
||||||
} |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
LinuxDumper::LinuxDumper(int pid) |
|
||||||
: pid_(pid), |
|
||||||
threads_suspended_(false), |
|
||||||
threads_(&allocator_, 8), |
|
||||||
mappings_(&allocator_) { |
|
||||||
} |
|
||||||
|
|
||||||
bool LinuxDumper::Init() { |
|
||||||
return EnumerateThreads(&threads_) && |
|
||||||
EnumerateMappings(&mappings_); |
|
||||||
} |
|
||||||
|
|
||||||
bool LinuxDumper::ThreadsSuspend() { |
|
||||||
if (threads_suspended_) |
|
||||||
return true; |
|
||||||
for (size_t i = 0; i < threads_.size(); ++i) { |
|
||||||
if (!SuspendThread(threads_[i])) { |
|
||||||
// If the thread either disappeared before we could attach to it, or if
|
|
||||||
// it was part of the seccomp sandbox's trusted code, it is OK to
|
|
||||||
// silently drop it from the minidump.
|
|
||||||
memmove(&threads_[i], &threads_[i+1], |
|
||||||
(threads_.size() - i - 1) * sizeof(threads_[i])); |
|
||||||
threads_.resize(threads_.size() - 1); |
|
||||||
--i; |
|
||||||
} |
|
||||||
} |
|
||||||
threads_suspended_ = true; |
|
||||||
return threads_.size() > 0; |
|
||||||
} |
|
||||||
|
|
||||||
bool LinuxDumper::ThreadsResume() { |
|
||||||
if (!threads_suspended_) |
|
||||||
return false; |
|
||||||
bool good = true; |
|
||||||
for (size_t i = 0; i < threads_.size(); ++i) |
|
||||||
good &= ResumeThread(threads_[i]); |
|
||||||
threads_suspended_ = false; |
|
||||||
return good; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { |
|
||||||
assert(path); |
|
||||||
if (!path) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
path[0] = '\0'; |
|
||||||
|
|
||||||
const unsigned pid_len = my_int_len(pid); |
|
||||||
|
|
||||||
assert(node); |
|
||||||
if (!node) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
size_t node_len = my_strlen(node); |
|
||||||
assert(node_len < NAME_MAX); |
|
||||||
if (node_len >= NAME_MAX) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
assert(node_len > 0); |
|
||||||
if (node_len == 0) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
assert(pid > 0); |
|
||||||
if (pid <= 0) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
const size_t total_length = 6 + pid_len + 1 + node_len; |
|
||||||
|
|
||||||
assert(total_length < NAME_MAX); |
|
||||||
if (total_length >= NAME_MAX) { |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
memcpy(path, "/proc/", 6); |
|
||||||
my_itos(path + 6, pid, pid_len); |
|
||||||
memcpy(path + 6 + pid_len, "/", 1); |
|
||||||
memcpy(path + 6 + pid_len + 1, node, node_len); |
|
||||||
path[total_length] = '\0'; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, |
|
||||||
bool member, |
|
||||||
unsigned int mapping_id, |
|
||||||
uint8_t identifier[sizeof(MDGUID)]) |
|
||||||
{ |
|
||||||
assert(!member || mapping_id < mappings_.size()); |
|
||||||
my_memset(identifier, 0, sizeof(MDGUID)); |
|
||||||
if (IsMappedFileOpenUnsafe(mapping)) |
|
||||||
return false; |
|
||||||
|
|
||||||
char filename[NAME_MAX]; |
|
||||||
size_t filename_len = my_strlen(mapping.name); |
|
||||||
assert(filename_len < NAME_MAX); |
|
||||||
if (filename_len >= NAME_MAX) |
|
||||||
return false; |
|
||||||
memcpy(filename, mapping.name, filename_len); |
|
||||||
filename[filename_len] = '\0'; |
|
||||||
bool filename_modified = HandleDeletedFileInMapping(filename); |
|
||||||
|
|
||||||
int fd = sys_open(filename, O_RDONLY, 0); |
|
||||||
if (fd < 0) |
|
||||||
return false; |
|
||||||
struct kernel_stat st; |
|
||||||
if (sys_fstat(fd, &st) != 0) { |
|
||||||
sys_close(fd); |
|
||||||
return false; |
|
||||||
} |
|
||||||
#if defined(__x86_64) |
|
||||||
#define sys_mmap2 sys_mmap |
|
||||||
#endif |
|
||||||
void* base = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
|
||||||
sys_close(fd); |
|
||||||
if (base == MAP_FAILED) |
|
||||||
return false; |
|
||||||
|
|
||||||
bool success = FileID::ElfFileIdentifierFromMappedFile(base, identifier); |
|
||||||
sys_munmap(base, st.st_size); |
|
||||||
if (success && member && filename_modified) { |
|
||||||
mappings_[mapping_id]->name[filename_len - |
|
||||||
sizeof(kDeletedSuffix) + 1] = '\0'; |
|
||||||
} |
|
||||||
|
|
||||||
return success; |
|
||||||
} |
|
||||||
|
|
||||||
void* |
|
||||||
LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { |
|
||||||
char auxv_path[NAME_MAX]; |
|
||||||
BuildProcPath(auxv_path, pid, "auxv"); |
|
||||||
|
|
||||||
// If BuildProcPath errors out due to invalid input, we'll handle it when
|
|
||||||
// we try to sys_open the file.
|
|
||||||
|
|
||||||
// Find the AT_SYSINFO_EHDR entry for linux-gate.so
|
|
||||||
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
|
|
||||||
// information.
|
|
||||||
int fd = sys_open(auxv_path, O_RDONLY, 0); |
|
||||||
if (fd < 0) { |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
elf_aux_entry one_aux_entry; |
|
||||||
while (sys_read(fd, |
|
||||||
&one_aux_entry, |
|
||||||
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && |
|
||||||
one_aux_entry.a_type != AT_NULL) { |
|
||||||
if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { |
|
||||||
close(fd); |
|
||||||
return reinterpret_cast<void*>(one_aux_entry.a_un.a_val); |
|
||||||
} |
|
||||||
} |
|
||||||
close(fd); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
bool |
|
||||||
LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const { |
|
||||||
char maps_path[NAME_MAX]; |
|
||||||
BuildProcPath(maps_path, pid_, "maps"); |
|
||||||
|
|
||||||
// linux_gate_loc is the beginning of the kernel's mapping of
|
|
||||||
// linux-gate.so in the process. It doesn't actually show up in the
|
|
||||||
// maps list as a filename, so we use the aux vector to find it's
|
|
||||||
// load location and special case it's entry when creating the list
|
|
||||||
// of mappings.
|
|
||||||
const void* linux_gate_loc; |
|
||||||
linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); |
|
||||||
|
|
||||||
const int fd = sys_open(maps_path, O_RDONLY, 0); |
|
||||||
if (fd < 0) |
|
||||||
return false; |
|
||||||
LineReader* const line_reader = new(allocator_) LineReader(fd); |
|
||||||
|
|
||||||
const char* line; |
|
||||||
unsigned line_len; |
|
||||||
while (line_reader->GetNextLine(&line, &line_len)) { |
|
||||||
uintptr_t start_addr, end_addr, offset; |
|
||||||
|
|
||||||
const char* i1 = my_read_hex_ptr(&start_addr, line); |
|
||||||
if (*i1 == '-') { |
|
||||||
const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); |
|
||||||
if (*i2 == ' ') { |
|
||||||
const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); |
|
||||||
if (*i3 == ' ') { |
|
||||||
const char* name = NULL; |
|
||||||
// Only copy name if the name is a valid path name, or if
|
|
||||||
// it's the VDSO image.
|
|
||||||
if (((name = my_strchr(line, '/')) == NULL) && |
|
||||||
linux_gate_loc && |
|
||||||
reinterpret_cast<void*>(start_addr) == linux_gate_loc) { |
|
||||||
name = kLinuxGateLibraryName; |
|
||||||
offset = 0; |
|
||||||
} |
|
||||||
// Merge adjacent mappings with the same name into one module,
|
|
||||||
// assuming they're a single library mapped by the dynamic linker
|
|
||||||
if (name && result->size()) { |
|
||||||
MappingInfo* module = (*result)[result->size() - 1]; |
|
||||||
if ((start_addr == module->start_addr + module->size) && |
|
||||||
(my_strlen(name) == my_strlen(module->name)) && |
|
||||||
(my_strncmp(name, module->name, my_strlen(name)) == 0)) { |
|
||||||
module->size = end_addr - module->start_addr; |
|
||||||
line_reader->PopLine(line_len); |
|
||||||
continue; |
|
||||||
} |
|
||||||
} |
|
||||||
MappingInfo* const module = new(allocator_) MappingInfo; |
|
||||||
memset(module, 0, sizeof(MappingInfo)); |
|
||||||
module->start_addr = start_addr; |
|
||||||
module->size = end_addr - start_addr; |
|
||||||
module->offset = offset; |
|
||||||
if (name != NULL) { |
|
||||||
const unsigned l = my_strlen(name); |
|
||||||
if (l < sizeof(module->name)) |
|
||||||
memcpy(module->name, name, l); |
|
||||||
} |
|
||||||
result->push_back(module); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
line_reader->PopLine(line_len); |
|
||||||
} |
|
||||||
|
|
||||||
sys_close(fd); |
|
||||||
|
|
||||||
return result->size() > 0; |
|
||||||
} |
|
||||||
|
|
||||||
// Parse /proc/$pid/task to list all the threads of the process identified by
|
|
||||||
// pid.
|
|
||||||
bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const { |
|
||||||
char task_path[NAME_MAX]; |
|
||||||
BuildProcPath(task_path, pid_, "task"); |
|
||||||
|
|
||||||
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); |
|
||||||
if (fd < 0) |
|
||||||
return false; |
|
||||||
DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); |
|
||||||
|
|
||||||
// The directory may contain duplicate entries which we filter by assuming
|
|
||||||
// that they are consecutive.
|
|
||||||
int last_tid = -1; |
|
||||||
const char* dent_name; |
|
||||||
while (dir_reader->GetNextEntry(&dent_name)) { |
|
||||||
if (my_strcmp(dent_name, ".") && |
|
||||||
my_strcmp(dent_name, "..")) { |
|
||||||
int tid = 0; |
|
||||||
if (my_strtoui(&tid, dent_name) && |
|
||||||
last_tid != tid) { |
|
||||||
last_tid = tid; |
|
||||||
result->push_back(tid); |
|
||||||
} |
|
||||||
} |
|
||||||
dir_reader->PopEntry(); |
|
||||||
} |
|
||||||
|
|
||||||
sys_close(fd); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// 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
|
|
||||||
// available.
|
|
||||||
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { |
|
||||||
assert(info != NULL); |
|
||||||
char status_path[NAME_MAX]; |
|
||||||
BuildProcPath(status_path, tid, "status"); |
|
||||||
|
|
||||||
const int fd = open(status_path, O_RDONLY); |
|
||||||
if (fd < 0) |
|
||||||
return false; |
|
||||||
|
|
||||||
LineReader* const line_reader = new(allocator_) LineReader(fd); |
|
||||||
const char* line; |
|
||||||
unsigned line_len; |
|
||||||
|
|
||||||
info->ppid = info->tgid = -1; |
|
||||||
|
|
||||||
while (line_reader->GetNextLine(&line, &line_len)) { |
|
||||||
if (my_strncmp("Tgid:\t", line, 6) == 0) { |
|
||||||
my_strtoui(&info->tgid, line + 6); |
|
||||||
} else if (my_strncmp("PPid:\t", line, 6) == 0) { |
|
||||||
my_strtoui(&info->ppid, line + 6); |
|
||||||
} |
|
||||||
|
|
||||||
line_reader->PopLine(line_len); |
|
||||||
} |
|
||||||
|
|
||||||
if (info->ppid == -1 || info->tgid == -1) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
#if !defined(__ANDROID__) |
|
||||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#if defined(__i386) |
|
||||||
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) |
|
||||||
return false; |
|
||||||
#endif |
|
||||||
|
|
||||||
#if defined(__i386) || defined(__x86_64) |
|
||||||
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { |
|
||||||
if (sys_ptrace( |
|
||||||
PTRACE_PEEKUSER, tid, |
|
||||||
reinterpret_cast<void*> (offsetof(struct user, |
|
||||||
u_debugreg[0]) + i * |
|
||||||
sizeof(debugreg_t)), |
|
||||||
&info->dregs[i]) == -1) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
const uint8_t* stack_pointer; |
|
||||||
#if defined(__i386) |
|
||||||
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); |
|
||||||
#elif defined(__x86_64) |
|
||||||
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); |
|
||||||
#elif defined(__ARM_EABI__) |
|
||||||
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); |
|
||||||
#else |
|
||||||
#error "This code hasn't been ported to your platform yet." |
|
||||||
#endif |
|
||||||
|
|
||||||
return GetStackInfo(&info->stack, &info->stack_len, |
|
||||||
(uintptr_t) stack_pointer); |
|
||||||
} |
|
||||||
|
|
||||||
// Get information about the stack, given the stack pointer. We don't try to
|
|
||||||
// walk the stack since we might not have all the information needed to do
|
|
||||||
// unwind. So we just grab, up to, 32k of stack.
|
|
||||||
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, |
|
||||||
uintptr_t int_stack_pointer) { |
|
||||||
// Move the stack pointer to the bottom of the page that it's in.
|
|
||||||
const uintptr_t page_size = getpagesize(); |
|
||||||
|
|
||||||
uint8_t* const stack_pointer = |
|
||||||
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); |
|
||||||
|
|
||||||
// The number of bytes of stack which we try to capture.
|
|
||||||
static const ptrdiff_t kStackToCapture = 32 * 1024; |
|
||||||
|
|
||||||
const MappingInfo* mapping = FindMapping(stack_pointer); |
|
||||||
if (!mapping) |
|
||||||
return false; |
|
||||||
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; |
|
||||||
const ptrdiff_t distance_to_end = |
|
||||||
static_cast<ptrdiff_t>(mapping->size) - offset; |
|
||||||
*stack_len = distance_to_end > kStackToCapture ? |
|
||||||
kStackToCapture : distance_to_end; |
|
||||||
*stack = stack_pointer; |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// static
|
|
||||||
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, |
|
||||||
size_t length) { |
|
||||||
unsigned long tmp = 55; |
|
||||||
size_t done = 0; |
|
||||||
static const size_t word_size = sizeof(tmp); |
|
||||||
uint8_t* const local = (uint8_t*) dest; |
|
||||||
uint8_t* const remote = (uint8_t*) src; |
|
||||||
|
|
||||||
while (done < length) { |
|
||||||
const size_t l = length - done > word_size ? word_size : length - done; |
|
||||||
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { |
|
||||||
tmp = 0; |
|
||||||
} |
|
||||||
memcpy(local + done, &tmp, l); |
|
||||||
done += l; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// 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; |
|
||||||
|
|
||||||
for (size_t i = 0; i < mappings_.size(); ++i) { |
|
||||||
const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr); |
|
||||||
if (addr >= start && addr - start < mappings_[i]->size) |
|
||||||
return mappings_[i]; |
|
||||||
} |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { |
|
||||||
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1; |
|
||||||
|
|
||||||
// Check for ' (deleted)' in |path|.
|
|
||||||
// |path| has to be at least as long as "/x (deleted)".
|
|
||||||
const size_t path_len = my_strlen(path); |
|
||||||
if (path_len < kDeletedSuffixLen + 2) |
|
||||||
return false; |
|
||||||
if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, |
|
||||||
kDeletedSuffixLen) != 0) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// Check |path| against the /proc/pid/exe 'symlink'.
|
|
||||||
char exe_link[NAME_MAX]; |
|
||||||
char new_path[NAME_MAX]; |
|
||||||
BuildProcPath(exe_link, pid_, "exe"); |
|
||||||
ssize_t new_path_len = sys_readlink(exe_link, new_path, NAME_MAX); |
|
||||||
if (new_path_len <= 0 || new_path_len == NAME_MAX) |
|
||||||
return false; |
|
||||||
new_path[new_path_len] = '\0'; |
|
||||||
if (my_strcmp(path, new_path) != 0) |
|
||||||
return false; |
|
||||||
|
|
||||||
// Check to see if someone actually named their executable 'foo (deleted)'.
|
|
||||||
struct kernel_stat exe_stat; |
|
||||||
struct kernel_stat new_path_stat; |
|
||||||
if (sys_stat(exe_link, &exe_stat) == 0 && |
|
||||||
sys_stat(new_path, &new_path_stat) == 0 && |
|
||||||
exe_stat.st_dev == new_path_stat.st_dev && |
|
||||||
exe_stat.st_ino == new_path_stat.st_ino) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
memcpy(path, exe_link, NAME_MAX); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,193 +0,0 @@ |
|||||||
// Copyright (c) 2010, 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 CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ |
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ |
|
||||||
|
|
||||||
#include <elf.h> |
|
||||||
#include <linux/limits.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#if !defined(__ANDROID__) |
|
||||||
#include <sys/user.h> |
|
||||||
#endif |
|
||||||
|
|
||||||
#include "common/memory.h" |
|
||||||
#include "google_breakpad/common/minidump_format.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
#if defined(__i386) || defined(__x86_64) |
|
||||||
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; |
|
||||||
#endif |
|
||||||
|
|
||||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
|
||||||
#if defined(__i386) || defined(__ARM_EABI__) |
|
||||||
#if !defined(__ANDROID__) |
|
||||||
typedef Elf32_auxv_t elf_aux_entry; |
|
||||||
#else |
|
||||||
// Android is missing this structure definition
|
|
||||||
typedef struct |
|
||||||
{ |
|
||||||
uint32_t a_type; /* Entry type */ |
|
||||||
union |
|
||||||
{ |
|
||||||
uint32_t a_val; /* Integer value */ |
|
||||||
} a_un; |
|
||||||
} elf_aux_entry; |
|
||||||
|
|
||||||
#if !defined(AT_SYSINFO_EHDR) |
|
||||||
#define AT_SYSINFO_EHDR 33 |
|
||||||
#endif |
|
||||||
#endif // __ANDROID__
|
|
||||||
#elif defined(__x86_64__) |
|
||||||
typedef Elf64_auxv_t elf_aux_entry; |
|
||||||
#endif |
|
||||||
// When we find the VDSO mapping in the process's address space, this
|
|
||||||
// is the name we use for it when writing it to the minidump.
|
|
||||||
// This should always be less than NAME_MAX!
|
|
||||||
const char kLinuxGateLibraryName[] = "linux-gate.so"; |
|
||||||
|
|
||||||
// We produce one of these structures for each thread in the crashed process.
|
|
||||||
struct ThreadInfo { |
|
||||||
pid_t tgid; // thread group id
|
|
||||||
pid_t ppid; // parent process
|
|
||||||
|
|
||||||
// Even on platforms where the stack grows down, the following will point to
|
|
||||||
// the smallest address in the stack.
|
|
||||||
const void* stack; // pointer to the stack area
|
|
||||||
size_t stack_len; // length of the stack to copy
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(__i386) || defined(__x86_64) |
|
||||||
user_regs_struct regs; |
|
||||||
user_fpregs_struct fpregs; |
|
||||||
static const unsigned kNumDebugRegisters = 8; |
|
||||||
debugreg_t dregs[8]; |
|
||||||
#if defined(__i386) |
|
||||||
user_fpxregs_struct fpxregs; |
|
||||||
#endif // defined(__i386)
|
|
||||||
|
|
||||||
#elif defined(__ARM_EABI__) |
|
||||||
// Mimicking how strace does this(see syscall.c, search for GETREGS)
|
|
||||||
#if defined(__ANDROID__) |
|
||||||
struct pt_regs regs; |
|
||||||
#else |
|
||||||
struct user_regs regs; |
|
||||||
struct user_fpregs fpregs; |
|
||||||
#endif // __ANDROID__
|
|
||||||
#endif |
|
||||||
}; |
|
||||||
|
|
||||||
// One of these is produced for each mapping in the process (i.e. line in
|
|
||||||
// /proc/$x/maps).
|
|
||||||
struct MappingInfo { |
|
||||||
uintptr_t start_addr; |
|
||||||
size_t size; |
|
||||||
size_t offset; // offset into the backed file.
|
|
||||||
char name[NAME_MAX]; |
|
||||||
}; |
|
||||||
|
|
||||||
class LinuxDumper { |
|
||||||
public: |
|
||||||
explicit LinuxDumper(pid_t pid); |
|
||||||
|
|
||||||
// Parse the data for |threads| and |mappings|.
|
|
||||||
bool Init(); |
|
||||||
|
|
||||||
// Suspend/resume all threads in the given process.
|
|
||||||
bool ThreadsSuspend(); |
|
||||||
bool ThreadsResume(); |
|
||||||
|
|
||||||
// Read information about the given thread. Returns true on success. One must
|
|
||||||
// have called |ThreadsSuspend| first.
|
|
||||||
bool ThreadInfoGet(pid_t tid, ThreadInfo* info); |
|
||||||
|
|
||||||
// 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 a block of memory to take as the stack given the top of stack pointer.
|
|
||||||
// stack: (output) the lowest address in the memory area
|
|
||||||
// stack_len: (output) the length of the memory area
|
|
||||||
// stack_top: the current top of the stack
|
|
||||||
bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); |
|
||||||
|
|
||||||
PageAllocator* allocator() { return &allocator_; } |
|
||||||
|
|
||||||
// memcpy from a remote process.
|
|
||||||
static void CopyFromProcess(void* dest, pid_t child, const void* src, |
|
||||||
size_t length); |
|
||||||
|
|
||||||
// Builds a proc path for a certain pid for a node. path is a
|
|
||||||
// character array that is overwritten, and node is the final node
|
|
||||||
// without any slashes.
|
|
||||||
void BuildProcPath(char* path, pid_t pid, const char* node) const; |
|
||||||
|
|
||||||
// Generate a File ID from the .text section of a mapped entry.
|
|
||||||
// If not a member, mapping_id is ignored.
|
|
||||||
bool ElfFileIdentifierForMapping(const MappingInfo& mapping, |
|
||||||
bool member, |
|
||||||
unsigned int mapping_id, |
|
||||||
uint8_t identifier[sizeof(MDGUID)]); |
|
||||||
|
|
||||||
// Utility method to find the location of where the kernel has
|
|
||||||
// mapped linux-gate.so in memory(shows up in /proc/pid/maps as
|
|
||||||
// [vdso], but we can't guarantee that it's the only virtual dynamic
|
|
||||||
// shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR
|
|
||||||
// is the safest way to go.)
|
|
||||||
void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const; |
|
||||||
private: |
|
||||||
bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const; |
|
||||||
bool EnumerateThreads(wasteful_vector<pid_t>* result) const; |
|
||||||
|
|
||||||
// For the case where a running program has been deleted, it'll show up in
|
|
||||||
// /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
|
|
||||||
// see if '/path/to/program (deleted)' matches /proc/pid/exe and return
|
|
||||||
// /proc/pid/exe in |path| so ELF identifier generation works correctly. This
|
|
||||||
// also checks to see if '/path/to/program (deleted)' exists, so it does not
|
|
||||||
// get fooled by a poorly named binary.
|
|
||||||
// For programs that don't end with ' (deleted)', this is a no-op.
|
|
||||||
// This assumes |path| is a buffer with length NAME_MAX.
|
|
||||||
// Returns true if |path| is modified.
|
|
||||||
bool HandleDeletedFileInMapping(char* path) const; |
|
||||||
|
|
||||||
const pid_t pid_; |
|
||||||
|
|
||||||
mutable PageAllocator allocator_; |
|
||||||
|
|
||||||
bool threads_suspended_; |
|
||||||
wasteful_vector<pid_t> threads_; // the ids of all the threads
|
|
||||||
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
|
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
|
|
@ -1,354 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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 <string> |
|
||||||
|
|
||||||
#include <fcntl.h> |
|
||||||
#include <limits.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <signal.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <sys/mman.h> |
|
||||||
#include <sys/poll.h> |
|
||||||
#include <sys/stat.h> |
|
||||||
#include <sys/types.h> |
|
||||||
|
|
||||||
#include "breakpad_googletest_includes.h" |
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h" |
|
||||||
#include "common/linux/eintr_wrapper.h" |
|
||||||
#include "common/linux/file_id.h" |
|
||||||
#include "common/memory.h" |
|
||||||
|
|
||||||
using std::string; |
|
||||||
using namespace google_breakpad; |
|
||||||
|
|
||||||
namespace { |
|
||||||
typedef testing::Test LinuxDumperTest; |
|
||||||
|
|
||||||
string GetHelperBinary() { |
|
||||||
// Locate helper binary next to the current binary.
|
|
||||||
char self_path[PATH_MAX]; |
|
||||||
if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) { |
|
||||||
return ""; |
|
||||||
} |
|
||||||
string helper_path(self_path); |
|
||||||
size_t pos = helper_path.rfind('/'); |
|
||||||
if (pos == string::npos) { |
|
||||||
return ""; |
|
||||||
} |
|
||||||
helper_path.erase(pos + 1); |
|
||||||
helper_path += "linux_dumper_unittest_helper"; |
|
||||||
|
|
||||||
return helper_path; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
TEST(LinuxDumperTest, Setup) { |
|
||||||
LinuxDumper dumper(getpid()); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LinuxDumperTest, FindMappings) { |
|
||||||
LinuxDumper dumper(getpid()); |
|
||||||
ASSERT_TRUE(dumper.Init()); |
|
||||||
|
|
||||||
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid))); |
|
||||||
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf))); |
|
||||||
ASSERT_FALSE(dumper.FindMapping(NULL)); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LinuxDumperTest, ThreadList) { |
|
||||||
LinuxDumper dumper(getpid()); |
|
||||||
ASSERT_TRUE(dumper.Init()); |
|
||||||
|
|
||||||
ASSERT_GE(dumper.threads().size(), (size_t)1); |
|
||||||
bool found = false; |
|
||||||
for (size_t i = 0; i < dumper.threads().size(); ++i) { |
|
||||||
if (dumper.threads()[i] == getpid()) { |
|
||||||
found = true; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Helper stack class to close a file descriptor and unmap
|
|
||||||
// a mmap'ed mapping.
|
|
||||||
class StackHelper { |
|
||||||
public: |
|
||||||
StackHelper(int fd, char* mapping, size_t size) |
|
||||||
: fd_(fd), mapping_(mapping), size_(size) {} |
|
||||||
~StackHelper() { |
|
||||||
munmap(mapping_, size_); |
|
||||||
close(fd_); |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
int fd_; |
|
||||||
char* mapping_; |
|
||||||
size_t size_; |
|
||||||
}; |
|
||||||
|
|
||||||
TEST(LinuxDumperTest, MergedMappings) { |
|
||||||
string helper_path(GetHelperBinary()); |
|
||||||
if (helper_path.empty()) { |
|
||||||
FAIL() << "Couldn't find helper binary"; |
|
||||||
exit(1); |
|
||||||
} |
|
||||||
|
|
||||||
// mmap two segments out of the helper binary, one
|
|
||||||
// enclosed in the other, but with different protections.
|
|
||||||
const size_t kPageSize = sysconf(_SC_PAGESIZE); |
|
||||||
const size_t kMappingSize = 3 * kPageSize; |
|
||||||
int fd = open(helper_path.c_str(), O_RDONLY); |
|
||||||
ASSERT_NE(-1, fd); |
|
||||||
char* mapping = |
|
||||||
reinterpret_cast<char*>(mmap(NULL, |
|
||||||
kMappingSize, |
|
||||||
PROT_READ, |
|
||||||
MAP_SHARED, |
|
||||||
fd, |
|
||||||
0)); |
|
||||||
ASSERT_TRUE(mapping); |
|
||||||
|
|
||||||
const u_int64_t kMappingAddress = reinterpret_cast<u_int64_t>(mapping); |
|
||||||
|
|
||||||
// Ensure that things get cleaned up.
|
|
||||||
StackHelper helper(fd, mapping, kMappingSize); |
|
||||||
|
|
||||||
// Carve a page out of the first mapping with different permissions.
|
|
||||||
char* inside_mapping = reinterpret_cast<char*>(mmap(mapping + 2 *kPageSize, |
|
||||||
kPageSize, |
|
||||||
PROT_NONE, |
|
||||||
MAP_SHARED | MAP_FIXED, |
|
||||||
fd, |
|
||||||
// Map a different offset just to
|
|
||||||
// better test real-world conditions.
|
|
||||||
kPageSize)); |
|
||||||
ASSERT_TRUE(inside_mapping); |
|
||||||
|
|
||||||
// Now check that LinuxDumper interpreted the mappings properly.
|
|
||||||
LinuxDumper dumper(getpid()); |
|
||||||
ASSERT_TRUE(dumper.Init()); |
|
||||||
int mapping_count = 0; |
|
||||||
for (unsigned i = 0; i < dumper.mappings().size(); ++i) { |
|
||||||
const MappingInfo& mapping = *dumper.mappings()[i]; |
|
||||||
if (strcmp(mapping.name, helper_path.c_str()) == 0) { |
|
||||||
// This mapping should encompass the entire original mapped
|
|
||||||
// range.
|
|
||||||
EXPECT_EQ(kMappingAddress, mapping.start_addr); |
|
||||||
EXPECT_EQ(kMappingSize, mapping.size); |
|
||||||
EXPECT_EQ(0, mapping.offset); |
|
||||||
mapping_count++; |
|
||||||
} |
|
||||||
} |
|
||||||
EXPECT_EQ(1, mapping_count); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LinuxDumperTest, 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 the child process to signal that it's ready.
|
|
||||||
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; |
|
||||||
read(fds[0], &junk, sizeof(junk)); |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
// Child is ready now.
|
|
||||||
LinuxDumper dumper(child_pid); |
|
||||||
ASSERT_TRUE(dumper.Init()); |
|
||||||
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size()); |
|
||||||
EXPECT_TRUE(dumper.ThreadsSuspend()); |
|
||||||
|
|
||||||
ThreadInfo one_thread; |
|
||||||
for(size_t i = 0; i < dumper.threads().size(); ++i) { |
|
||||||
EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread)); |
|
||||||
// In the helper program, we stored a pointer to the thread id in a
|
|
||||||
// specific register. Check that we can recover its value.
|
|
||||||
#if defined(__ARM_EABI__) |
|
||||||
pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]); |
|
||||||
#elif defined(__i386) |
|
||||||
pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx); |
|
||||||
#elif defined(__x86_64) |
|
||||||
pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx); |
|
||||||
#else |
|
||||||
#error This test has not been ported to this platform. |
|
||||||
#endif |
|
||||||
pid_t one_thread_id; |
|
||||||
dumper.CopyFromProcess(&one_thread_id, |
|
||||||
dumper.threads()[i], |
|
||||||
process_tid_location, |
|
||||||
4); |
|
||||||
EXPECT_EQ(dumper.threads()[i], one_thread_id); |
|
||||||
} |
|
||||||
kill(child_pid, SIGKILL); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(LinuxDumperTest, BuildProcPath) { |
|
||||||
const pid_t pid = getpid(); |
|
||||||
LinuxDumper dumper(pid); |
|
||||||
|
|
||||||
char maps_path[256] = "dummymappath"; |
|
||||||
char maps_path_expected[256]; |
|
||||||
snprintf(maps_path_expected, sizeof(maps_path_expected), |
|
||||||
"/proc/%d/maps", pid); |
|
||||||
dumper.BuildProcPath(maps_path, pid, "maps"); |
|
||||||
ASSERT_STREQ(maps_path, maps_path_expected); |
|
||||||
|
|
||||||
// In release mode, we expect BuildProcPath to handle the invalid
|
|
||||||
// parameters correctly and fill map_path with an empty
|
|
||||||
// NULL-terminated string.
|
|
||||||
#ifdef NDEBUG |
|
||||||
snprintf(maps_path, sizeof(maps_path), "dummymappath"); |
|
||||||
dumper.BuildProcPath(maps_path, 0, "maps"); |
|
||||||
EXPECT_STREQ(maps_path, ""); |
|
||||||
|
|
||||||
snprintf(maps_path, sizeof(maps_path), "dummymappath"); |
|
||||||
dumper.BuildProcPath(maps_path, getpid(), ""); |
|
||||||
EXPECT_STREQ(maps_path, ""); |
|
||||||
|
|
||||||
snprintf(maps_path, sizeof(maps_path), "dummymappath"); |
|
||||||
dumper.BuildProcPath(maps_path, getpid(), NULL); |
|
||||||
EXPECT_STREQ(maps_path, ""); |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
#if !defined(__ARM_EABI__) |
|
||||||
TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { |
|
||||||
LinuxDumper dumper(getpid()); |
|
||||||
ASSERT_TRUE(dumper.Init()); |
|
||||||
|
|
||||||
void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); |
|
||||||
ASSERT_TRUE(linux_gate_loc); |
|
||||||
bool found_linux_gate = false; |
|
||||||
|
|
||||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); |
|
||||||
const MappingInfo* mapping; |
|
||||||
for (unsigned i = 0; i < mappings.size(); ++i) { |
|
||||||
mapping = mappings[i]; |
|
||||||
if (!strcmp(mapping->name, kLinuxGateLibraryName)) { |
|
||||||
found_linux_gate = true; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
EXPECT_TRUE(found_linux_gate); |
|
||||||
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr)); |
|
||||||
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
TEST(LinuxDumperTest, FileIDsMatch) { |
|
||||||
// Calculate the File ID of our binary using both
|
|
||||||
// FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
|
|
||||||
// and ensure that we get the same result from both.
|
|
||||||
char exe_name[PATH_MAX]; |
|
||||||
ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1); |
|
||||||
ASSERT_NE(len, -1); |
|
||||||
exe_name[len] = '\0'; |
|
||||||
|
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(-1, pipe(fds)); |
|
||||||
|
|
||||||
// fork a child so we can ptrace it
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[1]); |
|
||||||
// now wait forever for the parent
|
|
||||||
char b; |
|
||||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b))); |
|
||||||
close(fds[0]); |
|
||||||
syscall(__NR_exit); |
|
||||||
} |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
LinuxDumper dumper(child); |
|
||||||
ASSERT_TRUE(dumper.Init()); |
|
||||||
const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); |
|
||||||
bool found_exe = false; |
|
||||||
unsigned i; |
|
||||||
for (i = 0; i < mappings.size(); ++i) { |
|
||||||
const MappingInfo* mapping = mappings[i]; |
|
||||||
if (!strcmp(mapping->name, exe_name)) { |
|
||||||
found_exe = true; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
ASSERT_TRUE(found_exe); |
|
||||||
|
|
||||||
uint8_t identifier1[sizeof(MDGUID)]; |
|
||||||
uint8_t identifier2[sizeof(MDGUID)]; |
|
||||||
EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i, |
|
||||||
identifier1)); |
|
||||||
FileID fileid(exe_name); |
|
||||||
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2)); |
|
||||||
char identifier_string1[37]; |
|
||||||
char identifier_string2[37]; |
|
||||||
FileID::ConvertIdentifierToString(identifier1, identifier_string1, |
|
||||||
37); |
|
||||||
FileID::ConvertIdentifierToString(identifier2, identifier_string2, |
|
||||||
37); |
|
||||||
EXPECT_STREQ(identifier_string1, identifier_string2); |
|
||||||
close(fds[1]); |
|
||||||
} |
|
@ -1,85 +0,0 @@ |
|||||||
// Copyright (c) 2010, 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.
|
|
||||||
//
|
|
||||||
// Helper program for the linux_dumper class, which creates a bunch of
|
|
||||||
// threads. The first word of each thread's stack is set to the thread
|
|
||||||
// id.
|
|
||||||
|
|
||||||
#include <pthread.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <sys/syscall.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
#include "third_party/lss/linux_syscall_support.h" |
|
||||||
|
|
||||||
#if defined(__ARM_EABI__) |
|
||||||
#define TID_PTR_REGISTER "r3" |
|
||||||
#elif defined(__i386) |
|
||||||
#define TID_PTR_REGISTER "ecx" |
|
||||||
#elif defined(__x86_64) |
|
||||||
#define TID_PTR_REGISTER "rcx" |
|
||||||
#else |
|
||||||
#error This test has not been ported to this platform. |
|
||||||
#endif |
|
||||||
|
|
||||||
void *thread_function(void *data) { |
|
||||||
volatile pid_t thread_id = syscall(__NR_gettid); |
|
||||||
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id; |
|
||||||
while (true) |
|
||||||
asm volatile ("" : : "r" (thread_id_ptr)); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
int main(int argc, char *argv[]) { |
|
||||||
if (argc < 2) { |
|
||||||
fprintf(stderr, |
|
||||||
"usage: linux_dumper_unittest_helper <pipe fd> <# of threads\n"); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
int pipefd = atoi(argv[1]); |
|
||||||
int num_threads = atoi(argv[2]); |
|
||||||
if (num_threads < 1) { |
|
||||||
fprintf(stderr, "ERROR: number of threads is 0"); |
|
||||||
return 1; |
|
||||||
} |
|
||||||
pthread_t threads[num_threads]; |
|
||||||
pthread_attr_t thread_attributes; |
|
||||||
pthread_attr_init(&thread_attributes); |
|
||||||
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED); |
|
||||||
for (int i = 1; i < num_threads; i++) { |
|
||||||
pthread_create(&threads[i], &thread_attributes, &thread_function, NULL); |
|
||||||
} |
|
||||||
// Signal parent that this process has started all threads.
|
|
||||||
uint8_t byte = 1; |
|
||||||
write(pipefd, &byte, sizeof(byte)); |
|
||||||
thread_function(NULL); |
|
||||||
return 0; |
|
||||||
} |
|
@ -1,75 +0,0 @@ |
|||||||
/* Copyright (c) 2010, 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. */ |
|
||||||
|
|
||||||
/* minidump_extension_linux.h: A definition of exception codes for
|
|
||||||
* Linux |
|
||||||
* |
|
||||||
* (This is C99 source, please don't corrupt it with C++.) |
|
||||||
* |
|
||||||
* Author: Adam Langley |
|
||||||
* Split into its own file: Markus Gutschke */ |
|
||||||
|
|
||||||
|
|
||||||
#ifndef SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_ |
|
||||||
#define SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_ |
|
||||||
|
|
||||||
#include <stddef.h> |
|
||||||
|
|
||||||
#include "google_breakpad/common/breakpad_types.h" |
|
||||||
#include "google_breakpad/common/minidump_format.h" |
|
||||||
|
|
||||||
// These are additional minidump stream values which are specific to the linux
|
|
||||||
// breakpad implementation.
|
|
||||||
enum { |
|
||||||
MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */ |
|
||||||
MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */ |
|
||||||
MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */ |
|
||||||
MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */ |
|
||||||
MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */ |
|
||||||
MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */ |
|
||||||
MD_LINUX_MAPS = 0x47670009, /* /proc/$x/maps */ |
|
||||||
MD_LINUX_DSO_DEBUG = 0x4767000A /* DSO data */ |
|
||||||
}; |
|
||||||
|
|
||||||
typedef struct { |
|
||||||
void* addr; |
|
||||||
MDRVA name; |
|
||||||
void* ld; |
|
||||||
} MDRawLinkMap; |
|
||||||
|
|
||||||
typedef struct { |
|
||||||
u_int32_t version; |
|
||||||
MDRVA map; |
|
||||||
u_int32_t dso_count; |
|
||||||
void* brk; |
|
||||||
void* ldbase; |
|
||||||
void* dynamic; |
|
||||||
} MDRawDebug; |
|
||||||
|
|
||||||
#endif // SRC_CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_EXTENSION_LINUX_H_
|
|
File diff suppressed because it is too large
Load Diff
@ -1,67 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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 CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ |
|
||||||
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
#include <list> |
|
||||||
#include <utility> |
|
||||||
|
|
||||||
#include "google_breakpad/common/minidump_format.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
// A list of <MappingInfo, GUID>
|
|
||||||
typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry; |
|
||||||
typedef std::list<MappingEntry> MappingList; |
|
||||||
|
|
||||||
// Write a minidump to the filesystem. This function does not malloc nor use
|
|
||||||
// libc functions which may. Thus, it can be used in contexts where the state
|
|
||||||
// of the heap may be corrupt.
|
|
||||||
// filename: the filename to write to. This is opened O_EXCL and fails if
|
|
||||||
// open fails.
|
|
||||||
// crashing_process: the pid of the crashing process. This must be trusted.
|
|
||||||
// blob: a blob of data from the crashing process. See exception_handler.h
|
|
||||||
// blob_size: the length of |blob|, in bytes
|
|
||||||
//
|
|
||||||
// Returns true iff successful.
|
|
||||||
bool WriteMinidump(const char* filename, pid_t crashing_process, |
|
||||||
const void* blob, size_t blob_size); |
|
||||||
|
|
||||||
// This overload also allows passing a list of known mappings.
|
|
||||||
bool WriteMinidump(const char* filename, pid_t crashing_process, |
|
||||||
const void* blob, size_t blob_size, |
|
||||||
const MappingList& mappings); |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
|
|
@ -1,396 +0,0 @@ |
|||||||
// 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.
|
|
||||||
|
|
||||||
#include <fcntl.h> |
|
||||||
#include <sys/poll.h> |
|
||||||
#include <sys/stat.h> |
|
||||||
#include <sys/syscall.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include "breakpad_googletest_includes.h" |
|
||||||
#include "client/linux/handler/exception_handler.h" |
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h" |
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h" |
|
||||||
#include "common/linux/eintr_wrapper.h" |
|
||||||
#include "common/linux/file_id.h" |
|
||||||
#include "google_breakpad/processor/minidump.h" |
|
||||||
|
|
||||||
using namespace google_breakpad; |
|
||||||
|
|
||||||
#if !defined(__ANDROID__) |
|
||||||
#define TEMPDIR "/tmp" |
|
||||||
#else |
|
||||||
#define TEMPDIR "/data/local/tmp" |
|
||||||
#endif |
|
||||||
|
|
||||||
// Length of a formatted GUID string =
|
|
||||||
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
|
|
||||||
const int kGUIDStringSize = 37; |
|
||||||
|
|
||||||
namespace { |
|
||||||
typedef testing::Test MinidumpWriterTest; |
|
||||||
} |
|
||||||
|
|
||||||
TEST(MinidumpWriterTest, Setup) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(-1, pipe(fds)); |
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[1]); |
|
||||||
char b; |
|
||||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b))); |
|
||||||
close(fds[0]); |
|
||||||
syscall(__NR_exit); |
|
||||||
} |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
ExceptionHandler::CrashContext context; |
|
||||||
memset(&context, 0, sizeof(context)); |
|
||||||
|
|
||||||
char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX"; |
|
||||||
mktemp(templ); |
|
||||||
// Set a non-zero tid to avoid tripping asserts.
|
|
||||||
context.tid = 1; |
|
||||||
ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context))); |
|
||||||
struct stat st; |
|
||||||
ASSERT_EQ(stat(templ, &st), 0); |
|
||||||
ASSERT_GT(st.st_size, 0u); |
|
||||||
unlink(templ); |
|
||||||
|
|
||||||
close(fds[1]); |
|
||||||
} |
|
||||||
|
|
||||||
// Test that mapping info can be specified when writing a minidump,
|
|
||||||
// and that it ends up in the module list of the minidump.
|
|
||||||
TEST(MinidumpWriterTest, MappingInfo) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(-1, pipe(fds)); |
|
||||||
|
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); |
|
||||||
const char* kMemoryName = "a fake module"; |
|
||||||
const u_int8_t kModuleGUID[sizeof(MDGUID)] = { |
|
||||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, |
|
||||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF |
|
||||||
}; |
|
||||||
char module_identifier_buffer[kGUIDStringSize]; |
|
||||||
FileID::ConvertIdentifierToString(kModuleGUID, |
|
||||||
module_identifier_buffer, |
|
||||||
sizeof(module_identifier_buffer)); |
|
||||||
string module_identifier(module_identifier_buffer); |
|
||||||
// Strip out dashes
|
|
||||||
size_t pos; |
|
||||||
while ((pos = module_identifier.find('-')) != string::npos) { |
|
||||||
module_identifier.erase(pos, 1); |
|
||||||
} |
|
||||||
// And append a zero, because module IDs include an "age" field
|
|
||||||
// which is always zero on Linux.
|
|
||||||
module_identifier += "0"; |
|
||||||
|
|
||||||
// Get some memory.
|
|
||||||
char* memory = |
|
||||||
reinterpret_cast<char*>(mmap(NULL, |
|
||||||
kMemorySize, |
|
||||||
PROT_READ | PROT_WRITE, |
|
||||||
MAP_PRIVATE | MAP_ANON, |
|
||||||
-1, |
|
||||||
0)); |
|
||||||
const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory); |
|
||||||
ASSERT_TRUE(memory); |
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[1]); |
|
||||||
char b; |
|
||||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b))); |
|
||||||
close(fds[0]); |
|
||||||
syscall(__NR_exit); |
|
||||||
} |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
ExceptionHandler::CrashContext context; |
|
||||||
memset(&context, 0, sizeof(context)); |
|
||||||
context.tid = 1; |
|
||||||
|
|
||||||
char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX"; |
|
||||||
mktemp(templ); |
|
||||||
|
|
||||||
// Add information about the mapped memory.
|
|
||||||
MappingInfo info; |
|
||||||
info.start_addr = kMemoryAddress; |
|
||||||
info.size = kMemorySize; |
|
||||||
info.offset = 0; |
|
||||||
strcpy(info.name, kMemoryName); |
|
||||||
|
|
||||||
MappingList mappings; |
|
||||||
MappingEntry mapping; |
|
||||||
mapping.first = info; |
|
||||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); |
|
||||||
mappings.push_back(mapping); |
|
||||||
ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context), mappings)); |
|
||||||
|
|
||||||
// Read the minidump. Load the module list, and ensure that
|
|
||||||
// the mmap'ed |memory| is listed with the given module name
|
|
||||||
// and debug ID.
|
|
||||||
Minidump minidump(templ); |
|
||||||
ASSERT_TRUE(minidump.Read()); |
|
||||||
|
|
||||||
MinidumpModuleList* module_list = minidump.GetModuleList(); |
|
||||||
ASSERT_TRUE(module_list); |
|
||||||
const MinidumpModule* module = |
|
||||||
module_list->GetModuleForAddress(kMemoryAddress); |
|
||||||
ASSERT_TRUE(module); |
|
||||||
|
|
||||||
EXPECT_EQ(kMemoryAddress, module->base_address()); |
|
||||||
EXPECT_EQ(kMemorySize, module->size()); |
|
||||||
EXPECT_EQ(kMemoryName, module->code_file()); |
|
||||||
EXPECT_EQ(module_identifier, module->debug_identifier()); |
|
||||||
|
|
||||||
unlink(templ); |
|
||||||
close(fds[1]); |
|
||||||
} |
|
||||||
|
|
||||||
// Test that mapping info can be specified, and that it overrides
|
|
||||||
// existing mappings that are wholly contained within the specified
|
|
||||||
// range.
|
|
||||||
TEST(MinidumpWriterTest, MappingInfoContained) { |
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(-1, pipe(fds)); |
|
||||||
|
|
||||||
// These are defined here so the parent can use them to check the
|
|
||||||
// data from the minidump afterwards.
|
|
||||||
const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE); |
|
||||||
const char* kMemoryName = "a fake module"; |
|
||||||
const u_int8_t kModuleGUID[sizeof(MDGUID)] = { |
|
||||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, |
|
||||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF |
|
||||||
}; |
|
||||||
char module_identifier_buffer[kGUIDStringSize]; |
|
||||||
FileID::ConvertIdentifierToString(kModuleGUID, |
|
||||||
module_identifier_buffer, |
|
||||||
sizeof(module_identifier_buffer)); |
|
||||||
string module_identifier(module_identifier_buffer); |
|
||||||
// Strip out dashes
|
|
||||||
size_t pos; |
|
||||||
while ((pos = module_identifier.find('-')) != string::npos) { |
|
||||||
module_identifier.erase(pos, 1); |
|
||||||
} |
|
||||||
// And append a zero, because module IDs include an "age" field
|
|
||||||
// which is always zero on Linux.
|
|
||||||
module_identifier += "0"; |
|
||||||
|
|
||||||
// mmap a file
|
|
||||||
char tempfile[] = TEMPDIR "/minidump-writer-unittest-temp-XXXXXX"; |
|
||||||
mktemp(tempfile); |
|
||||||
int fd = open(tempfile, O_RDWR | O_CREAT, 0); |
|
||||||
ASSERT_NE(-1, fd); |
|
||||||
unlink(tempfile); |
|
||||||
// fill with zeros
|
|
||||||
char buffer[kMemorySize]; |
|
||||||
memset(buffer, 0, kMemorySize); |
|
||||||
ASSERT_EQ(kMemorySize, write(fd, buffer, kMemorySize)); |
|
||||||
lseek(fd, 0, SEEK_SET); |
|
||||||
|
|
||||||
char* memory = |
|
||||||
reinterpret_cast<char*>(mmap(NULL, |
|
||||||
kMemorySize, |
|
||||||
PROT_READ | PROT_WRITE, |
|
||||||
MAP_PRIVATE, |
|
||||||
fd, |
|
||||||
0)); |
|
||||||
const u_int64_t kMemoryAddress = reinterpret_cast<u_int64_t>(memory); |
|
||||||
ASSERT_TRUE(memory); |
|
||||||
close(fd); |
|
||||||
|
|
||||||
const pid_t child = fork(); |
|
||||||
if (child == 0) { |
|
||||||
close(fds[1]); |
|
||||||
char b; |
|
||||||
HANDLE_EINTR(read(fds[0], &b, sizeof(b))); |
|
||||||
close(fds[0]); |
|
||||||
syscall(__NR_exit); |
|
||||||
} |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
ExceptionHandler::CrashContext context; |
|
||||||
memset(&context, 0, sizeof(context)); |
|
||||||
context.tid = 1; |
|
||||||
|
|
||||||
char dumpfile[] = TEMPDIR "/minidump-writer-unittest-XXXXXX"; |
|
||||||
mktemp(dumpfile); |
|
||||||
|
|
||||||
// Add information about the mapped memory. Report it as being larger than
|
|
||||||
// it actually is.
|
|
||||||
MappingInfo info; |
|
||||||
info.start_addr = kMemoryAddress - kMemorySize; |
|
||||||
info.size = kMemorySize * 3; |
|
||||||
info.offset = 0; |
|
||||||
strcpy(info.name, kMemoryName); |
|
||||||
|
|
||||||
MappingList mappings; |
|
||||||
MappingEntry mapping; |
|
||||||
mapping.first = info; |
|
||||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); |
|
||||||
mappings.push_back(mapping); |
|
||||||
ASSERT_TRUE( |
|
||||||
WriteMinidump(dumpfile, child, &context, sizeof(context), mappings)); |
|
||||||
|
|
||||||
// Read the minidump. Load the module list, and ensure that
|
|
||||||
// the mmap'ed |memory| is listed with the given module name
|
|
||||||
// and debug ID.
|
|
||||||
Minidump minidump(dumpfile); |
|
||||||
ASSERT_TRUE(minidump.Read()); |
|
||||||
|
|
||||||
MinidumpModuleList* module_list = minidump.GetModuleList(); |
|
||||||
ASSERT_TRUE(module_list); |
|
||||||
const MinidumpModule* module = |
|
||||||
module_list->GetModuleForAddress(kMemoryAddress); |
|
||||||
ASSERT_TRUE(module); |
|
||||||
|
|
||||||
EXPECT_EQ(info.start_addr, module->base_address()); |
|
||||||
EXPECT_EQ(info.size, module->size()); |
|
||||||
EXPECT_EQ(kMemoryName, module->code_file()); |
|
||||||
EXPECT_EQ(module_identifier, module->debug_identifier()); |
|
||||||
|
|
||||||
unlink(dumpfile); |
|
||||||
close(fds[1]); |
|
||||||
} |
|
||||||
|
|
||||||
TEST(MinidumpWriterTest, DeletedBinary) { |
|
||||||
static const int kNumberOfThreadsInHelperProgram = 1; |
|
||||||
char kNumberOfThreadsArgument[2]; |
|
||||||
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); |
|
||||||
|
|
||||||
// Locate helper binary next to the current binary.
|
|
||||||
char self_path[PATH_MAX]; |
|
||||||
if (readlink("/proc/self/exe", self_path, sizeof(self_path) - 1) == -1) { |
|
||||||
FAIL() << "readlink failed: " << strerror(errno); |
|
||||||
exit(1); |
|
||||||
} |
|
||||||
string helper_path(self_path); |
|
||||||
size_t pos = helper_path.rfind('/'); |
|
||||||
if (pos == string::npos) { |
|
||||||
FAIL() << "no trailing slash in path: " << helper_path; |
|
||||||
exit(1); |
|
||||||
} |
|
||||||
helper_path.erase(pos + 1); |
|
||||||
helper_path += "linux_dumper_unittest_helper"; |
|
||||||
|
|
||||||
// Copy binary to a temp file.
|
|
||||||
char binpath[] = TEMPDIR "/linux-dumper-unittest-helper-XXXXXX"; |
|
||||||
mktemp(binpath); |
|
||||||
char cmdline[2 * PATH_MAX]; |
|
||||||
sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(), binpath); |
|
||||||
ASSERT_EQ(0, system(cmdline)); |
|
||||||
ASSERT_EQ(0, chmod(binpath, 0755)); |
|
||||||
|
|
||||||
int fds[2]; |
|
||||||
ASSERT_NE(-1, pipe(fds)); |
|
||||||
|
|
||||||
pid_t child_pid = fork(); |
|
||||||
if (child_pid == 0) { |
|
||||||
// In child process.
|
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
// Pass the pipe fd and the number of threads as arguments.
|
|
||||||
char pipe_fd_string[8]; |
|
||||||
sprintf(pipe_fd_string, "%d", fds[1]); |
|
||||||
execl(binpath, |
|
||||||
binpath, |
|
||||||
pipe_fd_string, |
|
||||||
kNumberOfThreadsArgument, |
|
||||||
NULL); |
|
||||||
} |
|
||||||
close(fds[1]); |
|
||||||
// Wait for the child process to signal that it's ready.
|
|
||||||
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; |
|
||||||
read(fds[0], &junk, sizeof(junk)); |
|
||||||
close(fds[0]); |
|
||||||
|
|
||||||
// Child is ready now.
|
|
||||||
// Unlink the test binary.
|
|
||||||
unlink(binpath); |
|
||||||
|
|
||||||
ExceptionHandler::CrashContext context; |
|
||||||
memset(&context, 0, sizeof(context)); |
|
||||||
|
|
||||||
char templ[] = TEMPDIR "/minidump-writer-unittest-XXXXXX"; |
|
||||||
mktemp(templ); |
|
||||||
// Set a non-zero tid to avoid tripping asserts.
|
|
||||||
context.tid = 1; |
|
||||||
ASSERT_TRUE(WriteMinidump(templ, child_pid, &context, sizeof(context))); |
|
||||||
kill(child_pid, SIGKILL); |
|
||||||
|
|
||||||
struct stat st; |
|
||||||
ASSERT_EQ(stat(templ, &st), 0); |
|
||||||
ASSERT_GT(st.st_size, 0u); |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Minidump minidump(templ); |
|
||||||
ASSERT_TRUE(minidump.Read()); |
|
||||||
|
|
||||||
// Check that the main module filename is correct.
|
|
||||||
MinidumpModuleList* module_list = minidump.GetModuleList(); |
|
||||||
ASSERT_TRUE(module_list); |
|
||||||
const MinidumpModule* module = module_list->GetMainModule(); |
|
||||||
EXPECT_STREQ(binpath, module->code_file().c_str()); |
|
||||||
// Check that the file ID is correct.
|
|
||||||
FileID fileid(helper_path.c_str()); |
|
||||||
uint8_t identifier[sizeof(MDGUID)]; |
|
||||||
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier)); |
|
||||||
char identifier_string[kGUIDStringSize]; |
|
||||||
FileID::ConvertIdentifierToString(identifier, |
|
||||||
identifier_string, |
|
||||||
kGUIDStringSize); |
|
||||||
string module_identifier(identifier_string); |
|
||||||
// Strip out dashes
|
|
||||||
while ((pos = module_identifier.find('-')) != string::npos) { |
|
||||||
module_identifier.erase(pos, 1); |
|
||||||
} |
|
||||||
// And append a zero, because module IDs include an "age" field
|
|
||||||
// which is always zero on Linux.
|
|
||||||
module_identifier += "0"; |
|
||||||
EXPECT_EQ(module_identifier, module->debug_identifier()); |
|
||||||
|
|
||||||
unlink(templ); |
|
||||||
} |
|
@ -1,104 +0,0 @@ |
|||||||
// Copyright (c) 2009, 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/linux/google_crashdump_uploader.h" |
|
||||||
#include "third_party/linux/include/gflags/gflags.h" |
|
||||||
#include <string> |
|
||||||
#include <iostream> |
|
||||||
|
|
||||||
using std::string; |
|
||||||
|
|
||||||
DEFINE_string(crash_server, "http://clients2.google.com/cr", |
|
||||||
"The crash server to upload minidumps to."); |
|
||||||
DEFINE_string(product_name, "", |
|
||||||
"The product name that the minidump corresponds to."); |
|
||||||
DEFINE_string(product_version, "", |
|
||||||
"The version of the product that produced the minidump."); |
|
||||||
DEFINE_string(client_id, "", |
|
||||||
"The client GUID"); |
|
||||||
DEFINE_string(minidump_path, "", |
|
||||||
"The path of the minidump file."); |
|
||||||
DEFINE_string(ptime, "", |
|
||||||
"The process uptime in milliseconds."); |
|
||||||
DEFINE_string(ctime, "", |
|
||||||
"The cumulative process uptime in milliseconds."); |
|
||||||
DEFINE_string(email, "", |
|
||||||
"The user's email address."); |
|
||||||
DEFINE_string(comments, "", |
|
||||||
"Extra user comments"); |
|
||||||
DEFINE_string(proxy_host, "", |
|
||||||
"Proxy host"); |
|
||||||
DEFINE_string(proxy_userpasswd, "", |
|
||||||
"Proxy username/password in user:pass format."); |
|
||||||
|
|
||||||
|
|
||||||
bool CheckForRequiredFlagsOrDie() { |
|
||||||
std::string error_text = ""; |
|
||||||
if (FLAGS_product_name.empty()) { |
|
||||||
error_text.append("\nProduct name must be specified."); |
|
||||||
} |
|
||||||
|
|
||||||
if (FLAGS_product_version.empty()) { |
|
||||||
error_text.append("\nProduct version must be specified."); |
|
||||||
} |
|
||||||
|
|
||||||
if (FLAGS_client_id.empty()) { |
|
||||||
error_text.append("\nClient ID must be specified."); |
|
||||||
} |
|
||||||
|
|
||||||
if (FLAGS_minidump_path.empty()) { |
|
||||||
error_text.append("\nMinidump pathname must be specified."); |
|
||||||
} |
|
||||||
|
|
||||||
if (!error_text.empty()) { |
|
||||||
std::cout << error_text; |
|
||||||
return false; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
int main(int argc, char *argv[]) { |
|
||||||
google::InitGoogleLogging(argv[0]); |
|
||||||
google::ParseCommandLineFlags(&argc, &argv, true); |
|
||||||
if (!CheckForRequiredFlagsOrDie()) { |
|
||||||
return 1; |
|
||||||
} |
|
||||||
google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name, |
|
||||||
FLAGS_product_version, |
|
||||||
FLAGS_client_id, |
|
||||||
FLAGS_ptime, |
|
||||||
FLAGS_ctime, |
|
||||||
FLAGS_email, |
|
||||||
FLAGS_comments, |
|
||||||
FLAGS_minidump_path, |
|
||||||
FLAGS_crash_server, |
|
||||||
FLAGS_proxy_host, |
|
||||||
FLAGS_proxy_userpasswd); |
|
||||||
g.Upload(); |
|
||||||
} |
|
@ -1,316 +0,0 @@ |
|||||||
// Copyright (c) 2006, 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. Prompt the user (using CFUserNotification)
|
|
||||||
// 3. Invoke a command line reporting tool to send the minidump to a
|
|
||||||
// server
|
|
||||||
//
|
|
||||||
// By specifying parameters to the BreakpadCreate function, you can
|
|
||||||
// modify the default behavior to suit your needs and wants and
|
|
||||||
// desires.
|
|
||||||
|
|
||||||
typedef void *BreakpadRef; |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
#include <CoreFoundation/CoreFoundation.h> |
|
||||||
#include <Foundation/Foundation.h> |
|
||||||
|
|
||||||
// Keys for configuration file
|
|
||||||
#define kReporterMinidumpDirectoryKey "MinidumpDir" |
|
||||||
#define kReporterMinidumpIDKey "MinidumpID" |
|
||||||
|
|
||||||
// Filename for recording uploaded IDs
|
|
||||||
#define kReporterLogFilename "uploads.log" |
|
||||||
|
|
||||||
// The default subdirectory of the Library to put crash dumps in
|
|
||||||
// The subdirectory is
|
|
||||||
// ~/Library/<kDefaultLibrarySubdirectory>/<GoogleBreakpadProduct>
|
|
||||||
#define kDefaultLibrarySubdirectory "Breakpad" |
|
||||||
|
|
||||||
// Specify some special keys to be used in the configuration file that is
|
|
||||||
// generated by Breakpad and consumed by the crash_sender.
|
|
||||||
#define BREAKPAD_PRODUCT "BreakpadProduct" |
|
||||||
#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay" |
|
||||||
#define BREAKPAD_VERSION "BreakpadVersion" |
|
||||||
#define BREAKPAD_VENDOR "BreakpadVendor" |
|
||||||
#define BREAKPAD_URL "BreakpadURL" |
|
||||||
#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval" |
|
||||||
#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm" |
|
||||||
#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout" |
|
||||||
#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit" |
|
||||||
#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation" |
|
||||||
#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation" |
|
||||||
#define BREAKPAD_REPORTER_EXE_LOCATION \ |
|
||||||
"BreakpadReporterExeLocation" |
|
||||||
#define BREAKPAD_LOGFILES "BreakpadLogFiles" |
|
||||||
#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize" |
|
||||||
#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments" |
|
||||||
#define BREAKPAD_COMMENTS "BreakpadComments" |
|
||||||
#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail" |
|
||||||
#define BREAKPAD_EMAIL "BreakpadEmail" |
|
||||||
#define BREAKPAD_SERVER_TYPE "BreakpadServerType" |
|
||||||
#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters" |
|
||||||
|
|
||||||
// The keys below are NOT user supplied, and are used internally.
|
|
||||||
#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime" |
|
||||||
#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime" |
|
||||||
#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime" |
|
||||||
#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" |
|
||||||
#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_" |
|
||||||
#define BREAKPAD_ON_DEMAND "BreakpadOnDemand" |
|
||||||
|
|
||||||
// Optional user-defined function to dec 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_REPORT_INTERVAL # of seconds between sending
|
|
||||||
// reports. If an additional report is
|
|
||||||
// generated within this time, it will
|
|
||||||
// be ignored. Default: 3600sec.
|
|
||||||
// Specify 0 to send all reports.
|
|
||||||
//
|
|
||||||
// BREAKPAD_SKIP_CONFIRM If true, the reporter will send the report
|
|
||||||
// without any user intervention.
|
|
||||||
// Defaults to NO
|
|
||||||
//
|
|
||||||
// BREAKPAD_CONFIRM_TIMEOUT Number of seconds before the upload
|
|
||||||
// confirmation dialog will be automatically
|
|
||||||
// dismissed (cancelling the upload).
|
|
||||||
// Default: 300 seconds (min of 60).
|
|
||||||
// Specify 0 to prevent timeout.
|
|
||||||
//
|
|
||||||
// BREAKPAD_SEND_AND_EXIT If true, the handler will exit after sending.
|
|
||||||
// This will prevent any other handler (e.g.,
|
|
||||||
// CrashReporter) from getting the crash.
|
|
||||||
// Defaults TO YES
|
|
||||||
//
|
|
||||||
// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps
|
|
||||||
// in. By default, we use
|
|
||||||
// ~/Library/Breakpad/<BREAKPAD_PRODUCT>
|
|
||||||
// The path you specify here is tilde-expanded.
|
|
||||||
//
|
|
||||||
// BREAKPAD_INSPECTOR_LOCATION The full path to the Inspector executable.
|
|
||||||
// Defaults to <Framework resources>/Inspector
|
|
||||||
//
|
|
||||||
// BREAKPAD_REPORTER_EXE_LOCATION The full path to the Reporter/sender
|
|
||||||
// executable.
|
|
||||||
// Default:
|
|
||||||
// <Framework Resources>/crash_report_sender.app
|
|
||||||
//
|
|
||||||
// BREAKPAD_LOGFILES Indicates an array of log file paths that
|
|
||||||
// should be uploaded at crash time.
|
|
||||||
//
|
|
||||||
// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a
|
|
||||||
// text box for the user to enter comments.
|
|
||||||
// Default: NO
|
|
||||||
//
|
|
||||||
// BREAKPAD_REQUEST_EMAIL If true and BREAKPAD_REQUEST_COMMENTS is also
|
|
||||||
// true, the message dialog will have a text
|
|
||||||
// box for the user to enter their email address.
|
|
||||||
// Default: NO
|
|
||||||
//
|
|
||||||
// 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 BREAKPAD_SKIP_CONFIRM and BREAKPAD_SEND_AND_EXIT default
|
|
||||||
// values are NO and YES. However, they can be controlled by setting their
|
|
||||||
// values in a user or global plist.
|
|
||||||
//
|
|
||||||
// It's easiest to use Breakpad via the Framework, but if you're compiling the
|
|
||||||
// code in directly, BREAKPAD_INSPECTOR_LOCATION and
|
|
||||||
// BREAKPAD_REPORTER_EXE_LOCATION allow you to specify custom paths
|
|
||||||
// to the helper executables.
|
|
||||||
//
|
|
||||||
//=============================================================================
|
|
||||||
// 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_LOGFILE_KEY_PREFIX Used to find out which parameters in the
|
|
||||||
// parameter dictionary correspond to log
|
|
||||||
// file paths.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// BREAKPAD_ON_DEMAND Used internally to indicate to the
|
|
||||||
// Reporter that we're sending on-demand,
|
|
||||||
// not as result of a crash.
|
|
||||||
//
|
|
||||||
// BREAKPAD_COMMENTS The text the user provided as comments.
|
|
||||||
// Only used in crash_report_sender.
|
|
||||||
|
|
||||||
// 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); |
|
||||||
|
|
||||||
// Clients may set an optional callback which gets called when a crash
|
|
||||||
// occurs. The callback function should return |true| if we should
|
|
||||||
// handle the crash, generate a crash report, etc. or |false| if we
|
|
||||||
// should ignore it and forward the crash (normally to CrashReporter).
|
|
||||||
// Context is a pointer to arbitrary data to make the callback with.
|
|
||||||
void BreakpadSetFilterCallback(BreakpadRef ref, |
|
||||||
BreakpadFilterCallback callback, |
|
||||||
void *context); |
|
||||||
|
|
||||||
// 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); |
|
||||||
|
|
||||||
// Add a log file for Breakpad to read and send upon crash dump
|
|
||||||
void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname); |
|
||||||
|
|
||||||
// Generate a minidump and send
|
|
||||||
void BreakpadGenerateAndSendReport(BreakpadRef ref); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
@ -1,996 +0,0 @@ |
|||||||
// Copyright (c) 2006, 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/MachIPC.h" |
|
||||||
#import "common/mac/SimpleStringDictionary.h" |
|
||||||
|
|
||||||
#import "client/mac/crash_generation/Inspector.h" |
|
||||||
#import "client/mac/handler/exception_handler.h" |
|
||||||
#import "client/mac/Framework/Breakpad.h" |
|
||||||
#import "client/mac/Framework/OnDemandServer.h" |
|
||||||
#import "client/mac/handler/protected_memory_allocator.h" |
|
||||||
|
|
||||||
#import <sys/stat.h> |
|
||||||
#import <sys/sysctl.h> |
|
||||||
|
|
||||||
#import <Foundation/Foundation.h> |
|
||||||
|
|
||||||
|
|
||||||
using google_breakpad::KeyValueEntry; |
|
||||||
using google_breakpad::MachPortSender; |
|
||||||
using google_breakpad::MachReceiveMessage; |
|
||||||
using google_breakpad::MachSendMessage; |
|
||||||
using google_breakpad::ReceivePort; |
|
||||||
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); |
|
||||||
|
|
||||||
void GenerateAndSendReport(); |
|
||||||
|
|
||||||
void SetFilterCallback(BreakpadFilterCallback callback, void *context) { |
|
||||||
filter_callback_ = callback; |
|
||||||
filter_callback_context_ = context; |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
Breakpad() |
|
||||||
: handler_(NULL), |
|
||||||
config_params_(NULL), |
|
||||||
send_and_exit_(true), |
|
||||||
filter_callback_(NULL), |
|
||||||
filter_callback_context_(NULL) { |
|
||||||
inspector_path_[0] = 0; |
|
||||||
} |
|
||||||
|
|
||||||
bool Initialize(NSDictionary *parameters); |
|
||||||
|
|
||||||
bool ExtractParameters(NSDictionary *parameters); |
|
||||||
|
|
||||||
// Dispatches to HandleException() |
|
||||||
static bool ExceptionHandlerDirectCallback(void *context, |
|
||||||
int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t crashing_thread); |
|
||||||
|
|
||||||
bool HandleException(int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t crashing_thread); |
|
||||||
|
|
||||||
// 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) |
|
||||||
|
|
||||||
char inspector_path_[PATH_MAX]; // Path to inspector tool |
|
||||||
|
|
||||||
SimpleStringDictionary *config_params_; // Create parameters (STRONG) |
|
||||||
|
|
||||||
OnDemandServer inspector_; |
|
||||||
|
|
||||||
bool send_and_exit_; // Exit after sending, if true |
|
||||||
|
|
||||||
BreakpadFilterCallback filter_callback_; |
|
||||||
void *filter_callback_context_; |
|
||||||
}; |
|
||||||
|
|
||||||
#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::ExceptionHandlerDirectCallback(void *context, |
|
||||||
int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t crashing_thread) { |
|
||||||
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) |
|
||||||
return false; |
|
||||||
|
|
||||||
return breakpad->HandleException( exception_type, |
|
||||||
exception_code, |
|
||||||
exception_subcode, |
|
||||||
crashing_thread); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
#pragma mark - |
|
||||||
|
|
||||||
#include <dlfcn.h> |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
// Returns the pathname to the Resources directory for this version of |
|
||||||
// Breakpad which we are now running. |
|
||||||
// |
|
||||||
// Don't make the function static, since _dyld_lookup_and_bind_fully needs a |
|
||||||
// simple non-static C name |
|
||||||
// |
|
||||||
extern "C" { |
|
||||||
NSString * GetResourcePath(); |
|
||||||
NSString * GetResourcePath() { |
|
||||||
NSString *resourcePath = nil; |
|
||||||
|
|
||||||
// If there are multiple breakpads installed then calling bundleWithIdentifier |
|
||||||
// will not work properly, so only use that as a backup plan. |
|
||||||
// We want to find the bundle containing the code where this function lives |
|
||||||
// and work from there |
|
||||||
// |
|
||||||
|
|
||||||
// Get the pathname to the code which contains this function |
|
||||||
Dl_info info; |
|
||||||
if (dladdr((const void*)GetResourcePath, &info) != 0) { |
|
||||||
NSFileManager *filemgr = [NSFileManager defaultManager]; |
|
||||||
NSString *filePath = |
|
||||||
[filemgr stringWithFileSystemRepresentation:info.dli_fname |
|
||||||
length:strlen(info.dli_fname)]; |
|
||||||
NSString *bundlePath = [filePath stringByDeletingLastPathComponent]; |
|
||||||
// The "Resources" directory should be in the same directory as the |
|
||||||
// executable code, since that's how the Breakpad framework is built. |
|
||||||
resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"]; |
|
||||||
} else { |
|
||||||
DEBUGLOG(stderr, "Could not find GetResourcePath\n"); |
|
||||||
// fallback plan |
|
||||||
NSBundle *bundle = |
|
||||||
[NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"]; |
|
||||||
resourcePath = [bundle resourcePath]; |
|
||||||
} |
|
||||||
|
|
||||||
return resourcePath; |
|
||||||
} |
|
||||||
} // extern "C" |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
bool Breakpad::Initialize(NSDictionary *parameters) { |
|
||||||
// Initialize |
|
||||||
config_params_ = NULL; |
|
||||||
handler_ = NULL; |
|
||||||
|
|
||||||
// Check for debugger |
|
||||||
if (IsDebuggerActive()) { |
|
||||||
DEBUGLOG(stderr, "Debugger is active: Not installing handler\n"); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// Gather any user specified parameters |
|
||||||
if (!ExtractParameters(parameters)) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// Get path to Inspector executable. |
|
||||||
NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION); |
|
||||||
|
|
||||||
// Standardize path (resolve symlinkes, etc.) and escape spaces |
|
||||||
inspectorPathString = [inspectorPathString stringByStandardizingPath]; |
|
||||||
inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "] |
|
||||||
componentsJoinedByString:@"\\ "]; |
|
||||||
|
|
||||||
// Create an on-demand server object representing the Inspector. |
|
||||||
// In case of a crash, we simply need to call the LaunchOnDemand() |
|
||||||
// method on it, then send a mach message to its service port. |
|
||||||
// It will then launch and perform a process inspection of our crashed state. |
|
||||||
// See the HandleException() method for the details. |
|
||||||
#define RECEIVE_PORT_NAME "com.Breakpad.Inspector" |
|
||||||
|
|
||||||
name_t portName; |
|
||||||
snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid()); |
|
||||||
|
|
||||||
// Save the location of the Inspector |
|
||||||
strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation], |
|
||||||
sizeof(inspector_path_)); |
|
||||||
|
|
||||||
// Append a single command-line argument to the Inspector path |
|
||||||
// representing the bootstrap name of the launch-on-demand receive port. |
|
||||||
// When the Inspector is launched, it can use this to lookup the port |
|
||||||
// by calling bootstrap_check_in(). |
|
||||||
strlcat(inspector_path_, " ", sizeof(inspector_path_)); |
|
||||||
strlcat(inspector_path_, portName, sizeof(inspector_path_)); |
|
||||||
|
|
||||||
kern_return_t kr = inspector_.Initialize(inspector_path_, |
|
||||||
portName, |
|
||||||
true); // shutdown on exit |
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// Create the handler (allocating it in our special protected pool) |
|
||||||
handler_ = |
|
||||||
new (gBreakpadAllocator->Allocate( |
|
||||||
sizeof(google_breakpad::ExceptionHandler))) |
|
||||||
google_breakpad::ExceptionHandler( |
|
||||||
Breakpad::ExceptionHandlerDirectCallback, this, true); |
|
||||||
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 *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM]; |
|
||||||
NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT]; |
|
||||||
|
|
||||||
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 *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL]; |
|
||||||
NSString *inspectorPathString = |
|
||||||
[parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION]; |
|
||||||
NSString *reporterPathString = |
|
||||||
[parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION]; |
|
||||||
NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; |
|
||||||
NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; |
|
||||||
NSString *logFileTailSize = |
|
||||||
[parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; |
|
||||||
NSString *requestUserText = |
|
||||||
[parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; |
|
||||||
NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; |
|
||||||
NSString *vendor = |
|
||||||
[parameters objectForKey:@BREAKPAD_VENDOR]; |
|
||||||
NSString *dumpSubdirectory = |
|
||||||
[parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY]; |
|
||||||
|
|
||||||
NSDictionary *serverParameters = |
|
||||||
[parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT]; |
|
||||||
|
|
||||||
// These may have been set above as user prefs, which take priority. |
|
||||||
if (!skipConfirm) { |
|
||||||
skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM]; |
|
||||||
} |
|
||||||
if (!sendAndExit) { |
|
||||||
sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT]; |
|
||||||
} |
|
||||||
|
|
||||||
if (!product) |
|
||||||
product = [parameters objectForKey:@"CFBundleName"]; |
|
||||||
|
|
||||||
if (!display) { |
|
||||||
display = [parameters objectForKey:@"CFBundleDisplayName"]; |
|
||||||
if (!display) { |
|
||||||
display = product; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (!version) |
|
||||||
version = [parameters objectForKey:@"CFBundleVersion"]; |
|
||||||
|
|
||||||
if (!interval) |
|
||||||
interval = @"3600"; |
|
||||||
|
|
||||||
if (!timeout) |
|
||||||
timeout = @"300"; |
|
||||||
|
|
||||||
if (!logFileTailSize) |
|
||||||
logFileTailSize = @"200000"; |
|
||||||
|
|
||||||
if (!vendor) { |
|
||||||
vendor = @"Vendor not specified"; |
|
||||||
} |
|
||||||
|
|
||||||
// Normalize the values. |
|
||||||
if (skipConfirm) { |
|
||||||
skipConfirm = [skipConfirm uppercaseString]; |
|
||||||
|
|
||||||
if ([skipConfirm isEqualToString:@"YES"] || |
|
||||||
[skipConfirm isEqualToString:@"TRUE"] || |
|
||||||
[skipConfirm isEqualToString:@"1"]) |
|
||||||
skipConfirm = @"YES"; |
|
||||||
else |
|
||||||
skipConfirm = @"NO"; |
|
||||||
} else { |
|
||||||
skipConfirm = @"NO"; |
|
||||||
} |
|
||||||
|
|
||||||
send_and_exit_ = true; |
|
||||||
if (sendAndExit) { |
|
||||||
sendAndExit = [sendAndExit uppercaseString]; |
|
||||||
|
|
||||||
if ([sendAndExit isEqualToString:@"NO"] || |
|
||||||
[sendAndExit isEqualToString:@"FALSE"] || |
|
||||||
[sendAndExit isEqualToString:@"0"]) |
|
||||||
send_and_exit_ = false; |
|
||||||
} |
|
||||||
|
|
||||||
if (requestUserText) { |
|
||||||
requestUserText = [requestUserText uppercaseString]; |
|
||||||
|
|
||||||
if ([requestUserText isEqualToString:@"YES"] || |
|
||||||
[requestUserText isEqualToString:@"TRUE"] || |
|
||||||
[requestUserText isEqualToString:@"1"]) |
|
||||||
requestUserText = @"YES"; |
|
||||||
else |
|
||||||
requestUserText = @"NO"; |
|
||||||
} else { |
|
||||||
requestUserText = @"NO"; |
|
||||||
} |
|
||||||
|
|
||||||
// Find the helper applications if not specified in user config. |
|
||||||
NSString *resourcePath = nil; |
|
||||||
if (!inspectorPathString || !reporterPathString) { |
|
||||||
resourcePath = GetResourcePath(); |
|
||||||
if (!resourcePath) { |
|
||||||
DEBUGLOG(stderr, "Could not get resource path\n"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Find Inspector. |
|
||||||
if (!inspectorPathString) { |
|
||||||
inspectorPathString = |
|
||||||
[resourcePath stringByAppendingPathComponent:@"Inspector"]; |
|
||||||
} |
|
||||||
|
|
||||||
// Verify that there is an Inspector tool. |
|
||||||
if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { |
|
||||||
DEBUGLOG(stderr, "Cannot find Inspector tool\n"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// Find Reporter. |
|
||||||
if (!reporterPathString) { |
|
||||||
reporterPathString = |
|
||||||
[resourcePath |
|
||||||
stringByAppendingPathComponent:@"crash_report_sender.app"]; |
|
||||||
reporterPathString = |
|
||||||
[[NSBundle bundleWithPath:reporterPathString] executablePath]; |
|
||||||
} |
|
||||||
|
|
||||||
// Verify that there is a Reporter application. |
|
||||||
if (![[NSFileManager defaultManager] |
|
||||||
fileExistsAtPath:reporterPathString]) { |
|
||||||
DEBUGLOG(stderr, "Cannot find Reporter tool\n"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if (!dumpSubdirectory) { |
|
||||||
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_REPORT_INTERVAL, [interval UTF8String]); |
|
||||||
dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]); |
|
||||||
dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]); |
|
||||||
dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION, |
|
||||||
[inspectorPathString fileSystemRepresentation]); |
|
||||||
dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION, |
|
||||||
[reporterPathString fileSystemRepresentation]); |
|
||||||
dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE, |
|
||||||
[logFileTailSize UTF8String]); |
|
||||||
dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS, |
|
||||||
[requestUserText UTF8String]); |
|
||||||
dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail 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 (logFilePaths) { |
|
||||||
char logFileKey[255]; |
|
||||||
for(unsigned int i = 0; i < [logFilePaths count]; i++) { |
|
||||||
sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i); |
|
||||||
dictionary.SetKeyValue(logFileKey, |
|
||||||
[[logFilePaths objectAtIndex:i] |
|
||||||
fileSystemRepresentation]); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
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]); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
void Breakpad::GenerateAndSendReport() { |
|
||||||
config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); |
|
||||||
HandleException(0, 0, 0, mach_thread_self()); |
|
||||||
config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
bool Breakpad::HandleException(int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t crashing_thread) { |
|
||||||
DEBUGLOG(stderr, "Breakpad: an exception occurred\n"); |
|
||||||
|
|
||||||
if (filter_callback_) { |
|
||||||
bool should_handle = filter_callback_(exception_type, |
|
||||||
exception_code, |
|
||||||
crashing_thread, |
|
||||||
filter_callback_context_); |
|
||||||
if (!should_handle) return false; |
|
||||||
} |
|
||||||
|
|
||||||
// We need to reset the memory protections to be read/write, |
|
||||||
// since LaunchOnDemand() requires changing state. |
|
||||||
gBreakpadAllocator->Unprotect(); |
|
||||||
// Configure the server to launch when we message the service port. |
|
||||||
// The reason we do this here, rather than at startup, is that we |
|
||||||
// can leak a bootstrap service entry if this method is called and |
|
||||||
// there never ends up being a crash. |
|
||||||
inspector_.LaunchOnDemand(); |
|
||||||
gBreakpadAllocator->Protect(); |
|
||||||
|
|
||||||
// The Inspector should send a message to this port to verify it |
|
||||||
// received our information and has finished the inspection. |
|
||||||
ReceivePort acknowledge_port; |
|
||||||
|
|
||||||
// Send initial information to the Inspector. |
|
||||||
MachSendMessage message(kMsgType_InspectorInitialInfo); |
|
||||||
message.AddDescriptor(mach_task_self()); // our task |
|
||||||
message.AddDescriptor(crashing_thread); // crashing thread |
|
||||||
message.AddDescriptor(mach_thread_self()); // exception-handling thread |
|
||||||
message.AddDescriptor(acknowledge_port.GetPort());// message receive port |
|
||||||
|
|
||||||
InspectorInfo info; |
|
||||||
info.exception_type = exception_type; |
|
||||||
info.exception_code = exception_code; |
|
||||||
info.exception_subcode = exception_subcode; |
|
||||||
info.parameter_count = config_params_->GetCount(); |
|
||||||
message.SetData(&info, sizeof(info)); |
|
||||||
|
|
||||||
MachPortSender sender(inspector_.GetServicePort()); |
|
||||||
|
|
||||||
kern_return_t result = sender.SendMessage(message, 2000); |
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
// Now, send a series of key-value pairs to the Inspector. |
|
||||||
const KeyValueEntry *entry = NULL; |
|
||||||
SimpleStringDictionaryIterator iter(*config_params_); |
|
||||||
|
|
||||||
while ( (entry = iter.Next()) ) { |
|
||||||
KeyValueMessageData keyvalue_data(*entry); |
|
||||||
|
|
||||||
MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair); |
|
||||||
keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data)); |
|
||||||
|
|
||||||
result = sender.SendMessage(keyvalue_message, 2000); |
|
||||||
|
|
||||||
if (result != KERN_SUCCESS) { |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
// Wait for acknowledgement that the inspection has finished. |
|
||||||
MachReceiveMessage acknowledge_messsage; |
|
||||||
result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#if VERBOSE |
|
||||||
PRINT_MACH_RESULT(result, "Breakpad: SendMessage "); |
|
||||||
printf("Breakpad: Inspector service port = %#x\n", |
|
||||||
inspector_.GetServicePort()); |
|
||||||
#endif |
|
||||||
|
|
||||||
// If we don't want any forwarding, return true here to indicate that we've |
|
||||||
// processed things as much as we want. |
|
||||||
if (send_and_exit_) return true; |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
//============================================================================= |
|
||||||
|
|
||||||
#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"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
void BreakpadGenerateAndSendReport(BreakpadRef ref) { |
|
||||||
try { |
|
||||||
Breakpad *breakpad = (Breakpad *)ref; |
|
||||||
|
|
||||||
if (breakpad && gKeyValueAllocator) { |
|
||||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); |
|
||||||
|
|
||||||
gBreakpadAllocator->Unprotect(); |
|
||||||
breakpad->GenerateAndSendReport(); |
|
||||||
gBreakpadAllocator->Protect(); |
|
||||||
} |
|
||||||
} catch(...) { // don't let exceptions leave this C API |
|
||||||
fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
void BreakpadSetFilterCallback(BreakpadRef ref, |
|
||||||
BreakpadFilterCallback callback, |
|
||||||
void *context) { |
|
||||||
|
|
||||||
try { |
|
||||||
Breakpad *breakpad = (Breakpad *)ref; |
|
||||||
|
|
||||||
if (breakpad && gBreakpadAllocator) { |
|
||||||
// share the dictionary mutex here (we really don't need a mutex) |
|
||||||
ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator); |
|
||||||
|
|
||||||
breakpad->SetFilterCallback(callback, context); |
|
||||||
} |
|
||||||
} catch(...) { // don't let exceptions leave this C API |
|
||||||
fprintf(stderr, "BreakpadSetFilterCallback() : error\n"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================ |
|
||||||
void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) { |
|
||||||
int logFileCounter = 0; |
|
||||||
|
|
||||||
NSString *logFileKey = [NSString stringWithFormat:@"%@%d", |
|
||||||
@BREAKPAD_LOGFILE_KEY_PREFIX, |
|
||||||
logFileCounter]; |
|
||||||
|
|
||||||
NSString *existingLogFilename = nil; |
|
||||||
existingLogFilename = BreakpadKeyValue(ref, logFileKey); |
|
||||||
// Find the first log file key that we can use by testing for existence |
|
||||||
while (existingLogFilename) { |
|
||||||
if ([existingLogFilename isEqualToString:logPathname]) { |
|
||||||
return; |
|
||||||
} |
|
||||||
logFileCounter++; |
|
||||||
logFileKey = [NSString stringWithFormat:@"%@%d", |
|
||||||
@BREAKPAD_LOGFILE_KEY_PREFIX, |
|
||||||
logFileCounter]; |
|
||||||
existingLogFilename = BreakpadKeyValue(ref, logFileKey); |
|
||||||
} |
|
||||||
|
|
||||||
BreakpadSetKeyValue(ref, logFileKey, logPathname); |
|
||||||
} |
|
@ -1,8 +0,0 @@ |
|||||||
// |
|
||||||
// Prefix header for all source files of the 'Breakpad' target in the |
|
||||||
// 'Breakpad' project. |
|
||||||
// |
|
||||||
|
|
||||||
#ifdef __OBJC__ |
|
||||||
#import <Cocoa/Cocoa.h> |
|
||||||
#endif |
|
@ -1,26 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|
||||||
<plist version="1.0"> |
|
||||||
<dict> |
|
||||||
<key>CFBundleDevelopmentRegion</key> |
|
||||||
<string>English</string> |
|
||||||
<key>CFBundleExecutable</key> |
|
||||||
<string>${EXECUTABLE_NAME}</string> |
|
||||||
<key>CFBundleName</key> |
|
||||||
<string>${PRODUCT_NAME}</string> |
|
||||||
<key>CFBundleIconFile</key> |
|
||||||
<string></string> |
|
||||||
<key>CFBundleIdentifier</key> |
|
||||||
<string>com.googlecode.google-breakpad</string> |
|
||||||
<key>CFBundleInfoDictionaryVersion</key> |
|
||||||
<string>6.0</string> |
|
||||||
<key>CFBundlePackageType</key> |
|
||||||
<string>FMWK</string> |
|
||||||
<key>CFBundleSignature</key> |
|
||||||
<string>????</string> |
|
||||||
<key>CFBundleVersion</key> |
|
||||||
<string>1.0</string> |
|
||||||
<key>NSPrincipalClass</key> |
|
||||||
<string></string> |
|
||||||
</dict> |
|
||||||
</plist> |
|
@ -1,146 +0,0 @@ |
|||||||
// Copyright (c) 2007, 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.
|
|
||||||
|
|
||||||
#import <iostream> |
|
||||||
#import <mach/mach.h> |
|
||||||
#import <servers/bootstrap.h> |
|
||||||
#import <stdio.h> |
|
||||||
#import <stdlib.h> |
|
||||||
#import <sys/stat.h> |
|
||||||
#import <unistd.h> |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// class OnDemandServer :
|
|
||||||
// A basic on-demand server launcher supporting a single named service port
|
|
||||||
//
|
|
||||||
// Example Usage :
|
|
||||||
//
|
|
||||||
// kern_return_t result;
|
|
||||||
// OnDemandServer *server = OnDemandServer::Create("/tmp/myserver",
|
|
||||||
// "com.MyCompany.MyServiceName",
|
|
||||||
// true,
|
|
||||||
// &result);
|
|
||||||
//
|
|
||||||
// if (server) {
|
|
||||||
// server->LaunchOnDemand();
|
|
||||||
// mach_port_t service_port = GetServicePort();
|
|
||||||
//
|
|
||||||
// // Send a mach message to service_port and "myserver" will be launched
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// ---- Now in the server code ----
|
|
||||||
//
|
|
||||||
// // "myserver" should get the service port and read the message which
|
|
||||||
// // launched it:
|
|
||||||
// mach_port_t service_rcv_port_;
|
|
||||||
// kern_return_t kr = bootstrap_check_in(bootstrap_port,
|
|
||||||
// "com.MyCompany.MyServiceName",
|
|
||||||
// &service_rcv_port_);
|
|
||||||
// // mach_msg() read service_rcv_port_ ....
|
|
||||||
//
|
|
||||||
// ....
|
|
||||||
//
|
|
||||||
// // Later "myserver" may want to unregister the service if it doesn't
|
|
||||||
// // want its bootstrap service to stick around after it exits.
|
|
||||||
//
|
|
||||||
// // DO NOT use mach_port_deallocate() here -- it will fail and the
|
|
||||||
// // following bootstrap_register() will also fail leaving our service
|
|
||||||
// // name hanging around forever (until reboot)
|
|
||||||
// kern_return_t kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
|
|
||||||
//
|
|
||||||
// kr = bootstrap_register(bootstrap_port,
|
|
||||||
// "com.MyCompany.MyServiceName",
|
|
||||||
// MACH_PORT_NULL);
|
|
||||||
|
|
||||||
class OnDemandServer { |
|
||||||
public: |
|
||||||
// must call Initialize() to be useful
|
|
||||||
OnDemandServer() |
|
||||||
: server_port_(MACH_PORT_NULL), |
|
||||||
service_port_(MACH_PORT_NULL), |
|
||||||
unregister_on_cleanup_(true) { |
|
||||||
} |
|
||||||
|
|
||||||
// Creates the bootstrap server and service
|
|
||||||
kern_return_t Initialize(const char *server_command, |
|
||||||
const char *service_name, |
|
||||||
bool unregister_on_cleanup); |
|
||||||
|
|
||||||
// Returns an OnDemandServer object if successful, or NULL if there's
|
|
||||||
// an error. The error result will be returned in out_result.
|
|
||||||
//
|
|
||||||
// server_command : the full path name including optional command-line
|
|
||||||
// arguments to the executable representing the server
|
|
||||||
//
|
|
||||||
// service_name : represents service name
|
|
||||||
// something like "com.company.ServiceName"
|
|
||||||
//
|
|
||||||
// unregister_on_cleanup : if true, unregisters the service name
|
|
||||||
// when the OnDemandServer is deleted -- unregistering will
|
|
||||||
// ONLY be possible if LaunchOnDemand() has NOT been called.
|
|
||||||
// If false, then the service will continue to be registered
|
|
||||||
// even after the current process quits.
|
|
||||||
//
|
|
||||||
// out_result : if non-NULL, returns the result
|
|
||||||
// this value will be KERN_SUCCESS if Create() returns non-NULL
|
|
||||||
//
|
|
||||||
static OnDemandServer *Create(const char *server_command, |
|
||||||
const char *service_name, |
|
||||||
bool unregister_on_cleanup, |
|
||||||
kern_return_t *out_result); |
|
||||||
|
|
||||||
// Cleans up and if LaunchOnDemand() has not yet been called then
|
|
||||||
// the bootstrap service will be unregistered.
|
|
||||||
~OnDemandServer(); |
|
||||||
|
|
||||||
// This must be called if we intend to commit to launching the server
|
|
||||||
// by sending a mach message to our service port. Do not call it otherwise
|
|
||||||
// or it will be difficult (impossible?) to unregister the service name.
|
|
||||||
void LaunchOnDemand(); |
|
||||||
|
|
||||||
// This is the port we need to send a mach message to after calling
|
|
||||||
// LaunchOnDemand(). Sending a message causing an immediate launch
|
|
||||||
// of the server
|
|
||||||
mach_port_t GetServicePort() { return service_port_; }; |
|
||||||
|
|
||||||
private: |
|
||||||
// Disallow copy constructor
|
|
||||||
OnDemandServer(const OnDemandServer&); |
|
||||||
|
|
||||||
// Cleans up and if LaunchOnDemand() has not yet been called then
|
|
||||||
// the bootstrap service will be unregistered.
|
|
||||||
void Unregister(); |
|
||||||
|
|
||||||
name_t service_name_; |
|
||||||
|
|
||||||
mach_port_t server_port_; |
|
||||||
mach_port_t service_port_; |
|
||||||
bool unregister_on_cleanup_; |
|
||||||
}; |
|
@ -1,145 +0,0 @@ |
|||||||
// Copyright (c) 2007, 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. |
|
||||||
|
|
||||||
#import "OnDemandServer.h" |
|
||||||
|
|
||||||
#if DEBUG |
|
||||||
#define PRINT_MACH_RESULT(result_, message_) \ |
|
||||||
printf(message_"%s (%d)\n", mach_error_string(result_), result_ ); |
|
||||||
#else |
|
||||||
#define PRINT_MACH_RESULT(result_, message_) |
|
||||||
#endif |
|
||||||
|
|
||||||
//============================================================================== |
|
||||||
OnDemandServer *OnDemandServer::Create(const char *server_command, |
|
||||||
const char *service_name, |
|
||||||
bool unregister_on_cleanup, |
|
||||||
kern_return_t *out_result) { |
|
||||||
OnDemandServer *server = new OnDemandServer(); |
|
||||||
|
|
||||||
if (!server) return NULL; |
|
||||||
|
|
||||||
kern_return_t result = server->Initialize(server_command, |
|
||||||
service_name, |
|
||||||
unregister_on_cleanup); |
|
||||||
|
|
||||||
if (out_result) { |
|
||||||
*out_result = result; |
|
||||||
} |
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
return server; |
|
||||||
} |
|
||||||
|
|
||||||
delete server; |
|
||||||
return NULL; |
|
||||||
}; |
|
||||||
|
|
||||||
//============================================================================== |
|
||||||
kern_return_t OnDemandServer::Initialize(const char *server_command, |
|
||||||
const char *service_name, |
|
||||||
bool unregister_on_cleanup) { |
|
||||||
unregister_on_cleanup_ = unregister_on_cleanup; |
|
||||||
|
|
||||||
kern_return_t kr = |
|
||||||
bootstrap_create_server(bootstrap_port, |
|
||||||
const_cast<char*>(server_command), |
|
||||||
geteuid(), // server uid |
|
||||||
true, |
|
||||||
&server_port_); |
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) { |
|
||||||
PRINT_MACH_RESULT(kr, "bootstrap_create_server() : "); |
|
||||||
return kr; |
|
||||||
} |
|
||||||
|
|
||||||
strlcpy(service_name_, service_name, sizeof(service_name_)); |
|
||||||
|
|
||||||
// Create a service called service_name, and return send rights to |
|
||||||
// that port in service_port_. |
|
||||||
kr = bootstrap_create_service(server_port_, |
|
||||||
const_cast<char*>(service_name), |
|
||||||
&service_port_); |
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) { |
|
||||||
PRINT_MACH_RESULT(kr, "bootstrap_create_service() : "); |
|
||||||
|
|
||||||
// perhaps the service has already been created - try to look it up |
|
||||||
kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_); |
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) { |
|
||||||
PRINT_MACH_RESULT(kr, "bootstrap_look_up() : "); |
|
||||||
Unregister(); // clean up server port |
|
||||||
return kr; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================== |
|
||||||
OnDemandServer::~OnDemandServer() { |
|
||||||
if (unregister_on_cleanup_) { |
|
||||||
Unregister(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================== |
|
||||||
void OnDemandServer::LaunchOnDemand() { |
|
||||||
// We need to do this, since the launched server is another process |
|
||||||
// and holding on to this port delays launching until the current process |
|
||||||
// exits! |
|
||||||
mach_port_deallocate(mach_task_self(), server_port_); |
|
||||||
server_port_ = MACH_PORT_DEAD; |
|
||||||
|
|
||||||
// Now, the service is still registered and all we need to do is send |
|
||||||
// a mach message to the service port in order to launch the server. |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================== |
|
||||||
void OnDemandServer::Unregister() { |
|
||||||
if (service_port_ != MACH_PORT_NULL) { |
|
||||||
mach_port_deallocate(mach_task_self(), service_port_); |
|
||||||
service_port_ = MACH_PORT_NULL; |
|
||||||
} |
|
||||||
|
|
||||||
if (server_port_ != MACH_PORT_NULL) { |
|
||||||
// unregister the service |
|
||||||
kern_return_t kr = bootstrap_register(server_port_, |
|
||||||
service_name_, |
|
||||||
MACH_PORT_NULL); |
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) { |
|
||||||
PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : "); |
|
||||||
} |
|
||||||
|
|
||||||
mach_port_deallocate(mach_task_self(), server_port_); |
|
||||||
server_port_ = MACH_PORT_NULL; |
|
||||||
} |
|
||||||
} |
|
@ -1,20 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|
||||||
<plist version="1.0"> |
|
||||||
<dict> |
|
||||||
<key>CFBundleDevelopmentRegion</key> |
|
||||||
<string>English</string> |
|
||||||
<key>CFBundleExecutable</key> |
|
||||||
<string>${EXECUTABLE_NAME}</string> |
|
||||||
<key>CFBundleIdentifier</key> |
|
||||||
<string>com.yourcompany.${PRODUCT_NAME:identifier}</string> |
|
||||||
<key>CFBundleInfoDictionaryVersion</key> |
|
||||||
<string>6.0</string> |
|
||||||
<key>CFBundlePackageType</key> |
|
||||||
<string>BNDL</string> |
|
||||||
<key>CFBundleSignature</key> |
|
||||||
<string>????</string> |
|
||||||
<key>CFBundleVersion</key> |
|
||||||
<string>1.0</string> |
|
||||||
</dict> |
|
||||||
</plist> |
|
@ -1,193 +0,0 @@ |
|||||||
// Copyright (c) 2007, 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.
|
|
||||||
//
|
|
||||||
// Interface file between the Breakpad.framework and
|
|
||||||
// the Inspector process.
|
|
||||||
|
|
||||||
#import "common/mac/SimpleStringDictionary.h" |
|
||||||
|
|
||||||
#import <Foundation/Foundation.h> |
|
||||||
#import "client/mac/handler/minidump_generator.h" |
|
||||||
|
|
||||||
#define VERBOSE 0 |
|
||||||
|
|
||||||
extern bool gDebugLog; |
|
||||||
|
|
||||||
#define DEBUGLOG if (gDebugLog) fprintf |
|
||||||
|
|
||||||
// Types of mach messsages (message IDs)
|
|
||||||
enum { |
|
||||||
kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo
|
|
||||||
kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData
|
|
||||||
kMsgType_InspectorAcknowledgement = 2 // no data sent
|
|
||||||
}; |
|
||||||
|
|
||||||
// Initial information sent from the crashed process by
|
|
||||||
// Breakpad.framework to the Inspector process
|
|
||||||
// The mach message with this struct as data will also include
|
|
||||||
// several descriptors for sending mach port rights to the crashed
|
|
||||||
// task, etc.
|
|
||||||
struct InspectorInfo { |
|
||||||
int exception_type; |
|
||||||
int exception_code; |
|
||||||
int exception_subcode; |
|
||||||
unsigned int parameter_count; // key-value pairs
|
|
||||||
}; |
|
||||||
|
|
||||||
// Key/value message data to be sent to the Inspector
|
|
||||||
struct KeyValueMessageData { |
|
||||||
public: |
|
||||||
KeyValueMessageData() {} |
|
||||||
KeyValueMessageData(const google_breakpad::KeyValueEntry &inEntry) { |
|
||||||
strlcpy(key, inEntry.GetKey(), sizeof(key) ); |
|
||||||
strlcpy(value, inEntry.GetValue(), sizeof(value) ); |
|
||||||
} |
|
||||||
|
|
||||||
char key[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE]; |
|
||||||
char value[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE]; |
|
||||||
}; |
|
||||||
|
|
||||||
using std::string; |
|
||||||
using google_breakpad::MinidumpGenerator; |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
static BOOL EnsureDirectoryPathExists(NSString *dirPath); |
|
||||||
|
|
||||||
//=============================================================================
|
|
||||||
class ConfigFile { |
|
||||||
public: |
|
||||||
ConfigFile() { |
|
||||||
config_file_ = -1; |
|
||||||
config_file_path_[0] = 0; |
|
||||||
has_created_file_ = false; |
|
||||||
}; |
|
||||||
|
|
||||||
~ConfigFile() { |
|
||||||
}; |
|
||||||
|
|
||||||
void WriteFile(const SimpleStringDictionary *configurationParameters, |
|
||||||
const char *dump_dir, |
|
||||||
const char *minidump_id); |
|
||||||
|
|
||||||
const char *GetFilePath() { return config_file_path_; } |
|
||||||
|
|
||||||
void Unlink() { |
|
||||||
if (config_file_ != -1) |
|
||||||
unlink(config_file_path_); |
|
||||||
|
|
||||||
config_file_ = -1; |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
BOOL WriteData(const void *data, size_t length); |
|
||||||
|
|
||||||
BOOL AppendConfigData(const char *key, |
|
||||||
const void *data, |
|
||||||
size_t length); |
|
||||||
|
|
||||||
BOOL AppendConfigString(const char *key, |
|
||||||
const char *value); |
|
||||||
|
|
||||||
int config_file_; // descriptor for config file
|
|
||||||
char config_file_path_[PATH_MAX]; // Path to configuration file
|
|
||||||
bool has_created_file_; |
|
||||||
}; |
|
||||||
|
|
||||||
//=============================================================================
|
|
||||||
class MinidumpLocation { |
|
||||||
public: |
|
||||||
MinidumpLocation(const NSString *minidumpDir) { |
|
||||||
// Ensure that the path exists. Fallback to /tmp if unable to locate path.
|
|
||||||
assert(minidumpDir); |
|
||||||
if (!EnsureDirectoryPathExists(minidumpDir)) { |
|
||||||
DEBUGLOG(stderr, "Unable to create: %s\n", [minidumpDir UTF8String]); |
|
||||||
minidumpDir = @"/tmp"; |
|
||||||
} |
|
||||||
|
|
||||||
strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation], |
|
||||||
sizeof(minidump_dir_path_)); |
|
||||||
|
|
||||||
// now generate a unique ID
|
|
||||||
string dump_path(minidump_dir_path_); |
|
||||||
string next_minidump_id; |
|
||||||
|
|
||||||
string next_minidump_path_ = |
|
||||||
(MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id)); |
|
||||||
|
|
||||||
strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_)); |
|
||||||
}; |
|
||||||
|
|
||||||
const char *GetPath() { return minidump_dir_path_; } |
|
||||||
const char *GetID() { return minidump_id_; } |
|
||||||
|
|
||||||
private: |
|
||||||
char minidump_dir_path_[PATH_MAX]; // Path to minidump directory
|
|
||||||
char minidump_id_[128]; |
|
||||||
}; |
|
||||||
|
|
||||||
//=============================================================================
|
|
||||||
class Inspector { |
|
||||||
public: |
|
||||||
Inspector() {}; |
|
||||||
|
|
||||||
// given a bootstrap service name, receives mach messages
|
|
||||||
// from a crashed process, then inspects it, creates a minidump file
|
|
||||||
// and asks the user if he wants to upload it to a server.
|
|
||||||
void Inspect(const char *receive_port_name); |
|
||||||
|
|
||||||
private: |
|
||||||
kern_return_t ServiceCheckIn(const char *receive_port_name); |
|
||||||
kern_return_t ServiceCheckOut(const char *receive_port_name); |
|
||||||
|
|
||||||
kern_return_t ReadMessages(); |
|
||||||
|
|
||||||
bool InspectTask(); |
|
||||||
kern_return_t SendAcknowledgement(); |
|
||||||
void LaunchReporter(const char *inConfigFilePath); |
|
||||||
|
|
||||||
void SetCrashTimeParameters(); |
|
||||||
|
|
||||||
mach_port_t service_rcv_port_; |
|
||||||
|
|
||||||
int exception_type_; |
|
||||||
int exception_code_; |
|
||||||
int exception_subcode_; |
|
||||||
mach_port_t remote_task_; |
|
||||||
mach_port_t crashing_thread_; |
|
||||||
mach_port_t handler_thread_; |
|
||||||
mach_port_t ack_port_; |
|
||||||
|
|
||||||
SimpleStringDictionary config_params_; |
|
||||||
|
|
||||||
ConfigFile config_file_; |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,555 +0,0 @@ |
|||||||
// Copyright (c) 2007, 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. |
|
||||||
// |
|
||||||
// Utility that can inspect another process and write a crash dump |
|
||||||
|
|
||||||
#include <cstdio> |
|
||||||
#include <iostream> |
|
||||||
#include <stdio.h> |
|
||||||
#include <string.h> |
|
||||||
#include <string> |
|
||||||
#include <sys/time.h> |
|
||||||
|
|
||||||
#import "client/mac/crash_generation/Inspector.h" |
|
||||||
|
|
||||||
#import "client/mac/Framework/Breakpad.h" |
|
||||||
#import "client/mac/handler/minidump_generator.h" |
|
||||||
|
|
||||||
#import "common/mac/SimpleStringDictionary.h" |
|
||||||
#import "common/mac/MachIPC.h" |
|
||||||
|
|
||||||
#import "GTMDefines.h" |
|
||||||
|
|
||||||
#import <Foundation/Foundation.h> |
|
||||||
|
|
||||||
#if VERBOSE |
|
||||||
bool gDebugLog = true; |
|
||||||
#else |
|
||||||
bool gDebugLog = false; |
|
||||||
#endif |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
static BOOL EnsureDirectoryPathExists(NSString *dirPath) { |
|
||||||
NSFileManager *mgr = [NSFileManager defaultManager]; |
|
||||||
|
|
||||||
// If we got a relative path, prepend the current directory |
|
||||||
if (![dirPath isAbsolutePath]) |
|
||||||
dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath]; |
|
||||||
|
|
||||||
NSString *path = dirPath; |
|
||||||
|
|
||||||
// Ensure that no file exists within the path which would block creation |
|
||||||
while (1) { |
|
||||||
BOOL isDir; |
|
||||||
if ([mgr fileExistsAtPath:path isDirectory:&isDir]) { |
|
||||||
if (isDir) |
|
||||||
break; |
|
||||||
|
|
||||||
return NO; |
|
||||||
} |
|
||||||
|
|
||||||
path = [path stringByDeletingLastPathComponent]; |
|
||||||
} |
|
||||||
|
|
||||||
// Path now contains the first valid directory (or is empty) |
|
||||||
if (![path length]) |
|
||||||
return NO; |
|
||||||
|
|
||||||
NSString *common = |
|
||||||
[dirPath commonPrefixWithString:path options:NSLiteralSearch]; |
|
||||||
|
|
||||||
// If everything is good |
|
||||||
if ([common isEqualToString:dirPath]) |
|
||||||
return YES; |
|
||||||
|
|
||||||
// Break up the difference into components |
|
||||||
NSString *diff = [dirPath substringFromIndex:[common length] + 1]; |
|
||||||
NSArray *components = [diff pathComponents]; |
|
||||||
NSUInteger count = [components count]; |
|
||||||
|
|
||||||
// Rebuild the path one component at a time |
|
||||||
NSDictionary *attrs = |
|
||||||
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750] |
|
||||||
forKey:NSFilePosixPermissions]; |
|
||||||
path = common; |
|
||||||
for (NSUInteger i = 0; i < count; ++i) { |
|
||||||
path = [path stringByAppendingPathComponent:[components objectAtIndex:i]]; |
|
||||||
|
|
||||||
if (![mgr createDirectoryAtPath:path attributes:attrs]) |
|
||||||
return NO; |
|
||||||
} |
|
||||||
|
|
||||||
return YES; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
BOOL ConfigFile::WriteData(const void *data, size_t length) { |
|
||||||
size_t result = write(config_file_, data, length); |
|
||||||
|
|
||||||
return result == length; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
BOOL ConfigFile::AppendConfigData(const char *key, |
|
||||||
const void *data, size_t length) { |
|
||||||
assert(config_file_ != -1); |
|
||||||
|
|
||||||
if (!key) { |
|
||||||
DEBUGLOG(stderr, "Breakpad: Missing Key\n"); |
|
||||||
return NO; |
|
||||||
} |
|
||||||
|
|
||||||
if (!data) { |
|
||||||
DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key : |
|
||||||
"<Unknown Key>"); |
|
||||||
return NO; |
|
||||||
} |
|
||||||
|
|
||||||
// Write the key, \n, length of data (ascii integer), \n, data |
|
||||||
char buffer[16]; |
|
||||||
char nl = '\n'; |
|
||||||
BOOL result = WriteData(key, strlen(key)); |
|
||||||
|
|
||||||
snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length); |
|
||||||
result &= WriteData(buffer, strlen(buffer)); |
|
||||||
result &= WriteData(data, length); |
|
||||||
result &= WriteData(&nl, 1); |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
BOOL ConfigFile::AppendConfigString(const char *key, |
|
||||||
const char *value) { |
|
||||||
return AppendConfigData(key, value, strlen(value)); |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters, |
|
||||||
const char *dump_dir, |
|
||||||
const char *minidump_id) { |
|
||||||
|
|
||||||
assert(config_file_ == -1); |
|
||||||
|
|
||||||
// Open and write out configuration file preamble |
|
||||||
strlcpy(config_file_path_, "/tmp/Config-XXXXXX", |
|
||||||
sizeof(config_file_path_)); |
|
||||||
config_file_ = mkstemp(config_file_path_); |
|
||||||
|
|
||||||
if (config_file_ == -1) { |
|
||||||
DEBUGLOG(stderr, |
|
||||||
"mkstemp(config_file_path_) == -1 (%s)\n", |
|
||||||
strerror(errno)); |
|
||||||
return; |
|
||||||
} |
|
||||||
else { |
|
||||||
DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_); |
|
||||||
} |
|
||||||
|
|
||||||
has_created_file_ = true; |
|
||||||
|
|
||||||
// Add the minidump dir |
|
||||||
AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir); |
|
||||||
AppendConfigString(kReporterMinidumpIDKey, minidump_id); |
|
||||||
|
|
||||||
// Write out the configuration parameters |
|
||||||
BOOL result = YES; |
|
||||||
const SimpleStringDictionary &dictionary = *configurationParameters; |
|
||||||
|
|
||||||
const KeyValueEntry *entry = NULL; |
|
||||||
SimpleStringDictionaryIterator iter(dictionary); |
|
||||||
|
|
||||||
while ((entry = iter.Next())) { |
|
||||||
DEBUGLOG(stderr, |
|
||||||
"config: (%s) -> (%s)\n", |
|
||||||
entry->GetKey(), |
|
||||||
entry->GetValue()); |
|
||||||
result = AppendConfigString(entry->GetKey(), entry->GetValue()); |
|
||||||
|
|
||||||
if (!result) |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
close(config_file_); |
|
||||||
config_file_ = -1; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
void Inspector::Inspect(const char *receive_port_name) { |
|
||||||
kern_return_t result = ServiceCheckIn(receive_port_name); |
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
result = ReadMessages(); |
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
// Inspect the task and write a minidump file. |
|
||||||
bool wrote_minidump = InspectTask(); |
|
||||||
|
|
||||||
// Send acknowledgement to the crashed process that the inspection |
|
||||||
// has finished. It will then be able to cleanly exit. |
|
||||||
// The return value is ignored because failure isn't fatal. If the process |
|
||||||
// didn't get the message there's nothing we can do, and we still want to |
|
||||||
// send the report. |
|
||||||
SendAcknowledgement(); |
|
||||||
|
|
||||||
if (wrote_minidump) { |
|
||||||
// Ask the user if he wants to upload the crash report to a server, |
|
||||||
// and do so if he agrees. |
|
||||||
LaunchReporter(config_file_.GetFilePath()); |
|
||||||
} else { |
|
||||||
fprintf(stderr, "Inspection of crashed process failed\n"); |
|
||||||
} |
|
||||||
|
|
||||||
// Now that we're done reading messages, cleanup the service, but only |
|
||||||
// if there was an actual exception |
|
||||||
// Otherwise, it means the dump was generated on demand and the process |
|
||||||
// lives on, and we might be needed again in the future. |
|
||||||
if (exception_code_) { |
|
||||||
ServiceCheckOut(receive_port_name); |
|
||||||
} |
|
||||||
} else { |
|
||||||
PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()"); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) { |
|
||||||
// We need to get the mach port representing this service, so we can |
|
||||||
// get information from the crashed process. |
|
||||||
kern_return_t kr = bootstrap_check_in(bootstrap_port, |
|
||||||
(char*)receive_port_name, |
|
||||||
&service_rcv_port_); |
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) { |
|
||||||
#if VERBOSE |
|
||||||
PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()"); |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
return kr; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) { |
|
||||||
// We're done receiving mach messages from the crashed process, |
|
||||||
// so clean up a bit. |
|
||||||
kern_return_t kr; |
|
||||||
|
|
||||||
// DO NOT use mach_port_deallocate() here -- it will fail and the |
|
||||||
// following bootstrap_register() will also fail leaving our service |
|
||||||
// name hanging around forever (until reboot) |
|
||||||
kr = mach_port_destroy(mach_task_self(), service_rcv_port_); |
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) { |
|
||||||
PRINT_MACH_RESULT(kr, |
|
||||||
"Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()"); |
|
||||||
return kr; |
|
||||||
} |
|
||||||
|
|
||||||
// Unregister the service associated with the receive port. |
|
||||||
kr = bootstrap_register(bootstrap_port, |
|
||||||
(char*)receive_port_name, |
|
||||||
MACH_PORT_NULL); |
|
||||||
|
|
||||||
if (kr != KERN_SUCCESS) { |
|
||||||
PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()"); |
|
||||||
} |
|
||||||
|
|
||||||
return kr; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
kern_return_t Inspector::ReadMessages() { |
|
||||||
// Wait for an initial message from the crashed process containing basic |
|
||||||
// information about the crash. |
|
||||||
ReceivePort receive_port(service_rcv_port_); |
|
||||||
|
|
||||||
MachReceiveMessage message; |
|
||||||
kern_return_t result = receive_port.WaitForMessage(&message, 1000); |
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
InspectorInfo &info = (InspectorInfo &)*message.GetData(); |
|
||||||
exception_type_ = info.exception_type; |
|
||||||
exception_code_ = info.exception_code; |
|
||||||
exception_subcode_ = info.exception_subcode; |
|
||||||
|
|
||||||
#if VERBOSE |
|
||||||
printf("message ID = %d\n", message.GetMessageID()); |
|
||||||
#endif |
|
||||||
|
|
||||||
remote_task_ = message.GetTranslatedPort(0); |
|
||||||
crashing_thread_ = message.GetTranslatedPort(1); |
|
||||||
handler_thread_ = message.GetTranslatedPort(2); |
|
||||||
ack_port_ = message.GetTranslatedPort(3); |
|
||||||
|
|
||||||
#if VERBOSE |
|
||||||
printf("exception_type = %d\n", exception_type_); |
|
||||||
printf("exception_code = %d\n", exception_code_); |
|
||||||
printf("exception_subcode = %d\n", exception_subcode_); |
|
||||||
printf("remote_task = %d\n", remote_task_); |
|
||||||
printf("crashing_thread = %d\n", crashing_thread_); |
|
||||||
printf("handler_thread = %d\n", handler_thread_); |
|
||||||
printf("ack_port_ = %d\n", ack_port_); |
|
||||||
printf("parameter count = %d\n", info.parameter_count); |
|
||||||
#endif |
|
||||||
|
|
||||||
// In certain situations where multiple crash requests come |
|
||||||
// through quickly, we can end up with the mach IPC messages not |
|
||||||
// coming through correctly. Since we don't know what parameters |
|
||||||
// we've missed, we can't do much besides abort the crash dump |
|
||||||
// situation in this case. |
|
||||||
unsigned int parameters_read = 0; |
|
||||||
// The initial message contains the number of key value pairs that |
|
||||||
// we are expected to read. |
|
||||||
// Read each key/value pair, one mach message per key/value pair. |
|
||||||
for (unsigned int i = 0; i < info.parameter_count; ++i) { |
|
||||||
MachReceiveMessage parameter_message; |
|
||||||
result = receive_port.WaitForMessage(¶meter_message, 1000); |
|
||||||
|
|
||||||
if(result == KERN_SUCCESS) { |
|
||||||
KeyValueMessageData &key_value_data = |
|
||||||
(KeyValueMessageData&)*parameter_message.GetData(); |
|
||||||
// If we get a blank key, make sure we don't increment the |
|
||||||
// parameter count; in some cases (notably on-demand generation |
|
||||||
// many times in a short period of time) caused the Mach IPC |
|
||||||
// messages to not come through correctly. |
|
||||||
if (strlen(key_value_data.key) == 0) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
parameters_read++; |
|
||||||
|
|
||||||
config_params_.SetKeyValue(key_value_data.key, key_value_data.value); |
|
||||||
} else { |
|
||||||
PRINT_MACH_RESULT(result, "Inspector: key/value message"); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
if (parameters_read != info.parameter_count) { |
|
||||||
DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash " |
|
||||||
"dump generation.", parameters_read, info.parameter_count); |
|
||||||
return KERN_FAILURE; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
// Sets keys in the parameters dictionary that are specific to process uptime. |
|
||||||
// The two we set are process up time, and process crash time. |
|
||||||
void Inspector::SetCrashTimeParameters() { |
|
||||||
// Set process uptime parameter |
|
||||||
struct timeval tv; |
|
||||||
gettimeofday(&tv, NULL); |
|
||||||
|
|
||||||
char processUptimeString[32], processCrashtimeString[32]; |
|
||||||
const char *processStartTimeString = |
|
||||||
config_params_.GetValueForKey(BREAKPAD_PROCESS_START_TIME); |
|
||||||
|
|
||||||
// Set up time if we've received the start time. |
|
||||||
if (processStartTimeString) { |
|
||||||
time_t processStartTime = strtol(processStartTimeString, NULL, 10); |
|
||||||
time_t processUptime = tv.tv_sec - processStartTime; |
|
||||||
sprintf(processUptimeString, "%zd", processUptime); |
|
||||||
config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString); |
|
||||||
} |
|
||||||
|
|
||||||
sprintf(processCrashtimeString, "%zd", tv.tv_sec); |
|
||||||
config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME, |
|
||||||
processCrashtimeString); |
|
||||||
} |
|
||||||
|
|
||||||
bool Inspector::InspectTask() { |
|
||||||
// keep the task quiet while we're looking at it |
|
||||||
task_suspend(remote_task_); |
|
||||||
DEBUGLOG(stderr, "Suspended Remote task\n"); |
|
||||||
|
|
||||||
NSString *minidumpDir; |
|
||||||
|
|
||||||
const char *minidumpDirectory = |
|
||||||
config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY); |
|
||||||
|
|
||||||
SetCrashTimeParameters(); |
|
||||||
// If the client app has not specified a minidump directory, |
|
||||||
// use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name> |
|
||||||
if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) { |
|
||||||
NSArray *libraryDirectories = |
|
||||||
NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, |
|
||||||
NSUserDomainMask, |
|
||||||
YES); |
|
||||||
|
|
||||||
NSString *applicationSupportDirectory = |
|
||||||
[libraryDirectories objectAtIndex:0]; |
|
||||||
NSString *library_subdirectory = [NSString |
|
||||||
stringWithUTF8String:kDefaultLibrarySubdirectory]; |
|
||||||
NSString *breakpad_product = [NSString |
|
||||||
stringWithUTF8String:config_params_.GetValueForKey(BREAKPAD_PRODUCT)]; |
|
||||||
|
|
||||||
NSArray *path_components = [NSArray |
|
||||||
arrayWithObjects:applicationSupportDirectory, |
|
||||||
library_subdirectory, |
|
||||||
breakpad_product, |
|
||||||
nil]; |
|
||||||
|
|
||||||
minidumpDir = [NSString pathWithComponents:path_components]; |
|
||||||
} else { |
|
||||||
minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory] |
|
||||||
stringByExpandingTildeInPath]; |
|
||||||
} |
|
||||||
DEBUGLOG(stderr, |
|
||||||
"Writing minidump to directory (%s)\n", |
|
||||||
[minidumpDir UTF8String]); |
|
||||||
|
|
||||||
MinidumpLocation minidumpLocation(minidumpDir); |
|
||||||
|
|
||||||
// Obscure bug alert: |
|
||||||
// Don't use [NSString stringWithFormat] to build up the path here since it |
|
||||||
// assumes system encoding and in RTL locales will prepend an LTR override |
|
||||||
// character for paths beginning with '/' which fileSystemRepresentation does |
|
||||||
// not remove. Filed as rdar://6889706 . |
|
||||||
NSString *path_ns = [NSString |
|
||||||
stringWithUTF8String:minidumpLocation.GetPath()]; |
|
||||||
NSString *pathid_ns = [NSString |
|
||||||
stringWithUTF8String:minidumpLocation.GetID()]; |
|
||||||
NSString *minidumpPath = [path_ns stringByAppendingPathComponent:pathid_ns]; |
|
||||||
minidumpPath = [minidumpPath |
|
||||||
stringByAppendingPathExtension:@"dmp"]; |
|
||||||
|
|
||||||
DEBUGLOG(stderr, |
|
||||||
"minidump path (%s)\n", |
|
||||||
[minidumpPath UTF8String]); |
|
||||||
|
|
||||||
|
|
||||||
config_file_.WriteFile( &config_params_, |
|
||||||
minidumpLocation.GetPath(), |
|
||||||
minidumpLocation.GetID()); |
|
||||||
|
|
||||||
|
|
||||||
MinidumpGenerator generator(remote_task_, handler_thread_); |
|
||||||
|
|
||||||
if (exception_type_ && exception_code_) { |
|
||||||
generator.SetExceptionInformation(exception_type_, |
|
||||||
exception_code_, |
|
||||||
exception_subcode_, |
|
||||||
crashing_thread_); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool result = generator.Write([minidumpPath fileSystemRepresentation]); |
|
||||||
|
|
||||||
if (result) { |
|
||||||
DEBUGLOG(stderr, "Wrote minidump - OK\n"); |
|
||||||
} else { |
|
||||||
DEBUGLOG(stderr, "Error writing minidump - errno=%s\n", strerror(errno)); |
|
||||||
} |
|
||||||
|
|
||||||
// let the task continue |
|
||||||
task_resume(remote_task_); |
|
||||||
DEBUGLOG(stderr, "Resumed remote task\n"); |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
// The crashed task needs to be told that the inspection has finished. |
|
||||||
// It will wait on a mach port (with timeout) until we send acknowledgement. |
|
||||||
kern_return_t Inspector::SendAcknowledgement() { |
|
||||||
if (ack_port_ != MACH_PORT_DEAD) { |
|
||||||
MachPortSender sender(ack_port_); |
|
||||||
MachSendMessage ack_message(kMsgType_InspectorAcknowledgement); |
|
||||||
|
|
||||||
DEBUGLOG(stderr, "Inspector: trying to send acknowledgement to port %d\n", |
|
||||||
ack_port_); |
|
||||||
|
|
||||||
kern_return_t result = sender.SendMessage(ack_message, 2000); |
|
||||||
|
|
||||||
#if VERBOSE |
|
||||||
PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement"); |
|
||||||
#endif |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
DEBUGLOG(stderr, "Inspector: port translation failure!\n"); |
|
||||||
return KERN_INVALID_NAME; |
|
||||||
} |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
void Inspector::LaunchReporter(const char *inConfigFilePath) { |
|
||||||
// Extract the path to the reporter executable. |
|
||||||
const char *reporterExecutablePath = |
|
||||||
config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION); |
|
||||||
DEBUGLOG(stderr, "reporter path = %s\n", reporterExecutablePath); |
|
||||||
|
|
||||||
// Setup and launch the crash dump sender. |
|
||||||
const char *argv[3]; |
|
||||||
argv[0] = reporterExecutablePath; |
|
||||||
argv[1] = inConfigFilePath; |
|
||||||
argv[2] = NULL; |
|
||||||
|
|
||||||
// Launch the reporter |
|
||||||
pid_t pid = fork(); |
|
||||||
|
|
||||||
// If we're in the child, load in our new executable and run. |
|
||||||
// The parent will not wait for the child to complete. |
|
||||||
if (pid == 0) { |
|
||||||
execv(argv[0], (char * const *)argv); |
|
||||||
config_file_.Unlink(); // launch failed - get rid of config file |
|
||||||
DEBUGLOG(stderr, "Inspector: unable to launch reporter app\n"); |
|
||||||
_exit(1); |
|
||||||
} |
|
||||||
|
|
||||||
// Wait until the Reporter child process exits. |
|
||||||
// |
|
||||||
|
|
||||||
// We'll use a timeout of one minute. |
|
||||||
int timeoutCount = 60; // 60 seconds |
|
||||||
|
|
||||||
while (timeoutCount-- > 0) { |
|
||||||
int status; |
|
||||||
pid_t result = waitpid(pid, &status, WNOHANG); |
|
||||||
|
|
||||||
if (result == 0) { |
|
||||||
// The child has not yet finished. |
|
||||||
sleep(1); |
|
||||||
} else if (result == -1) { |
|
||||||
DEBUGLOG(stderr, "Inspector: waitpid error (%d) waiting for reporter app\n", |
|
||||||
errno); |
|
||||||
break; |
|
||||||
} else { |
|
||||||
// child has finished |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace google_breakpad |
|
||||||
|
|
@ -1,65 +0,0 @@ |
|||||||
// Copyright (c) 2007, 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. |
|
||||||
// |
|
||||||
// Main driver for Inspector |
|
||||||
|
|
||||||
#import "client/mac/crash_generation/Inspector.h" |
|
||||||
#import <Cocoa/Cocoa.h> |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
//============================================================================= |
|
||||||
extern "C" { |
|
||||||
|
|
||||||
int main(int argc, char *const argv[]) { |
|
||||||
#if DEBUG |
|
||||||
// Since we're launched on-demand, this is necessary to see debugging |
|
||||||
// output in the console window. |
|
||||||
freopen("/dev/console", "w", stdout); |
|
||||||
freopen("/dev/console", "w", stderr); |
|
||||||
#endif |
|
||||||
|
|
||||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; |
|
||||||
|
|
||||||
if (argc != 2) { |
|
||||||
exit(0); |
|
||||||
} |
|
||||||
// Our first command-line argument contains the name of the service |
|
||||||
// that we're providing. |
|
||||||
google_breakpad::Inspector inspector; |
|
||||||
inspector.Inspect(argv[1]); |
|
||||||
|
|
||||||
[pool release]; |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
} // extern "C" |
|
||||||
|
|
||||||
} // namespace google_breakpad |
|
@ -1,47 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_ |
|
||||||
#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_ |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
class ClientInfo { |
|
||||||
public: |
|
||||||
explicit ClientInfo(pid_t pid) : pid_(pid) {} |
|
||||||
|
|
||||||
pid_t pid() const { return pid_; } |
|
||||||
|
|
||||||
private: |
|
||||||
pid_t pid_; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_
|
|
@ -1,72 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 "client/mac/crash_generation/crash_generation_client.h" |
|
||||||
|
|
||||||
#include "client/mac/crash_generation/crash_generation_server.h" |
|
||||||
#include "common/mac/MachIPC.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
bool CrashGenerationClient::RequestDumpForException( |
|
||||||
int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t crashing_thread) { |
|
||||||
// The server will send a message to this port indicating that it
|
|
||||||
// has finished its work.
|
|
||||||
ReceivePort acknowledge_port; |
|
||||||
|
|
||||||
MachSendMessage message(kDumpRequestMessage); |
|
||||||
message.AddDescriptor(mach_task_self()); // this task
|
|
||||||
message.AddDescriptor(crashing_thread); // crashing thread
|
|
||||||
message.AddDescriptor(mach_thread_self()); // handler thread
|
|
||||||
message.AddDescriptor(acknowledge_port.GetPort()); // message receive port
|
|
||||||
|
|
||||||
ExceptionInfo info; |
|
||||||
info.exception_type = exception_type; |
|
||||||
info.exception_code = exception_code; |
|
||||||
info.exception_subcode = exception_subcode; |
|
||||||
message.SetData(&info, sizeof(info)); |
|
||||||
|
|
||||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; |
|
||||||
kern_return_t result = sender_.SendMessage(message, kSendTimeoutMs); |
|
||||||
if (result != KERN_SUCCESS) |
|
||||||
return false; |
|
||||||
|
|
||||||
// Give the server slightly longer to reply since it has to
|
|
||||||
// inspect this task and write the minidump.
|
|
||||||
const mach_msg_timeout_t kReceiveTimeoutMs = 5 * 1000; |
|
||||||
MachReceiveMessage acknowledge_message; |
|
||||||
result = acknowledge_port.WaitForMessage(&acknowledge_message, |
|
||||||
kReceiveTimeoutMs); |
|
||||||
return result == KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,65 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ |
|
||||||
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ |
|
||||||
|
|
||||||
#include "common/mac/MachIPC.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
class CrashGenerationClient { |
|
||||||
public: |
|
||||||
explicit CrashGenerationClient(const char* mach_port_name) |
|
||||||
: sender_(mach_port_name) { |
|
||||||
} |
|
||||||
|
|
||||||
// Request the crash server to generate a dump.
|
|
||||||
//
|
|
||||||
// Return true if the dump was successful; false otherwise.
|
|
||||||
bool RequestDumpForException(int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t crashing_thread); |
|
||||||
|
|
||||||
bool RequestDump() { |
|
||||||
return RequestDumpForException(0, 0, 0, MACH_PORT_NULL); |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
MachPortSender sender_; |
|
||||||
|
|
||||||
// Prevent copy construction and assignment.
|
|
||||||
CrashGenerationClient(const CrashGenerationClient&); |
|
||||||
CrashGenerationClient& operator=(const CrashGenerationClient&); |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
|
@ -1,160 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 "client/mac/crash_generation/crash_generation_server.h" |
|
||||||
|
|
||||||
#include "client/mac/crash_generation/client_info.h" |
|
||||||
#include "client/mac/handler/minidump_generator.h" |
|
||||||
#include "common/mac/scoped_task_suspend-inl.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
CrashGenerationServer::CrashGenerationServer( |
|
||||||
const char *mach_port_name, |
|
||||||
OnClientDumpRequestCallback dump_callback, |
|
||||||
void *dump_context, |
|
||||||
OnClientExitingCallback exit_callback, |
|
||||||
void *exit_context, |
|
||||||
bool generate_dumps, |
|
||||||
const std::string &dump_path) |
|
||||||
: dump_callback_(dump_callback), |
|
||||||
dump_context_(dump_context), |
|
||||||
exit_callback_(exit_callback), |
|
||||||
exit_context_(exit_context), |
|
||||||
generate_dumps_(generate_dumps), |
|
||||||
dump_dir_(dump_path.empty() ? "/tmp" : dump_path), |
|
||||||
started_(false), |
|
||||||
receive_port_(mach_port_name), |
|
||||||
mach_port_name_(mach_port_name) { |
|
||||||
} |
|
||||||
|
|
||||||
CrashGenerationServer::~CrashGenerationServer() { |
|
||||||
if (started_) |
|
||||||
Stop(); |
|
||||||
} |
|
||||||
|
|
||||||
bool CrashGenerationServer::Start() { |
|
||||||
int thread_create_result = pthread_create(&server_thread_, NULL, |
|
||||||
&WaitForMessages, this); |
|
||||||
started_ = thread_create_result == 0; |
|
||||||
return started_; |
|
||||||
} |
|
||||||
|
|
||||||
bool CrashGenerationServer::Stop() { |
|
||||||
if (!started_) |
|
||||||
return false; |
|
||||||
|
|
||||||
// Send a quit message to the background thread, and then join it.
|
|
||||||
MachPortSender sender(mach_port_name_.c_str()); |
|
||||||
MachSendMessage quit_message(kQuitMessage); |
|
||||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; |
|
||||||
kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs); |
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
int thread_join_result = pthread_join(server_thread_, NULL); |
|
||||||
started_ = thread_join_result != 0; |
|
||||||
} |
|
||||||
|
|
||||||
return !started_; |
|
||||||
} |
|
||||||
|
|
||||||
// static
|
|
||||||
void *CrashGenerationServer::WaitForMessages(void *server) { |
|
||||||
CrashGenerationServer *self = |
|
||||||
reinterpret_cast<CrashGenerationServer*>(server); |
|
||||||
while (self->WaitForOneMessage()) {} |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
bool CrashGenerationServer::WaitForOneMessage() { |
|
||||||
MachReceiveMessage message; |
|
||||||
kern_return_t result = receive_port_.WaitForMessage(&message, |
|
||||||
MACH_MSG_TIMEOUT_NONE); |
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
switch (message.GetMessageID()) { |
|
||||||
case kDumpRequestMessage: { |
|
||||||
ExceptionInfo &info = (ExceptionInfo &)*message.GetData(); |
|
||||||
|
|
||||||
mach_port_t remote_task = message.GetTranslatedPort(0); |
|
||||||
mach_port_t crashing_thread = message.GetTranslatedPort(1); |
|
||||||
mach_port_t handler_thread = message.GetTranslatedPort(2); |
|
||||||
mach_port_t ack_port = message.GetTranslatedPort(3); |
|
||||||
pid_t remote_pid = -1; |
|
||||||
pid_for_task(remote_task, &remote_pid); |
|
||||||
ClientInfo client(remote_pid); |
|
||||||
|
|
||||||
bool result; |
|
||||||
std::string dump_path; |
|
||||||
if (generate_dumps_) { |
|
||||||
ScopedTaskSuspend suspend(remote_task); |
|
||||||
|
|
||||||
MinidumpGenerator generator(remote_task, handler_thread); |
|
||||||
dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL); |
|
||||||
|
|
||||||
if (info.exception_type && info.exception_code) { |
|
||||||
generator.SetExceptionInformation(info.exception_type, |
|
||||||
info.exception_code, |
|
||||||
info.exception_subcode, |
|
||||||
crashing_thread); |
|
||||||
} |
|
||||||
result = generator.Write(dump_path.c_str()); |
|
||||||
} else { |
|
||||||
result = true; |
|
||||||
} |
|
||||||
|
|
||||||
if (result && dump_callback_) { |
|
||||||
dump_callback_(dump_context_, client, dump_path); |
|
||||||
} |
|
||||||
|
|
||||||
// TODO(ted): support a way for the client to send additional data,
|
|
||||||
// perhaps with a callback so users of the server can read the data
|
|
||||||
// themselves?
|
|
||||||
|
|
||||||
if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) { |
|
||||||
MachPortSender sender(ack_port); |
|
||||||
MachSendMessage ack_message(kAcknowledgementMessage); |
|
||||||
const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; |
|
||||||
|
|
||||||
sender.SendMessage(ack_message, kSendTimeoutMs); |
|
||||||
} |
|
||||||
|
|
||||||
if (exit_callback_) { |
|
||||||
exit_callback_(exit_context_, client); |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
case kQuitMessage: |
|
||||||
return false; |
|
||||||
} |
|
||||||
} else { // result != KERN_SUCCESS
|
|
||||||
return false; |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,141 +0,0 @@ |
|||||||
// Copyright (c) 2010 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ |
|
||||||
#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
|
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include "common/mac/MachIPC.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
class ClientInfo; |
|
||||||
|
|
||||||
// Messages the server can read via its mach port
|
|
||||||
enum { |
|
||||||
kDumpRequestMessage = 1, |
|
||||||
kAcknowledgementMessage = 2, |
|
||||||
kQuitMessage = 3 |
|
||||||
}; |
|
||||||
|
|
||||||
// Exception details sent by the client when requesting a dump.
|
|
||||||
struct ExceptionInfo { |
|
||||||
int32_t exception_type; |
|
||||||
int32_t exception_code; |
|
||||||
int32_t exception_subcode; |
|
||||||
}; |
|
||||||
|
|
||||||
class CrashGenerationServer { |
|
||||||
public: |
|
||||||
// WARNING: callbacks may be invoked on a different thread
|
|
||||||
// than that which creates the CrashGenerationServer. They must
|
|
||||||
// be thread safe.
|
|
||||||
typedef void (*OnClientDumpRequestCallback)(void *context, |
|
||||||
const ClientInfo &client_info, |
|
||||||
const std::string &file_path); |
|
||||||
|
|
||||||
typedef void (*OnClientExitingCallback)(void *context, |
|
||||||
const ClientInfo &client_info); |
|
||||||
|
|
||||||
// Create an instance with the given parameters.
|
|
||||||
//
|
|
||||||
// mach_port_name: Named server port to listen on.
|
|
||||||
// dump_callback: Callback for a client crash dump request.
|
|
||||||
// dump_context: Context for client crash dump request callback.
|
|
||||||
// exit_callback: Callback for client process exit.
|
|
||||||
// exit_context: Context for client exit callback.
|
|
||||||
// generate_dumps: Whether to automatically generate dumps.
|
|
||||||
// Client code of this class might want to generate dumps explicitly
|
|
||||||
// in the crash dump request callback. In that case, false can be
|
|
||||||
// passed for this parameter.
|
|
||||||
// dump_path: Path for generating dumps; required only if true is
|
|
||||||
// passed for generateDumps parameter; NULL can be passed otherwise.
|
|
||||||
CrashGenerationServer(const char *mach_port_name, |
|
||||||
OnClientDumpRequestCallback dump_callback, |
|
||||||
void *dump_context, |
|
||||||
OnClientExitingCallback exit_callback, |
|
||||||
void *exit_context, |
|
||||||
bool generate_dumps, |
|
||||||
const std::string &dump_path); |
|
||||||
|
|
||||||
~CrashGenerationServer(); |
|
||||||
|
|
||||||
// Perform initialization steps needed to start listening to clients.
|
|
||||||
//
|
|
||||||
// Return true if initialization is successful; false otherwise.
|
|
||||||
bool Start(); |
|
||||||
|
|
||||||
// Stop the server.
|
|
||||||
bool Stop(); |
|
||||||
|
|
||||||
private: |
|
||||||
// Return a unique filename at which a minidump can be written.
|
|
||||||
bool MakeMinidumpFilename(std::string &outFilename); |
|
||||||
|
|
||||||
// Loop reading client messages and responding to them until
|
|
||||||
// a quit message is received.
|
|
||||||
static void *WaitForMessages(void *server); |
|
||||||
|
|
||||||
// Wait for a single client message and respond to it. Returns false
|
|
||||||
// if a quit message was received or if an error occurred.
|
|
||||||
bool WaitForOneMessage(); |
|
||||||
|
|
||||||
OnClientDumpRequestCallback dump_callback_; |
|
||||||
void *dump_context_; |
|
||||||
|
|
||||||
OnClientExitingCallback exit_callback_; |
|
||||||
void *exit_context_; |
|
||||||
|
|
||||||
bool generate_dumps_; |
|
||||||
|
|
||||||
std::string dump_dir_; |
|
||||||
|
|
||||||
bool started_; |
|
||||||
|
|
||||||
// The mach port that receives requests to dump from child processes.
|
|
||||||
ReceivePort receive_port_; |
|
||||||
|
|
||||||
// The name of the mach port. Stored so the Stop method can message
|
|
||||||
// the background thread to shut it down.
|
|
||||||
std::string mach_port_name_; |
|
||||||
|
|
||||||
// The thread that waits on the receive port.
|
|
||||||
pthread_t server_thread_; |
|
||||||
|
|
||||||
// Disable copy constructor and operator=.
|
|
||||||
CrashGenerationServer(const CrashGenerationServer&); |
|
||||||
CrashGenerationServer& operator=(const CrashGenerationServer&); |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,258 +0,0 @@ |
|||||||
#ifndef _exc_user_ |
|
||||||
#define _exc_user_ |
|
||||||
|
|
||||||
/* Module exc */ |
|
||||||
|
|
||||||
#include <string.h> |
|
||||||
#include <mach/ndr.h> |
|
||||||
#include <mach/boolean.h> |
|
||||||
#include <mach/kern_return.h> |
|
||||||
#include <mach/notify.h> |
|
||||||
#include <mach/mach_types.h> |
|
||||||
#include <mach/message.h> |
|
||||||
#include <mach/mig_errors.h> |
|
||||||
#include <mach/port.h> |
|
||||||
|
|
||||||
#ifdef AUTOTEST |
|
||||||
#ifndef FUNCTION_PTR_T |
|
||||||
#define FUNCTION_PTR_T |
|
||||||
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t); |
|
||||||
typedef struct { |
|
||||||
char *name; |
|
||||||
function_ptr_t function; |
|
||||||
} function_table_entry; |
|
||||||
typedef function_table_entry *function_table_t; |
|
||||||
#endif /* FUNCTION_PTR_T */ |
|
||||||
#endif /* AUTOTEST */ |
|
||||||
|
|
||||||
#ifndef exc_MSG_COUNT |
|
||||||
#define exc_MSG_COUNT 3 |
|
||||||
#endif /* exc_MSG_COUNT */ |
|
||||||
|
|
||||||
#include <mach/std_types.h> |
|
||||||
#include <mach/mig.h> |
|
||||||
#include <mach/mig.h> |
|
||||||
#include <mach/mach_types.h> |
|
||||||
|
|
||||||
#ifdef __BeforeMigUserHeader |
|
||||||
__BeforeMigUserHeader |
|
||||||
#endif /* __BeforeMigUserHeader */ |
|
||||||
|
|
||||||
#include <sys/cdefs.h> |
|
||||||
__BEGIN_DECLS |
|
||||||
|
|
||||||
|
|
||||||
/* Routine exception_raise */ |
|
||||||
#ifdef mig_external |
|
||||||
mig_external |
|
||||||
#else |
|
||||||
extern |
|
||||||
#endif /* mig_external */ |
|
||||||
kern_return_t exception_raise |
|
||||||
( |
|
||||||
mach_port_t exception_port, |
|
||||||
mach_port_t thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t codeCnt |
|
||||||
); |
|
||||||
|
|
||||||
/* Routine exception_raise_state */ |
|
||||||
#ifdef mig_external |
|
||||||
mig_external |
|
||||||
#else |
|
||||||
extern |
|
||||||
#endif /* mig_external */ |
|
||||||
kern_return_t exception_raise_state |
|
||||||
( |
|
||||||
mach_port_t exception_port, |
|
||||||
exception_type_t exception, |
|
||||||
const exception_data_t code, |
|
||||||
mach_msg_type_number_t codeCnt, |
|
||||||
int *flavor, |
|
||||||
const thread_state_t old_state, |
|
||||||
mach_msg_type_number_t old_stateCnt, |
|
||||||
thread_state_t new_state, |
|
||||||
mach_msg_type_number_t *new_stateCnt |
|
||||||
); |
|
||||||
|
|
||||||
/* Routine exception_raise_state_identity */ |
|
||||||
#ifdef mig_external |
|
||||||
mig_external |
|
||||||
#else |
|
||||||
extern |
|
||||||
#endif /* mig_external */ |
|
||||||
kern_return_t exception_raise_state_identity |
|
||||||
( |
|
||||||
mach_port_t exception_port, |
|
||||||
mach_port_t thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t codeCnt, |
|
||||||
int *flavor, |
|
||||||
thread_state_t old_state, |
|
||||||
mach_msg_type_number_t old_stateCnt, |
|
||||||
thread_state_t new_state, |
|
||||||
mach_msg_type_number_t *new_stateCnt |
|
||||||
); |
|
||||||
|
|
||||||
__END_DECLS |
|
||||||
|
|
||||||
/********************** Caution **************************/ |
|
||||||
/* The following data types should be used to calculate */ |
|
||||||
/* maximum message sizes only. The actual message may be */ |
|
||||||
/* smaller, and the position of the arguments within the */ |
|
||||||
/* message layout may vary from what is presented here. */ |
|
||||||
/* For example, if any of the arguments are variable- */ |
|
||||||
/* sized, and less than the maximum is sent, the data */ |
|
||||||
/* will be packed tight in the actual message to reduce */ |
|
||||||
/* the presence of holes. */ |
|
||||||
/********************** Caution **************************/ |
|
||||||
|
|
||||||
/* typedefs for all requests */ |
|
||||||
|
|
||||||
#ifndef __Request__exc_subsystem__defined |
|
||||||
#define __Request__exc_subsystem__defined |
|
||||||
|
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack(4) |
|
||||||
#endif |
|
||||||
typedef struct { |
|
||||||
mach_msg_header_t Head; |
|
||||||
/* start of the kernel processed data */ |
|
||||||
mach_msg_body_t msgh_body; |
|
||||||
mach_msg_port_descriptor_t thread; |
|
||||||
mach_msg_port_descriptor_t task; |
|
||||||
/* end of the kernel processed data */ |
|
||||||
NDR_record_t NDR; |
|
||||||
exception_type_t exception; |
|
||||||
mach_msg_type_number_t codeCnt; |
|
||||||
integer_t code[2]; |
|
||||||
} __Request__exception_raise_t; |
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack() |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack(4) |
|
||||||
#endif |
|
||||||
typedef struct { |
|
||||||
mach_msg_header_t Head; |
|
||||||
NDR_record_t NDR; |
|
||||||
exception_type_t exception; |
|
||||||
mach_msg_type_number_t codeCnt; |
|
||||||
integer_t code[2]; |
|
||||||
int flavor; |
|
||||||
mach_msg_type_number_t old_stateCnt; |
|
||||||
natural_t old_state[144]; |
|
||||||
} __Request__exception_raise_state_t; |
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack() |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack(4) |
|
||||||
#endif |
|
||||||
typedef struct { |
|
||||||
mach_msg_header_t Head; |
|
||||||
/* start of the kernel processed data */ |
|
||||||
mach_msg_body_t msgh_body; |
|
||||||
mach_msg_port_descriptor_t thread; |
|
||||||
mach_msg_port_descriptor_t task; |
|
||||||
/* end of the kernel processed data */ |
|
||||||
NDR_record_t NDR; |
|
||||||
exception_type_t exception; |
|
||||||
mach_msg_type_number_t codeCnt; |
|
||||||
integer_t code[2]; |
|
||||||
int flavor; |
|
||||||
mach_msg_type_number_t old_stateCnt; |
|
||||||
natural_t old_state[144]; |
|
||||||
} __Request__exception_raise_state_identity_t; |
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack() |
|
||||||
#endif |
|
||||||
#endif /* !__Request__exc_subsystem__defined */ |
|
||||||
|
|
||||||
/* union of all requests */ |
|
||||||
|
|
||||||
#ifndef __RequestUnion__exc_subsystem__defined |
|
||||||
#define __RequestUnion__exc_subsystem__defined |
|
||||||
union __RequestUnion__exc_subsystem { |
|
||||||
__Request__exception_raise_t Request_exception_raise; |
|
||||||
__Request__exception_raise_state_t Request_exception_raise_state; |
|
||||||
__Request__exception_raise_state_identity_t Request_exception_raise_state_identity; |
|
||||||
}; |
|
||||||
#endif /* !__RequestUnion__exc_subsystem__defined */ |
|
||||||
/* typedefs for all replies */ |
|
||||||
|
|
||||||
#ifndef __Reply__exc_subsystem__defined |
|
||||||
#define __Reply__exc_subsystem__defined |
|
||||||
|
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack(4) |
|
||||||
#endif |
|
||||||
typedef struct { |
|
||||||
mach_msg_header_t Head; |
|
||||||
NDR_record_t NDR; |
|
||||||
kern_return_t RetCode; |
|
||||||
} __Reply__exception_raise_t; |
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack() |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack(4) |
|
||||||
#endif |
|
||||||
typedef struct { |
|
||||||
mach_msg_header_t Head; |
|
||||||
NDR_record_t NDR; |
|
||||||
kern_return_t RetCode; |
|
||||||
int flavor; |
|
||||||
mach_msg_type_number_t new_stateCnt; |
|
||||||
natural_t new_state[144]; |
|
||||||
} __Reply__exception_raise_state_t; |
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack() |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack(4) |
|
||||||
#endif |
|
||||||
typedef struct { |
|
||||||
mach_msg_header_t Head; |
|
||||||
NDR_record_t NDR; |
|
||||||
kern_return_t RetCode; |
|
||||||
int flavor; |
|
||||||
mach_msg_type_number_t new_stateCnt; |
|
||||||
natural_t new_state[144]; |
|
||||||
} __Reply__exception_raise_state_identity_t; |
|
||||||
#ifdef __MigPackStructs |
|
||||||
#pragma pack() |
|
||||||
#endif |
|
||||||
#endif /* !__Reply__exc_subsystem__defined */ |
|
||||||
|
|
||||||
/* union of all replies */ |
|
||||||
|
|
||||||
#ifndef __ReplyUnion__exc_subsystem__defined |
|
||||||
#define __ReplyUnion__exc_subsystem__defined |
|
||||||
union __ReplyUnion__exc_subsystem { |
|
||||||
__Reply__exception_raise_t Reply_exception_raise; |
|
||||||
__Reply__exception_raise_state_t Reply_exception_raise_state; |
|
||||||
__Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity; |
|
||||||
}; |
|
||||||
#endif /* !__RequestUnion__exc_subsystem__defined */ |
|
||||||
|
|
||||||
#ifndef subsystem_to_name_map_exc |
|
||||||
#define subsystem_to_name_map_exc \ |
|
||||||
{ "exception_raise", 2401 },\
|
|
||||||
{ "exception_raise_state", 2402 },\
|
|
||||||
{ "exception_raise_state_identity", 2403 } |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifdef __AfterMigUserHeader |
|
||||||
__AfterMigUserHeader |
|
||||||
#endif /* __AfterMigUserHeader */ |
|
||||||
|
|
||||||
#endif /* _exc_user_ */ |
|
@ -1,412 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved. |
|
||||||
* |
|
||||||
* @APPLE_LICENSE_HEADER_START@ |
|
||||||
*
|
|
||||||
* This file contains Original Code and/or Modifications of Original Code |
|
||||||
* as defined in and that are subject to the Apple Public Source License |
|
||||||
* Version 2.0 (the 'License'). You may not use this file except in |
|
||||||
* compliance with the License. Please obtain a copy of the License at |
|
||||||
* http://www.opensource.apple.com/apsl/ and read it before using this
|
|
||||||
* file. |
|
||||||
*
|
|
||||||
* The Original Code and all software distributed under the License are |
|
||||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
|
||||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
|
||||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
|
||||||
* Please see the License for the specific language governing rights and |
|
||||||
* limitations under the License. |
|
||||||
*
|
|
||||||
* @APPLE_LICENSE_HEADER_END@ |
|
||||||
*/ |
|
||||||
/*
|
|
||||||
* Copyright (c) 1989, 1993 |
|
||||||
* The Regents of the University of California. All rights reserved. |
|
||||||
* |
|
||||||
* Redistribution and use in source and binary forms, with or without |
|
||||||
* modification, are permitted provided that the following conditions |
|
||||||
* are met: |
|
||||||
* 1. Redistributions of source code must retain the above copyright |
|
||||||
* notice, this list of conditions and the following disclaimer. |
|
||||||
* 2. 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. |
|
||||||
* 3. All advertising materials mentioning features or use of this software |
|
||||||
* must display the following acknowledgement: |
|
||||||
* This product includes software developed by the University of |
|
||||||
* California, Berkeley and its contributors. |
|
||||||
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file was copied from libc/gen/nlist.c from Darwin's source code
|
|
||||||
* The version of nlist used as a base is from 10.5.2, libc-498
|
|
||||||
* http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c
|
|
||||||
*
|
|
||||||
* The full tarball is at:
|
|
||||||
* http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz
|
|
||||||
*
|
|
||||||
* I've modified it to be compatible with 64-bit images. |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "breakpad_nlist_64.h" |
|
||||||
|
|
||||||
#include <fcntl.h> |
|
||||||
#include <mach-o/nlist.h> |
|
||||||
#include <mach-o/loader.h> |
|
||||||
#include <mach-o/fat.h> |
|
||||||
#include <mach/mach.h> |
|
||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <sys/types.h> |
|
||||||
#include <sys/uio.h> |
|
||||||
#include <TargetConditionals.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
/* Stuff lifted from <a.out.h> and <sys/exec.h> since they are gone */ |
|
||||||
/*
|
|
||||||
* Header prepended to each a.out file. |
|
||||||
*/ |
|
||||||
struct exec { |
|
||||||
unsigned short a_machtype; /* machine type */ |
|
||||||
unsigned short a_magic; /* magic number */ |
|
||||||
unsigned long a_text; /* size of text segment */ |
|
||||||
unsigned long a_data; /* size of initialized data */ |
|
||||||
unsigned long a_bss; /* size of uninitialized data */ |
|
||||||
unsigned long a_syms; /* size of symbol table */ |
|
||||||
unsigned long a_entry; /* entry point */ |
|
||||||
unsigned long a_trsize; /* size of text relocation */ |
|
||||||
unsigned long a_drsize; /* size of data relocation */ |
|
||||||
}; |
|
||||||
|
|
||||||
#define OMAGIC 0407 /* old impure format */ |
|
||||||
#define NMAGIC 0410 /* read-only text */ |
|
||||||
#define ZMAGIC 0413 /* demand load format */ |
|
||||||
|
|
||||||
#define N_BADMAG(x) \ |
|
||||||
(((x).a_magic)!=OMAGIC && ((x).a_magic)!=NMAGIC && ((x).a_magic)!=ZMAGIC) |
|
||||||
#define N_TXTOFF(x) \ |
|
||||||
((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec)) |
|
||||||
#define N_SYMOFF(x) \ |
|
||||||
(N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize) |
|
||||||
|
|
||||||
// Traits structs for specializing function templates to handle
|
|
||||||
// 32-bit/64-bit Mach-O files.
|
|
||||||
template<typename T> |
|
||||||
struct MachBits {}; |
|
||||||
|
|
||||||
typedef struct nlist nlist32; |
|
||||||
typedef struct nlist_64 nlist64; |
|
||||||
|
|
||||||
template<> |
|
||||||
struct MachBits<nlist32> { |
|
||||||
typedef mach_header mach_header_type; |
|
||||||
typedef uint32_t word_type; |
|
||||||
static const uint32_t magic = MH_MAGIC; |
|
||||||
}; |
|
||||||
|
|
||||||
template<> |
|
||||||
struct MachBits<nlist64> { |
|
||||||
typedef mach_header_64 mach_header_type; |
|
||||||
typedef uint64_t word_type; |
|
||||||
static const uint32_t magic = MH_MAGIC_64; |
|
||||||
}; |
|
||||||
|
|
||||||
template<typename nlist_type> |
|
||||||
int |
|
||||||
__breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, |
|
||||||
cpu_type_t cpu_type); |
|
||||||
|
|
||||||
/*
|
|
||||||
* nlist - retreive attributes from name list (string table version) |
|
||||||
*/ |
|
||||||
|
|
||||||
template <typename nlist_type> |
|
||||||
int breakpad_nlist_common(const char *name, |
|
||||||
nlist_type *list, |
|
||||||
const char **symbolNames, |
|
||||||
cpu_type_t cpu_type) { |
|
||||||
int fd = open(name, O_RDONLY, 0); |
|
||||||
if (fd < 0) |
|
||||||
return -1; |
|
||||||
int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type); |
|
||||||
close(fd); |
|
||||||
return n; |
|
||||||
} |
|
||||||
|
|
||||||
int breakpad_nlist(const char *name, |
|
||||||
struct nlist *list, |
|
||||||
const char **symbolNames, |
|
||||||
cpu_type_t cpu_type) { |
|
||||||
return breakpad_nlist_common(name, list, symbolNames, cpu_type); |
|
||||||
} |
|
||||||
|
|
||||||
int breakpad_nlist(const char *name, |
|
||||||
struct nlist_64 *list, |
|
||||||
const char **symbolNames, |
|
||||||
cpu_type_t cpu_type) { |
|
||||||
return breakpad_nlist_common(name, list, symbolNames, cpu_type); |
|
||||||
} |
|
||||||
|
|
||||||
/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */ |
|
||||||
|
|
||||||
template<typename nlist_type> |
|
||||||
int __breakpad_fdnlist(int fd, nlist_type *list, const char **symbolNames, |
|
||||||
cpu_type_t cpu_type) { |
|
||||||
typedef typename MachBits<nlist_type>::mach_header_type mach_header_type; |
|
||||||
typedef typename MachBits<nlist_type>::word_type word_type; |
|
||||||
|
|
||||||
const uint32_t magic = MachBits<nlist_type>::magic; |
|
||||||
|
|
||||||
int maxlen = 500; |
|
||||||
int nreq = 0; |
|
||||||
for (nlist_type* q = list; |
|
||||||
symbolNames[q-list] && symbolNames[q-list][0]; |
|
||||||
q++, nreq++) { |
|
||||||
|
|
||||||
q->n_type = 0; |
|
||||||
q->n_value = 0; |
|
||||||
q->n_desc = 0; |
|
||||||
q->n_sect = 0; |
|
||||||
q->n_un.n_strx = 0; |
|
||||||
} |
|
||||||
|
|
||||||
struct exec buf; |
|
||||||
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf) || |
|
||||||
(N_BADMAG(buf) && *((long *)&buf) != magic && |
|
||||||
NXSwapBigLongToHost(*((long *)&buf)) != FAT_MAGIC) && |
|
||||||
/* The following is the big-endian ppc64 check */ |
|
||||||
(*((long*)&buf)) != FAT_MAGIC) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/* Deal with fat file if necessary */ |
|
||||||
unsigned arch_offset = 0; |
|
||||||
if (NXSwapBigLongToHost(*((long *)&buf)) == FAT_MAGIC || |
|
||||||
/* The following is the big-endian ppc64 check */ |
|
||||||
*((unsigned int *)&buf) == FAT_MAGIC) { |
|
||||||
/* Get host info */ |
|
||||||
host_t host = mach_host_self(); |
|
||||||
unsigned i = HOST_BASIC_INFO_COUNT; |
|
||||||
struct host_basic_info hbi; |
|
||||||
kern_return_t kr; |
|
||||||
if ((kr = host_info(host, HOST_BASIC_INFO, |
|
||||||
(host_info_t)(&hbi), &i)) != KERN_SUCCESS) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
mach_port_deallocate(mach_task_self(), host); |
|
||||||
|
|
||||||
/* Read in the fat header */ |
|
||||||
struct fat_header fh; |
|
||||||
if (lseek(fd, 0, SEEK_SET) == -1) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
if (read(fd, (char *)&fh, sizeof(fh)) != sizeof(fh)) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/* Convert fat_narchs to host byte order */ |
|
||||||
fh.nfat_arch = NXSwapBigIntToHost(fh.nfat_arch); |
|
||||||
|
|
||||||
/* Read in the fat archs */ |
|
||||||
struct fat_arch *fat_archs = |
|
||||||
(struct fat_arch *)malloc(fh.nfat_arch * sizeof(struct fat_arch)); |
|
||||||
if (fat_archs == NULL) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
if (read(fd, (char *)fat_archs, |
|
||||||
sizeof(struct fat_arch) * fh.nfat_arch) != |
|
||||||
(ssize_t)sizeof(struct fat_arch) * fh.nfat_arch) { |
|
||||||
free(fat_archs); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* Convert archs to host byte ordering (a constraint of |
|
||||||
* cpusubtype_getbestarch() |
|
||||||
*/ |
|
||||||
for (unsigned i = 0; i < fh.nfat_arch; i++) { |
|
||||||
fat_archs[i].cputype = |
|
||||||
NXSwapBigIntToHost(fat_archs[i].cputype); |
|
||||||
fat_archs[i].cpusubtype = |
|
||||||
NXSwapBigIntToHost(fat_archs[i].cpusubtype); |
|
||||||
fat_archs[i].offset = |
|
||||||
NXSwapBigIntToHost(fat_archs[i].offset); |
|
||||||
fat_archs[i].size = |
|
||||||
NXSwapBigIntToHost(fat_archs[i].size); |
|
||||||
fat_archs[i].align = |
|
||||||
NXSwapBigIntToHost(fat_archs[i].align); |
|
||||||
} |
|
||||||
|
|
||||||
struct fat_arch *fap = NULL; |
|
||||||
for (unsigned i = 0; i < fh.nfat_arch; i++) { |
|
||||||
if (fat_archs[i].cputype == cpu_type) { |
|
||||||
fap = &fat_archs[i]; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (!fap) { |
|
||||||
free(fat_archs); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
arch_offset = fap->offset; |
|
||||||
free(fat_archs); |
|
||||||
|
|
||||||
/* Read in the beginning of the architecture-specific file */ |
|
||||||
if (lseek(fd, arch_offset, SEEK_SET) == -1) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
if (read(fd, (char *)&buf, sizeof(buf)) != sizeof(buf)) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
off_t sa; /* symbol address */ |
|
||||||
off_t ss; /* start of strings */ |
|
||||||
register register_t n; |
|
||||||
if (*((unsigned int *)&buf) == magic) { |
|
||||||
if (lseek(fd, arch_offset, SEEK_SET) == -1) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
mach_header_type mh; |
|
||||||
if (read(fd, (char *)&mh, sizeof(mh)) != sizeof(mh)) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
struct load_command *load_commands = |
|
||||||
(struct load_command *)malloc(mh.sizeofcmds); |
|
||||||
if (load_commands == NULL) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
if (read(fd, (char *)load_commands, mh.sizeofcmds) != |
|
||||||
mh.sizeofcmds) { |
|
||||||
free(load_commands); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
struct symtab_command *stp = NULL; |
|
||||||
struct load_command *lcp = load_commands; |
|
||||||
// iterate through all load commands, looking for
|
|
||||||
// LC_SYMTAB load command
|
|
||||||
for (long i = 0; i < mh.ncmds; i++) { |
|
||||||
if (lcp->cmdsize % sizeof(word_type) != 0 || |
|
||||||
lcp->cmdsize <= 0 || |
|
||||||
(char *)lcp + lcp->cmdsize > |
|
||||||
(char *)load_commands + mh.sizeofcmds) { |
|
||||||
free(load_commands); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
if (lcp->cmd == LC_SYMTAB) { |
|
||||||
if (lcp->cmdsize != |
|
||||||
sizeof(struct symtab_command)) { |
|
||||||
free(load_commands); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
stp = (struct symtab_command *)lcp; |
|
||||||
break; |
|
||||||
} |
|
||||||
lcp = (struct load_command *) |
|
||||||
((char *)lcp + lcp->cmdsize); |
|
||||||
} |
|
||||||
if (stp == NULL) { |
|
||||||
free(load_commands); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
// sa points to the beginning of the symbol table
|
|
||||||
sa = stp->symoff + arch_offset; |
|
||||||
// ss points to the beginning of the string table
|
|
||||||
ss = stp->stroff + arch_offset; |
|
||||||
// n is the number of bytes in the symbol table
|
|
||||||
// each symbol table entry is an nlist structure
|
|
||||||
n = stp->nsyms * sizeof(nlist_type); |
|
||||||
free(load_commands); |
|
||||||
} else { |
|
||||||
sa = N_SYMOFF(buf) + arch_offset; |
|
||||||
ss = sa + buf.a_syms + arch_offset; |
|
||||||
n = buf.a_syms; |
|
||||||
} |
|
||||||
|
|
||||||
if (lseek(fd, sa, SEEK_SET) == -1) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
// the algorithm here is to read the nlist entries in m-sized
|
|
||||||
// chunks into q. q is then iterated over. for each entry in q,
|
|
||||||
// use the string table index(q->n_un.n_strx) to read the symbol
|
|
||||||
// name, then scan the nlist entries passed in by the user(via p),
|
|
||||||
// and look for a match
|
|
||||||
while (n) { |
|
||||||
nlist_type space[BUFSIZ/sizeof (nlist_type)]; |
|
||||||
register register_t m = sizeof (space); |
|
||||||
|
|
||||||
if (n < m) |
|
||||||
m = n; |
|
||||||
if (read(fd, (char *)space, m) != m) |
|
||||||
break; |
|
||||||
n -= m; |
|
||||||
long savpos = lseek(fd, 0, SEEK_CUR); |
|
||||||
if (savpos == -1) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) { |
|
||||||
char nambuf[BUFSIZ]; |
|
||||||
|
|
||||||
if (q->n_un.n_strx == 0 || q->n_type & N_STAB) |
|
||||||
continue; |
|
||||||
|
|
||||||
// seek to the location in the binary where the symbol
|
|
||||||
// name is stored & read it into memory
|
|
||||||
if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
if (read(fd, nambuf, maxlen+1) == -1) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
const char *s2 = nambuf; |
|
||||||
for (nlist_type *p = list;
|
|
||||||
symbolNames[p-list] && symbolNames[p-list][0]; |
|
||||||
p++) { |
|
||||||
// get the symbol name the user has passed in that
|
|
||||||
// corresponds to the nlist entry that we're looking at
|
|
||||||
const char *s1 = symbolNames[p - list]; |
|
||||||
while (*s1) { |
|
||||||
if (*s1++ != *s2++) |
|
||||||
goto cont; |
|
||||||
} |
|
||||||
if (*s2) |
|
||||||
goto cont; |
|
||||||
|
|
||||||
p->n_value = q->n_value; |
|
||||||
p->n_type = q->n_type; |
|
||||||
p->n_desc = q->n_desc; |
|
||||||
p->n_sect = q->n_sect; |
|
||||||
p->n_un.n_strx = q->n_un.n_strx; |
|
||||||
if (--nreq == 0) |
|
||||||
return nreq; |
|
||||||
|
|
||||||
break; |
|
||||||
cont: ; |
|
||||||
} |
|
||||||
} |
|
||||||
if (lseek(fd, savpos, SEEK_SET) == -1) { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
} |
|
||||||
return nreq; |
|
||||||
} |
|
@ -1,47 +0,0 @@ |
|||||||
// Copyright (c) 2008, 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.
|
|
||||||
|
|
||||||
// breakpad_nlist.h
|
|
||||||
//
|
|
||||||
// This file is meant to provide a header for clients of the modified
|
|
||||||
// nlist function implemented to work on 64-bit.
|
|
||||||
|
|
||||||
#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ |
|
||||||
|
|
||||||
#include <mach/machine.h> |
|
||||||
|
|
||||||
int breakpad_nlist(const char *name, |
|
||||||
struct nlist *list, |
|
||||||
const char **symbolNames, |
|
||||||
cpu_type_t cpu_type); |
|
||||||
int breakpad_nlist(const char *name, |
|
||||||
struct nlist_64 *list, |
|
||||||
const char **symbolNames, |
|
||||||
cpu_type_t cpu_type); |
|
||||||
|
|
||||||
#endif /* CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ */ |
|
@ -1,567 +0,0 @@ |
|||||||
// Copyright (c) 2007, 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 "client/mac/handler/dynamic_images.h" |
|
||||||
|
|
||||||
extern "C" { // needed to compile on Leopard
|
|
||||||
#include <mach-o/nlist.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdio.h> |
|
||||||
} |
|
||||||
|
|
||||||
#include "breakpad_nlist_64.h" |
|
||||||
#include <AvailabilityMacros.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <CoreServices/CoreServices.h> |
|
||||||
#include <dlfcn.h> |
|
||||||
#include <mach/mach_vm.h> |
|
||||||
#include <mach/task_info.h> |
|
||||||
#include <sys/sysctl.h> |
|
||||||
|
|
||||||
#include <algorithm> |
|
||||||
#include <string> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
#ifndef MAC_OS_X_VERSION_10_6 |
|
||||||
#define MAC_OS_X_VERSION_10_6 1060 |
|
||||||
#endif |
|
||||||
|
|
||||||
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 |
|
||||||
|
|
||||||
// Fallback declarations for TASK_DYLD_INFO and friends, introduced in
|
|
||||||
// <mach/task_info.h> in the Mac OS X 10.6 SDK.
|
|
||||||
#define TASK_DYLD_INFO 17 |
|
||||||
struct task_dyld_info { |
|
||||||
mach_vm_address_t all_image_info_addr; |
|
||||||
mach_vm_size_t all_image_info_size; |
|
||||||
}; |
|
||||||
typedef struct task_dyld_info task_dyld_info_data_t; |
|
||||||
typedef struct task_dyld_info *task_dyld_info_t; |
|
||||||
#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t)) |
|
||||||
|
|
||||||
#endif |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
using std::string; |
|
||||||
using std::vector; |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// Returns the size of the memory region containing |address| and the
|
|
||||||
// number of bytes from |address| to the end of the region.
|
|
||||||
// We potentially, will extend the size of the original
|
|
||||||
// region by the size of the following region if it's contiguous with the
|
|
||||||
// first in order to handle cases when we're reading strings and they
|
|
||||||
// straddle two vm regions.
|
|
||||||
//
|
|
||||||
static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task, |
|
||||||
const uint64_t address, |
|
||||||
mach_vm_size_t *size_to_end) { |
|
||||||
mach_vm_address_t region_base = (mach_vm_address_t)address; |
|
||||||
mach_vm_size_t region_size; |
|
||||||
natural_t nesting_level = 0; |
|
||||||
vm_region_submap_info_64 submap_info; |
|
||||||
mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; |
|
||||||
|
|
||||||
// Get information about the vm region containing |address|
|
|
||||||
vm_region_recurse_info_t region_info; |
|
||||||
region_info = reinterpret_cast<vm_region_recurse_info_t>(&submap_info); |
|
||||||
|
|
||||||
kern_return_t result = |
|
||||||
mach_vm_region_recurse(target_task, |
|
||||||
®ion_base, |
|
||||||
®ion_size, |
|
||||||
&nesting_level, |
|
||||||
region_info, |
|
||||||
&info_count); |
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
// Get distance from |address| to the end of this region
|
|
||||||
*size_to_end = region_base + region_size -(mach_vm_address_t)address; |
|
||||||
|
|
||||||
// If we want to handle strings as long as 4096 characters we may need
|
|
||||||
// to check if there's a vm region immediately following the first one.
|
|
||||||
// If so, we need to extend |*size_to_end| to go all the way to the end
|
|
||||||
// of the second region.
|
|
||||||
if (*size_to_end < 4096) { |
|
||||||
// Second region starts where the first one ends
|
|
||||||
mach_vm_address_t region_base2 = |
|
||||||
(mach_vm_address_t)(region_base + region_size); |
|
||||||
mach_vm_size_t region_size2; |
|
||||||
|
|
||||||
// Get information about the following vm region
|
|
||||||
result = |
|
||||||
mach_vm_region_recurse(target_task, |
|
||||||
®ion_base2, |
|
||||||
®ion_size2, |
|
||||||
&nesting_level, |
|
||||||
region_info, |
|
||||||
&info_count); |
|
||||||
|
|
||||||
// Extend region_size to go all the way to the end of the 2nd region
|
|
||||||
if (result == KERN_SUCCESS |
|
||||||
&& region_base2 == region_base + region_size) { |
|
||||||
region_size += region_size2; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
*size_to_end = region_base + region_size -(mach_vm_address_t)address; |
|
||||||
} else { |
|
||||||
region_size = 0; |
|
||||||
*size_to_end = 0; |
|
||||||
} |
|
||||||
|
|
||||||
return region_size; |
|
||||||
} |
|
||||||
|
|
||||||
#define kMaxStringLength 8192 |
|
||||||
//==============================================================================
|
|
||||||
// Reads a NULL-terminated string from another task.
|
|
||||||
//
|
|
||||||
// Warning! This will not read any strings longer than kMaxStringLength-1
|
|
||||||
//
|
|
||||||
static string ReadTaskString(task_port_t target_task, |
|
||||||
const uint64_t address) { |
|
||||||
// The problem is we don't know how much to read until we know how long
|
|
||||||
// the string is. And we don't know how long the string is, until we've read
|
|
||||||
// the memory! So, we'll try to read kMaxStringLength bytes
|
|
||||||
// (or as many bytes as we can until we reach the end of the vm region).
|
|
||||||
mach_vm_size_t size_to_end; |
|
||||||
GetMemoryRegionSize(target_task, address, &size_to_end); |
|
||||||
|
|
||||||
if (size_to_end > 0) { |
|
||||||
mach_vm_size_t size_to_read = |
|
||||||
size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end; |
|
||||||
|
|
||||||
vector<uint8_t> bytes; |
|
||||||
if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) != |
|
||||||
KERN_SUCCESS) |
|
||||||
return string(); |
|
||||||
|
|
||||||
return string(reinterpret_cast<const char*>(&bytes[0])); |
|
||||||
} |
|
||||||
|
|
||||||
return string(); |
|
||||||
} |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// Reads an address range from another task. The bytes read will be returned
|
|
||||||
// in bytes, which will be resized as necessary.
|
|
||||||
kern_return_t ReadTaskMemory(task_port_t target_task, |
|
||||||
const uint64_t address, |
|
||||||
size_t length, |
|
||||||
vector<uint8_t> &bytes) { |
|
||||||
int systemPageSize = getpagesize(); |
|
||||||
|
|
||||||
// use the negative of the page size for the mask to find the page address
|
|
||||||
mach_vm_address_t page_address = address & (-systemPageSize); |
|
||||||
|
|
||||||
mach_vm_address_t last_page_address = |
|
||||||
(address + length + (systemPageSize - 1)) & (-systemPageSize); |
|
||||||
|
|
||||||
mach_vm_size_t page_size = last_page_address - page_address; |
|
||||||
uint8_t* local_start; |
|
||||||
uint32_t local_length; |
|
||||||
|
|
||||||
kern_return_t r = mach_vm_read(target_task, |
|
||||||
page_address, |
|
||||||
page_size, |
|
||||||
reinterpret_cast<vm_offset_t*>(&local_start), |
|
||||||
&local_length); |
|
||||||
|
|
||||||
if (r != KERN_SUCCESS) |
|
||||||
return r; |
|
||||||
|
|
||||||
bytes.resize(length); |
|
||||||
memcpy(&bytes[0], |
|
||||||
&local_start[(mach_vm_address_t)address - page_address], |
|
||||||
length); |
|
||||||
mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length); |
|
||||||
return KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
#pragma mark - |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// Traits structs for specializing function templates to handle
|
|
||||||
// 32-bit/64-bit Mach-O files.
|
|
||||||
struct MachO32 { |
|
||||||
typedef mach_header mach_header_type; |
|
||||||
typedef segment_command mach_segment_command_type; |
|
||||||
typedef dyld_image_info32 dyld_image_info; |
|
||||||
typedef dyld_all_image_infos32 dyld_all_image_infos; |
|
||||||
typedef struct nlist nlist_type; |
|
||||||
static const uint32_t magic = MH_MAGIC; |
|
||||||
static const uint32_t segment_load_command = LC_SEGMENT; |
|
||||||
}; |
|
||||||
|
|
||||||
struct MachO64 { |
|
||||||
typedef mach_header_64 mach_header_type; |
|
||||||
typedef segment_command_64 mach_segment_command_type; |
|
||||||
typedef dyld_image_info64 dyld_image_info; |
|
||||||
typedef dyld_all_image_infos64 dyld_all_image_infos; |
|
||||||
typedef struct nlist_64 nlist_type; |
|
||||||
static const uint32_t magic = MH_MAGIC_64; |
|
||||||
static const uint32_t segment_load_command = LC_SEGMENT_64; |
|
||||||
}; |
|
||||||
|
|
||||||
template<typename MachBits> |
|
||||||
bool FindTextSection(DynamicImage& image) { |
|
||||||
typedef typename MachBits::mach_header_type mach_header_type; |
|
||||||
typedef typename MachBits::mach_segment_command_type |
|
||||||
mach_segment_command_type; |
|
||||||
|
|
||||||
const mach_header_type* header = |
|
||||||
reinterpret_cast<const mach_header_type*>(&image.header_[0]); |
|
||||||
|
|
||||||
if(header->magic != MachBits::magic) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
const struct load_command *cmd = |
|
||||||
reinterpret_cast<const struct load_command *>(header + 1); |
|
||||||
|
|
||||||
bool found_text_section = false; |
|
||||||
bool found_dylib_id_command = false; |
|
||||||
for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) { |
|
||||||
if (!found_text_section) { |
|
||||||
if (cmd->cmd == MachBits::segment_load_command) { |
|
||||||
const mach_segment_command_type *seg = |
|
||||||
reinterpret_cast<const mach_segment_command_type *>(cmd); |
|
||||||
|
|
||||||
if (!strcmp(seg->segname, "__TEXT")) { |
|
||||||
image.vmaddr_ = seg->vmaddr; |
|
||||||
image.vmsize_ = seg->vmsize; |
|
||||||
image.slide_ = 0; |
|
||||||
|
|
||||||
if (seg->fileoff == 0 && seg->filesize != 0) { |
|
||||||
image.slide_ = |
|
||||||
(uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr; |
|
||||||
} |
|
||||||
found_text_section = true; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (!found_dylib_id_command) { |
|
||||||
if (cmd->cmd == LC_ID_DYLIB) { |
|
||||||
const struct dylib_command *dc = |
|
||||||
reinterpret_cast<const struct dylib_command *>(cmd); |
|
||||||
|
|
||||||
image.version_ = dc->dylib.current_version; |
|
||||||
found_dylib_id_command = true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (found_dylib_id_command && found_text_section) { |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
cmd = reinterpret_cast<const struct load_command *> |
|
||||||
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize); |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// Initializes vmaddr_, vmsize_, and slide_
|
|
||||||
void DynamicImage::CalculateMemoryAndVersionInfo() { |
|
||||||
// unless we can process the header, ensure that calls to
|
|
||||||
// IsValid() will return false
|
|
||||||
vmaddr_ = 0; |
|
||||||
vmsize_ = 0; |
|
||||||
slide_ = 0; |
|
||||||
version_ = 0; |
|
||||||
|
|
||||||
// The function template above does all the real work.
|
|
||||||
if (Is64Bit()) |
|
||||||
FindTextSection<MachO64>(*this); |
|
||||||
else |
|
||||||
FindTextSection<MachO32>(*this); |
|
||||||
} |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// The helper function template abstracts the 32/64-bit differences.
|
|
||||||
template<typename MachBits> |
|
||||||
uint32_t GetFileTypeFromHeader(DynamicImage& image) { |
|
||||||
typedef typename MachBits::mach_header_type mach_header_type; |
|
||||||
|
|
||||||
const mach_header_type* header = |
|
||||||
reinterpret_cast<const mach_header_type*>(&image.header_[0]); |
|
||||||
return header->filetype; |
|
||||||
} |
|
||||||
|
|
||||||
uint32_t DynamicImage::GetFileType() { |
|
||||||
if (Is64Bit()) |
|
||||||
return GetFileTypeFromHeader<MachO64>(*this); |
|
||||||
|
|
||||||
return GetFileTypeFromHeader<MachO32>(*this); |
|
||||||
} |
|
||||||
|
|
||||||
#pragma mark - |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// Loads information about dynamically loaded code in the given task.
|
|
||||||
DynamicImages::DynamicImages(mach_port_t task) |
|
||||||
: task_(task), |
|
||||||
cpu_type_(DetermineTaskCPUType(task)), |
|
||||||
image_list_() { |
|
||||||
ReadImageInfoForTask(); |
|
||||||
} |
|
||||||
|
|
||||||
template<typename MachBits> |
|
||||||
static uint64_t LookupSymbol(const char* symbol_name, |
|
||||||
const char* filename, |
|
||||||
cpu_type_t cpu_type) { |
|
||||||
typedef typename MachBits::nlist_type nlist_type; |
|
||||||
|
|
||||||
nlist_type symbol_info[8] = {}; |
|
||||||
const char *symbolNames[2] = { symbol_name, "\0" }; |
|
||||||
nlist_type &list = symbol_info[0]; |
|
||||||
int invalidEntriesCount = breakpad_nlist(filename, |
|
||||||
&list, |
|
||||||
symbolNames, |
|
||||||
cpu_type); |
|
||||||
|
|
||||||
if(invalidEntriesCount != 0) { |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
assert(list.n_value); |
|
||||||
return list.n_value; |
|
||||||
} |
|
||||||
|
|
||||||
static SInt32 GetOSVersionInternal() { |
|
||||||
SInt32 os_version = 0; |
|
||||||
Gestalt(gestaltSystemVersion, &os_version); |
|
||||||
return os_version; |
|
||||||
} |
|
||||||
|
|
||||||
static SInt32 GetOSVersion() { |
|
||||||
static SInt32 os_version = GetOSVersionInternal(); |
|
||||||
return os_version; |
|
||||||
} |
|
||||||
|
|
||||||
static bool IsSnowLeopardOrLater() { |
|
||||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 |
|
||||||
return true; |
|
||||||
#else |
|
||||||
return GetOSVersion() >= 0x1060; |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
uint64_t DynamicImages::GetDyldAllImageInfosPointer() { |
|
||||||
if (IsSnowLeopardOrLater()) { |
|
||||||
task_dyld_info_data_t task_dyld_info; |
|
||||||
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; |
|
||||||
if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, |
|
||||||
&count) != KERN_SUCCESS) { |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
return (uint64_t)task_dyld_info.all_image_info_addr; |
|
||||||
} else { |
|
||||||
const char *imageSymbolName = "_dyld_all_image_infos"; |
|
||||||
const char *dyldPath = "/usr/lib/dyld"; |
|
||||||
|
|
||||||
if (Is64Bit()) |
|
||||||
return LookupSymbol<MachO64>(imageSymbolName, dyldPath, cpu_type_); |
|
||||||
return LookupSymbol<MachO32>(imageSymbolName, dyldPath, cpu_type_); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// This code was written using dyld_debug.c (from Darwin) as a guide.
|
|
||||||
|
|
||||||
template<typename MachBits> |
|
||||||
void ReadImageInfo(DynamicImages& images, |
|
||||||
uint64_t image_list_address) { |
|
||||||
typedef typename MachBits::dyld_image_info dyld_image_info; |
|
||||||
typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos; |
|
||||||
typedef typename MachBits::mach_header_type mach_header_type; |
|
||||||
|
|
||||||
// Read the structure inside of dyld that contains information about
|
|
||||||
// loaded images. We're reading from the desired task's address space.
|
|
||||||
|
|
||||||
// Here we make the assumption that dyld loaded at the same address in
|
|
||||||
// the crashed process vs. this one. This is an assumption made in
|
|
||||||
// "dyld_debug.c" and is said to be nearly always valid.
|
|
||||||
vector<uint8_t> dyld_all_info_bytes; |
|
||||||
if (ReadTaskMemory(images.task_, |
|
||||||
image_list_address, |
|
||||||
sizeof(dyld_all_image_infos), |
|
||||||
dyld_all_info_bytes) != KERN_SUCCESS) |
|
||||||
return; |
|
||||||
|
|
||||||
dyld_all_image_infos *dyldInfo = |
|
||||||
reinterpret_cast<dyld_all_image_infos*>(&dyld_all_info_bytes[0]); |
|
||||||
|
|
||||||
// number of loaded images
|
|
||||||
int count = dyldInfo->infoArrayCount; |
|
||||||
|
|
||||||
// Read an array of dyld_image_info structures each containing
|
|
||||||
// information about a loaded image.
|
|
||||||
vector<uint8_t> dyld_info_array_bytes; |
|
||||||
if (ReadTaskMemory(images.task_, |
|
||||||
dyldInfo->infoArray, |
|
||||||
count * sizeof(dyld_image_info), |
|
||||||
dyld_info_array_bytes) != KERN_SUCCESS) |
|
||||||
return; |
|
||||||
|
|
||||||
dyld_image_info *infoArray = |
|
||||||
reinterpret_cast<dyld_image_info*>(&dyld_info_array_bytes[0]); |
|
||||||
images.image_list_.reserve(count); |
|
||||||
|
|
||||||
for (int i = 0; i < count; ++i) { |
|
||||||
dyld_image_info &info = infoArray[i]; |
|
||||||
|
|
||||||
// First read just the mach_header from the image in the task.
|
|
||||||
vector<uint8_t> mach_header_bytes; |
|
||||||
if (ReadTaskMemory(images.task_, |
|
||||||
info.load_address_, |
|
||||||
sizeof(mach_header_type), |
|
||||||
mach_header_bytes) != KERN_SUCCESS) |
|
||||||
continue; // bail on this dynamic image
|
|
||||||
|
|
||||||
mach_header_type *header = |
|
||||||
reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]); |
|
||||||
|
|
||||||
// Now determine the total amount necessary to read the header
|
|
||||||
// plus all of the load commands.
|
|
||||||
size_t header_size = |
|
||||||
sizeof(mach_header_type) + header->sizeofcmds; |
|
||||||
|
|
||||||
if (ReadTaskMemory(images.task_, |
|
||||||
info.load_address_, |
|
||||||
header_size, |
|
||||||
mach_header_bytes) != KERN_SUCCESS) |
|
||||||
continue; |
|
||||||
|
|
||||||
header = reinterpret_cast<mach_header_type*>(&mach_header_bytes[0]); |
|
||||||
|
|
||||||
// Read the file name from the task's memory space.
|
|
||||||
string file_path; |
|
||||||
if (info.file_path_) { |
|
||||||
// Although we're reading kMaxStringLength bytes, it's copied in the
|
|
||||||
// the DynamicImage constructor below with the correct string length,
|
|
||||||
// so it's not really wasting memory.
|
|
||||||
file_path = ReadTaskString(images.task_, info.file_path_); |
|
||||||
} |
|
||||||
|
|
||||||
// Create an object representing this image and add it to our list.
|
|
||||||
DynamicImage *new_image; |
|
||||||
new_image = new DynamicImage(&mach_header_bytes[0], |
|
||||||
header_size, |
|
||||||
info.load_address_, |
|
||||||
file_path, |
|
||||||
info.file_mod_date_, |
|
||||||
images.task_, |
|
||||||
images.cpu_type_); |
|
||||||
|
|
||||||
if (new_image->IsValid()) { |
|
||||||
images.image_list_.push_back(DynamicImageRef(new_image)); |
|
||||||
} else { |
|
||||||
delete new_image; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// sorts based on loading address
|
|
||||||
sort(images.image_list_.begin(), images.image_list_.end()); |
|
||||||
// remove duplicates - this happens in certain strange cases
|
|
||||||
// You can see it in DashboardClient when Google Gadgets plugin
|
|
||||||
// is installed. Apple's crash reporter log and gdb "info shared"
|
|
||||||
// both show the same library multiple times at the same address
|
|
||||||
|
|
||||||
vector<DynamicImageRef>::iterator it = unique(images.image_list_.begin(), |
|
||||||
images.image_list_.end()); |
|
||||||
images.image_list_.erase(it, images.image_list_.end()); |
|
||||||
} |
|
||||||
|
|
||||||
void DynamicImages::ReadImageInfoForTask() { |
|
||||||
uint64_t imageList = GetDyldAllImageInfosPointer(); |
|
||||||
|
|
||||||
if (imageList) { |
|
||||||
if (Is64Bit()) |
|
||||||
ReadImageInfo<MachO64>(*this, imageList); |
|
||||||
else |
|
||||||
ReadImageInfo<MachO32>(*this, imageList); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
DynamicImage *DynamicImages::GetExecutableImage() { |
|
||||||
int executable_index = GetExecutableImageIndex(); |
|
||||||
|
|
||||||
if (executable_index >= 0) { |
|
||||||
return GetImage(executable_index); |
|
||||||
} |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// returns -1 if failure to find executable
|
|
||||||
int DynamicImages::GetExecutableImageIndex() { |
|
||||||
int image_count = GetImageCount(); |
|
||||||
|
|
||||||
for (int i = 0; i < image_count; ++i) { |
|
||||||
DynamicImage *image = GetImage(i); |
|
||||||
if (image->GetFileType() == MH_EXECUTE) { |
|
||||||
return i; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// static
|
|
||||||
cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) { |
|
||||||
if (task == mach_task_self()) |
|
||||||
return GetNativeCPUType(); |
|
||||||
|
|
||||||
int mib[CTL_MAXNAME]; |
|
||||||
size_t mibLen = CTL_MAXNAME; |
|
||||||
int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen); |
|
||||||
if (err == 0) { |
|
||||||
assert(mibLen < CTL_MAXNAME); |
|
||||||
pid_for_task(task, &mib[mibLen]); |
|
||||||
mibLen += 1; |
|
||||||
|
|
||||||
cpu_type_t cpu_type; |
|
||||||
size_t cpuTypeSize = sizeof(cpu_type); |
|
||||||
sysctl(mib, mibLen, &cpu_type, &cpuTypeSize, 0, 0); |
|
||||||
return cpu_type; |
|
||||||
} |
|
||||||
|
|
||||||
return GetNativeCPUType(); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,313 +0,0 @@ |
|||||||
// Copyright (c) 2007, 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.
|
|
||||||
|
|
||||||
// dynamic_images.h
|
|
||||||
//
|
|
||||||
// Implements most of the function of the dyld API, but allowing an
|
|
||||||
// arbitrary task to be introspected, unlike the dyld API which
|
|
||||||
// only allows operation on the current task. The current implementation
|
|
||||||
// is limited to use by 32-bit tasks.
|
|
||||||
|
|
||||||
#ifndef CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ |
|
||||||
#define CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ |
|
||||||
|
|
||||||
#include <mach/mach.h> |
|
||||||
#include <mach-o/dyld.h> |
|
||||||
#include <mach-o/loader.h> |
|
||||||
#include <sys/types.h> |
|
||||||
|
|
||||||
#include <string> |
|
||||||
#include <vector> |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
using std::string; |
|
||||||
using std::vector; |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// The memory layout of this struct matches the dyld_image_info struct
|
|
||||||
// defined in "dyld_gdb.h" in the darwin source.
|
|
||||||
typedef struct dyld_image_info32 { |
|
||||||
uint32_t load_address_; // struct mach_header*
|
|
||||||
uint32_t file_path_; // char*
|
|
||||||
uint32_t file_mod_date_; |
|
||||||
} dyld_image_info32; |
|
||||||
|
|
||||||
typedef struct dyld_image_info64 { |
|
||||||
uint64_t load_address_; // struct mach_header*
|
|
||||||
uint64_t file_path_; // char*
|
|
||||||
uint64_t file_mod_date_; |
|
||||||
} dyld_image_info64; |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// This is as defined in "dyld_gdb.h" in the darwin source.
|
|
||||||
// _dyld_all_image_infos (in dyld) is a structure of this type
|
|
||||||
// which will be used to determine which dynamic code has been loaded.
|
|
||||||
typedef struct dyld_all_image_infos32 { |
|
||||||
uint32_t version; // == 1 in Mac OS X 10.4
|
|
||||||
uint32_t infoArrayCount; |
|
||||||
uint32_t infoArray; // const struct dyld_image_info*
|
|
||||||
uint32_t notification; |
|
||||||
bool processDetachedFromSharedRegion; |
|
||||||
} dyld_all_image_infos32; |
|
||||||
|
|
||||||
typedef struct dyld_all_image_infos64 { |
|
||||||
uint32_t version; // == 1 in Mac OS X 10.4
|
|
||||||
uint32_t infoArrayCount; |
|
||||||
uint64_t infoArray; // const struct dyld_image_info*
|
|
||||||
uint64_t notification; |
|
||||||
bool processDetachedFromSharedRegion; |
|
||||||
} dyld_all_image_infos64; |
|
||||||
|
|
||||||
// some typedefs to isolate 64/32 bit differences
|
|
||||||
#ifdef __LP64__ |
|
||||||
typedef mach_header_64 breakpad_mach_header; |
|
||||||
typedef segment_command_64 breakpad_mach_segment_command; |
|
||||||
#else |
|
||||||
typedef mach_header breakpad_mach_header; |
|
||||||
typedef segment_command breakpad_mach_segment_command; |
|
||||||
#endif |
|
||||||
|
|
||||||
// Helper functions to deal with 32-bit/64-bit Mach-O differences.
|
|
||||||
class DynamicImage; |
|
||||||
template<typename MachBits> |
|
||||||
bool FindTextSection(DynamicImage& image); |
|
||||||
|
|
||||||
template<typename MachBits> |
|
||||||
uint32_t GetFileTypeFromHeader(DynamicImage& image); |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// Represents a single dynamically loaded mach-o image
|
|
||||||
class DynamicImage { |
|
||||||
public: |
|
||||||
DynamicImage(uint8_t *header, // data is copied
|
|
||||||
size_t header_size, // includes load commands
|
|
||||||
uint64_t load_address, |
|
||||||
string file_path, |
|
||||||
uintptr_t image_mod_date, |
|
||||||
mach_port_t task, |
|
||||||
cpu_type_t cpu_type) |
|
||||||
: header_(header, header + header_size), |
|
||||||
header_size_(header_size), |
|
||||||
load_address_(load_address), |
|
||||||
vmaddr_(0), |
|
||||||
vmsize_(0), |
|
||||||
slide_(0), |
|
||||||
version_(0), |
|
||||||
file_path_(file_path), |
|
||||||
file_mod_date_(image_mod_date), |
|
||||||
task_(task), |
|
||||||
cpu_type_(cpu_type) { |
|
||||||
CalculateMemoryAndVersionInfo(); |
|
||||||
} |
|
||||||
|
|
||||||
// Size of mach_header plus load commands
|
|
||||||
size_t GetHeaderSize() const {return header_.size();} |
|
||||||
|
|
||||||
// Full path to mach-o binary
|
|
||||||
string GetFilePath() {return file_path_;} |
|
||||||
|
|
||||||
uint64_t GetModDate() const {return file_mod_date_;} |
|
||||||
|
|
||||||
// Actual address where the image was loaded
|
|
||||||
uint64_t GetLoadAddress() const {return load_address_;} |
|
||||||
|
|
||||||
// Address where the image should be loaded
|
|
||||||
mach_vm_address_t GetVMAddr() const {return vmaddr_;} |
|
||||||
|
|
||||||
// Difference between GetLoadAddress() and GetVMAddr()
|
|
||||||
ptrdiff_t GetVMAddrSlide() const {return slide_;} |
|
||||||
|
|
||||||
// Size of the image
|
|
||||||
mach_vm_size_t GetVMSize() const {return vmsize_;} |
|
||||||
|
|
||||||
// Task owning this loaded image
|
|
||||||
mach_port_t GetTask() {return task_;} |
|
||||||
|
|
||||||
// CPU type of the task
|
|
||||||
cpu_type_t GetCPUType() {return cpu_type_;} |
|
||||||
|
|
||||||
// filetype from the Mach-O header.
|
|
||||||
uint32_t GetFileType(); |
|
||||||
|
|
||||||
// Return true if the task is a 64-bit architecture.
|
|
||||||
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; } |
|
||||||
|
|
||||||
uint32_t GetVersion() {return version_;} |
|
||||||
// For sorting
|
|
||||||
bool operator<(const DynamicImage &inInfo) { |
|
||||||
return GetLoadAddress() < inInfo.GetLoadAddress(); |
|
||||||
} |
|
||||||
|
|
||||||
// Sanity checking
|
|
||||||
bool IsValid() {return GetVMSize() != 0;} |
|
||||||
|
|
||||||
private: |
|
||||||
DynamicImage(const DynamicImage &); |
|
||||||
DynamicImage &operator=(const DynamicImage &); |
|
||||||
|
|
||||||
friend class DynamicImages; |
|
||||||
template<typename MachBits> |
|
||||||
friend bool FindTextSection(DynamicImage& image); |
|
||||||
template<typename MachBits> |
|
||||||
friend uint32_t GetFileTypeFromHeader(DynamicImage& image); |
|
||||||
|
|
||||||
// Initializes vmaddr_, vmsize_, and slide_
|
|
||||||
void CalculateMemoryAndVersionInfo(); |
|
||||||
|
|
||||||
const vector<uint8_t> header_; // our local copy of the header
|
|
||||||
size_t header_size_; // mach_header plus load commands
|
|
||||||
uint64_t load_address_; // base address image is mapped into
|
|
||||||
mach_vm_address_t vmaddr_; |
|
||||||
mach_vm_size_t vmsize_; |
|
||||||
ptrdiff_t slide_; |
|
||||||
uint32_t version_; // Dylib version
|
|
||||||
string file_path_; // path dyld used to load the image
|
|
||||||
uintptr_t file_mod_date_; // time_t of image file
|
|
||||||
|
|
||||||
mach_port_t task_; |
|
||||||
cpu_type_t cpu_type_; // CPU type of task_
|
|
||||||
}; |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// DynamicImageRef is just a simple wrapper for a pointer to
|
|
||||||
// DynamicImage. The reason we use it instead of a simple typedef is so
|
|
||||||
// that we can use stl::sort() on a vector of DynamicImageRefs
|
|
||||||
// and simple class pointers can't implement operator<().
|
|
||||||
//
|
|
||||||
class DynamicImageRef { |
|
||||||
public: |
|
||||||
explicit DynamicImageRef(DynamicImage *inP) : p(inP) {} |
|
||||||
// The copy constructor is required by STL
|
|
||||||
DynamicImageRef(const DynamicImageRef &inRef) : p(inRef.p) {} |
|
||||||
|
|
||||||
bool operator<(const DynamicImageRef &inRef) const { |
|
||||||
return (*const_cast<DynamicImageRef*>(this)->p) |
|
||||||
< (*const_cast<DynamicImageRef&>(inRef).p); |
|
||||||
} |
|
||||||
|
|
||||||
bool operator==(const DynamicImageRef &inInfo) const { |
|
||||||
return (*const_cast<DynamicImageRef*>(this)->p).GetLoadAddress() == |
|
||||||
(*const_cast<DynamicImageRef&>(inInfo)).GetLoadAddress(); |
|
||||||
} |
|
||||||
|
|
||||||
// Be just like DynamicImage*
|
|
||||||
DynamicImage *operator->() {return p;} |
|
||||||
operator DynamicImage*() {return p;} |
|
||||||
|
|
||||||
private: |
|
||||||
DynamicImage *p; |
|
||||||
}; |
|
||||||
|
|
||||||
// Helper function to deal with 32-bit/64-bit Mach-O differences.
|
|
||||||
class DynamicImages; |
|
||||||
template<typename MachBits> |
|
||||||
void ReadImageInfo(DynamicImages& images, uint64_t image_list_address); |
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
// An object of type DynamicImages may be created to allow introspection of
|
|
||||||
// an arbitrary task's dynamically loaded mach-o binaries. This makes the
|
|
||||||
// assumption that the current task has send rights to the target task.
|
|
||||||
class DynamicImages { |
|
||||||
public: |
|
||||||
explicit DynamicImages(mach_port_t task); |
|
||||||
|
|
||||||
~DynamicImages() { |
|
||||||
for (int i = 0; i < GetImageCount(); ++i) { |
|
||||||
delete image_list_[i]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Returns the number of dynamically loaded mach-o images.
|
|
||||||
int GetImageCount() const {return static_cast<int>(image_list_.size());} |
|
||||||
|
|
||||||
// Returns an individual image.
|
|
||||||
DynamicImage *GetImage(int i) { |
|
||||||
if (i < (int)image_list_.size()) { |
|
||||||
return image_list_[i]; |
|
||||||
} |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
// Returns the image corresponding to the main executable.
|
|
||||||
DynamicImage *GetExecutableImage(); |
|
||||||
int GetExecutableImageIndex(); |
|
||||||
|
|
||||||
// Returns the task which we're looking at.
|
|
||||||
mach_port_t GetTask() const {return task_;} |
|
||||||
|
|
||||||
// CPU type of the task
|
|
||||||
cpu_type_t GetCPUType() {return cpu_type_;} |
|
||||||
|
|
||||||
// Return true if the task is a 64-bit architecture.
|
|
||||||
bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; } |
|
||||||
|
|
||||||
// Determine the CPU type of the task being dumped.
|
|
||||||
static cpu_type_t DetermineTaskCPUType(task_t task); |
|
||||||
|
|
||||||
// Get the native CPU type of this task.
|
|
||||||
static cpu_type_t GetNativeCPUType() { |
|
||||||
#if defined(__i386__) |
|
||||||
return CPU_TYPE_I386; |
|
||||||
#elif defined(__x86_64__) |
|
||||||
return CPU_TYPE_X86_64; |
|
||||||
#elif defined(__ppc__) |
|
||||||
return CPU_TYPE_POWERPC; |
|
||||||
#elif defined(__ppc64__) |
|
||||||
return CPU_TYPE_POWERPC64; |
|
||||||
#else |
|
||||||
#error "GetNativeCPUType not implemented for this architecture" |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
template<typename MachBits> |
|
||||||
friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address); |
|
||||||
|
|
||||||
bool IsOurTask() {return task_ == mach_task_self();} |
|
||||||
|
|
||||||
// Initialization
|
|
||||||
void ReadImageInfoForTask(); |
|
||||||
uint64_t GetDyldAllImageInfosPointer(); |
|
||||||
|
|
||||||
mach_port_t task_; |
|
||||||
cpu_type_t cpu_type_; // CPU type of task_
|
|
||||||
vector<DynamicImageRef> image_list_; |
|
||||||
}; |
|
||||||
|
|
||||||
// Fill bytes with the contents of memory at a particular
|
|
||||||
// location in another task.
|
|
||||||
kern_return_t ReadTaskMemory(task_port_t target_task, |
|
||||||
const uint64_t address, |
|
||||||
size_t length, |
|
||||||
vector<uint8_t> &bytes); |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__
|
|
@ -1,813 +0,0 @@ |
|||||||
// Copyright (c) 2006, 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 <map> |
|
||||||
#include <pthread.h> |
|
||||||
|
|
||||||
#include "client/mac/handler/exception_handler.h" |
|
||||||
#include "client/mac/handler/minidump_generator.h" |
|
||||||
#include "common/mac/macho_utilities.h" |
|
||||||
#include "common/mac/scoped_task_suspend-inl.h" |
|
||||||
|
|
||||||
#ifndef USE_PROTECTED_ALLOCATIONS |
|
||||||
#define USE_PROTECTED_ALLOCATIONS 0 |
|
||||||
#endif |
|
||||||
|
|
||||||
// If USE_PROTECTED_ALLOCATIONS is activated then the
|
|
||||||
// gBreakpadAllocator needs to be setup in other code
|
|
||||||
// ahead of time. Please see ProtectedMemoryAllocator.h
|
|
||||||
// for more details.
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS |
|
||||||
#include "protected_memory_allocator.h" |
|
||||||
extern ProtectedMemoryAllocator *gBreakpadAllocator; |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
using std::map; |
|
||||||
|
|
||||||
// These structures and techniques are illustrated in
|
|
||||||
// Mac OS X Internals, Amit Singh, ch 9.7
|
|
||||||
struct ExceptionMessage { |
|
||||||
mach_msg_header_t header; |
|
||||||
mach_msg_body_t body; |
|
||||||
mach_msg_port_descriptor_t thread; |
|
||||||
mach_msg_port_descriptor_t task; |
|
||||||
NDR_record_t ndr; |
|
||||||
exception_type_t exception; |
|
||||||
mach_msg_type_number_t code_count; |
|
||||||
integer_t code[EXCEPTION_CODE_MAX]; |
|
||||||
char padding[512]; |
|
||||||
}; |
|
||||||
|
|
||||||
struct ExceptionParameters { |
|
||||||
ExceptionParameters() : count(0) {} |
|
||||||
mach_msg_type_number_t count; |
|
||||||
exception_mask_t masks[EXC_TYPES_COUNT]; |
|
||||||
mach_port_t ports[EXC_TYPES_COUNT]; |
|
||||||
exception_behavior_t behaviors[EXC_TYPES_COUNT]; |
|
||||||
thread_state_flavor_t flavors[EXC_TYPES_COUNT]; |
|
||||||
}; |
|
||||||
|
|
||||||
struct ExceptionReplyMessage { |
|
||||||
mach_msg_header_t header; |
|
||||||
NDR_record_t ndr; |
|
||||||
kern_return_t return_code; |
|
||||||
}; |
|
||||||
|
|
||||||
// Only catch these three exceptions. The other ones are nebulously defined
|
|
||||||
// and may result in treating a non-fatal exception as fatal.
|
|
||||||
exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS | |
|
||||||
EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; |
|
||||||
|
|
||||||
extern "C" |
|
||||||
{ |
|
||||||
// Forward declarations for functions that need "C" style compilation
|
|
||||||
boolean_t exc_server(mach_msg_header_t *request, |
|
||||||
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.
|
|
||||||
kern_return_t catch_exception_raise(mach_port_t target_port, |
|
||||||
mach_port_t failed_thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t code_count) |
|
||||||
__attribute__((visibility("default"))); |
|
||||||
|
|
||||||
kern_return_t ForwardException(mach_port_t task, |
|
||||||
mach_port_t failed_thread, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t code_count); |
|
||||||
|
|
||||||
kern_return_t exception_raise(mach_port_t target_port, |
|
||||||
mach_port_t failed_thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t exception_code, |
|
||||||
mach_msg_type_number_t exception_code_count); |
|
||||||
|
|
||||||
kern_return_t |
|
||||||
exception_raise_state(mach_port_t target_port, |
|
||||||
mach_port_t failed_thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t exception_code, |
|
||||||
mach_msg_type_number_t code_count, |
|
||||||
thread_state_flavor_t *target_flavor, |
|
||||||
thread_state_t in_thread_state, |
|
||||||
mach_msg_type_number_t in_thread_state_count, |
|
||||||
thread_state_t out_thread_state, |
|
||||||
mach_msg_type_number_t *out_thread_state_count); |
|
||||||
|
|
||||||
kern_return_t |
|
||||||
exception_raise_state_identity(mach_port_t target_port, |
|
||||||
mach_port_t failed_thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t exception_code, |
|
||||||
mach_msg_type_number_t exception_code_count, |
|
||||||
thread_state_flavor_t *target_flavor, |
|
||||||
thread_state_t in_thread_state, |
|
||||||
mach_msg_type_number_t in_thread_state_count, |
|
||||||
thread_state_t out_thread_state, |
|
||||||
mach_msg_type_number_t *out_thread_state_count); |
|
||||||
|
|
||||||
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port, |
|
||||||
exception_type_t exception, |
|
||||||
const exception_data_t code, |
|
||||||
mach_msg_type_number_t codeCnt, |
|
||||||
int *flavor, |
|
||||||
const thread_state_t old_state, |
|
||||||
mach_msg_type_number_t old_stateCnt, |
|
||||||
thread_state_t new_state, |
|
||||||
mach_msg_type_number_t *new_stateCnt |
|
||||||
); |
|
||||||
|
|
||||||
kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port, |
|
||||||
mach_port_t thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t codeCnt, |
|
||||||
int *flavor, |
|
||||||
thread_state_t old_state, |
|
||||||
mach_msg_type_number_t old_stateCnt, |
|
||||||
thread_state_t new_state, |
|
||||||
mach_msg_type_number_t *new_stateCnt |
|
||||||
); |
|
||||||
|
|
||||||
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t code_count); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
kern_return_t breakpad_exception_raise_state(mach_port_t exception_port, |
|
||||||
exception_type_t exception, |
|
||||||
const exception_data_t code, |
|
||||||
mach_msg_type_number_t codeCnt, |
|
||||||
int *flavor, |
|
||||||
const thread_state_t old_state, |
|
||||||
mach_msg_type_number_t old_stateCnt, |
|
||||||
thread_state_t new_state, |
|
||||||
mach_msg_type_number_t *new_stateCnt |
|
||||||
) |
|
||||||
{ |
|
||||||
return KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
kern_return_t breakpad_exception_raise_state_identity(mach_port_t exception_port, |
|
||||||
mach_port_t thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t codeCnt, |
|
||||||
int *flavor, |
|
||||||
thread_state_t old_state, |
|
||||||
mach_msg_type_number_t old_stateCnt, |
|
||||||
thread_state_t new_state, |
|
||||||
mach_msg_type_number_t *new_stateCnt |
|
||||||
) |
|
||||||
{ |
|
||||||
return KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
kern_return_t breakpad_exception_raise(mach_port_t port, mach_port_t failed_thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t code_count) { |
|
||||||
|
|
||||||
if (task != mach_task_self()) { |
|
||||||
return KERN_FAILURE; |
|
||||||
} |
|
||||||
return ForwardException(task, failed_thread, exception, code, code_count); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
ExceptionHandler::ExceptionHandler(const string &dump_path, |
|
||||||
FilterCallback filter, |
|
||||||
MinidumpCallback callback, |
|
||||||
void *callback_context, |
|
||||||
bool install_handler, |
|
||||||
const char *port_name) |
|
||||||
: dump_path_(), |
|
||||||
filter_(filter), |
|
||||||
callback_(callback), |
|
||||||
callback_context_(callback_context), |
|
||||||
directCallback_(NULL), |
|
||||||
handler_thread_(NULL), |
|
||||||
handler_port_(MACH_PORT_NULL), |
|
||||||
previous_(NULL), |
|
||||||
installed_exception_handler_(false), |
|
||||||
is_in_teardown_(false), |
|
||||||
last_minidump_write_result_(false), |
|
||||||
use_minidump_write_mutex_(false) { |
|
||||||
// This will update to the ID and C-string pointers
|
|
||||||
set_dump_path(dump_path); |
|
||||||
MinidumpGenerator::GatherSystemInformation(); |
|
||||||
if (port_name) |
|
||||||
crash_generation_client_.reset(new CrashGenerationClient(port_name)); |
|
||||||
Setup(install_handler); |
|
||||||
} |
|
||||||
|
|
||||||
// special constructor if we want to bypass minidump writing and
|
|
||||||
// simply get a callback with the exception information
|
|
||||||
ExceptionHandler::ExceptionHandler(DirectCallback callback, |
|
||||||
void *callback_context, |
|
||||||
bool install_handler) |
|
||||||
: dump_path_(), |
|
||||||
filter_(NULL), |
|
||||||
callback_(NULL), |
|
||||||
callback_context_(callback_context), |
|
||||||
directCallback_(callback), |
|
||||||
handler_thread_(NULL), |
|
||||||
handler_port_(MACH_PORT_NULL), |
|
||||||
previous_(NULL), |
|
||||||
installed_exception_handler_(false), |
|
||||||
is_in_teardown_(false), |
|
||||||
last_minidump_write_result_(false), |
|
||||||
use_minidump_write_mutex_(false) { |
|
||||||
MinidumpGenerator::GatherSystemInformation(); |
|
||||||
Setup(install_handler); |
|
||||||
} |
|
||||||
|
|
||||||
ExceptionHandler::~ExceptionHandler() { |
|
||||||
Teardown(); |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::WriteMinidump(bool write_exception_stream) { |
|
||||||
// If we're currently writing, just return
|
|
||||||
if (use_minidump_write_mutex_) |
|
||||||
return false; |
|
||||||
|
|
||||||
use_minidump_write_mutex_ = true; |
|
||||||
last_minidump_write_result_ = false; |
|
||||||
|
|
||||||
// Lock the mutex. Since we just created it, this will return immediately.
|
|
||||||
if (pthread_mutex_lock(&minidump_write_mutex_) == 0) { |
|
||||||
// Send an empty message to the handle port so that a minidump will
|
|
||||||
// be written
|
|
||||||
SendMessageToHandlerThread(write_exception_stream ? |
|
||||||
kWriteDumpWithExceptionMessage : |
|
||||||
kWriteDumpMessage); |
|
||||||
|
|
||||||
// Wait for the minidump writer to complete its writing. It will unlock
|
|
||||||
// the mutex when completed
|
|
||||||
pthread_mutex_lock(&minidump_write_mutex_); |
|
||||||
} |
|
||||||
|
|
||||||
use_minidump_write_mutex_ = false; |
|
||||||
UpdateNextID(); |
|
||||||
return last_minidump_write_result_; |
|
||||||
} |
|
||||||
|
|
||||||
// static
|
|
||||||
bool ExceptionHandler::WriteMinidump(const string &dump_path, |
|
||||||
bool write_exception_stream, |
|
||||||
MinidumpCallback callback, |
|
||||||
void *callback_context) { |
|
||||||
ExceptionHandler handler(dump_path, NULL, callback, callback_context, false, |
|
||||||
NULL); |
|
||||||
return handler.WriteMinidump(write_exception_stream); |
|
||||||
} |
|
||||||
|
|
||||||
// static
|
|
||||||
bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child, |
|
||||||
mach_port_t child_blamed_thread, |
|
||||||
const string &dump_path, |
|
||||||
MinidumpCallback callback, |
|
||||||
void *callback_context) { |
|
||||||
ScopedTaskSuspend suspend(child); |
|
||||||
|
|
||||||
MinidumpGenerator generator(child, MACH_PORT_NULL); |
|
||||||
string dump_id; |
|
||||||
string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id); |
|
||||||
|
|
||||||
generator.SetExceptionInformation(EXC_BREAKPOINT, |
|
||||||
#if defined (__i386__) || defined(__x86_64__) |
|
||||||
EXC_I386_BPT, |
|
||||||
#elif defined (__ppc__) || defined (__ppc64__) |
|
||||||
EXC_PPC_BREAKPOINT, |
|
||||||
#else |
|
||||||
#error architecture not supported |
|
||||||
#endif |
|
||||||
0, |
|
||||||
child_blamed_thread); |
|
||||||
bool result = generator.Write(dump_filename.c_str()); |
|
||||||
|
|
||||||
if (callback) { |
|
||||||
return callback(dump_path.c_str(), dump_id.c_str(), |
|
||||||
callback_context, result); |
|
||||||
} |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::WriteMinidumpWithException(int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t thread_name, |
|
||||||
bool exit_after_write) { |
|
||||||
bool result = false; |
|
||||||
|
|
||||||
if (directCallback_) { |
|
||||||
if (directCallback_(callback_context_, |
|
||||||
exception_type, |
|
||||||
exception_code, |
|
||||||
exception_subcode, |
|
||||||
thread_name) ) { |
|
||||||
if (exit_after_write) |
|
||||||
_exit(exception_type); |
|
||||||
} |
|
||||||
} else if (IsOutOfProcess()) { |
|
||||||
if (exception_type && exception_code) { |
|
||||||
// If this is a real exception, give the filter (if any) a chance to
|
|
||||||
// decide if this should be sent.
|
|
||||||
if (filter_ && !filter_(callback_context_)) |
|
||||||
return false; |
|
||||||
return crash_generation_client_->RequestDumpForException( |
|
||||||
exception_type, |
|
||||||
exception_code, |
|
||||||
exception_subcode, |
|
||||||
thread_name); |
|
||||||
} |
|
||||||
} else { |
|
||||||
string minidump_id; |
|
||||||
|
|
||||||
// Putting the MinidumpGenerator in its own context will ensure that the
|
|
||||||
// destructor is executed, closing the newly created minidump file.
|
|
||||||
if (!dump_path_.empty()) { |
|
||||||
MinidumpGenerator md; |
|
||||||
if (exception_type && exception_code) { |
|
||||||
// If this is a real exception, give the filter (if any) a chance to
|
|
||||||
// decide if this should be sent.
|
|
||||||
if (filter_ && !filter_(callback_context_)) |
|
||||||
return false; |
|
||||||
|
|
||||||
md.SetExceptionInformation(exception_type, exception_code, |
|
||||||
exception_subcode, thread_name); |
|
||||||
} |
|
||||||
|
|
||||||
result = md.Write(next_minidump_path_c_); |
|
||||||
} |
|
||||||
|
|
||||||
// Call user specified callback (if any)
|
|
||||||
if (callback_) { |
|
||||||
// If the user callback returned true and we're handling an exception
|
|
||||||
// (rather than just writing out the file), then we should exit without
|
|
||||||
// forwarding the exception to the next handler.
|
|
||||||
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, |
|
||||||
result)) { |
|
||||||
if (exit_after_write) |
|
||||||
_exit(exception_type); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t code_count) { |
|
||||||
// At this time, we should have called Uninstall() on the exception handler
|
|
||||||
// so that the current exception ports are the ones that we should be
|
|
||||||
// forwarding to.
|
|
||||||
ExceptionParameters current; |
|
||||||
|
|
||||||
current.count = EXC_TYPES_COUNT; |
|
||||||
mach_port_t current_task = mach_task_self(); |
|
||||||
kern_return_t result = task_get_exception_ports(current_task, |
|
||||||
s_exception_mask, |
|
||||||
current.masks, |
|
||||||
¤t.count, |
|
||||||
current.ports, |
|
||||||
current.behaviors, |
|
||||||
current.flavors); |
|
||||||
|
|
||||||
// Find the first exception handler that matches the exception
|
|
||||||
unsigned int found; |
|
||||||
for (found = 0; found < current.count; ++found) { |
|
||||||
if (current.masks[found] & (1 << exception)) { |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Nothing to forward
|
|
||||||
if (found == current.count) { |
|
||||||
fprintf(stderr, "** No previous ports for forwarding!! \n"); |
|
||||||
exit(KERN_FAILURE); |
|
||||||
} |
|
||||||
|
|
||||||
mach_port_t target_port = current.ports[found]; |
|
||||||
exception_behavior_t target_behavior = current.behaviors[found]; |
|
||||||
thread_state_flavor_t target_flavor = current.flavors[found]; |
|
||||||
|
|
||||||
mach_msg_type_number_t thread_state_count = THREAD_STATE_MAX; |
|
||||||
breakpad_thread_state_data_t thread_state; |
|
||||||
switch (target_behavior) { |
|
||||||
case EXCEPTION_DEFAULT: |
|
||||||
result = exception_raise(target_port, failed_thread, task, exception, |
|
||||||
code, code_count); |
|
||||||
break; |
|
||||||
|
|
||||||
case EXCEPTION_STATE: |
|
||||||
result = thread_get_state(failed_thread, target_flavor, thread_state, |
|
||||||
&thread_state_count); |
|
||||||
if (result == KERN_SUCCESS) |
|
||||||
result = exception_raise_state(target_port, failed_thread, task, |
|
||||||
exception, code, |
|
||||||
code_count, &target_flavor, |
|
||||||
thread_state, thread_state_count, |
|
||||||
thread_state, &thread_state_count); |
|
||||||
if (result == KERN_SUCCESS) |
|
||||||
result = thread_set_state(failed_thread, target_flavor, thread_state, |
|
||||||
thread_state_count); |
|
||||||
break; |
|
||||||
|
|
||||||
case EXCEPTION_STATE_IDENTITY: |
|
||||||
result = thread_get_state(failed_thread, target_flavor, thread_state, |
|
||||||
&thread_state_count); |
|
||||||
if (result == KERN_SUCCESS) |
|
||||||
result = exception_raise_state_identity(target_port, failed_thread, |
|
||||||
task, exception, code, |
|
||||||
code_count, &target_flavor, |
|
||||||
thread_state, |
|
||||||
thread_state_count, |
|
||||||
thread_state, |
|
||||||
&thread_state_count); |
|
||||||
if (result == KERN_SUCCESS) |
|
||||||
result = thread_set_state(failed_thread, target_flavor, thread_state, |
|
||||||
thread_state_count); |
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
fprintf(stderr, "** Unknown exception behavior\n"); |
|
||||||
result = KERN_FAILURE; |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
// Callback from exc_server()
|
|
||||||
kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, |
|
||||||
mach_port_t task, |
|
||||||
exception_type_t exception, |
|
||||||
exception_data_t code, |
|
||||||
mach_msg_type_number_t code_count) { |
|
||||||
if (task != mach_task_self()) { |
|
||||||
return KERN_FAILURE; |
|
||||||
} |
|
||||||
return ForwardException(task, failed_thread, exception, code, code_count); |
|
||||||
} |
|
||||||
|
|
||||||
// static
|
|
||||||
void *ExceptionHandler::WaitForMessage(void *exception_handler_class) { |
|
||||||
ExceptionHandler *self = |
|
||||||
reinterpret_cast<ExceptionHandler *>(exception_handler_class); |
|
||||||
ExceptionMessage receive; |
|
||||||
|
|
||||||
// Wait for the exception info
|
|
||||||
while (1) { |
|
||||||
receive.header.msgh_local_port = self->handler_port_; |
|
||||||
receive.header.msgh_size = static_cast<mach_msg_size_t>(sizeof(receive)); |
|
||||||
kern_return_t result = mach_msg(&(receive.header), |
|
||||||
MACH_RCV_MSG | MACH_RCV_LARGE, 0, |
|
||||||
receive.header.msgh_size, |
|
||||||
self->handler_port_, |
|
||||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
|
||||||
|
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
// Uninstall our handler so that we don't get in a loop if the process of
|
|
||||||
// writing out a minidump causes an exception. However, if the exception
|
|
||||||
// was caused by a fork'd process, don't uninstall things
|
|
||||||
|
|
||||||
// If the actual exception code is zero, then we're calling this handler
|
|
||||||
// in a way that indicates that we want to either exit this thread or
|
|
||||||
// generate a minidump
|
|
||||||
//
|
|
||||||
// While reporting, all threads (except this one) must be suspended
|
|
||||||
// to avoid misleading stacks. If appropriate they will be resumed
|
|
||||||
// afterwards.
|
|
||||||
if (!receive.exception) { |
|
||||||
// Don't touch self, since this message could have been sent
|
|
||||||
// from its destructor.
|
|
||||||
if (receive.header.msgh_id == kShutdownMessage) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
self->SuspendThreads(); |
|
||||||
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS |
|
||||||
if(gBreakpadAllocator) |
|
||||||
gBreakpadAllocator->Unprotect(); |
|
||||||
#endif |
|
||||||
|
|
||||||
mach_port_t thread = MACH_PORT_NULL; |
|
||||||
int exception_type = 0; |
|
||||||
int exception_code = 0; |
|
||||||
if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) { |
|
||||||
thread = receive.thread.name; |
|
||||||
exception_type = EXC_BREAKPOINT; |
|
||||||
#if defined (__i386__) || defined(__x86_64__) |
|
||||||
exception_code = EXC_I386_BPT; |
|
||||||
#elif defined (__ppc__) || defined (__ppc64__) |
|
||||||
exception_code = EXC_PPC_BREAKPOINT; |
|
||||||
#else |
|
||||||
#error architecture not supported |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
// Write out the dump and save the result for later retrieval
|
|
||||||
self->last_minidump_write_result_ = |
|
||||||
self->WriteMinidumpWithException(exception_type, exception_code, |
|
||||||
0, thread, |
|
||||||
false); |
|
||||||
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS |
|
||||||
if(gBreakpadAllocator) |
|
||||||
gBreakpadAllocator->Protect(); |
|
||||||
#endif |
|
||||||
|
|
||||||
self->ResumeThreads(); |
|
||||||
|
|
||||||
if (self->use_minidump_write_mutex_) |
|
||||||
pthread_mutex_unlock(&self->minidump_write_mutex_); |
|
||||||
} else { |
|
||||||
// When forking a child process with the exception handler installed,
|
|
||||||
// if the child crashes, it will send the exception back to the parent
|
|
||||||
// process. The check for task == self_task() ensures that only
|
|
||||||
// exceptions that occur in the parent process are caught and
|
|
||||||
// processed. If the exception was not caused by this task, we
|
|
||||||
// still need to call into the exception server and have it return
|
|
||||||
// KERN_FAILURE (see breakpad_exception_raise) in order for the kernel
|
|
||||||
// to move onto the host exception handler for the child task
|
|
||||||
if (receive.task.name == mach_task_self()) { |
|
||||||
self->SuspendThreads(); |
|
||||||
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS |
|
||||||
if(gBreakpadAllocator) |
|
||||||
gBreakpadAllocator->Unprotect(); |
|
||||||
#endif |
|
||||||
|
|
||||||
int subcode = 0; |
|
||||||
if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1) |
|
||||||
subcode = receive.code[1]; |
|
||||||
|
|
||||||
// Generate the minidump with the exception data.
|
|
||||||
self->WriteMinidumpWithException(receive.exception, receive.code[0], |
|
||||||
subcode, receive.thread.name, true); |
|
||||||
|
|
||||||
self->UninstallHandler(true); |
|
||||||
|
|
||||||
#if USE_PROTECTED_ALLOCATIONS |
|
||||||
if(gBreakpadAllocator) |
|
||||||
gBreakpadAllocator->Protect(); |
|
||||||
#endif |
|
||||||
} |
|
||||||
// Pass along the exception to the server, which will setup the
|
|
||||||
// message and call breakpad_exception_raise() and put the return
|
|
||||||
// code into the reply.
|
|
||||||
ExceptionReplyMessage reply; |
|
||||||
if (!exc_server(&receive.header, &reply.header)) |
|
||||||
exit(1); |
|
||||||
|
|
||||||
// Send a reply and exit
|
|
||||||
result = mach_msg(&(reply.header), MACH_SEND_MSG, |
|
||||||
reply.header.msgh_size, 0, MACH_PORT_NULL, |
|
||||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::InstallHandler() { |
|
||||||
try { |
|
||||||
#if USE_PROTECTED_ALLOCATIONS |
|
||||||
previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) ) |
|
||||||
ExceptionParameters(); |
|
||||||
#else |
|
||||||
previous_ = new ExceptionParameters(); |
|
||||||
#endif |
|
||||||
|
|
||||||
} |
|
||||||
catch (std::bad_alloc) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// Save the current exception ports so that we can forward to them
|
|
||||||
previous_->count = EXC_TYPES_COUNT; |
|
||||||
mach_port_t current_task = mach_task_self(); |
|
||||||
kern_return_t result = task_get_exception_ports(current_task, |
|
||||||
s_exception_mask, |
|
||||||
previous_->masks, |
|
||||||
&previous_->count, |
|
||||||
previous_->ports, |
|
||||||
previous_->behaviors, |
|
||||||
previous_->flavors); |
|
||||||
|
|
||||||
// Setup the exception ports on this task
|
|
||||||
if (result == KERN_SUCCESS) |
|
||||||
result = task_set_exception_ports(current_task, s_exception_mask, |
|
||||||
handler_port_, EXCEPTION_DEFAULT, |
|
||||||
THREAD_STATE_NONE); |
|
||||||
|
|
||||||
installed_exception_handler_ = (result == KERN_SUCCESS); |
|
||||||
|
|
||||||
return installed_exception_handler_; |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::UninstallHandler(bool in_exception) { |
|
||||||
kern_return_t result = KERN_SUCCESS; |
|
||||||
|
|
||||||
if (installed_exception_handler_) { |
|
||||||
mach_port_t current_task = mach_task_self(); |
|
||||||
|
|
||||||
// Restore the previous ports
|
|
||||||
for (unsigned int i = 0; i < previous_->count; ++i) { |
|
||||||
result = task_set_exception_ports(current_task, previous_->masks[i], |
|
||||||
previous_->ports[i], |
|
||||||
previous_->behaviors[i], |
|
||||||
previous_->flavors[i]); |
|
||||||
if (result != KERN_SUCCESS) |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
// this delete should NOT happen if an exception just occurred!
|
|
||||||
if (!in_exception) { |
|
||||||
#if USE_PROTECTED_ALLOCATIONS |
|
||||||
previous_->~ExceptionParameters(); |
|
||||||
#else |
|
||||||
delete previous_; |
|
||||||
#endif |
|
||||||
} |
|
||||||
|
|
||||||
previous_ = NULL; |
|
||||||
installed_exception_handler_ = false; |
|
||||||
} |
|
||||||
|
|
||||||
return result == KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::Setup(bool install_handler) { |
|
||||||
if (pthread_mutex_init(&minidump_write_mutex_, NULL)) |
|
||||||
return false; |
|
||||||
|
|
||||||
// Create a receive right
|
|
||||||
mach_port_t current_task = mach_task_self(); |
|
||||||
kern_return_t result = mach_port_allocate(current_task, |
|
||||||
MACH_PORT_RIGHT_RECEIVE, |
|
||||||
&handler_port_); |
|
||||||
// Add send right
|
|
||||||
if (result == KERN_SUCCESS) |
|
||||||
result = mach_port_insert_right(current_task, handler_port_, handler_port_, |
|
||||||
MACH_MSG_TYPE_MAKE_SEND); |
|
||||||
|
|
||||||
if (install_handler && result == KERN_SUCCESS) |
|
||||||
if (!InstallHandler()) |
|
||||||
return false; |
|
||||||
|
|
||||||
if (result == KERN_SUCCESS) { |
|
||||||
// Install the handler in its own thread, detached as we won't be joining.
|
|
||||||
pthread_attr_t attr; |
|
||||||
pthread_attr_init(&attr); |
|
||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
|
||||||
int thread_create_result = pthread_create(&handler_thread_, &attr, |
|
||||||
&WaitForMessage, this); |
|
||||||
pthread_attr_destroy(&attr); |
|
||||||
result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
return result == KERN_SUCCESS ? true : false; |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::Teardown() { |
|
||||||
kern_return_t result = KERN_SUCCESS; |
|
||||||
is_in_teardown_ = true; |
|
||||||
|
|
||||||
if (!UninstallHandler(false)) |
|
||||||
return false; |
|
||||||
|
|
||||||
// Send an empty message so that the handler_thread exits
|
|
||||||
if (SendMessageToHandlerThread(kShutdownMessage)) { |
|
||||||
mach_port_t current_task = mach_task_self(); |
|
||||||
result = mach_port_deallocate(current_task, handler_port_); |
|
||||||
if (result != KERN_SUCCESS) |
|
||||||
return false; |
|
||||||
} else { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
handler_thread_ = NULL; |
|
||||||
handler_port_ = NULL; |
|
||||||
pthread_mutex_destroy(&minidump_write_mutex_); |
|
||||||
|
|
||||||
return result == KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::SendMessageToHandlerThread( |
|
||||||
HandlerThreadMessage message_id) { |
|
||||||
ExceptionMessage msg; |
|
||||||
memset(&msg, 0, sizeof(msg)); |
|
||||||
msg.header.msgh_id = message_id; |
|
||||||
if (message_id == kWriteDumpMessage || |
|
||||||
message_id == kWriteDumpWithExceptionMessage) { |
|
||||||
// Include this thread's port.
|
|
||||||
msg.thread.name = mach_thread_self(); |
|
||||||
msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND; |
|
||||||
msg.thread.type = MACH_MSG_PORT_DESCRIPTOR; |
|
||||||
} |
|
||||||
msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding); |
|
||||||
msg.header.msgh_remote_port = handler_port_; |
|
||||||
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, |
|
||||||
MACH_MSG_TYPE_MAKE_SEND_ONCE); |
|
||||||
kern_return_t result = mach_msg(&(msg.header), |
|
||||||
MACH_SEND_MSG | MACH_SEND_TIMEOUT, |
|
||||||
msg.header.msgh_size, 0, 0, |
|
||||||
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); |
|
||||||
|
|
||||||
return result == KERN_SUCCESS; |
|
||||||
} |
|
||||||
|
|
||||||
void ExceptionHandler::UpdateNextID() { |
|
||||||
next_minidump_path_ = |
|
||||||
(MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); |
|
||||||
|
|
||||||
next_minidump_path_c_ = next_minidump_path_.c_str(); |
|
||||||
next_minidump_id_c_ = next_minidump_id_.c_str(); |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::SuspendThreads() { |
|
||||||
thread_act_port_array_t threads_for_task; |
|
||||||
mach_msg_type_number_t thread_count; |
|
||||||
|
|
||||||
if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) |
|
||||||
return false; |
|
||||||
|
|
||||||
// suspend all of the threads except for this one
|
|
||||||
for (unsigned int i = 0; i < thread_count; ++i) { |
|
||||||
if (threads_for_task[i] != mach_thread_self()) { |
|
||||||
if (thread_suspend(threads_for_task[i])) |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
bool ExceptionHandler::ResumeThreads() { |
|
||||||
thread_act_port_array_t threads_for_task; |
|
||||||
mach_msg_type_number_t thread_count; |
|
||||||
|
|
||||||
if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) |
|
||||||
return false; |
|
||||||
|
|
||||||
// resume all of the threads except for this one
|
|
||||||
for (unsigned int i = 0; i < thread_count; ++i) { |
|
||||||
if (threads_for_task[i] != mach_thread_self()) { |
|
||||||
if (thread_resume(threads_for_task[i])) |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
@ -1,259 +0,0 @@ |
|||||||
// Copyright (c) 2006, 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.
|
|
||||||
|
|
||||||
// exception_handler.h: MacOS exception handler
|
|
||||||
// This class can install a Mach exception port handler to trap most common
|
|
||||||
// programming errors. If an exception occurs, a minidump file will be
|
|
||||||
// generated which contains detailed information about the process and the
|
|
||||||
// exception.
|
|
||||||
|
|
||||||
#ifndef CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ |
|
||||||
#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ |
|
||||||
|
|
||||||
#include <mach/mach.h> |
|
||||||
|
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include "client/mac/crash_generation/crash_generation_client.h" |
|
||||||
#include "processor/scoped_ptr.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
using std::string; |
|
||||||
|
|
||||||
struct ExceptionParameters; |
|
||||||
|
|
||||||
enum HandlerThreadMessage { |
|
||||||
// Message ID telling the handler thread to write a dump.
|
|
||||||
kWriteDumpMessage = 0, |
|
||||||
// Message ID telling the handler thread to write a dump and include
|
|
||||||
// an exception stream.
|
|
||||||
kWriteDumpWithExceptionMessage = 1, |
|
||||||
// Message ID telling the handler thread to quit.
|
|
||||||
kShutdownMessage = 2 |
|
||||||
}; |
|
||||||
|
|
||||||
class ExceptionHandler { |
|
||||||
public: |
|
||||||
// A callback function to run before Breakpad performs any substantial
|
|
||||||
// processing of an exception. A FilterCallback is called before writing
|
|
||||||
// a minidump. context is the parameter supplied by the user as
|
|
||||||
// callback_context when the handler was created.
|
|
||||||
//
|
|
||||||
// If a FilterCallback returns true, Breakpad will continue processing,
|
|
||||||
// attempting to write a minidump. If a FilterCallback returns false, Breakpad
|
|
||||||
// will immediately report the exception as unhandled without writing a
|
|
||||||
// minidump, allowing another handler the opportunity to handle it.
|
|
||||||
typedef bool (*FilterCallback)(void *context); |
|
||||||
|
|
||||||
// A callback function to run after the minidump has been written.
|
|
||||||
// |minidump_id| is a unique id for the dump, so the minidump
|
|
||||||
// file is <dump_dir>/<minidump_id>.dmp.
|
|
||||||
// |context| is the value passed into the constructor.
|
|
||||||
// |succeeded| indicates whether a minidump file was successfully written.
|
|
||||||
// Return true if the exception was fully handled and breakpad should exit.
|
|
||||||
// Return false to allow any other exception handlers to process the
|
|
||||||
// exception.
|
|
||||||
typedef bool (*MinidumpCallback)(const char *dump_dir, |
|
||||||
const char *minidump_id, |
|
||||||
void *context, bool succeeded); |
|
||||||
|
|
||||||
// A callback function which will be called directly if an exception occurs.
|
|
||||||
// This bypasses the minidump file writing and simply gives the client
|
|
||||||
// the exception information.
|
|
||||||
typedef bool (*DirectCallback)( void *context, |
|
||||||
int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t thread_name); |
|
||||||
|
|
||||||
// Creates a new ExceptionHandler instance to handle writing minidumps.
|
|
||||||
// Minidump files will be written to dump_path, and the optional callback
|
|
||||||
// is called after writing the dump file, as described above.
|
|
||||||
// If install_handler is true, then a minidump will be written whenever
|
|
||||||
// an unhandled exception occurs. If it is false, minidumps will only
|
|
||||||
// be written when WriteMinidump is called.
|
|
||||||
// If port_name is non-NULL, attempt to perform out-of-process dump generation
|
|
||||||
// If port_name is NULL, in-process dump generation will be used.
|
|
||||||
ExceptionHandler(const string &dump_path, |
|
||||||
FilterCallback filter, MinidumpCallback callback, |
|
||||||
void *callback_context, bool install_handler, |
|
||||||
const char *port_name); |
|
||||||
|
|
||||||
// A special constructor if we want to bypass minidump writing and
|
|
||||||
// simply get a callback with the exception information.
|
|
||||||
ExceptionHandler(DirectCallback callback, |
|
||||||
void *callback_context, |
|
||||||
bool install_handler); |
|
||||||
|
|
||||||
~ExceptionHandler(); |
|
||||||
|
|
||||||
// Get and set the minidump path.
|
|
||||||
string dump_path() const { return dump_path_; } |
|
||||||
void set_dump_path(const string &dump_path) { |
|
||||||
dump_path_ = dump_path; |
|
||||||
dump_path_c_ = dump_path_.c_str(); |
|
||||||
UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_.
|
|
||||||
} |
|
||||||
|
|
||||||
// Writes a minidump immediately. This can be used to capture the
|
|
||||||
// execution state independently of a crash. Returns true on success.
|
|
||||||
bool WriteMinidump() { |
|
||||||
return WriteMinidump(false); |
|
||||||
} |
|
||||||
|
|
||||||
bool WriteMinidump(bool write_exception_stream); |
|
||||||
|
|
||||||
// Convenience form of WriteMinidump which does not require an
|
|
||||||
// ExceptionHandler instance.
|
|
||||||
static bool WriteMinidump(const string &dump_path, MinidumpCallback callback, |
|
||||||
void *callback_context) { |
|
||||||
return WriteMinidump(dump_path, false, callback, callback_context); |
|
||||||
} |
|
||||||
|
|
||||||
static bool WriteMinidump(const string &dump_path, |
|
||||||
bool write_exception_stream, |
|
||||||
MinidumpCallback callback, |
|
||||||
void *callback_context); |
|
||||||
|
|
||||||
// Write a minidump of child immediately. This can be used to capture
|
|
||||||
// the execution state of a child process independently of a crash.
|
|
||||||
static bool WriteMinidumpForChild(mach_port_t child, |
|
||||||
mach_port_t child_blamed_thread, |
|
||||||
const std::string &dump_path, |
|
||||||
MinidumpCallback callback, |
|
||||||
void *callback_context); |
|
||||||
|
|
||||||
// Returns whether out-of-process dump generation is used or not.
|
|
||||||
bool IsOutOfProcess() const { |
|
||||||
return crash_generation_client_.get() != NULL; |
|
||||||
} |
|
||||||
|
|
||||||
private: |
|
||||||
// Install the mach exception handler
|
|
||||||
bool InstallHandler(); |
|
||||||
|
|
||||||
// Uninstall the mach exception handler (if any)
|
|
||||||
bool UninstallHandler(bool in_exception); |
|
||||||
|
|
||||||
// Setup the handler thread, and if |install_handler| is true, install the
|
|
||||||
// mach exception port handler
|
|
||||||
bool Setup(bool install_handler); |
|
||||||
|
|
||||||
// Uninstall the mach exception handler (if any) and terminate the helper
|
|
||||||
// thread
|
|
||||||
bool Teardown(); |
|
||||||
|
|
||||||
// Send a mach message to the exception handler. Return true on
|
|
||||||
// success, false otherwise.
|
|
||||||
bool SendMessageToHandlerThread(HandlerThreadMessage message_id); |
|
||||||
|
|
||||||
// All minidump writing goes through this one routine
|
|
||||||
bool WriteMinidumpWithException(int exception_type, |
|
||||||
int exception_code, |
|
||||||
int exception_subcode, |
|
||||||
mach_port_t thread_name, |
|
||||||
bool exit_after_write); |
|
||||||
|
|
||||||
// When installed, this static function will be call from a newly created
|
|
||||||
// pthread with |this| as the argument
|
|
||||||
static void *WaitForMessage(void *exception_handler_class); |
|
||||||
|
|
||||||
// disallow copy ctor and operator=
|
|
||||||
explicit ExceptionHandler(const ExceptionHandler &); |
|
||||||
void operator=(const ExceptionHandler &); |
|
||||||
|
|
||||||
// Generates a new ID and stores it in next_minidump_id_, and stores the
|
|
||||||
// path of the next minidump to be written in next_minidump_path_.
|
|
||||||
void UpdateNextID(); |
|
||||||
|
|
||||||
// These functions will suspend/resume all threads except for the
|
|
||||||
// reporting thread
|
|
||||||
bool SuspendThreads(); |
|
||||||
bool ResumeThreads(); |
|
||||||
|
|
||||||
// The destination directory for the minidump
|
|
||||||
string dump_path_; |
|
||||||
|
|
||||||
// The basename of the next minidump w/o extension
|
|
||||||
string next_minidump_id_; |
|
||||||
|
|
||||||
// The full path to the next minidump to be written, including extension
|
|
||||||
string next_minidump_path_; |
|
||||||
|
|
||||||
// Pointers to the UTF-8 versions of above
|
|
||||||
const char *dump_path_c_; |
|
||||||
const char *next_minidump_id_c_; |
|
||||||
const char *next_minidump_path_c_; |
|
||||||
|
|
||||||
// The callback function and pointer to be passed back after the minidump
|
|
||||||
// has been written
|
|
||||||
FilterCallback filter_; |
|
||||||
MinidumpCallback callback_; |
|
||||||
void *callback_context_; |
|
||||||
|
|
||||||
// The callback function to be passed back when we don't want a minidump
|
|
||||||
// file to be written
|
|
||||||
DirectCallback directCallback_; |
|
||||||
|
|
||||||
// The thread that is created for the handler
|
|
||||||
pthread_t handler_thread_; |
|
||||||
|
|
||||||
// The port that is waiting on an exception message to be sent, if the
|
|
||||||
// handler is installed
|
|
||||||
mach_port_t handler_port_; |
|
||||||
|
|
||||||
// These variables save the previous exception handler's data so that it
|
|
||||||
// can be re-installed when this handler is uninstalled
|
|
||||||
ExceptionParameters *previous_; |
|
||||||
|
|
||||||
// True, if we've installed the exception handler
|
|
||||||
bool installed_exception_handler_; |
|
||||||
|
|
||||||
// True, if we're in the process of uninstalling the exception handler and
|
|
||||||
// the thread.
|
|
||||||
bool is_in_teardown_; |
|
||||||
|
|
||||||
// Save the last result of the last minidump
|
|
||||||
bool last_minidump_write_result_; |
|
||||||
|
|
||||||
// A mutex for use when writing out a minidump that was requested on a
|
|
||||||
// thread other than the exception handler.
|
|
||||||
pthread_mutex_t minidump_write_mutex_; |
|
||||||
|
|
||||||
// True, if we're using the mutext to indicate when mindump writing occurs
|
|
||||||
bool use_minidump_write_mutex_; |
|
||||||
|
|
||||||
// Client for out-of-process dump generation.
|
|
||||||
scoped_ptr<CrashGenerationClient> crash_generation_client_; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__
|
|
File diff suppressed because it is too large
Load Diff
@ -1,191 +0,0 @@ |
|||||||
// Copyright (c) 2006, 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.
|
|
||||||
|
|
||||||
// minidump_generator.h: Create a minidump of the current MacOS process.
|
|
||||||
|
|
||||||
#ifndef CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ |
|
||||||
#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ |
|
||||||
|
|
||||||
#include <mach/mach.h> |
|
||||||
|
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include "client/minidump_file_writer.h" |
|
||||||
#include "common/memory.h" |
|
||||||
#include "common/mac/macho_utilities.h" |
|
||||||
#include "google_breakpad/common/minidump_format.h" |
|
||||||
|
|
||||||
#include "dynamic_images.h" |
|
||||||
|
|
||||||
namespace google_breakpad { |
|
||||||
|
|
||||||
using std::string; |
|
||||||
|
|
||||||
const u_int64_t TOP_OF_THREAD0_STACK_64BIT = 0x00007fff5fbff000LL; |
|
||||||
const u_int32_t TOP_OF_THREAD0_STACK_32BIT = 0xbffff000; |
|
||||||
|
|
||||||
// Use the REGISTER_FROM_THREADSTATE to access a register name from the
|
|
||||||
// breakpad_thread_state_t structure.
|
|
||||||
#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 |
|
||||||
// In The 10.5 SDK Headers Apple prepended __ to the variable names in the
|
|
||||||
// i386_thread_state_t structure. There's no good way to tell what version of
|
|
||||||
// the SDK we're compiling against so we just toggle on the same preprocessor
|
|
||||||
// symbol Apple's headers use.
|
|
||||||
#define REGISTER_FROM_THREADSTATE(a, b) ((a)->__ ## b) |
|
||||||
#else |
|
||||||
#define REGISTER_FROM_THREADSTATE(a, b) (a->b) |
|
||||||
#endif |
|
||||||
|
|
||||||
// Creates a minidump file of the current process. If there is exception data,
|
|
||||||
// use SetExceptionInformation() to add this to the minidump. The minidump
|
|
||||||
// file is generated by the Write() function.
|
|
||||||
// Usage:
|
|
||||||
// MinidumpGenerator minidump();
|
|
||||||
// minidump.Write("/tmp/minidump");
|
|
||||||
//
|
|
||||||
class MinidumpGenerator { |
|
||||||
public: |
|
||||||
MinidumpGenerator(); |
|
||||||
MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread); |
|
||||||
|
|
||||||
~MinidumpGenerator(); |
|
||||||
|
|
||||||
// Return <dir>/<unique_name>.dmp
|
|
||||||
// Sets |unique_name| (if requested) to the unique name for the minidump
|
|
||||||
static string UniqueNameInDirectory(const string &dir, string *unique_name); |
|
||||||
|
|
||||||
// Write out the minidump into |path|
|
|
||||||
// All of the components of |path| must exist and be writable
|
|
||||||
// Return true if successful, false otherwise
|
|
||||||
bool Write(const char *path); |
|
||||||
|
|
||||||
// Specify some exception information, if applicable
|
|
||||||
void SetExceptionInformation(int type, int code, int subcode, |
|
||||||
mach_port_t thread_name) { |
|
||||||
exception_type_ = type; |
|
||||||
exception_code_ = code; |
|
||||||
exception_subcode_ = subcode; |
|
||||||
exception_thread_ = thread_name; |
|
||||||
} |
|
||||||
|
|
||||||
// Gather system information. This should be call at least once before using
|
|
||||||
// the MinidumpGenerator class.
|
|
||||||
static void GatherSystemInformation(); |
|
||||||
|
|
||||||
private: |
|
||||||
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *); |
|
||||||
|
|
||||||
// Stream writers
|
|
||||||
bool WriteThreadListStream(MDRawDirectory *thread_list_stream); |
|
||||||
bool WriteMemoryListStream(MDRawDirectory *memory_list_stream); |
|
||||||
bool WriteExceptionStream(MDRawDirectory *exception_stream); |
|
||||||
bool WriteSystemInfoStream(MDRawDirectory *system_info_stream); |
|
||||||
bool WriteModuleListStream(MDRawDirectory *module_list_stream); |
|
||||||
bool WriteMiscInfoStream(MDRawDirectory *misc_info_stream); |
|
||||||
bool WriteBreakpadInfoStream(MDRawDirectory *breakpad_info_stream); |
|
||||||
|
|
||||||
// Helpers
|
|
||||||
u_int64_t CurrentPCForStack(breakpad_thread_state_data_t state); |
|
||||||
bool GetThreadState(thread_act_t target_thread, thread_state_t state, |
|
||||||
mach_msg_type_number_t *count); |
|
||||||
bool WriteStackFromStartAddress(mach_vm_address_t start_addr, |
|
||||||
MDMemoryDescriptor *stack_location); |
|
||||||
bool WriteStack(breakpad_thread_state_data_t state, |
|
||||||
MDMemoryDescriptor *stack_location); |
|
||||||
bool WriteContext(breakpad_thread_state_data_t state, |
|
||||||
MDLocationDescriptor *register_location); |
|
||||||
bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread); |
|
||||||
bool WriteCVRecord(MDRawModule *module, int cpu_type,
|
|
||||||
const char *module_path); |
|
||||||
bool WriteModuleStream(unsigned int index, MDRawModule *module); |
|
||||||
size_t CalculateStackSize(mach_vm_address_t start_addr); |
|
||||||
int FindExecutableModule(); |
|
||||||
|
|
||||||
// Per-CPU implementations of these methods
|
|
||||||
bool WriteStackPPC(breakpad_thread_state_data_t state, |
|
||||||
MDMemoryDescriptor *stack_location); |
|
||||||
bool WriteContextPPC(breakpad_thread_state_data_t state, |
|
||||||
MDLocationDescriptor *register_location); |
|
||||||
u_int64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state); |
|
||||||
bool WriteStackPPC64(breakpad_thread_state_data_t state, |
|
||||||
MDMemoryDescriptor *stack_location); |
|
||||||
bool WriteContextPPC64(breakpad_thread_state_data_t state, |
|
||||||
MDLocationDescriptor *register_location); |
|
||||||
u_int64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state); |
|
||||||
bool WriteStackX86(breakpad_thread_state_data_t state, |
|
||||||
MDMemoryDescriptor *stack_location); |
|
||||||
bool WriteContextX86(breakpad_thread_state_data_t state, |
|
||||||
MDLocationDescriptor *register_location); |
|
||||||
u_int64_t CurrentPCForStackX86(breakpad_thread_state_data_t state); |
|
||||||
bool WriteStackX86_64(breakpad_thread_state_data_t state, |
|
||||||
MDMemoryDescriptor *stack_location); |
|
||||||
bool WriteContextX86_64(breakpad_thread_state_data_t state, |
|
||||||
MDLocationDescriptor *register_location); |
|
||||||
u_int64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state); |
|
||||||
|
|
||||||
// disallow copy ctor and operator=
|
|
||||||
explicit MinidumpGenerator(const MinidumpGenerator &); |
|
||||||
void operator=(const MinidumpGenerator &); |
|
||||||
|
|
||||||
// Use this writer to put the data to disk
|
|
||||||
MinidumpFileWriter writer_; |
|
||||||
|
|
||||||
// Exception information
|
|
||||||
int exception_type_; |
|
||||||
int exception_code_; |
|
||||||
int exception_subcode_; |
|
||||||
mach_port_t exception_thread_; |
|
||||||
mach_port_t crashing_task_; |
|
||||||
mach_port_t handler_thread_; |
|
||||||
|
|
||||||
// CPU type of the task being dumped.
|
|
||||||
cpu_type_t cpu_type_; |
|
||||||
|
|
||||||
// System information
|
|
||||||
static char build_string_[16]; |
|
||||||
static int os_major_version_; |
|
||||||
static int os_minor_version_; |
|
||||||
static int os_build_number_; |
|
||||||
|
|
||||||
// Information about dynamically loaded code
|
|
||||||
DynamicImages *dynamic_images_; |
|
||||||
|
|
||||||
// PageAllocator makes it possible to allocate memory
|
|
||||||
// directly from the system, even while handling an exception.
|
|
||||||
mutable PageAllocator allocator_; |
|
||||||
|
|
||||||
// Blocks of memory written to the dump. These are all currently
|
|
||||||
// written while writing the thread list stream, but saved here
|
|
||||||
// so a memory list stream can be written afterwards.
|
|
||||||
wasteful_vector<MDMemoryDescriptor> memory_blocks_; |
|
||||||
}; |
|
||||||
|
|
||||||
} // namespace google_breakpad
|
|
||||||
|
|
||||||
#endif // CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__
|
|
@ -1,20 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|
||||||
<plist version="1.0"> |
|
||||||
<dict> |
|
||||||
<key>CFBundleDevelopmentRegion</key> |
|
||||||
<string>English</string> |
|
||||||
<key>CFBundleExecutable</key> |
|
||||||
<string>${EXECUTABLE_NAME}</string> |
|
||||||
<key>CFBundleIdentifier</key> |
|
||||||
<string>com.google.breakpad.minidump_tests32</string> |
|
||||||
<key>CFBundleInfoDictionaryVersion</key> |
|
||||||
<string>6.0</string> |
|
||||||
<key>CFBundlePackageType</key> |
|
||||||
<string>BNDL</string> |
|
||||||
<key>CFBundleSignature</key> |
|
||||||
<string>????</string> |
|
||||||
<key>CFBundleVersion</key> |
|
||||||
<string>1.0</string> |
|
||||||
</dict> |
|
||||||
</plist> |
|
@ -1,22 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|
||||||
<plist version="1.0"> |
|
||||||
<dict> |
|
||||||
<key>CFBundleDevelopmentRegion</key> |
|
||||||
<string>English</string> |
|
||||||
<key>CFBundleExecutable</key> |
|
||||||
<string>${EXECUTABLE_NAME}</string> |
|
||||||
<key>CFBundleIdentifier</key> |
|
||||||
<string>com.google.breakpad.minidump_tests64</string> |
|
||||||
<key>CFBundleInfoDictionaryVersion</key> |
|
||||||
<string>6.0</string> |
|
||||||
<key>CFBundlePackageType</key> |
|
||||||
<string>BNDL</string> |
|
||||||
<key>CFBundleSignature</key> |
|
||||||
<string>????</string> |
|
||||||
<key>CFBundleVersion</key> |
|
||||||
<string>1.0</string> |
|
||||||
<key>CSResourcesFileMapped</key> |
|
||||||
<string>yes</string> |
|
||||||
</dict> |
|
||||||
</plist> |
|
@ -1,20 +0,0 @@ |
|||||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|
||||||
<plist version="1.0"> |
|
||||||
<dict> |
|
||||||
<key>CFBundleDevelopmentRegion</key> |
|
||||||
<string>English</string> |
|
||||||
<key>CFBundleExecutable</key> |
|
||||||
<string>${EXECUTABLE_NAME}</string> |
|
||||||
<key>CFBundleIdentifier</key> |
|
||||||
<string>com.yourcompany.${PRODUCT_NAME:identifier}</string> |
|
||||||
<key>CFBundleInfoDictionaryVersion</key> |
|
||||||
<string>6.0</string> |
|
||||||
<key>CFBundlePackageType</key> |
|
||||||
<string>BNDL</string> |
|
||||||
<key>CFBundleSignature</key> |
|
||||||
<string>????</string> |
|
||||||
<key>CFBundleVersion</key> |
|
||||||
<string>1.0</string> |
|
||||||
</dict> |
|
||||||
</plist> |
|
@ -1,92 +0,0 @@ |
|||||||
// Copyright (c) 2006, 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.
|
|
||||||
//
|
|
||||||
// ProtectedMemoryAllocator
|
|
||||||
//
|
|
||||||
// See the header file for documentation
|
|
||||||
|
|
||||||
#include "protected_memory_allocator.h" |
|
||||||
#include <assert.h> |
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
ProtectedMemoryAllocator::ProtectedMemoryAllocator(vm_size_t pool_size)
|
|
||||||
: pool_size_(pool_size), |
|
||||||
next_alloc_offset_(0), |
|
||||||
valid_(false) { |
|
||||||
|
|
||||||
kern_return_t result = vm_allocate(mach_task_self(), |
|
||||||
&base_address_, |
|
||||||
pool_size, |
|
||||||
TRUE |
|
||||||
); |
|
||||||
|
|
||||||
valid_ = (result == KERN_SUCCESS); |
|
||||||
assert(valid_); |
|
||||||
} |
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
ProtectedMemoryAllocator::~ProtectedMemoryAllocator() { |
|
||||||
vm_deallocate(mach_task_self(), |
|
||||||
base_address_, |
|
||||||
pool_size_ |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
char *ProtectedMemoryAllocator::Allocate(vm_size_t bytes) { |
|
||||||
if (valid_ && next_alloc_offset_ + bytes <= pool_size_) { |
|
||||||
char *p = (char*)base_address_ + next_alloc_offset_; |
|
||||||
next_alloc_offset_ += bytes; |
|
||||||
return p; |
|
||||||
} |
|
||||||
|
|
||||||
return NULL; // ran out of memory in our allocation block
|
|
||||||
} |
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
kern_return_t ProtectedMemoryAllocator::Protect() { |
|
||||||
kern_return_t result = vm_protect(mach_task_self(), |
|
||||||
base_address_, |
|
||||||
pool_size_, |
|
||||||
FALSE, |
|
||||||
VM_PROT_READ); |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
kern_return_t ProtectedMemoryAllocator::Unprotect() { |
|
||||||
kern_return_t result = vm_protect(mach_task_self(), |
|
||||||
base_address_, |
|
||||||
pool_size_, |
|
||||||
FALSE, |
|
||||||
VM_PROT_READ | VM_PROT_WRITE); |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
@ -1,85 +0,0 @@ |
|||||||
// Copyright (c) 2006, 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.
|
|
||||||
//
|
|
||||||
// ProtectedMemoryAllocator
|
|
||||||
//
|
|
||||||
// A very simple allocator class which allows allocation, but not deallocation.
|
|
||||||
// The allocations can be made read-only with the Protect() method.
|
|
||||||
// This class is NOT useful as a general-purpose memory allocation system,
|
|
||||||
// since it does not allow deallocation. It is useful to use for a group
|
|
||||||
// of allocations which are created in the same time-frame and destroyed
|
|
||||||
// in the same time-frame. It is useful for making allocations of memory
|
|
||||||
// which will not need to change often once initialized. This memory can then
|
|
||||||
// be protected from memory smashers by calling the Protect() method.
|
|
||||||
|
|
||||||
#ifndef PROTECTED_MEMORY_ALLOCATOR_H__ |
|
||||||
#define PROTECTED_MEMORY_ALLOCATOR_H__ |
|
||||||
|
|
||||||
#include <mach/mach.h> |
|
||||||
|
|
||||||
//
|
|
||||||
class ProtectedMemoryAllocator { |
|
||||||
public: |
|
||||||
ProtectedMemoryAllocator(vm_size_t pool_size);
|
|
||||||
~ProtectedMemoryAllocator(); |
|
||||||
|
|
||||||
// Returns a pointer to an allocation of size n within the pool.
|
|
||||||
// Fails by returning NULL is no more space is available.
|
|
||||||
// Please note that the pointers returned from this method should not
|
|
||||||
// be freed in any way (for example by calling free() on them ).
|
|
||||||
char * Allocate(vm_size_t n); |
|
||||||
|
|
||||||
// Returns the base address of the allocation pool.
|
|
||||||
char * GetBaseAddress() { return (char*)base_address_; } |
|
||||||
|
|
||||||
// Returns the size of the allocation pool, including allocated
|
|
||||||
// plus free space.
|
|
||||||
vm_size_t GetTotalSize() { return pool_size_; } |
|
||||||
|
|
||||||
// Returns the number of bytes already allocated in the pool.
|
|
||||||
vm_size_t GetAllocatedSize() { return next_alloc_offset_; } |
|
||||||
|
|
||||||
// Returns the number of bytes available for allocation.
|
|
||||||
vm_size_t GetFreeSize() { return pool_size_ - next_alloc_offset_; } |
|
||||||
|
|
||||||
// Makes the entire allocation pool read-only including, of course,
|
|
||||||
// all allocations made from the pool.
|
|
||||||
kern_return_t Protect();
|
|
||||||
|
|
||||||
// Makes the entire allocation pool read/write.
|
|
||||||
kern_return_t Unprotect();
|
|
||||||
|
|
||||||
private: |
|
||||||
vm_size_t pool_size_; |
|
||||||
vm_address_t base_address_; |
|
||||||
vm_size_t next_alloc_offset_; |
|
||||||
bool valid_; |
|
||||||
}; |
|
||||||
|
|
||||||
#endif // PROTECTED_MEMORY_ALLOCATOR_H__
|
|
@ -1,85 +0,0 @@ |
|||||||
// Copyright (c) 2008, 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.
|
|
||||||
|
|
||||||
//
|
|
||||||
// DynamicImagesTests.cpp
|
|
||||||
// minidump_test
|
|
||||||
//
|
|
||||||
// Created by Neal Sidhwaney on 4/17/08.
|
|
||||||
// Copyright 2008 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "client/mac/handler/testcases/DynamicImagesTests.h" |
|
||||||
#include "client/mac/handler/dynamic_images.h" |
|
||||||
|
|
||||||
DynamicImagesTests test2(TEST_INVOCATION(DynamicImagesTests, |
|
||||||
ReadTaskMemoryTest)); |
|
||||||
DynamicImagesTests test3(TEST_INVOCATION(DynamicImagesTests, |
|
||||||
ReadLibrariesFromLocalTaskTest)); |
|
||||||
|
|
||||||
DynamicImagesTests::DynamicImagesTests(TestInvocation *invocation) |
|
||||||
: TestCase(invocation) { |
|
||||||
} |
|
||||||
|
|
||||||
DynamicImagesTests::~DynamicImagesTests() { |
|
||||||
} |
|
||||||
|
|
||||||
void DynamicImagesTests::ReadTaskMemoryTest() { |
|
||||||
kern_return_t kr; |
|
||||||
|
|
||||||
// pick test2 as a symbol we know to be valid to read
|
|
||||||
// anything will work, really
|
|
||||||
void *addr = reinterpret_cast<void*>(&test2); |
|
||||||
void *buf; |
|
||||||
|
|
||||||
fprintf(stderr, "reading 0x%p\n", addr); |
|
||||||
buf = google_breakpad::ReadTaskMemory(mach_task_self(), |
|
||||||
addr, |
|
||||||
getpagesize(), |
|
||||||
&kr); |
|
||||||
|
|
||||||
CPTAssert(kr == KERN_SUCCESS); |
|
||||||
|
|
||||||
CPTAssert(buf != NULL); |
|
||||||
|
|
||||||
CPTAssert(0 == memcmp(buf, (const void*)addr, getpagesize())); |
|
||||||
|
|
||||||
free(buf); |
|
||||||
} |
|
||||||
|
|
||||||
void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() { |
|
||||||
|
|
||||||
mach_port_t me = mach_task_self(); |
|
||||||
google_breakpad::DynamicImages *d = new google_breakpad::DynamicImages(me); |
|
||||||
|
|
||||||
fprintf(stderr,"Local task image count: %d\n", d->GetImageCount()); |
|
||||||
|
|
||||||
d->TestPrint(); |
|
||||||
|
|
||||||
CPTAssert(d->GetImageCount() > 0); |
|
||||||
} |
|
@ -1,52 +0,0 @@ |
|||||||
// Copyright (c) 2008, 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.
|
|
||||||
|
|
||||||
//
|
|
||||||
// DynamicImagesTests.h
|
|
||||||
// minidump_test
|
|
||||||
//
|
|
||||||
// Created by Neal Sidhwaney on 4/17/08.
|
|
||||||
// Copyright 2008 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ |
|
||||||
#define _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ |
|
||||||
|
|
||||||
#include <CPlusTest/CPlusTest.h> |
|
||||||
|
|
||||||
class DynamicImagesTests : public TestCase { |
|
||||||
public: |
|
||||||
explicit DynamicImagesTests(TestInvocation* invocation); |
|
||||||
virtual ~DynamicImagesTests(); |
|
||||||
|
|
||||||
void ReadTaskMemoryTest(); |
|
||||||
void ReadLibrariesFromLocalTaskTest(); |
|
||||||
}; |
|
||||||
|
|
||||||
#endif /* _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ */ |
|
@ -1,106 +0,0 @@ |
|||||||
// Copyright (c) 2008, 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.
|
|
||||||
|
|
||||||
//
|
|
||||||
// breakpad_nlist_test.cc
|
|
||||||
// minidump_test
|
|
||||||
//
|
|
||||||
// Created by Neal Sidhwaney on 4/13/08.
|
|
||||||
// Copyright 2008 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "client/mac/handler/testcases/breakpad_nlist_test.h" |
|
||||||
#include <mach-o/nlist.h> |
|
||||||
#include "client/mac/handler/breakpad_nlist_64.h" |
|
||||||
|
|
||||||
BreakpadNlistTest test1(TEST_INVOCATION(BreakpadNlistTest, CompareToNM)); |
|
||||||
|
|
||||||
BreakpadNlistTest::BreakpadNlistTest(TestInvocation *invocation) |
|
||||||
: TestCase(invocation) { |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
BreakpadNlistTest::~BreakpadNlistTest() { |
|
||||||
} |
|
||||||
|
|
||||||
void BreakpadNlistTest::CompareToNM() { |
|
||||||
#if TARGET_CPU_X86_64 |
|
||||||
system("/usr/bin/nm -arch x86_64 /usr/lib/dyld > /tmp/dyld-namelist.txt"); |
|
||||||
#elif TARGET_CPU_PPC64 |
|
||||||
system("/usr/bin/nm -arch ppc64 /usr/lib/dyld > /tmp/dyld-namelist.txt"); |
|
||||||
#endif |
|
||||||
|
|
||||||
FILE *fd = fopen("/tmp/dyld-namelist.txt", "rt"); |
|
||||||
|
|
||||||
char oneNMAddr[30]; |
|
||||||
char symbolType; |
|
||||||
char symbolName[500]; |
|
||||||
while (!feof(fd)) { |
|
||||||
fscanf(fd, "%s %c %s", oneNMAddr, &symbolType, symbolName); |
|
||||||
breakpad_nlist symbolList[2]; |
|
||||||
breakpad_nlist &list = symbolList[0]; |
|
||||||
|
|
||||||
memset(symbolList, 0, sizeof(breakpad_nlist)*2); |
|
||||||
const char *symbolNames[2]; |
|
||||||
symbolNames[0] = (const char*)symbolName; |
|
||||||
symbolNames[1] = "\0"; |
|
||||||
breakpad_nlist_64("/usr/lib/dyld", &list, symbolNames); |
|
||||||
uint64_t nmAddr = strtol(oneNMAddr, NULL, 16); |
|
||||||
if (!IsSymbolMoreThanOnceInDyld(symbolName)) { |
|
||||||
CPTAssert(nmAddr == symbolList[0].n_value); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fclose(fd); |
|
||||||
} |
|
||||||
|
|
||||||
bool BreakpadNlistTest::IsSymbolMoreThanOnceInDyld(const char *symbolName) { |
|
||||||
// These are the symbols that occur more than once when nm dumps
|
|
||||||
// the symbol table of /usr/lib/dyld. Our nlist program returns
|
|
||||||
// the first address because it's doing a search so we need to exclude
|
|
||||||
// these from causing the test to fail
|
|
||||||
const char *multipleSymbols[] = { |
|
||||||
"__Z41__static_initialization_and_destruction_0ii", |
|
||||||
"___tcf_0", |
|
||||||
"___tcf_1", |
|
||||||
"_read_encoded_value_with_base", |
|
||||||
"_read_sleb128", |
|
||||||
"_read_uleb128", |
|
||||||
"\0"}; |
|
||||||
|
|
||||||
bool found = false; |
|
||||||
|
|
||||||
for (int i = 0; multipleSymbols[i][0]; i++) { |
|
||||||
if (!strcmp(multipleSymbols[i], symbolName)) { |
|
||||||
found = true; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return found; |
|
||||||
} |
|
@ -1,62 +0,0 @@ |
|||||||
// Copyright (c) 2008, 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.
|
|
||||||
|
|
||||||
//
|
|
||||||
// breakpad_nlist_test.h
|
|
||||||
// minidump_test
|
|
||||||
//
|
|
||||||
// Created by Neal Sidhwaney on 4/13/08.
|
|
||||||
// Copyright 2008 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__ |
|
||||||
#define CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__ |
|
||||||
|
|
||||||
#include <CPlusTest/CPlusTest.h> |
|
||||||
|
|
||||||
class BreakpadNlistTest : public TestCase { |
|
||||||
private: |
|
||||||
|
|
||||||
// nm dumps multiple addresses for the same symbol in
|
|
||||||
// /usr/lib/dyld. So we track those so we don't report failures
|
|
||||||
// in mismatches between what our nlist returns and what nm has
|
|
||||||
// for the duplicate symbols.
|
|
||||||
bool IsSymbolMoreThanOnceInDyld(const char *symbolName); |
|
||||||
|
|
||||||
public: |
|
||||||
explicit BreakpadNlistTest(TestInvocation* invocation); |
|
||||||
virtual ~BreakpadNlistTest(); |
|
||||||
|
|
||||||
|
|
||||||
/* This test case runs nm on /usr/lib/dyld and then compares the
|
|
||||||
output of every symbol to what our nlist implementation returns */ |
|
||||||
void CompareToNM(); |
|
||||||
}; |
|
||||||
|
|
||||||
#endif /* CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__*/ |
|
@ -1,46 +0,0 @@ |
|||||||
// Copyright (c) 2008, 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.
|
|
||||||
|
|
||||||
//
|
|
||||||
// dwarftests.h
|
|
||||||
// minidump_test
|
|
||||||
//
|
|
||||||
// Created by Neal Sidhwaney on 9/24/08.
|
|
||||||
// Copyright 2008 Google Inc. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <SenTestingKit/SenTestingKit.h> |
|
||||||
|
|
||||||
|
|
||||||
@interface dwarftests : SenTestCase { |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
- (void) testDWARFSymbolFileGeneration; |
|
||||||
|
|
||||||
@end |
|
@ -1,60 +0,0 @@ |
|||||||
// Copyright (c) 2008, 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. |
|
||||||
|
|
||||||
// |
|
||||||
// dwarftests.m |
|
||||||
// minidump_test |
|
||||||
// |
|
||||||
// Created by Neal Sidhwaney on 9/24/08. |
|
||||||
// Copyright 2008 Google Inc. All rights reserved. |
|
||||||
// |
|
||||||
|
|
||||||
#import "dwarftests.h" |
|
||||||
#import "dump_syms.h" |
|
||||||
|
|
||||||
@implementation dwarftests |
|
||||||
- (void) testDWARFSymbolFileGeneration { |
|
||||||
NSString *inputBreakpadSymbolFile = @"testcases/testdata/dump_syms_i386_breakpad.sym"; |
|
||||||
NSString *outputBreakpadSymbolFile = @"/tmp/dump_syms_i386.breakpad"; |
|
||||||
|
|
||||||
DumpSymbols *dump = [[DumpSymbols alloc] initWithContentsOfFile:@"testcases/testdata/dump_syms_dwarf_data"]; |
|
||||||
|
|
||||||
STAssertNotNil(dump, @"DumpSymbols is nil"); |
|
||||||
[dump setArchitecture:@"i386"]; |
|
||||||
[dump writeSymbolFile:outputBreakpadSymbolFile]; |
|
||||||
|
|
||||||
NSData *d = [[NSData alloc] initWithContentsOfFile:inputBreakpadSymbolFile]; |
|
||||||
STAssertNotNil(d, @"Input breakpad symbol file not found"); |
|
||||||
|
|
||||||
NSData *d1 = [[NSData alloc] initWithContentsOfFile:outputBreakpadSymbolFile]; |
|
||||||
STAssertNotNil(d1, @"Output breakpad symbol file not found"); |
|
||||||
|
|
||||||
STAssertTrue([d isEqualToData:d1], |
|
||||||
@"Symbol files were not equal!",nil); |
|
||||||
} |
|
||||||
@end |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue