1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-07-31 19:30:21 +02:00

Merge pull request #229 from tomahawk-player/breakpad_external

Use libcrashreporter-qt in submodule instead of breakpad in tree
This commit is contained in:
Dominik Schmidt
2014-04-25 01:27:19 +02:00
1165 changed files with 96 additions and 465961 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "thirdparty/libcrashreporter-qt"]
path = thirdparty/libcrashreporter-qt
url = https://github.com/dschmidt/libcrashreporter-qt

View File

@@ -50,7 +50,6 @@ option(BUILD_TESTS "Build Tomahawk with unit tests" ON)
option(BUILD_HATCHET "Build the Hatchet plugin" OFF)
option(BUILD_WITH_QT4 "Build Tomahawk with Qt4 no matter if Qt5 was found" ON)
option(WITH_BREAKPAD "Build with breakpad integration" ON)
option(WITH_CRASHREPORTER "Build with CrashReporter" ON)
option(WITH_BINARY_ATTICA "Enable support for downloading binary resolvers automatically" ON)
option(LEGACY_KDE_INTEGRATION "Install tomahawk.protocol file, deprecated since 4.6.0" OFF)
@@ -61,21 +60,11 @@ CMAKE_DEPENDENT_OPTION(WITH_UPOWER "Build with support for UPower events" ON
CMAKE_DEPENDENT_OPTION(WITH_GNOMESHORTCUTHANDLER "Build with shortcut handler for GNOME" ON
"UNIX;NOT APPLE" OFF)
IF( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" )
message(STATUS "Build of breakpad library disabled on this platform.")
SET(WITH_BREAKPAD OFF)
IF( CMAKE_SYSTEM_PROCESSOR MATCHES "arm" OR NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/libcrashreporter-qt/CMakeLists.txt")
message(STATUS "Build of crashreporter disabled.")
SET(WITH_CRASHREPORTER OFF)
ENDIF()
# add definitions based on build options
IF( WITH_BREAKPAD )
message(STATUS "Build with support for breakpad.")
IF( CMAKE_COMPILER_IS_GNUCXX )
ADD_DEFINITIONS( -DSTDC_HEADERS -std=gnu++98 )
# SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTDC_HEADERS -std=gnu++98" )
ENDIF()
ENDIF()
# generate version string
# base string used in release and unstable builds

View File

@@ -57,7 +57,6 @@ The following dependencies are optional, but recommended:
Third party libraries that we ship with our source:
* MiniUPnP 1.6 - http://miniupnp.free.fr/
* Breakpad - http://code.google.com/p/google-breakpad/
* Qocoa - https://github.com/mikemcquaid/Qocoa/
* libqnetwm - http://code.google.com/p/libqnetwm/
* libqxt (QxtWeb module) - http://libqxt.org/

View File

@@ -21,7 +21,10 @@ add_subdirectory( viewpages )
# application
add_subdirectory( tomahawk )
add_subdirectory( crashreporter )
if(WITH_CRASHREPORTER)
add_subdirectory( crashreporter )
endif()
# tests
add_subdirectory( tests )

View File

@@ -3,14 +3,16 @@ cmake_policy(SET CMP0017 NEW)
setup_qt()
set(crashreporter_SOURCES main.cpp CrashReporter.cpp)
set(crashreporter_UI CrashReporter.ui)
set(crashreporter_RC ../../resources.qrc)
list(APPEND crashreporter_SOURCES main.cpp)
list(APPEND crashreporter_RC resources.qrc)
qt_wrap_ui( crashreporter_UI_HEADERS ${crashreporter_UI} )
qt_add_resources( crashreporter_RC_RCC ${crashreporter_RC} )
include_directories( ${CMAKE_CURRENT_BINARY_DIR} ../../libtomahawk )
include_directories( ${CMAKE_CURRENT_BINARY_DIR}
../../libtomahawk
../../thirdparty/libcrashreporter-qt/src
)
add_executable( tomahawk_crash_reporter WIN32
${crashreporter_SOURCES}
@@ -22,6 +24,7 @@ add_executable( tomahawk_crash_reporter WIN32
target_link_libraries(tomahawk_crash_reporter
tomahawklib
crashreporter-gui
${QT_LIBRARIES}
)

View File

@@ -1,208 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CrashReporter.h"
#include <QIcon>
#include <QDebug>
#include <QTimer>
#include <QDir>
#include <QDateTime>
#include "utils/TomahawkUtils.h"
#define LOGFILE TomahawkUtils::appLogDir().filePath( "Tomahawk.log" ).toLocal8Bit()
#define RESPATH ":/data/"
CrashReporter::CrashReporter( const QUrl& url, const QStringList& args )
: m_reply( 0 )
, m_url( url )
{
setWindowIcon( QIcon( RESPATH "icons/tomahawk-icon-128x128.png" ) );
ui.setupUi( this );
ui.logoLabel->setPixmap( QPixmap( RESPATH "icons/tomahawk-icon-128x128.png" ).scaled( QSize( 55, 55 ), Qt::KeepAspectRatio, Qt::SmoothTransformation ) );
ui.progressBar->setRange( 0, 100 );
ui.progressBar->setValue( 0 );
ui.progressLabel->setPalette( Qt::gray );
#ifdef Q_OS_MAC
QFont f = ui.bottomLabel->font();
f.setPointSize( 10 );
ui.bottomLabel->setFont( f );
f.setPointSize( 11 );
ui.progressLabel->setFont( f );
ui.progressLabel->setIndent( 3 );
#else
ui.vboxLayout->setSpacing( 16 );
ui.hboxLayout1->setSpacing( 16 );
ui.progressBar->setTextVisible( false );
ui.progressLabel->setIndent( 1 );
ui.bottomLabel->setDisabled( true );
ui.bottomLabel->setIndent( 1 );
#endif //Q_OS_MAC
m_request = new QNetworkRequest( m_url );
m_dir = args.value( 1 );
m_minidump = m_dir + '/' + args.value( 2 ) + ".dmp";
m_product_name = args.value( 3 );
//hide until "send report" has been clicked
ui.progressBar->setVisible( false );
ui.button->setVisible( false );
ui.progressLabel->setVisible( false );
connect( ui.sendButton, SIGNAL( clicked() ), SLOT( onSendButton() ) );
adjustSize();
setFixedSize( size() );
}
CrashReporter::~CrashReporter()
{
delete m_request;
delete m_reply;
}
static QByteArray
contents( const QString& path )
{
QFile f( path );
f.open( QFile::ReadOnly );
return f.readAll();
}
void
CrashReporter::send()
{
QByteArray body;
// socorro expects a 10 digit build id
QRegExp rx( "(\\d+\\.\\d+\\.\\d+).(\\d+)" );
rx.exactMatch( TomahawkUtils::appFriendlyVersion() );
QString const version = rx.cap( 1 );
QString const buildId = rx.cap( 2 ).leftJustified( 10, '0' );
// add parameters
typedef QPair<QByteArray, QByteArray> Pair;
QList<Pair> pairs;
pairs << Pair( "BuildID", buildId.toUtf8() )
<< Pair( "ProductName", m_product_name.toUtf8() )
<< Pair( "Version", TomahawkUtils::appFriendlyVersion().toLocal8Bit() )
<< Pair( "Vendor", "Tomahawk" )
<< Pair( "timestamp", QByteArray::number( QDateTime::currentDateTime().toTime_t() ) );
foreach ( Pair const pair, pairs )
{
body += "--thkboundary\r\n";
body += "Content-Disposition: form-data; name=\"" +
pair.first + "\"\r\n\r\n" +
pair.second + "\r\n";
}
// add minidump file
body += "--thkboundary\r\n";
body += "Content-Disposition: form-data; name=\"upload_file_minidump\"; filename=\""
+ QFileInfo( m_minidump ).fileName() + "\"\r\n";
body += "Content-Type: application/octet-stream\r\n";
body += "\r\n";
body += contents( m_minidump );
body += "\r\n";
// add logfile
body += "--thkboundary\r\n";
body += "Content-Disposition: form-data; name=\"upload_file_tomahawklog\"; filename=\"Tomahawk.log\"\r\n";
body += "Content-Type: application/x-gzip\r\n";
body += "\r\n";
body += qCompress( contents( LOGFILE ) );
body += "\r\n";
body += "--thkboundary--\r\n";
QNetworkAccessManager* nam = new QNetworkAccessManager( this );
m_request->setHeader( QNetworkRequest::ContentTypeHeader, "multipart/form-data; boundary=thkboundary" );
m_reply = nam->post( *m_request, body );
#if QT_VERSION < QT_VERSION_CHECK( 5, 0, 0 )
connect( m_reply, SIGNAL( finished() ), SLOT( onDone() ), Qt::QueuedConnection );
connect( m_reply, SIGNAL( uploadProgress( qint64, qint64 ) ), SLOT( onProgress( qint64, qint64 ) ) );
#else
connect( m_reply, &QNetworkReply::finished, this, &CrashReporter::onDone, Qt::QueuedConnection );
connect( m_reply, &QNetworkReply::uploadProgress, this, &CrashReporter::onProgress );
#endif
}
void
CrashReporter::onProgress( qint64 done, qint64 total )
{
if ( total )
{
QString const msg = tr( "Uploaded %L1 of %L2 KB." ).arg( done / 1024 ).arg( total / 1024 );
ui.progressBar->setMaximum( total );
ui.progressBar->setValue( done );
ui.progressLabel->setText( msg );
}
}
void
CrashReporter::onDone()
{
QByteArray data = m_reply->readAll();
ui.progressBar->setValue( ui.progressBar->maximum() );
ui.button->setText( tr( "Close" ) );
QString const response = QString::fromUtf8( data );
if ( ( m_reply->error() != QNetworkReply::NoError ) || !response.startsWith( "CrashID=" ) )
{
onFail( m_reply->error(), m_reply->errorString() );
}
else
ui.progressLabel->setText( tr( "Sent! <b>Many thanks</b>." ) );
}
void
CrashReporter::onFail( int error, const QString& errorString )
{
ui.button->setText( tr( "Close" ) );
ui.progressLabel->setText( tr( "Failed to send crash info." ) );
qDebug() << "Error:" << error << errorString;
}
void
CrashReporter::onSendButton()
{
ui.progressBar->setVisible( true );
ui.button->setVisible( true );
ui.progressLabel->setVisible( true );
ui.sendButton->setEnabled( false );
ui.dontSendButton->setEnabled( false );
adjustSize();
setFixedSize( size() );
QTimer::singleShot( 0, this, SLOT( send() ) );
}

View File

@@ -1,58 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CRASHREPORTER_H
#define CRASHREPORTER_H
#include <QDialog>
#include <QFile>
#include <QNetworkReply>
#include <QNetworkRequest>
#include "ui_CrashReporter.h"
class CrashReporter : public QDialog
{
Q_OBJECT
public:
CrashReporter( const QUrl& url, const QStringList& argv );
virtual ~CrashReporter( );
private:
Ui::CrashReporter ui;
QString m_minidump;
QString m_dir;
QString m_product_name;
QNetworkRequest* m_request;
QNetworkReply* m_reply;
QUrl m_url;
public slots:
void send();
private slots:
void onDone();
void onProgress( qint64 done, qint64 total );
void onFail( int error, const QString& errorString );
void onSendButton();
};
#endif // CRASHREPORTER_H

View File

@@ -1,257 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CrashReporter</class>
<widget class="QDialog" name="CrashReporter">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>376</width>
<height>216</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Tomahawk Crash Reporter</string>
</property>
<property name="modal">
<bool>false</bool>
</property>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="margin">
<number>9</number>
</property>
<item>
<layout class="QHBoxLayout" name="hboxLayout">
<property name="spacing">
<number>12</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="logoLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>55</width>
<height>55</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>55</width>
<height>55</height>
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="topLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Sorry!&lt;/span&gt; Tomahawk crashed. Please tell us about it! Tomahawk has created an error report for you that can help improve the stability in the future. You can now send this report directly to the Tomahawk developers.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>6</width>
<height>6</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hboxLayout1">
<property name="spacing">
<number>-1</number>
</property>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="sendButton">
<property name="text">
<string>Send this report</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="dontSendButton">
<property name="text">
<string>Don't send</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="vboxLayout1">
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="progressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
<property name="spacing">
<number>9</number>
</property>
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Abort</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="bottomLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>You can disable sending crash reports in the configuration dialog.</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>button</sender>
<signal>clicked()</signal>
<receiver>CrashReporter</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>426</x>
<y>203</y>
</hint>
<hint type="destinationlabel">
<x>247</x>
<y>195</y>
</hint>
</hints>
</connection>
<connection>
<sender>dontSendButton</sender>
<signal>clicked()</signal>
<receiver>CrashReporter</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>380</x>
<y>117</y>
</hint>
<hint type="destinationlabel">
<x>218</x>
<y>122</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -16,17 +16,20 @@
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "CrashReporter.h"
#include <libcrashreporter-gui/CrashReporter.h>
#include <QTranslator>
#include <iostream>
#include <QApplication>
#include <QFileInfo>
#include "utils/TomahawkUtils.h"
#include "utils/Logger.h"
const char* k_usage =
"Usage:\n"
" CrashReporter <logDir> <dumpFileName> <productName>\n";
" CrashReporter <dumpFilePath>\n";
int main( int argc, char* argv[] )
{
@@ -39,13 +42,71 @@ int main( int argc, char* argv[] )
QApplication app( argc, argv );
TomahawkUtils::installTranslator( &app );
if ( app.arguments().size() != 4 )
if ( app.arguments().size() != 2 )
{
std::cout << k_usage;
return 1;
}
CrashReporter reporter( QUrl( "http://oops.tomahawk-player.org/addreport.php" ), app.arguments() );
CrashReporter reporter( QUrl( "http://crash-reports.tomahawk-player.org/submit" ), app.arguments() );
reporter.setLogo(QPixmap(":/tomahawk-icon.png"));
// // socorro expects a 10 digit build id
// QRegExp rx( "(\\d+\\.\\d+\\.\\d+).(\\d+)" );
// rx.exactMatch( TomahawkUtils::appFriendlyVersion() );
// //QString const version = rx.cap( 1 );
// QString const buildId = rx.cap( 2 ).leftJustified( 10, '0' );
reporter.setReportData( "BuildID", "YYYYMMDDHH" );
reporter.setReportData( "ProductName", "WaterWolf" );
reporter.setReportData( "Version", TomahawkUtils::appFriendlyVersion().toLocal8Bit() );
//reporter.setReportData( "timestamp", QByteArray::number( QDateTime::currentDateTime().toTime_t() ) );
// add parameters
// QList<Pair> pairs;
// pairs //<< Pair( "BuildID", buildId.toUtf8() )
// << Pair( )
// //<< Pair( "Version", TomahawkUtils::appFriendlyVersion().toLocal8Bit() )
// //<< Pair( "Vendor", "Tomahawk" )
// //<< Pair( )
// << Pair("InstallTime", "1357622062")
// << Pair("Theme", "classic/1.0")
// << Pair("Version", "30")
// << Pair("id", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
// << Pair("Vendor", "Mozilla")
// << Pair("EMCheckCompatibility", "true")
// << Pair("Throttleable", "0")
// << Pair("URL", "http://code.google.com/p/crashme/")
// << Pair("version", "20.0a1")
// << Pair("CrashTime", "1357770042")
// << Pair("ReleaseChannel", "nightly")
// << Pair("submitted_timestamp", "2013-01-09T22:21:18.646733+00:00")
// << Pair("buildid", "20130107030932")
// << Pair("timestamp", "1357770078.646789")
// << Pair("Notes", "OpenGL: NVIDIA Corporation -- GeForce 8600M GT/PCIe/SSE2 -- 3.3.0 NVIDIA 313.09 -- texture_from_pixmap\r\n")
// << Pair("StartupTime", "1357769913")
// << Pair("FramePoisonSize", "4096")
// << Pair("FramePoisonBase", "7ffffffff0dea000")
// << Pair("Add-ons", "%7B972ce4c6-7e08-4474-a285-3208198ce6fd%7D:20.0a1,crashme%40ted.mielczarek.org:0.4")
// << Pair("BuildID", "YYYYMMDDHH")
// << Pair("SecondsSinceLastCrash", "1831736")
// << Pair("ProductName", "WaterWolf")
// << Pair("legacy_processing", "0")
// << Pair("ProductID", "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}")
;
// send log
QFile logFile( Logger::logFile() );
logFile.open( QFile::ReadOnly );
reporter.setReportData( "upload_file_tomahawklog", qCompress( logFile.readAll() ), "application/x-gzip", QFileInfo( Logger::logFile() ).fileName().toUtf8());
logFile.close();
reporter.show();
return app.exec();

View File

@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file alias="tomahawk-icon.png">../../data/icons/tomahawk-icon-128x128.png</file>
</qresource>
</RCC>

View File

@@ -71,10 +71,6 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
widgets/UnstyledFrame.cpp
)
IF( WITH_BREAKPAD )
LIST(APPEND tomahawkSourcesGui breakpad/BreakPad.cpp)
ENDIF()
SET( tomahawkUI ${tomahawkUI}
dialogs/DiagnosticsDialog.ui
dialogs/HostDialog.ui
@@ -100,7 +96,7 @@ INCLUDE_DIRECTORIES(
../libtomahawk
mac
${THIRDPARTY_DIR}/breakpad
${THIRDPARTY_DIR}/libcrashreporter-qt/src/
${TAGLIB_INCLUDES}
${LIBATTICA_INCLUDE_DIR}
@@ -177,8 +173,8 @@ ENDIF( LIBLASTFM_FOUND )
IF( QCA2_FOUND )
LIST(APPEND LINK_LIBRARIES ${LINK_LIBRARIES} ${QCA2_LIBRARIES} )
ENDIF( QCA2_FOUND )
IF( WITH_BREAKPAD )
LIST(APPEND LINK_LIBRARIES ${LINK_LIBRARIES} tomahawk_breakpad )
IF( WITH_CRASHREPORTER )
LIST(APPEND LINK_LIBRARIES ${LINK_LIBRARIES} crashreporter-handler )
ENDIF()
TARGET_LINK_LIBRARIES( tomahawk_bin

View File

@@ -12,7 +12,6 @@
#cmakedefine LEOPARD
#cmakedefine HAVE_SPARKLE
#cmakedefine WITH_BREAKPAD
#cmakedefine WITH_CRASHREPORTER
#cmakedefine WITH_BINARY_ATTICA
#cmakedefine WITH_QTSPARKLE

View File

@@ -1,179 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BreakPad.h"
#include "config.h"
#include "utils/Logger.h"
#include <QCoreApplication>
#include <QString>
#include <QFileInfo>
#include <string.h>
#define CRASH_REPORTER_BINARY "tomahawk_crash_reporter"
bool s_active = true;
#ifndef WIN32
#include <unistd.h>
static bool
LaunchUploader( const char* dump_dir, const char* minidump_id, void* that, bool succeeded )
{
// DON'T USE THE HEAP!!!
// So that indeed means, no QStrings, no qDebug(), no QAnything, seriously!
if ( !succeeded )
return false;
const char* crashReporter = static_cast<BreakPad*>(that)->crashReporter();
if ( !s_active || strlen( crashReporter ) == 0 )
return false;
pid_t pid = fork();
if ( pid == -1 ) // fork failed
return false;
if ( pid == 0 )
{
// we are the fork
execl( crashReporter,
crashReporter,
dump_dir,
minidump_id,
minidump_id,
(char*) 0 );
// execl replaces this process, so no more code will be executed
// unless it failed. If it failed, then we should return false.
printf( "Error: Can't launch CrashReporter!\n" );
return false;
}
// we called fork()
return true;
}
BreakPad::BreakPad( const QString& path, bool active )
#ifdef Q_OS_LINUX
: google_breakpad::ExceptionHandler( path.toStdString(), 0, LaunchUploader, this, true )
#else
: google_breakpad::ExceptionHandler( path.toStdString(), 0, LaunchUploader, this, true, 0 )
#endif
{
s_active = active;
QString reporter;
QString localReporter = QString( "%1/%2" ).arg( qApp->applicationDirPath() ).arg( CRASH_REPORTER_BINARY );
QString globalReporter = QString( "%1/%2" ).arg( CMAKE_INSTALL_FULL_LIBEXECDIR ).arg( CRASH_REPORTER_BINARY );
if ( QFileInfo( localReporter ).exists() )
reporter = localReporter;
else if ( QFileInfo( globalReporter ).exists() )
reporter = globalReporter;
else
qDebug() << "Could not find \"" CRASH_REPORTER_BINARY "\" in \"" CMAKE_INSTALL_FULL_LIBEXECDIR "\" or application path";
char* creporter;
std::string sreporter = reporter.toStdString();
creporter = new char[ sreporter.size() + 1 ];
strcpy( creporter, sreporter.c_str() );
m_crashReporter = creporter;
}
#else
static bool
LaunchUploader( const wchar_t* dump_dir, const wchar_t* minidump_id, void* that, EXCEPTION_POINTERS *exinfo, MDRawAssertionInfo *assertion, bool succeeded )
{
if ( !succeeded )
return false;
// DON'T USE THE HEAP!!!
// So that indeed means, no QStrings, no qDebug(), no QAnything, seriously!
// broken in mingw, hardcode it for now
// const char* productName = static_cast<BreakPad*>(that)->productName();s
// convert productName to widechars, which sadly means the product name must be Latin1
wchar_t product_name[ 256 ] = L"tomahawk";;
// char* out = (char*)product_name;
// const char* in = productName - 1;
// do {
// *out++ = *++in; //latin1 chars fit in first byte of each wchar
// *out++ = '\0'; //every second byte is NULL
// }
// while (*in);
wchar_t command[MAX_PATH * 3 + 6];
wcscpy( command, CRASH_REPORTER_BINARY L" \"" );
wcscat( command, dump_dir );
wcscat( command, L"\" \"" );
wcscat( command, minidump_id );
wcscat( command, L"\" \"" );
wcscat( command, product_name );
wcscat( command, L"\"" );
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof( si ) );
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL;
ZeroMemory( &pi, sizeof(pi) );
if ( CreateProcess( NULL, command, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ) )
{
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
TerminateProcess( GetCurrentProcess(), 1 );
}
return false;
}
BreakPad::BreakPad( const QString& path, bool active )
: google_breakpad::ExceptionHandler( path.toStdWString(), 0, LaunchUploader, this, true )
{
s_active = active;
}
#endif // WIN32
void
BreakPad::setActive( bool enabled )
{
s_active = enabled;
}
bool
BreakPad::isActive()
{
return s_active;
}

View File

@@ -1,50 +0,0 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk 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.
*
* Tomahawk 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 Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QString>
#ifdef __APPLE__
# include "client/mac/handler/exception_handler.h"
#elif defined WIN32
# include "client/windows/handler/exception_handler.h"
#elif defined __linux__
# include "client/linux/handler/exception_handler.h"
#endif
class BreakPad : public google_breakpad::ExceptionHandler
{
const char* m_productName; // yes! It MUST be const char[]
const char* m_crashReporter; // again, const char[]
public:
BreakPad( const QString& dump_write_dirpath, bool active );
~BreakPad()
{}
static void setActive( bool enabled );
static bool isActive();
void setProductName( const char* s ) { m_productName = s; };
const char* productName() const { return m_productName; }
void setCrashReporter( const char* s ) { m_crashReporter = s; };
const char* crashReporter() const { return m_crashReporter; }
};
#undef char

View File

@@ -36,8 +36,8 @@
#ifndef ENABLE_HEADLESS
#include "TomahawkSettingsGui.h"
#ifdef WITH_BREAKPAD
#include "breakpad/BreakPad.h"
#ifdef WITH_CRASHREPORTER
#include "libcrashreporter-handler/Handler.h"
#endif
#ifdef Q_WS_X11 // This is probably a very bad idea with Qt5 anyway... because (if at all) X lives in a QPA plugin
@@ -165,8 +165,8 @@ main( int argc, char *argv[] )
#endif
#ifndef ENABLE_HEADLESS
#ifdef WITH_BREAKPAD
new BreakPad( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() && !TomahawkUtils::headless() );
#ifdef WITH_CRASHREPORTER
new CrashReporter::Handler( QDir::tempPath(), TomahawkSettings::instance()->crashReporterEnabled() && !TomahawkUtils::headless(), "tomahawk_crash_reporter" );
#endif
#endif

View File

@@ -1,6 +1,6 @@
ADD_SUBDIRECTORY( qxt )
IF( WITH_BREAKPAD )
ADD_SUBDIRECTORY( breakpad )
IF( WITH_CRASHREPORTER )
ADD_SUBDIRECTORY( libcrashreporter-qt )
ENDIF()
IF( APPLE )
ADD_SUBDIRECTORY( SPMediaKeyTap )

View File

@@ -1,84 +0,0 @@
PROJECT(breakpad)
cmake_minimum_required(VERSION 2.8)
set( CMAKE_MODULE_PATH
${CMAKE_MODULE_PATH}
${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules
)
if( CMAKE_COMPILER_IS_GNUCXX )
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++98")
endif()
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
add_definitions(-DNDEBUG)
endif(${CMAKE_BUILD_TYPE} MATCHES "Release")
IF(UNIX)
IF(APPLE)
SET( breakpadSources
client/mac/crash_generation/crash_generation_client.cc
client/mac/crash_generation/crash_generation_server.cc
client/mac/handler/breakpad_nlist_64.cc
client/mac/handler/dynamic_images.cc
client/mac/handler/exception_handler.cc
client/mac/handler/minidump_generator.cc
client/mac/handler/protected_memory_allocator.cc
# client/mac/Framework/Breakpad.mm
# client/mac/Framework/OnDemandServer.mm
common/mac/file_id.cc
common/mac/macho_id.cc
common/mac/macho_reader.cc
common/mac/macho_utilities.cc
common/mac/macho_walker.cc
common/mac/string_utilities.cc
common/md5.cc
common/mac/dump_syms.mm
common/mac/MachIPC.mm
common/mac/SimpleStringDictionary.mm
common/mac/bootstrap_compat.cc
)
ELSE(APPLE)
SET( breakpadSources
common/linux/safe_readlink.cc
client/linux/crash_generation/crash_generation_client.cc
client/linux/crash_generation/crash_generation_server.cc
client/linux/minidump_writer/minidump_writer.cc
client/linux/minidump_writer/linux_dumper.cc
client/linux/handler/exception_handler.cc
common/linux/dump_symbols.cc
common/linux/file_id.cc
common/linux/libcurl_wrapper.cc
common/linux/google_crashdump_uploader.cc
common/linux/synth_elf.cc
common/linux/http_upload.cc
common/linux/guid_creator.cc
common/linux/elf_symbols_to_module.cc
client/minidump_file_writer.cc
client/linux/minidump_writer/linux_ptrace_dumper.cc
common/linux/memory_mapped_file.cc
client/linux/log/log.cc
)
ENDIF(APPLE)
LIST(APPEND breakpadSources
client/minidump_file_writer.cc
common/convert_UTF.c
common/string_conversion.cc
)
ENDIF(UNIX)
IF(WIN32)
ADD_DEFINITIONS( -DUNICODE -fshort-wchar )
SET( breakpadSources
client/windows/handler/exception_handler.cc
client/windows/crash_generation/crash_generation_client.cc
common/windows/guid_string.cc
)
ENDIF(WIN32)
INCLUDE_DIRECTORIES(.)
ADD_DEFINITIONS( -fPIC )
ADD_LIBRARY( tomahawk_breakpad STATIC ${breakpadSources} )
TARGET_LINK_LIBRARIES( tomahawk_breakpad )

View File

@@ -1,72 +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.
// 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"

View File

@@ -1,48 +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_
// TODO(zhenghao): exec_elf.h conflicts with linux/elf.h.
// But we still need ELFSIZE.
//#include <sys/exec_elf.h>
#include <machine/exec.h>
#define ELFSIZE ARCH_ELFSIZE
#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_

View File

@@ -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_

View File

@@ -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_

View File

@@ -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);
}
}

View File

@@ -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_

View File

@@ -1,467 +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"
#include "common/linux/safe_readlink.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[PATH_MAX];
if (!google_breakpad::SafeReadLink(path, buf)) {
return false;
}
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[PATH_MAX];
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

View File

@@ -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_

View File

@@ -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

View File

@@ -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

View File

@@ -1,523 +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>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#if !defined(__ANDROID__)
#include <sys/signal.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <ucontext.h>
#endif
#include <algorithm>
#include <utility>
#include <vector>
#include "common/linux/linux_libc_support.h"
#include "common/memory.h"
#include "client/linux/log/log.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"
#include "linux/sched.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;
stack_t stack;
// Only set an alternative stack if there isn't already one, or if the current
// one is too small.
if (sys_sigaltstack(NULL, &stack) == -1 || !stack.ss_sp ||
stack.ss_size < kSigStackSize) {
memset(&stack, 0, sizeof(stack));
stack.ss_sp = malloc(kSigStackSize);
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:";
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
#if defined(__ANDROID__)
const pid_t child = clone(
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
&thread_arg);
#else
const pid_t child = sys_clone(
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
&thread_arg, NULL, NULL, NULL);
#endif
int r, status;
// Allow the child to ptrace us
sys_prctl(PR_SET_PTRACER, child);
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:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\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:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\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:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\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(), sizeof(info.name) - 1);
info.name[sizeof(info.name) - 1] = '\0';
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, identifier, sizeof(MDGUID));
mapping_list_.push_back(mapping);
}
} // namespace google_breakpad

View File

@@ -1,258 +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_;
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_

View File

@@ -1,786 +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 "common/tests/auto_tempdir.h"
#include "third_party/lss/linux_syscall_support.h"
#include "google_breakpad/processor/minidump.h"
using namespace google_breakpad;
// 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) {
AutoTempDir temp_dir;
ExceptionHandler handler(temp_dir.path(), 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) {
AutoTempDir temp_dir;
int fds[2];
ASSERT_NE(pipe(fds), -1);
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(temp_dir.path(), 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 = temp_dir.path() + "/" + 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) {
AutoTempDir temp_dir;
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(temp_dir.path(), 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 = temp_dir.path() + "/" + 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) {
AutoTempDir temp_dir;
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(temp_dir.path(), 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 = temp_dir.path() + "/" + 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) {
AutoTempDir temp_dir;
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(temp_dir.path(), 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 = temp_dir.path() + "/" + 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) {
AutoTempDir temp_dir;
int fds[2];
ASSERT_NE(pipe(fds), -1);
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler(temp_dir.path(), 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 = temp_dir.path() + "/" + 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 uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
string minidump_filename;
AutoTempDir temp_dir;
ExceptionHandler handler(temp_dir.path(), 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];
if (pipe(fds) == -1) {
// There doesn't seem to be any way to reliably handle
// this failure without the parent process hanging
// At least make sure that this process doesn't access
// unexpected file descriptors
fds[0] = -1;
fds[1] = -1;
}
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);
ASSERT_EQ(close(fds[0]), 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);
AutoTempDir temp_dir;
std::string templ = temp_dir.path() + "/exception-handler-unittest";
ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context,
kCrashContextSize));
static const char b = 0;
HANDLE_EINTR(write(signal_fd, &b, 1));
ASSERT_EQ(close(signal_fd), 0);
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.c_str(), &st), 0);
ASSERT_GT(st.st_size, 0u);
unlink(templ.c_str());
}

View File

@@ -1,48 +0,0 @@
// Copyright (c) 2012 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/linux/log/log.h"
#if defined(__ANDROID__)
#include <android/log.h>
#else
#include "third_party/lss/linux_syscall_support.h"
#endif
namespace logger {
int write(const char* buf, size_t nbytes) {
#if defined(__ANDROID__)
return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf);
#else
return sys_write(2, buf, nbytes);
#endif
}
} // namespace logger

View File

@@ -1,41 +0,0 @@
// Copyright (c) 2012, 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_LOG_LOG_H_
#define CLIENT_LINUX_LOG_LOG_H_
#include <stddef.h>
namespace logger {
int write(const char* buf, size_t nbytes);
} // namespace logger
#endif // CLIENT_LINUX_LOG_LOG_H_

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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_

View File

@@ -1,197 +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"
#include "common/linux/eintr_wrapper.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();
const int r = HANDLE_EINTR(write(fd, "a\n", 2));
ASSERT_EQ(2, r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned int len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned int)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}
TEST(LineReaderTest, OneLine) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a", 1));
ASSERT_EQ(1, r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}
TEST(LineReaderTest, TwoLinesTerminated) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a\nb\n", 4));
ASSERT_EQ(4, r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('b', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}
TEST(LineReaderTest, TwoLines) {
const int fd = TemporaryFile();
const int r = HANDLE_EINTR(write(fd, "a\nb", 3));
ASSERT_EQ(3, r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('b', line[0]);
ASSERT_EQ('\0', line[1]);
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));
const int r = HANDLE_EINTR(write(fd, l, sizeof(l)));
ASSERT_EQ(sizeof(l), r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(sizeof(l), len);
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
ASSERT_EQ('\0', line[len]);
close(fd);
}
TEST(LineReaderTest, TooLong) {
const int fd = TemporaryFile();
char l[LineReader::kMaxLineLen];
memset(l, 'a', sizeof(l));
const int r = HANDLE_EINTR(write(fd, l, sizeof(l)));
ASSERT_EQ(sizeof(l), r);
lseek(fd, 0, SEEK_SET);
LineReader reader(fd);
const char *line;
unsigned len;
ASSERT_FALSE(reader.GetNextLine(&line, &len));
close(fd);
}

View File

@@ -1,234 +0,0 @@
// Copyright (c) 2012, 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.
// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
// See linux_core_dumper.h for details.
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include <asm/ptrace.h>
#include <assert.h>
#include <elf.h>
#include <stdio.h>
#include <string.h>
#include <sys/procfs.h>
#include "common/linux/linux_libc_support.h"
namespace google_breakpad {
LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
const char* core_path,
const char* procfs_path)
: LinuxDumper(pid),
core_path_(core_path),
procfs_path_(procfs_path),
thread_infos_(&allocator_, 8) {
assert(core_path_);
}
bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
const char* node) const {
if (!path || !node)
return false;
size_t node_len = my_strlen(node);
if (node_len == 0)
return false;
size_t procfs_path_len = my_strlen(procfs_path_);
size_t total_length = procfs_path_len + 1 + node_len;
if (total_length >= NAME_MAX)
return false;
memcpy(path, procfs_path_, procfs_path_len);
path[procfs_path_len] = '/';
memcpy(path + procfs_path_len + 1, node, node_len);
path[total_length] = '\0';
return true;
}
void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
const void* src, size_t length) {
ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
// TODO(benchan): Investigate whether the data to be copied could span
// across multiple segments in the core dump file. ElfCoreDump::CopyData
// and this method do not handle that case yet.
if (!core_.CopyData(dest, virtual_address, length)) {
// If the data segment is not found in the core dump, fill the result
// with marker characters.
memset(dest, 0xab, length);
}
}
bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (index >= thread_infos_.size())
return false;
*info = thread_infos_[index];
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,
reinterpret_cast<uintptr_t>(stack_pointer));
}
bool LinuxCoreDumper::IsPostMortem() const {
return true;
}
bool LinuxCoreDumper::ThreadsSuspend() {
return true;
}
bool LinuxCoreDumper::ThreadsResume() {
return true;
}
bool LinuxCoreDumper::EnumerateThreads() {
if (!mapped_core_file_.Map(core_path_)) {
fprintf(stderr, "Could not map core dump file into memory\n");
return false;
}
core_.SetContent(mapped_core_file_.content());
if (!core_.IsValid()) {
fprintf(stderr, "Invalid core dump file\n");
return false;
}
ElfCoreDump::Note note = core_.GetFirstNote();
if (!note.IsValid()) {
fprintf(stderr, "PT_NOTE section not found\n");
return false;
}
bool first_thread = true;
do {
ElfCoreDump::Word type = note.GetType();
MemoryRange name = note.GetName();
MemoryRange description = note.GetDescription();
if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
fprintf(stderr, "Could not found a valid PT_NOTE.\n");
return false;
}
// Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
// ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
// Thread Name Type
// -------------------------------------------------------------------
// 1st thread CORE NT_PRSTATUS
// process-wide CORE NT_PRPSINFO
// process-wide CORE NT_AUXV
// 1st thread CORE NT_FPREGSET
// 1st thread LINUX NT_PRXFPREG
// 1st thread LINUX NT_386_TLS
//
// 2nd thread CORE NT_PRSTATUS
// 2nd thread CORE NT_FPREGSET
// 2nd thread LINUX NT_PRXFPREG
// 2nd thread LINUX NT_386_TLS
//
// 3rd thread CORE NT_PRSTATUS
// 3rd thread CORE NT_FPREGSET
// 3rd thread LINUX NT_PRXFPREG
// 3rd thread LINUX NT_386_TLS
//
// The following code only works if notes are ordered as expected.
switch (type) {
case NT_PRSTATUS: {
if (description.length() != sizeof(elf_prstatus)) {
fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
return false;
}
const elf_prstatus* status =
reinterpret_cast<const elf_prstatus*>(description.data());
pid_t pid = status->pr_pid;
ThreadInfo info;
memset(&info, 0, sizeof(ThreadInfo));
info.tgid = status->pr_pgrp;
info.ppid = status->pr_ppid;
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
if (first_thread) {
crash_thread_ = pid;
crash_signal_ = status->pr_info.si_signo;
}
first_thread = false;
threads_.push_back(pid);
thread_infos_.push_back(info);
break;
}
#if defined(__i386) || defined(__x86_64)
case NT_FPREGSET: {
if (thread_infos_.empty())
return false;
ThreadInfo* info = &thread_infos_.back();
if (description.length() != sizeof(info->fpregs)) {
fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
return false;
}
memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
break;
}
#endif
#if defined(__i386)
case NT_PRXFPREG: {
if (thread_infos_.empty())
return false;
ThreadInfo* info = &thread_infos_.back();
if (description.length() != sizeof(info->fpxregs)) {
fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
return false;
}
memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
break;
}
#endif
}
note = note.GetNextNote();
} while (note.IsValid());
return true;
}
} // namespace google_breakpad

View File

@@ -1,122 +0,0 @@
// Copyright (c) 2012, 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.
// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper
// class, which is derived from google_breakpad::LinuxDumper to extract
// information from a crashed process via its core dump and proc files.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/elf_core_dump.h"
#include "common/linux/memory_mapped_file.h"
namespace google_breakpad {
class LinuxCoreDumper : public LinuxDumper {
public:
// Constructs a dumper for extracting information of a given process
// with a process ID of |pid| via its core dump file at |core_path| and
// its proc files at |procfs_path|. If |procfs_path| is a copy of
// /proc/<pid>, it should contain the following files:
// auxv, cmdline, environ, exe, maps, status
LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path);
// Implements LinuxDumper::BuildProcPath().
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result.|node| is the final node without any slashes. Return true on
// success.
//
// As this dumper performs a post-mortem dump and makes use of a copy
// of the proc files of the crashed process, this derived method does
// not actually make use of |pid| and always returns a subpath of
// |procfs_path_| regardless of whether |pid| corresponds to the main
// process or a thread of the process, i.e. assuming both the main process
// and its threads have the following proc files with the same content:
// auxv, cmdline, environ, exe, maps, status
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
// Implements LinuxDumper::CopyFromProcess().
// Copies content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. This method extracts the content
// the core dump and fills |dest| with a sequence of marker bytes
// if the expected data is not found in the core dump.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length);
// Implements LinuxDumper::GetThreadInfoByIndex().
// Reads information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// Implements LinuxDumper::IsPostMortem().
// Always returns true to indicate that this dumper performs a
// post-mortem dump of a crashed process via a core dump file.
virtual bool IsPostMortem() const;
// Implements LinuxDumper::ThreadsSuspend().
// As the dumper performs a post-mortem dump via a core dump file,
// there is no threads to suspend. This method does nothing and
// always returns true.
virtual bool ThreadsSuspend();
// Implements LinuxDumper::ThreadsResume().
// As the dumper performs a post-mortem dump via a core dump file,
// there is no threads to resume. This method does nothing and
// always returns true.
virtual bool ThreadsResume();
protected:
// Implements LinuxDumper::EnumerateThreads().
// Enumerates all threads of the given process into |threads_|.
virtual bool EnumerateThreads();
private:
// Path of the core dump file.
const char* core_path_;
// Path of the directory containing the proc files of the given process,
// which is usually a copy of /proc/<pid>.
const char* procfs_path_;
// Memory-mapped core dump file at |core_path_|.
MemoryMappedFile mapped_core_file_;
// Content of the core dump file.
ElfCoreDump core_;
// Thread info found in the core dump file.
wasteful_vector<ThreadInfo> thread_infos_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_

View File

@@ -1,109 +0,0 @@
// Copyright (c) 2012, 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.
// linux_core_dumper_unittest.cc:
// Unit tests for google_breakpad::LinuxCoreDumoer.
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include "common/linux/tests/crash_generator.h"
using std::string;
using namespace google_breakpad;
TEST(LinuxCoreDumperTest, BuildProcPath) {
const pid_t pid = getpid();
const char procfs_path[] = "/procfs_copy";
LinuxCoreDumper dumper(getpid(), "core_file", procfs_path);
char maps_path[NAME_MAX] = "";
char maps_path_expected[NAME_MAX];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"%s/maps", procfs_path);
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
EXPECT_STREQ(maps_path_expected, maps_path);
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
char long_node[NAME_MAX];
size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1;
memset(long_node, 'a', long_node_len);
long_node[long_node_len] = '\0';
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node));
}
TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
CrashGenerator crash_generator;
if (!crash_generator.HasDefaultCorePattern()) {
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to non-default core pattern\n");
return;
}
const unsigned kNumOfThreads = 3;
const unsigned kCrashThread = 1;
const int kCrashSignal = SIGABRT;
pid_t child_pid;
// TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
// CrashGenerator is identified and fixed.
if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
kCrashSignal, &child_pid)) {
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to no core dump generated\n");
return;
}
pid_t pid = getpid();
const string core_file = crash_generator.GetCoreFilePath();
const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
dumper.Init();
EXPECT_TRUE(dumper.IsPostMortem());
// These are no-ops and should always return true.
EXPECT_TRUE(dumper.ThreadsSuspend());
EXPECT_TRUE(dumper.ThreadsResume());
// LinuxCoreDumper cannot determine the crash address and thus it always
// sets the crash address to 0.
EXPECT_EQ(0, dumper.crash_address());
EXPECT_EQ(kCrashSignal, dumper.crash_signal());
EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
dumper.crash_thread());
EXPECT_EQ(kNumOfThreads, dumper.threads().size());
for (unsigned i = 0; i < kNumOfThreads; ++i) {
ThreadInfo info;
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info));
EXPECT_EQ(getpid(), info.ppid);
}
}

View File

@@ -1,358 +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.
// linux_dumper.cc: Implement google_breakpad::LinuxDumper.
// See linux_dumper.h for details.
// 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 <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/file_id.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/memory_mapped_file.h"
#include "common/linux/safe_readlink.h"
#include "third_party/lss/linux_syscall_support.h"
static const char kMappedFileUnsafePrefix[] = "/dev/";
static const char kDeletedSuffix[] = " (deleted)";
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(pid_t pid)
: pid_(pid),
crash_address_(0),
crash_signal_(0),
crash_thread_(0),
threads_(&allocator_, 8),
mappings_(&allocator_) {
}
LinuxDumper::~LinuxDumper() {
}
bool LinuxDumper::Init() {
return EnumerateThreads() && EnumerateMappings();
}
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;
// Special-case linux-gate because it's not a real file.
if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
const uintptr_t kPageSize = getpagesize();
void* linux_gate = NULL;
if (pid_ == sys_getpid()) {
linux_gate = reinterpret_cast<void*>(mapping.start_addr);
} else {
linux_gate = allocator_.Alloc(kPageSize);
CopyFromProcess(linux_gate, pid_,
reinterpret_cast<const void*>(mapping.start_addr),
kPageSize);
}
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
}
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);
MemoryMappedFile mapped_file(filename);
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
return false;
bool success =
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
if (success && member && filename_modified) {
mappings_[mapping_id]->name[filename_len -
sizeof(kDeletedSuffix) + 1] = '\0';
}
return success;
}
void*
LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(pid_t pid) const {
char auxv_path[NAME_MAX];
if (!BuildProcPath(auxv_path, pid, "auxv"))
return NULL;
// 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;
}
void*
LinuxDumper::FindEntryPoint(pid_t pid) const {
char auxv_path[NAME_MAX];
if (!BuildProcPath(auxv_path, pid, "auxv"))
return NULL;
int fd = sys_open(auxv_path, O_RDONLY, 0);
if (fd < 0) {
return NULL;
}
// Find the AT_ENTRY entry
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_ENTRY) {
close(fd);
return reinterpret_cast<void*>(one_aux_entry.a_un.a_val);
}
}
close(fd);
return NULL;
}
bool LinuxDumper::EnumerateMappings() {
char maps_path[NAME_MAX];
if (!BuildProcPath(maps_path, pid_, "maps"))
return false;
// 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_);
// Although the initial executable is usually the first mapping, it's not
// guaranteed (see http://crosbug.com/25355); therefore, try to use the
// actual entry point to find the mapping.
const void* entry_point_loc = FindEntryPoint(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 && !mappings_.empty()) {
MappingInfo* module = mappings_.back();
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);
}
// If this is the entry-point mapping, and it's not already the
// first one, then we need to make it be first. This is because
// the minidump format assumes the first module is the one that
// corresponds to the main executable (as codified in
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
if (entry_point_loc &&
(entry_point_loc >=
reinterpret_cast<void*>(module->start_addr)) &&
(entry_point_loc <
reinterpret_cast<void*>(module->start_addr+module->size)) &&
!mappings_.empty()) {
// push the module onto the front of the list.
mappings_.resize(mappings_.size() + 1);
for (size_t idx = mappings_.size() - 1; idx > 0; idx--)
mappings_[idx] = mappings_[idx - 1];
mappings_[0] = module;
} else {
mappings_.push_back(module);
}
}
}
}
line_reader->PopLine(line_len);
}
sys_close(fd);
return !mappings_.empty();
}
// 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;
}
// 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];
if (!BuildProcPath(exe_link, pid_, "exe"))
return false;
if (!SafeReadLink(exe_link, new_path))
return false;
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

View File

@@ -1,235 +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.
// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
// is a base class for extracting information of a crashed process. It
// was originally a complete implementation using the ptrace API, but
// has been refactored to allow derived implementations supporting both
// ptrace and core dump. A portion of the original implementation is now
// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
// details).
#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);
virtual ~LinuxDumper();
// Parse the data for |threads| and |mappings|.
virtual bool Init();
// Return true if the dumper performs a post-mortem dump.
virtual bool IsPostMortem() const = 0;
// Suspend/resume all threads in the given process.
virtual bool ThreadsSuspend() = 0;
virtual bool ThreadsResume() = 0;
// Read information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
// 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_; }
// Copy content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) = 0;
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result.|node| is the final node without any slashes. Returns true on
// success.
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
// 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(pid_t pid) const;
// Utility method to find the entry point location.
void* FindEntryPoint(pid_t pid) const;
uintptr_t crash_address() const { return crash_address_; }
void set_crash_address(uintptr_t crash_address) {
crash_address_ = crash_address;
}
int crash_signal() const { return crash_signal_; }
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
pid_t crash_thread() const { return crash_thread_; }
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
protected:
virtual bool EnumerateMappings();
virtual bool EnumerateThreads() = 0;
// 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;
// ID of the crashed process.
const pid_t pid_;
// Virtual address at which the process crashed.
uintptr_t crash_address_;
// Signal that terminated the crashed process.
int crash_signal_;
// ID of the crashed thread.
pid_t crash_thread_;
mutable PageAllocator allocator_;
// IDs of all the threads.
wasteful_vector<pid_t> threads_;
// Info from /proc/<pid>/maps.
wasteful_vector<MappingInfo*> mappings_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_

View File

@@ -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.
//
// 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) {
int pipefd = *static_cast<int *>(data);
volatile pid_t thread_id = syscall(__NR_gettid);
// Signal parent that a thread has started.
uint8_t byte = 1;
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
perror("ERROR: parent notification failed");
return NULL;
}
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
while (true)
asm volatile ("" : : "r" (thread_id_ptr));
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
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, &pipefd);
}
thread_function(&pipefd);
return 0;
}

View File

@@ -1,293 +0,0 @@
// Copyright (c) 2012, 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.
// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
// See linux_ptrace_dumper.h for detals.
// This class was originally splitted from google_breakpad::LinuxDumper.
// 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_ptrace_dumper.h"
#include <asm/ptrace.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include "client/linux/minidump_writer/directory_reader.h"
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
// Suspends 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, &regs) == -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;
}
// Resumes a thread by detaching from it.
static bool ResumeThread(pid_t pid) {
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
}
namespace google_breakpad {
LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid)
: LinuxDumper(pid),
threads_suspended_(false) {
}
bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
const char* node) const {
if (!path || !node || pid <= 0)
return false;
size_t node_len = my_strlen(node);
if (node_len == 0)
return false;
const unsigned pid_len = my_int_len(pid);
const size_t total_length = 6 + pid_len + 1 + node_len;
if (total_length >= NAME_MAX)
return false;
memcpy(path, "/proc/", 6);
my_itos(path + 6, pid, pid_len);
path[6 + pid_len] = '/';
memcpy(path + 6 + pid_len + 1, node, node_len);
path[total_length] = '\0';
return true;
}
void LinuxPtraceDumper::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;
}
}
// 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 LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (index >= threads_.size())
return false;
pid_t tid = threads_[index];
assert(info != NULL);
char status_path[NAME_MAX];
if (!BuildProcPath(status_path, tid, "status"))
return false;
const int fd = sys_open(status_path, O_RDONLY, 0);
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);
}
sys_close(fd);
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);
}
bool LinuxPtraceDumper::IsPostMortem() const {
return false;
}
bool LinuxPtraceDumper::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 LinuxPtraceDumper::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;
}
// Parse /proc/$pid/task to list all the threads of the process identified by
// pid.
bool LinuxPtraceDumper::EnumerateThreads() {
char task_path[NAME_MAX];
if (!BuildProcPath(task_path, pid_, "task"))
return false;
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;
threads_.push_back(tid);
}
}
dir_reader->PopEntry();
}
sys_close(fd);
return true;
}
} // namespace google_breakpad

View File

@@ -1,92 +0,0 @@
// Copyright (c) 2012, 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.
// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper
// class, which is derived from google_breakpad::LinuxDumper to extract
// information from a crashed process via ptrace.
// This class was originally splitted from google_breakpad::LinuxDumper.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
#include "client/linux/minidump_writer/linux_dumper.h"
namespace google_breakpad {
class LinuxPtraceDumper : public LinuxDumper {
public:
// Constructs a dumper for extracting information of a given process
// with a process ID of |pid|.
explicit LinuxPtraceDumper(pid_t pid);
// Implements LinuxDumper::BuildProcPath().
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result. |node| is the final node without any slashes. Returns true on
// success.
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
// Implements LinuxDumper::CopyFromProcess().
// Copies content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. This method uses ptrace to extract
// the content from the target process.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length);
// Implements LinuxDumper::GetThreadInfoByIndex().
// Reads information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// Implements LinuxDumper::IsPostMortem().
// Always returns false to indicate this dumper performs a dump of
// a crashed process via ptrace.
virtual bool IsPostMortem() const;
// Implements LinuxDumper::ThreadsSuspend().
// Suspends all threads in the given process. Returns true on success.
virtual bool ThreadsSuspend();
// Implements LinuxDumper::ThreadsResume().
// Resumes all threads in the given process. Returns true on success.
virtual bool ThreadsResume();
protected:
// Implements LinuxDumper::EnumerateThreads().
// Enumerates all threads of the given process into |threads_|.
virtual bool EnumerateThreads();
private:
// Set to true if all threads of the crashed process are suspended.
bool threads_suspended_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_

View File

@@ -1,445 +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.
// linux_ptrace_dumper_unittest.cc:
// Unit tests for google_breakpad::LinuxPtraceDumoer.
//
// This file was renamed from linux_dumper_unittest.cc and modified due
// to LinuxDumper being splitted into two classes.
#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 <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/linux/safe_readlink.h"
#include "common/memory.h"
using std::string;
using namespace google_breakpad;
namespace {
typedef testing::Test LinuxPtraceDumperTest;
string GetHelperBinary() {
// Locate helper binary next to the current binary.
char self_path[PATH_MAX];
if (!SafeReadLink("/proc/self/exe", self_path)) {
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;
}
} // namespace
TEST(LinuxPtraceDumperTest, Setup) {
LinuxPtraceDumper dumper(getpid());
}
TEST(LinuxPtraceDumperTest, FindMappings) {
LinuxPtraceDumper 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(LinuxPtraceDumperTest, ThreadList) {
LinuxPtraceDumper 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(LinuxPtraceDumperTest, 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 uintptr_t kMappingAddress = reinterpret_cast<uintptr_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 LinuxPtraceDumper interpreted the mappings properly.
LinuxPtraceDumper 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(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
static const int kNumberOfThreadsInHelperProgram = 5;
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(helper_path.c_str(),
"linux_dumper_unittest_helper",
pipe_fd_string,
kNumberOfThreadsArgument,
NULL);
// Kill if we get here.
printf("Errno from exec: %d", errno);
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
exit(0);
}
close(fds[1]);
// Wait for all child threads to indicate that they have started
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
ASSERT_EQ(1, r);
ASSERT_TRUE(pfd.revents & POLLIN);
uint8_t junk;
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), sizeof(junk));
}
close(fds[0]);
// There is a race here because we may stop a child thread before
// it is actually running the busy loop. Empirically this sleep
// is sufficient to avoid the race.
usleep(100000);
// Children are ready now.
LinuxPtraceDumper 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.GetThreadInfoByIndex(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);
}
EXPECT_TRUE(dumper.ThreadsResume());
kill(child_pid, SIGKILL);
// Reap child
int status;
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(SIGKILL, WTERMSIG(status));
}
TEST(LinuxPtraceDumperTest, BuildProcPath) {
const pid_t pid = getpid();
LinuxPtraceDumper dumper(pid);
char maps_path[NAME_MAX] = "";
char maps_path_expected[NAME_MAX];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"/proc/%d/maps", pid);
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
EXPECT_STREQ(maps_path_expected, maps_path);
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
char long_node[NAME_MAX];
size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1;
memset(long_node, 'a', long_node_len);
long_node[long_node_len] = '\0';
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
}
#if !defined(__ARM_EABI__)
// Ensure that the linux-gate VDSO is included in the mapping list.
TEST(LinuxPtraceDumperTest, MappingsIncludeLinuxGate) {
LinuxPtraceDumper 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));
}
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
TEST(LinuxPtraceDumperTest, LinuxGateMappingID) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
unsigned index = 0;
for (unsigned i = 0; i < mappings.size(); ++i) {
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
index = i;
break;
}
}
ASSERT_TRUE(found_linux_gate);
uint8_t identifier[sizeof(MDGUID)];
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
true,
index,
identifier));
uint8_t empty_identifier[sizeof(MDGUID)];
memset(empty_identifier, 0, sizeof(empty_identifier));
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
}
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID
// from a child process.
TEST(LinuxPtraceDumperTest, LinuxGateMappingIDChild) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// Fork a child so ptrace works.
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]);
LinuxPtraceDumper dumper(child);
ASSERT_TRUE(dumper.Init());
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
unsigned index = 0;
for (unsigned i = 0; i < mappings.size(); ++i) {
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
index = i;
break;
}
}
ASSERT_TRUE(found_linux_gate);
// Need to suspend the child so ptrace actually works.
ASSERT_TRUE(dumper.ThreadsSuspend());
uint8_t identifier[sizeof(MDGUID)];
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
true,
index,
identifier));
uint8_t empty_identifier[sizeof(MDGUID)];
memset(empty_identifier, 0, sizeof(empty_identifier));
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
EXPECT_TRUE(dumper.ThreadsResume());
close(fds[1]);
}
#endif
TEST(LinuxPtraceDumperTest, 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];
ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
int fds[2];
ASSERT_NE(-1, pipe(fds));
// Fork a child so ptrace works.
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]);
LinuxPtraceDumper 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]);
}

View File

@@ -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

View File

@@ -1,78 +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 "client/linux/minidump_writer/linux_dumper.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
class ExceptionHandler;
struct MappingEntry {
MappingInfo first;
u_int8_t second[sizeof(MDGUID)];
};
// A list of <MappingInfo, GUID>
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);
bool WriteMinidump(const char* filename,
const MappingList& mappings,
LinuxDumper* dumper);
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_

View File

@@ -1,390 +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 "common/linux/safe_readlink.h"
#include "common/tests/auto_tempdir.h"
#include "google_breakpad/processor/minidump.h"
using namespace google_breakpad;
// 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));
AutoTempDir temp_dir;
std::string templ = temp_dir.path() + "/minidump-writer-unittest";
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
struct stat st;
ASSERT_EQ(stat(templ.c_str(), &st), 0);
ASSERT_GT(st.st_size, 0u);
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 uintptr_t kMemoryAddress = reinterpret_cast<uintptr_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;
AutoTempDir temp_dir;
std::string templ = temp_dir.path() + "/minidump-writer-unittest";
// 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.c_str(), 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.c_str());
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());
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
AutoTempDir temp_dir;
std::string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
ASSERT_NE(-1, fd);
unlink(tempfile.c_str());
// 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 uintptr_t kMemoryAddress = reinterpret_cast<uintptr_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;
std::string dumpfile = temp_dir.path() + "/minidump-writer-unittest";
// 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.c_str(), 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.c_str());
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());
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 (!SafeReadLink("/proc/self/exe", self_path)) {
FAIL() << "readlink failed";
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.
AutoTempDir temp_dir;
std::string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
char cmdline[2 * PATH_MAX];
sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(),
binpath.c_str());
ASSERT_EQ(0, system(cmdline));
ASSERT_EQ(0, chmod(binpath.c_str(), 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.c_str(),
binpath.c_str(),
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;
const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
ASSERT_EQ(sizeof(junk), nr);
close(fds[0]);
// Child is ready now.
// Unlink the test binary.
unlink(binpath.c_str());
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
std::string templ = temp_dir.path() + "/minidump-writer-unittest";
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
sizeof(context)));
kill(child_pid, SIGKILL);
struct stat st;
ASSERT_EQ(stat(templ.c_str(), &st), 0);
ASSERT_GT(st.st_size, 0u);
Minidump minidump(templ.c_str());
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.c_str(), 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());
}

View File

@@ -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, "https://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();
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,280 +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.
// A service name associated with the original bootstrap parent port, saved in
// OnDemandServer and restored in Inspector.
#define BREAKPAD_BOOTSTRAP_PARENT_PORT "com.Breakpad.BootstrapParent"
typedef void *BreakpadRef;
#ifdef __cplusplus
extern "C" {
#endif
#include <CoreFoundation/CoreFoundation.h>
#include <Foundation/Foundation.h>
#include "BreakpadDefines.h"
// 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, in seconds since the Epoch, the
// process started
//
// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the
// process crashed.
//
// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds 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

View File

@@ -1,995 +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 "client/mac/Framework/Breakpad.h"
#import <Foundation/Foundation.h>
#import <sys/stat.h>
#import <sys/sysctl.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 "common/mac/MachIPC.h"
#import "common/mac/SimpleStringDictionary.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);
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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_;
};

View File

@@ -1,186 +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"
#import "Breakpad.h"
#include "common/mac/bootstrap_compat.h"
#if DEBUG
#define PRINT_MACH_RESULT(result_, message_) \
printf(message_"%s (%d)\n", mach_error_string(result_), result_ );
#if defined(MAC_OS_X_VERSION_10_5) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
#define PRINT_BOOTSTRAP_RESULT(result_, message_) \
printf(message_"%s (%d)\n", bootstrap_strerror(result_), result_ );
#else
#define PRINT_BOOTSTRAP_RESULT(result_, message_) \
PRINT_MACH_RESULT(result_, message_)
#endif
#else
#define PRINT_MACH_RESULT(result_, message_)
#define PRINT_BOOTSTRAP_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;
mach_port_t self_task = mach_task_self();
mach_port_t bootstrap_port;
kern_return_t kr = task_get_bootstrap_port(self_task, &bootstrap_port);
if (kr != KERN_SUCCESS) {
PRINT_MACH_RESULT(kr, "task_get_bootstrap_port(): ");
return kr;
}
mach_port_t bootstrap_subset_port;
kr = bootstrap_subset(bootstrap_port, self_task, &bootstrap_subset_port);
if (kr != BOOTSTRAP_SUCCESS) {
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_subset(): ");
return kr;
}
// The inspector will be invoked with its bootstrap port set to the subset,
// but the sender will need access to the original bootstrap port. Although
// the original port is the subset's parent, bootstrap_parent can't be used
// because it requires extra privileges. Stash the original bootstrap port
// in the subset by registering it under a known name. The inspector will
// recover this port and set it as its own bootstrap port in Inspector.mm
// Inspector::ResetBootstrapPort.
kr = breakpad::BootstrapRegister(
bootstrap_subset_port,
const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT),
bootstrap_port);
if (kr != BOOTSTRAP_SUCCESS) {
PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_register(): ");
return kr;
}
kr = bootstrap_create_server(bootstrap_subset_port,
const_cast<char*>(server_command),
geteuid(), // server uid
true,
&server_port_);
if (kr != BOOTSTRAP_SUCCESS) {
PRINT_BOOTSTRAP_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 != BOOTSTRAP_SUCCESS) {
PRINT_BOOTSTRAP_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 != BOOTSTRAP_SUCCESS) {
PRINT_BOOTSTRAP_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 = breakpad::BootstrapRegister(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;
}
}

View File

@@ -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>

View File

@@ -1,83 +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.
//
// Utility class that can persist a SimpleStringDictionary to disk.
#import <Foundation/Foundation.h>
#import "common/mac/SimpleStringDictionary.h"
namespace google_breakpad {
BOOL EnsureDirectoryPathExists(NSString *dirPath);
//=============================================================================
class ConfigFile {
public:
ConfigFile() {
config_file_ = -1;
config_file_path_[0] = 0;
has_created_file_ = false;
};
~ConfigFile() {
};
void WriteFile(const char* directory,
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);
BOOL AppendCrashTimeParameters(const char *processStartTimeString);
int config_file_; // descriptor for config file
char config_file_path_[PATH_MAX]; // Path to configuration file
bool has_created_file_;
};
} // namespace google_breakpad

View File

@@ -1,190 +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.
//
// Utility class that can persist a SimpleStringDictionary to disk.
#import "client/mac/crash_generation/ConfigFile.h"
#import <Foundation/Foundation.h>
#include <stdio.h>
#include <sys/time.h>
#import "client/apple/Framework/BreakpadDefines.h"
#import "common/mac/SimpleStringDictionary.h"
#import "GTMDefines.h"
#define VERBOSE 0
#if VERBOSE
bool gDebugLog = true;
#else
bool gDebugLog = false;
#endif
#define DEBUGLOG if (gDebugLog) fprintf
namespace google_breakpad {
//=============================================================================
BOOL EnsureDirectoryPathExists(NSString *dirPath) {
NSFileManager *mgr = [NSFileManager defaultManager];
NSDictionary *attrs =
[NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750]
forKey:NSFilePosixPermissions];
return [mgr createDirectoryAtPath:dirPath
withIntermediateDirectories:YES
attributes:attrs
error:nil];
}
//=============================================================================
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));
}
//=============================================================================
BOOL ConfigFile::AppendCrashTimeParameters(const char *processStartTimeString) {
// Set process uptime parameter
struct timeval tv;
gettimeofday(&tv, NULL);
char processUptimeString[32], processCrashtimeString[32];
// 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;
// Store the uptime in milliseconds.
sprintf(processUptimeString, "%llu",
static_cast<unsigned long long int>(processUptime) * 1000);
if (!AppendConfigString(BREAKPAD_PROCESS_UP_TIME, processUptimeString))
return false;
}
sprintf(processCrashtimeString, "%zd", tv.tv_sec);
return AppendConfigString(BREAKPAD_PROCESS_CRASH_TIME,
processCrashtimeString);
}
//=============================================================================
void ConfigFile::WriteFile(const char* directory,
const SimpleStringDictionary *configurationParameters,
const char *dump_dir,
const char *minidump_id) {
assert(config_file_ == -1);
// Open and write out configuration file preamble
if (directory) {
snprintf(config_file_path_, sizeof(config_file_path_), "%s/Config-XXXXXX",
directory);
} else {
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;
}
AppendCrashTimeParameters(
configurationParameters->GetValueForKey(BREAKPAD_PROCESS_START_TIME));
close(config_file_);
config_file_ = -1;
}
} // namespace google_breakpad

View File

@@ -1,166 +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>
#include <mach/mach.h>
#import "client/mac/crash_generation/ConfigFile.h"
#import "client/mac/handler/minidump_generator.h"
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 {
//=============================================================================
class MinidumpLocation {
public:
MinidumpLocation(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:
// The Inspector is invoked with its bootstrap port set to the bootstrap
// subset established in OnDemandServer.mm OnDemandServer::Initialize.
// For proper communication with the system, the sender (which will inherit
// the Inspector's bootstrap port) needs the per-session bootstrap namespace
// available directly in its bootstrap port. OnDemandServer stashed this
// port into the subset namespace under a special name. ResetBootstrapPort
// recovers this port and switches this task to use it as its own bootstrap
// (ensuring that children like the sender will inherit it), and saves the
// subset in bootstrap_subset_port_ for use by ServiceCheckIn and
// ServiceCheckOut.
kern_return_t ResetBootstrapPort();
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);
// The bootstrap port in which the inspector is registered and into which it
// must check in.
mach_port_t bootstrap_subset_port_;
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

View File

@@ -1,431 +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 <servers/bootstrap.h>
#include <stdio.h>
#include <string.h>
#include <string>
#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"
#include "common/mac/bootstrap_compat.h"
#import "GTMDefines.h"
#import <Foundation/Foundation.h>
namespace google_breakpad {
//=============================================================================
void Inspector::Inspect(const char *receive_port_name) {
kern_return_t result = ResetBootstrapPort();
if (result != KERN_SUCCESS) {
return;
}
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::ResetBootstrapPort() {
// A reasonable default, in case anything fails.
bootstrap_subset_port_ = bootstrap_port;
mach_port_t self_task = mach_task_self();
kern_return_t kr = task_get_bootstrap_port(self_task,
&bootstrap_subset_port_);
if (kr != KERN_SUCCESS) {
NSLog(@"ResetBootstrapPort: task_get_bootstrap_port failed: %s (%d)",
mach_error_string(kr), kr);
return kr;
}
mach_port_t bootstrap_parent_port;
kr = bootstrap_look_up(bootstrap_subset_port_,
const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT),
&bootstrap_parent_port);
if (kr != BOOTSTRAP_SUCCESS) {
NSLog(@"ResetBootstrapPort: bootstrap_look_up failed: %s (%d)",
#if defined(MAC_OS_X_VERSION_10_5) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
bootstrap_strerror(kr),
#else
mach_error_string(kr),
#endif
kr);
return kr;
}
kr = task_set_bootstrap_port(self_task, bootstrap_parent_port);
if (kr != KERN_SUCCESS) {
NSLog(@"ResetBootstrapPort: task_set_bootstrap_port failed: %s (%d)",
mach_error_string(kr), kr);
return kr;
}
// Some things access the bootstrap port through this global variable
// instead of calling task_get_bootstrap_port.
bootstrap_port = bootstrap_parent_port;
return KERN_SUCCESS;
}
//=============================================================================
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_subset_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 = breakpad::BootstrapRegister(bootstrap_subset_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(&parameter_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;
}
//=============================================================================
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);
// 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( 0,
&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

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -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

View File

@@ -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_

View File

@@ -1,413 +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 <CoreFoundation/CoreFoundation.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) && *((uint32_t *)&buf) != magic &&
CFSwapInt32BigToHost(*((uint32_t *)&buf)) != FAT_MAGIC &&
/* The following is the big-endian ppc64 check */
(*((uint32_t*)&buf)) != FAT_MAGIC)) {
return -1;
}
/* Deal with fat file if necessary */
unsigned arch_offset = 0;
if (CFSwapInt32BigToHost(*((uint32_t *)&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 = CFSwapInt32BigToHost(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 =
CFSwapInt32BigToHost(fat_archs[i].cputype);
fat_archs[i].cpusubtype =
CFSwapInt32BigToHost(fat_archs[i].cpusubtype);
fat_archs[i].offset =
CFSwapInt32BigToHost(fat_archs[i].offset);
fat_archs[i].size =
CFSwapInt32BigToHost(fat_archs[i].size);
fat_archs[i].align =
CFSwapInt32BigToHost(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) !=
(ssize_t)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 (uint32_t 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;
}

View File

@@ -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__ */

View File

@@ -1,578 +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 <assert.h>
#include <AvailabilityMacros.h>
#include <dlfcn.h>
#include <mach/task_info.h>
#include <sys/sysctl.h>
#include <TargetConditionals.h>
#include <algorithm>
#include <string>
#include <vector>
#include "breakpad_nlist_64.h"
#if !TARGET_OS_IPHONE
#include <CoreServices/CoreServices.h>
#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
#endif // !TARGET_OS_IPHONE
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,
&region_base,
&region_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,
&region_base2,
&region_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;
}
#if TARGET_OS_IPHONE
static bool HasTaskDyldInfo() {
return true;
}
#else
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 HasTaskDyldInfo() {
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
return true;
#else
return GetOSVersion() >= 0x1060;
#endif
}
#endif // TARGET_OS_IPHONE
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
if (HasTaskDyldInfo()) {
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 0;
}
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

View File

@@ -1,317 +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>
#include "mach_vm_compat.h"
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;
#elif defined(__arm__)
return CPU_TYPE_ARM;
#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__

View File

@@ -1,830 +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 <mach/exc.h>
#include <mach/mig.h>
#include <pthread.h>
#include <signal.h>
#include <TargetConditionals.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"
#include "google_breakpad/common/minidump_exception_mac.h"
#ifndef USE_PROTECTED_ALLOCATIONS
#if TARGET_OS_IPHONE
#define USE_PROTECTED_ALLOCATIONS 1
#else
#define USE_PROTECTED_ALLOCATIONS 0
#endif
#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 {
static union {
#if USE_PROTECTED_ALLOCATIONS
char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
#endif
google_breakpad::ExceptionHandler *handler;
} gProtectedData;
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;
#if !TARGET_OS_IPHONE
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")));
}
#endif
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);
#if TARGET_OS_IPHONE
// Implementation is based on the implementation generated by mig.
boolean_t breakpad_exc_server(mach_msg_header_t *InHeadP,
mach_msg_header_t *OutHeadP) {
OutHeadP->msgh_bits =
MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0);
OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port;
/* Minimal size: routine() will update it if different */
OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
OutHeadP->msgh_local_port = MACH_PORT_NULL;
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
if (InHeadP->msgh_id != 2401) {
((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
return FALSE;
}
#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];
mach_msg_trailer_t trailer;
} Request;
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} Reply;
#ifdef __MigPackStructs
#pragma pack()
#endif
Request *In0P = (Request *)InHeadP;
Reply *OutP = (Reply *)OutHeadP;
if (In0P->task.name != mach_task_self()) {
return FALSE;
}
OutP->RetCode = ForwardException(In0P->task.name,
In0P->thread.name,
In0P->exception,
In0P->code,
In0P->codeCnt);
OutP->NDR = NDR_record;
return TRUE;
}
#else
boolean_t breakpad_exc_server(mach_msg_header_t *request,
mach_msg_header_t *reply) {
return exc_server(request, reply);
}
// 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);
}
#endif
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 !TARGET_OS_IPHONE
if (port_name)
crash_generation_client_.reset(new CrashGenerationClient(port_name));
#endif
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,
#elif defined (__arm__)
EXC_ARM_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 report_current_thread) {
bool result = false;
if (directCallback_) {
if (directCallback_(callback_context_,
exception_type,
exception_code,
exception_subcode,
thread_name) ) {
if (exit_after_write)
_exit(exception_type);
}
#if !TARGET_OS_IPHONE
} 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);
}
#endif
} 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(mach_task_self(),
report_current_thread ? MACH_PORT_NULL :
mach_thread_self());
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();
task_get_exception_ports(current_task,
s_exception_mask,
current.masks,
&current.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];
kern_return_t result;
switch (target_behavior) {
case EXCEPTION_DEFAULT:
result = exception_raise(target_port, failed_thread, task, exception,
code, code_count);
break;
default:
fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior);
result = KERN_FAILURE;
break;
}
return result;
}
// 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;
#elif defined (__arm__)
exception_code = EXC_ARM_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, 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 catch_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,
false);
#if USE_PROTECTED_ALLOCATIONS
// This may have become protected again within
// WriteMinidumpWithException, but it needs to be unprotected for
// UninstallHandler.
if (gBreakpadAllocator)
gBreakpadAllocator->Unprotect();
#endif
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 catch_exception_raise() and put the return
// code into the reply.
ExceptionReplyMessage reply;
if (!breakpad_exc_server(&receive.header, &reply.header))
exit(1);
// Send a reply and exit
mach_msg(&(reply.header), MACH_SEND_MSG,
reply.header.msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
}
}
return NULL;
}
//static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
gBreakpadAllocator->Unprotect();
#endif
gProtectedData.handler->WriteMinidumpWithException(
EXC_SOFTWARE,
MD_EXCEPTION_CODE_MAC_ABORT,
0,
mach_thread_self(),
true,
true);
#if USE_PROTECTED_ALLOCATIONS
if (gBreakpadAllocator)
gBreakpadAllocator->Protect();
#endif
}
bool ExceptionHandler::InstallHandler() {
// If a handler is already installed, something is really wrong.
if (gProtectedData.handler != NULL) {
return false;
}
#if TARGET_OS_IPHONE
if (!IsOutOfProcess()) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGABRT);
sa.sa_sigaction = ExceptionHandler::SignalHandler;
sa.sa_flags = SA_SIGINFO;
scoped_ptr<struct sigaction> old(new struct sigaction);
if (sigaction(SIGABRT, &sa, old.get()) == -1) {
return false;
}
old_handler_.swap(old);
gProtectedData.handler = this;
#if USE_PROTECTED_ALLOCATIONS
assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0);
mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ);
#endif
}
#endif
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 (old_handler_.get()) {
sigaction(SIGABRT, old_handler_.get(), NULL);
#if USE_PROTECTED_ALLOCATIONS
mprotect(gProtectedData.protected_buffer, PAGE_SIZE,
PROT_READ | PROT_WRITE);
#endif
old_handler_.reset();
gProtectedData.handler = NULL;
}
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_ = MACH_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

View File

@@ -1,277 +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 <TargetConditionals.h>
#include <string>
#include "processor/scoped_ptr.h"
#if !TARGET_OS_IPHONE
#include "client/mac/crash_generation/crash_generation_client.h"
#endif
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 {
#if TARGET_OS_IPHONE
return false;
#else
return crash_generation_client_.get() != NULL;
#endif
}
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,
bool report_current_thread);
// 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);
// Signal handler for SIGABRT.
static void SignalHandler(int sig, siginfo_t* info, void* uc);
// 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_;
// Old signal handler for SIGABRT. Used to be able to restore it when
// uninstalling.
scoped_ptr<struct sigaction> old_handler_;
#if !TARGET_OS_IPHONE
// Client for out-of-process dump generation.
scoped_ptr<CrashGenerationClient> crash_generation_client_;
#endif
};
} // namespace google_breakpad
#endif // CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__

View File

@@ -1,49 +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.
#ifndef CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_
#define CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_
#include <TargetConditionals.h>
// On iOS 5, mach/mach_vm.h is not supported anymore. As the architecture is 32
// bits, we can use the simple vm_ functions instead of the mach_vm_ ones.
#if TARGET_OS_IPHONE
#include <mach/vm_map.h>
#define mach_vm_address_t vm_address_t
#define mach_vm_deallocate vm_deallocate
#define mach_vm_read vm_read
#define mach_vm_region vm_region
#define mach_vm_region_recurse vm_region_recurse
#define mach_vm_size_t vm_size_t
#else
#include <mach/mach_vm.h>
#endif // TARGET_OS_IPHONE
#endif // CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_

File diff suppressed because it is too large Load Diff

View File

@@ -1,218 +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 <TargetConditionals.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"
#include "mach_vm_compat.h"
#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7)
#define HAS_PPC_SUPPORT
#endif
#if defined(__arm__)
#define HAS_ARM_SUPPORT
#elif defined(__i386__) || defined(__x86_64__)
#define HAS_X86_SUPPORT
#endif
namespace google_breakpad {
using std::string;
// 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 || TARGET_CPU_ARM
// 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);
virtual ~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();
protected:
// Overridable Stream writers
virtual bool WriteExceptionStream(MDRawDirectory *exception_stream);
// Overridable Helper
virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread *thread);
private:
typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory *);
// Stream writers
bool WriteThreadListStream(MDRawDirectory *thread_list_stream);
bool WriteMemoryListStream(MDRawDirectory *memory_list_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 WriteCVRecord(MDRawModule *module, int cpu_type,
const char *module_path, bool in_memory);
bool WriteModuleStream(unsigned int index, MDRawModule *module);
size_t CalculateStackSize(mach_vm_address_t start_addr);
int FindExecutableModule();
// Per-CPU implementations of these methods
#ifdef HAS_ARM_SUPPORT
bool WriteStackARM(breakpad_thread_state_data_t state,
MDMemoryDescriptor *stack_location);
bool WriteContextARM(breakpad_thread_state_data_t state,
MDLocationDescriptor *register_location);
u_int64_t CurrentPCForStackARM(breakpad_thread_state_data_t state);
#endif
#ifdef HAS_PPC_SUPPORT
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);
#endif
#ifdef HAS_X86_SUPPORT
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);
#endif
// disallow copy ctor and operator=
explicit MinidumpGenerator(const MinidumpGenerator &);
void operator=(const MinidumpGenerator &);
protected:
// Use this writer to put the data to disk
MinidumpFileWriter writer_;
private:
// 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_;
protected:
// 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__

View File

@@ -1,841 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; };
8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; };
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
9B35FF5A0B267D5F008DE8C7 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; };
9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; };
9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */; };
9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */; };
9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
9BC1D2940B336F2300F2A2B4 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; };
9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; };
9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */; };
9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */; };
9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; };
9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; };
9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; };
9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0A0B0133520055103E /* exception_handler.h */; };
9BD82C110B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; };
9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0C0B0133520055103E /* minidump_generator.h */; };
9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; };
9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C240B01344C0055103E /* minidump_file_writer.h */; };
9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; };
9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; };
9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C2C0B01345E0055103E /* string_utilities.h */; };
D2F651000BEF947200920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
D2F651010BEF947200920385 /* file_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FB0BEF947200920385 /* file_id.h */; };
D2F651020BEF947200920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
D2F651030BEF947200920385 /* macho_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FD0BEF947200920385 /* macho_id.h */; };
D2F651040BEF947200920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FF0BEF947200920385 /* macho_utilities.h */; };
D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F651080BEF949A00920385 /* dynamic_images.h */; };
D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F6510D0BEF94EB00920385 /* macho_walker.h */; };
D2F651110BEF951700920385 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; };
D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF590B267D5F008DE8C7 /* string_conversion.h */; };
D2F651150BEF953000920385 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.c */; };
D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */; };
D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
D2F6511D0BEF973500920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
D2F6511E0BEF973600920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
D2F651210BEF975400920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; };
F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; };
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F310E8B07E800D7E813 /* dwarftests.mm */; };
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.mm */; };
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; };
F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.cc */; };
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; };
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; };
F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */; };
F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; };
F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; };
F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; };
F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
8DD76F690486A84900D96B5E /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 8;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */,
9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */,
9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */,
9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */,
9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */,
D2F651010BEF947200920385 /* file_id.h in CopyFiles */,
D2F651030BEF947200920385 /* macho_id.h in CopyFiles */,
D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */,
D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */,
D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */,
D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */,
D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
8BFC815411FF9B7F002CB4DC /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
8BFC819211FF9C23002CB4DC /* CPlusTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CPlusTest.framework; path = Library/Frameworks/CPlusTest.framework; sourceTree = DEVELOPER_DIR; };
8DD76F6C0486A84900D96B5E /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; };
9B35FF560B267D5F008DE8C7 /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = convert_UTF.c; path = ../../../common/convert_UTF.c; sourceTree = SOURCE_ROOT; };
9B35FF570B267D5F008DE8C7 /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; };
9B35FF580B267D5F008DE8C7 /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; };
9B35FF590B267D5F008DE8C7 /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../../common/string_conversion.h; sourceTree = SOURCE_ROOT; };
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
9B7CA84E0B1297F200CD3A1D /* unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unit_test; sourceTree = BUILT_PRODUCTS_DIR; };
9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../../minidump_file_writer_unittest.cc; sourceTree = "<group>"; };
9BD82A9B0B00267E0055103E /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; };
9BD82BFD0B01333D0055103E /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler_test.cc; sourceTree = SOURCE_ROOT; };
9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator_test.cc; sourceTree = SOURCE_ROOT; };
9BD82C090B0133520055103E /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = SOURCE_ROOT; };
9BD82C0A0B0133520055103E /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = SOURCE_ROOT; };
9BD82C0B0B0133520055103E /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = SOURCE_ROOT; };
9BD82C0C0B0133520055103E /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = SOURCE_ROOT; };
9BD82C230B01344C0055103E /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; };
9BD82C240B01344C0055103E /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../../minidump_file_writer.h; sourceTree = SOURCE_ROOT; };
9BD82C2B0B01345E0055103E /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; };
9BD82C2C0B01345E0055103E /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; };
9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; };
D2F650FA0BEF947200920385 /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; };
D2F650FB0BEF947200920385 /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; };
D2F650FC0BEF947200920385 /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; };
D2F650FD0BEF947200920385 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; };
D2F650FE0BEF947200920385 /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; };
D2F650FF0BEF947200920385 /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; };
D2F651070BEF949A00920385 /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = "<group>"; };
D2F651080BEF949A00920385 /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = "<group>"; };
D2F6510C0BEF94EB00920385 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; };
D2F6510D0BEF94EB00920385 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; };
F917C4F70E03265A00F86017 /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = breakpad_exc_server.c; sourceTree = "<group>"; };
F917C4F80E03265A00F86017 /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_exc_server.h; sourceTree = "<group>"; };
F93A88750E8B4C700026AF89 /* octestcases.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = octestcases.octest; sourceTree = BUILT_PRODUCTS_DIR; };
F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "obj-cTestCases-Info.plist"; sourceTree = "<group>"; };
F9721F300E8B07E800D7E813 /* dwarftests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarftests.h; sourceTree = "<group>"; };
F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = "<group>"; };
F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; };
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; };
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; };
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
F9721FA80E8B0E4800D7E813 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.cc; path = ../../../common/md5.cc; sourceTree = SOURCE_ROOT; };
F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = "<group>"; };
F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = "<group>"; };
F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = "<group>"; };
F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = "<group>"; };
F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests32-Info.plist"; sourceTree = "<group>"; };
F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests64.cptest; sourceTree = BUILT_PRODUCTS_DIR; };
F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests32.cptest; sourceTree = BUILT_PRODUCTS_DIR; };
F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests64-Info.plist"; sourceTree = "<group>"; };
F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicImagesTests.h; sourceTree = "<group>"; };
F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicImagesTests.cc; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8DD76F660486A84900D96B5E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */,
8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9B7CA84C0B1297F200CD3A1D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9BD82A990B00267E0055103E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */,
8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F93A88720E8B4C700026AF89 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */,
8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE19C00DB04A9500C98454 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE5B300DBFDBA300505983 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */,
F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */,
8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */,
8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
08FB7794FE84155DC02AAC07 /* MinidumpWriter */ = {
isa = PBXGroup;
children = (
8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */,
8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */,
8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */,
F9721FA80E8B0E4800D7E813 /* md5.cc */,
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */,
F9721F380E8B0CFC00D7E813 /* dump_syms.h */,
F917C4F70E03265A00F86017 /* breakpad_exc_server.c */,
F917C4F80E03265A00F86017 /* breakpad_exc_server.h */,
F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */,
F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */,
D2F6510C0BEF94EB00920385 /* macho_walker.cc */,
D2F6510D0BEF94EB00920385 /* macho_walker.h */,
D2F651070BEF949A00920385 /* dynamic_images.cc */,
D2F651080BEF949A00920385 /* dynamic_images.h */,
D2F650FA0BEF947200920385 /* file_id.cc */,
D2F650FB0BEF947200920385 /* file_id.h */,
D2F650FC0BEF947200920385 /* macho_id.cc */,
D2F650FD0BEF947200920385 /* macho_id.h */,
D2F650FE0BEF947200920385 /* macho_utilities.cc */,
D2F650FF0BEF947200920385 /* macho_utilities.h */,
F9C5A41F0DB82DB000209C76 /* testcases */,
9BD82C040B0133420055103E /* Breakpad */,
08FB7795FE84155DC02AAC07 /* Source */,
9B37CEEA0AF98EB600FA4BD4 /* Frameworks */,
1AB674ADFE9D54B511CA2CBB /* Products */,
F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */,
F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */,
F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */,
);
name = MinidumpWriter;
sourceTree = "<group>";
};
08FB7795FE84155DC02AAC07 /* Source */ = {
isa = PBXGroup;
children = (
9BD82BFD0B01333D0055103E /* exception_handler_test.cc */,
9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */,
9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */,
);
name = Source;
sourceTree = "<group>";
};
1AB674ADFE9D54B511CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
8DD76F6C0486A84900D96B5E /* generator_test */,
9BD82A9B0B00267E0055103E /* handler_test */,
9B7CA84E0B1297F200CD3A1D /* unit_test */,
F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */,
F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */,
F93A88750E8B4C700026AF89 /* octestcases.octest */,
);
name = Products;
sourceTree = "<group>";
};
9B37CEEA0AF98EB600FA4BD4 /* Frameworks */ = {
isa = PBXGroup;
children = (
8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */,
8BFC815411FF9B7F002CB4DC /* Carbon.framework */,
F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */,
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */,
9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */,
8BFC819211FF9C23002CB4DC /* CPlusTest.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9BD82C040B0133420055103E /* Breakpad */ = {
isa = PBXGroup;
children = (
9B35FF560B267D5F008DE8C7 /* convert_UTF.c */,
9B35FF570B267D5F008DE8C7 /* convert_UTF.h */,
9B35FF580B267D5F008DE8C7 /* string_conversion.cc */,
9B35FF590B267D5F008DE8C7 /* string_conversion.h */,
9BD82C090B0133520055103E /* exception_handler.cc */,
9BD82C0A0B0133520055103E /* exception_handler.h */,
9BD82C0B0B0133520055103E /* minidump_generator.cc */,
9BD82C0C0B0133520055103E /* minidump_generator.h */,
9BD82C230B01344C0055103E /* minidump_file_writer.cc */,
9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */,
9BD82C240B01344C0055103E /* minidump_file_writer.h */,
9BD82C2B0B01345E0055103E /* string_utilities.cc */,
9BD82C2C0B01345E0055103E /* string_utilities.h */,
);
name = Breakpad;
sourceTree = "<group>";
};
F9C5A41F0DB82DB000209C76 /* testcases */ = {
isa = PBXGroup;
children = (
F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */,
F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */,
F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */,
F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */,
F9721F300E8B07E800D7E813 /* dwarftests.h */,
F9721F310E8B07E800D7E813 /* dwarftests.mm */,
);
path = testcases;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
8DD76F620486A84900D96B5E /* generator_test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */;
buildPhases = (
8DD76F640486A84900D96B5E /* Sources */,
8DD76F660486A84900D96B5E /* Frameworks */,
8DD76F690486A84900D96B5E /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = generator_test;
productInstallPath = "$(HOME)/bin";
productName = MinidumpWriter;
productReference = 8DD76F6C0486A84900D96B5E /* generator_test */;
productType = "com.apple.product-type.tool";
};
9B7CA84D0B1297F200CD3A1D /* unit_test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */;
buildPhases = (
9B7CA84B0B1297F200CD3A1D /* Sources */,
9B7CA84C0B1297F200CD3A1D /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = unit_test;
productName = "filewriter unit test";
productReference = 9B7CA84E0B1297F200CD3A1D /* unit_test */;
productType = "com.apple.product-type.tool";
};
9BD82A9A0B00267E0055103E /* handler_test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */;
buildPhases = (
9BD82A980B00267E0055103E /* Sources */,
9BD82A990B00267E0055103E /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = handler_test;
productName = ExceptionTester;
productReference = 9BD82A9B0B00267E0055103E /* handler_test */;
productType = "com.apple.product-type.tool";
};
F93A88740E8B4C700026AF89 /* obj-c_TestCases */ = {
isa = PBXNativeTarget;
buildConfigurationList = F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */;
buildPhases = (
F93A88700E8B4C700026AF89 /* Resources */,
F93A88710E8B4C700026AF89 /* Sources */,
F93A88720E8B4C700026AF89 /* Frameworks */,
F93A88730E8B4C700026AF89 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = "obj-c_TestCases";
productName = octestcases;
productReference = F93A88750E8B4C700026AF89 /* octestcases.octest */;
productType = "com.apple.product-type.bundle";
};
F9AE19C20DB04A9500C98454 /* minidump_tests64 */ = {
isa = PBXNativeTarget;
buildConfigurationList = F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */;
buildPhases = (
F9AE19BE0DB04A9500C98454 /* Resources */,
F9AE19BF0DB04A9500C98454 /* Sources */,
F9AE19C00DB04A9500C98454 /* Frameworks */,
F9AE19C10DB04A9500C98454 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = minidump_tests64;
productName = minidump_tests;
productReference = F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */;
productType = "com.apple.product-type.bundle";
};
F9AE5B320DBFDBA300505983 /* minidump_tests32 */ = {
isa = PBXNativeTarget;
buildConfigurationList = F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */;
buildPhases = (
F9AE5B2E0DBFDBA300505983 /* Resources */,
F9AE5B2F0DBFDBA300505983 /* Sources */,
F9AE5B300DBFDBA300505983 /* Frameworks */,
F9AE5B310DBFDBA300505983 /* ShellScript */,
);
buildRules = (
);
dependencies = (
);
name = minidump_tests32;
productName = Untitled;
productReference = F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
08FB7793FE84155DC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
);
mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */;
projectDirPath = "";
projectRoot = "";
targets = (
8DD76F620486A84900D96B5E /* generator_test */,
9BD82A9A0B00267E0055103E /* handler_test */,
9B7CA84D0B1297F200CD3A1D /* unit_test */,
F9AE19C20DB04A9500C98454 /* minidump_tests64 */,
F9AE5B320DBFDBA300505983 /* minidump_tests32 */,
F93A88740E8B4C700026AF89 /* obj-c_TestCases */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
F93A88700E8B4C700026AF89 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE19BE0DB04A9500C98454 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE5B2E0DBFDBA300505983 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
F93A88730E8B4C700026AF89 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n";
};
F9AE19C10DB04A9500C98454 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n# Run gcov on the framework getting tested\nif [ \"${CONFIGURATION}\" = 'Coverage' ];\nthen\n FRAMEWORK_NAME=minidump_tests64\n FRAMEWORK_OBJ_DIR=${OBJROOT}/${PROJECT_NAME}.build/${CONFIGURATION}/${FRAMEWORK_NAME}.build/Objects-normal/${NATIVE_ARCH_ACTUAL}\n mkdir -p coverage\n pushd coverage\n echo find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n popd\nfi ";
};
F9AE5B310DBFDBA300505983 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8DD76F640486A84900D96B5E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */,
9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */,
9BD82C110B0133520055103E /* minidump_generator.cc in Sources */,
9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */,
9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */,
D2F651000BEF947200920385 /* file_id.cc in Sources */,
D2F651020BEF947200920385 /* macho_id.cc in Sources */,
D2F651040BEF947200920385 /* macho_utilities.cc in Sources */,
D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */,
D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */,
D2F651110BEF951700920385 /* string_conversion.cc in Sources */,
D2F651150BEF953000920385 /* convert_UTF.c in Sources */,
8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9B7CA84B0B1297F200CD3A1D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */,
9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */,
9BC1D2940B336F2300F2A2B4 /* convert_UTF.c in Sources */,
9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */,
8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9BD82A980B00267E0055103E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */,
9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */,
9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */,
9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */,
9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */,
9B35FF5A0B267D5F008DE8C7 /* convert_UTF.c in Sources */,
9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */,
D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */,
D2F6511D0BEF973500920385 /* file_id.cc in Sources */,
D2F6511E0BEF973600920385 /* macho_id.cc in Sources */,
D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */,
D2F651210BEF975400920385 /* macho_walker.cc in Sources */,
8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F93A88710E8B4C700026AF89 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */,
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */,
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */,
F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */,
F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */,
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */,
F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE19BF0DB04A9500C98454 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */,
F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */,
F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */,
F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F9AE5B2F0DBFDBA300505983 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */,
F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */,
8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
1DEB923208733DC60010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
);
PRODUCT_NAME = generator_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
};
name = Debug;
};
1DEB923308733DC60010E9CD /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
);
PRODUCT_NAME = generator_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
};
name = Release;
};
1DEB923608733DC60010E9CD /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */;
buildSettings = {
};
name = Debug;
};
1DEB923708733DC60010E9CD /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */;
buildSettings = {
};
name = Release;
};
9B7CA8510B12984300CD3A1D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = unit_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
};
name = Debug;
};
9B7CA8520B12984300CD3A1D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = unit_test;
USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)";
};
name = Release;
};
9BD82AA70B0026BF0055103E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = handler_test;
USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
};
name = Debug;
};
9BD82AA80B0026BF0055103E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
PRODUCT_NAME = handler_test;
USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)";
};
name = Release;
};
F93A88770E8B4C700026AF89 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "obj-cTestCases-Info.plist";
PRODUCT_NAME = octestcases;
USER_HEADER_SEARCH_PATHS = "../../../..//**";
WRAPPER_EXTENSION = octest;
};
name = Debug;
};
F93A88780E8B4C700026AF89 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "obj-cTestCases-Info.plist";
PRODUCT_NAME = octestcases;
USER_HEADER_SEARCH_PATHS = "../../../..//**";
WRAPPER_EXTENSION = octest;
};
name = Release;
};
F9AE19C40DB04A9500C98454 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "minidump_tests64-Info.plist";
PRODUCT_NAME = minidump_tests64;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
name = Debug;
};
F9AE19C50DB04A9500C98454 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "minidump_tests64-Info.plist";
PRODUCT_NAME = minidump_tests64;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
name = Release;
};
F9AE5B350DBFDBA300505983 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "minidump_tests32-Info.plist";
PRODUCT_NAME = minidump_tests32;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
name = Debug;
};
F9AE5B370DBFDBA300505983 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks";
INFOPLIST_FILE = "minidump_tests32-Info.plist";
PRODUCT_NAME = minidump_tests32;
USER_HEADER_SEARCH_PATHS = "../../../**";
WRAPPER_EXTENSION = cptest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB923208733DC60010E9CD /* Debug */,
1DEB923308733DC60010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1DEB923608733DC60010E9CD /* Debug */,
1DEB923708733DC60010E9CD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9B7CA8510B12984300CD3A1D /* Debug */,
9B7CA8520B12984300CD3A1D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9BD82AA70B0026BF0055103E /* Debug */,
9BD82AA80B0026BF0055103E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F93A88770E8B4C700026AF89 /* Debug */,
F93A88780E8B4C700026AF89 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F9AE19C40DB04A9500C98454 /* Debug */,
F9AE19C50DB04A9500C98454 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F9AE5B350DBFDBA300505983 /* Debug */,
F9AE5B370DBFDBA300505983 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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__

View File

@@ -1,79 +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);
std::vector<uint8_t> buf(getpagesize());
fprintf(stderr, "reading 0x%p\n", addr);
kr = google_breakpad::ReadTaskMemory(mach_task_self(),
(uint64_t)addr,
getpagesize(),
buf);
CPTAssert(kr == KERN_SUCCESS);
CPTAssert(0 == memcmp(&buf[0], (const void*)addr, getpagesize()));
}
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());
CPTAssert(d->GetImageCount() > 0);
}

View File

@@ -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__ */

View File

@@ -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;
}

View File

@@ -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__*/

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +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>CFBundleDisplayName</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>crash_report_sender</string>
<key>CFBundleIdentifier</key>
<string>com.Breakpad.${PRODUCT_NAME:identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>LSHasLocalizedDisplayName</key>
<true/>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@@ -1,117 +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.
//
// This component uses the HTTPMultipartUpload of the breakpad project to send
// the minidump and associated data to the crash reporting servers.
// It will perform throttling based on the parameters passed to it and will
// prompt the user to send the minidump.
#import <Cocoa/Cocoa.h>
#include "client/mac/sender/uploader.h"
#import "GTMDefines.h"
// We're sublcassing NSTextField in order to override a particular
// method (see the implementation) that lets us reject changes if they
// are longer than a particular length. Bindings would normally solve
// this problem, but when we implemented a validation method, and
// returned NO for strings that were too long, the UI was not updated
// right away, which was a poor user experience. The UI would be
// updated as soon as the text field lost first responder status,
// which isn't soon enough. It is a known bug that the UI KVO didn't
// work in the middle of a validation.
@interface LengthLimitingTextField : NSTextField {
@private
NSUInteger maximumLength_;
}
- (void)setMaximumLength:(NSUInteger)maxLength;
@end
@interface Reporter : NSObject {
@public
IBOutlet NSWindow *alertWindow_; // The alert window
// Grouping boxes used for resizing.
IBOutlet NSBox *headerBox_;
IBOutlet NSBox *preEmailBox_;
IBOutlet NSBox *emailSectionBox_;
// Localized elements (or things that need to be moved during localization).
IBOutlet NSTextField *dialogTitle_;
IBOutlet NSTextField *commentMessage_;
IBOutlet NSTextField *emailMessage_;
IBOutlet NSTextField *emailLabel_;
IBOutlet NSTextField *privacyLinkLabel_;
IBOutlet NSButton *sendButton_;
IBOutlet NSButton *cancelButton_;
IBOutlet LengthLimitingTextField *emailEntryField_;
IBOutlet LengthLimitingTextField *commentsEntryField_;
IBOutlet NSTextField *countdownLabel_;
IBOutlet NSView *privacyLinkArrow_;
// Text field bindings, for user input.
NSString *commentsValue_; // Comments from the user
NSString *emailValue_; // Email from the user
NSString *countdownMessage_; // Message indicating time
// left for input.
@private
NSTimeInterval remainingDialogTime_; // Keeps track of how long
// we have until we cancel
// the dialog
NSTimer *messageTimer_; // Timer we use to update
// the dialog
Uploader* uploader_; // Uploader we use to send the data.
}
// Stops the modal panel with an NSAlertDefaultReturn value. This is the action
// invoked by the "Send Report" button.
- (IBAction)sendReport:(id)sender;
// Stops the modal panel with an NSAlertAlternateReturn value. This is the
// action invoked by the "Cancel" button.
- (IBAction)cancel:(id)sender;
// Opens the Privacy Policy url in the default web browser.
- (IBAction)showPrivacyPolicy:(id)sender;
// Delegate methods for the NSTextField for comments. We want to capture the
// Return key and use it to send the message when no text has been entered.
// Otherwise, we want Return to add a carriage return to the comments field.
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView
doCommandBySelector:(SEL)commandSelector;
// Accessors to make bindings work
- (NSString *)commentsValue;
- (void)setCommentsValue:(NSString *)value;
- (NSString *)emailValue;
- (void)setEmailValue:(NSString *)value;
- (NSString *)countdownMessage;
- (void)setCountdownMessage:(NSString *)value;
@end

View File

@@ -1,753 +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.
#import "client/mac/sender/crash_report_sender.h"
#import <Cocoa/Cocoa.h>
#import <pwd.h>
#import <sys/stat.h>
#import <SystemConfiguration/SystemConfiguration.h>
#import <unistd.h>
#import "client/apple/Framework/BreakpadDefines.h"
#import "common/mac/GTMLogger.h"
#import "common/mac/HTTPMultipartUpload.h"
#define kLastSubmission @"LastSubmission"
const int kUserCommentsMaxLength = 1500;
const int kEmailMaxLength = 64;
#define kApplePrefsSyncExcludeAllKey \
@"com.apple.PreferenceSync.ExcludeAllSyncKeys"
#pragma mark -
@interface NSView (ResizabilityExtentions)
// Shifts the view vertically by the given amount.
- (void)breakpad_shiftVertically:(CGFloat)offset;
// Shifts the view horizontally by the given amount.
- (void)breakpad_shiftHorizontally:(CGFloat)offset;
@end
@implementation NSView (ResizabilityExtentions)
- (void)breakpad_shiftVertically:(CGFloat)offset {
NSPoint origin = [self frame].origin;
origin.y += offset;
[self setFrameOrigin:origin];
}
- (void)breakpad_shiftHorizontally:(CGFloat)offset {
NSPoint origin = [self frame].origin;
origin.x += offset;
[self setFrameOrigin:origin];
}
@end
@interface NSWindow (ResizabilityExtentions)
// Adjusts the window height by heightDelta relative to its current height,
// keeping all the content at the same size.
- (void)breakpad_adjustHeight:(CGFloat)heightDelta;
@end
@implementation NSWindow (ResizabilityExtentions)
- (void)breakpad_adjustHeight:(CGFloat)heightDelta {
[[self contentView] setAutoresizesSubviews:NO];
NSRect windowFrame = [self frame];
windowFrame.size.height += heightDelta;
[self setFrame:windowFrame display:YES];
// For some reason the content view is resizing, but not adjusting its origin,
// so correct it manually.
[[self contentView] setFrameOrigin:NSMakePoint(0, 0)];
[[self contentView] setAutoresizesSubviews:YES];
}
@end
@interface NSTextField (ResizabilityExtentions)
// Grows or shrinks the height of the field to the minimum required to show the
// current text, preserving the existing width and origin.
// Returns the change in height.
- (CGFloat)breakpad_adjustHeightToFit;
// Grows or shrinks the width of the field to the minimum required to show the
// current text, preserving the existing height and origin.
// Returns the change in width.
- (CGFloat)breakpad_adjustWidthToFit;
@end
@implementation NSTextField (ResizabilityExtentions)
- (CGFloat)breakpad_adjustHeightToFit {
NSRect oldFrame = [self frame];
// Starting with the 10.5 SDK, height won't grow, so make it huge to start.
NSRect presizeFrame = oldFrame;
presizeFrame.size.height = MAXFLOAT;
// sizeToFit will blow out the width rather than making the field taller, so
// we do it manually.
NSSize newSize = [[self cell] cellSizeForBounds:presizeFrame];
NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y,
NSWidth(oldFrame), newSize.height);
[self setFrame:newFrame];
return newSize.height - NSHeight(oldFrame);
}
- (CGFloat)breakpad_adjustWidthToFit {
NSRect oldFrame = [self frame];
[self sizeToFit];
return NSWidth([self frame]) - NSWidth(oldFrame);
}
@end
@interface NSButton (ResizabilityExtentions)
// Resizes to fit the label using IB-style size-to-fit metrics and enforcing a
// minimum width of 70, while preserving the right edge location.
// Returns the change in width.
- (CGFloat)breakpad_smartSizeToFit;
@end
@implementation NSButton (ResizabilityExtentions)
- (CGFloat)breakpad_smartSizeToFit {
NSRect oldFrame = [self frame];
[self sizeToFit];
NSRect newFrame = [self frame];
// sizeToFit gives much worse results that IB's Size to Fit option. This is
// the amount of padding IB adds over a sizeToFit, empirically determined.
const float kExtraPaddingAmount = 12;
const float kMinButtonWidth = 70; // The default button size in IB.
newFrame.size.width = NSWidth(newFrame) + kExtraPaddingAmount;
if (NSWidth(newFrame) < kMinButtonWidth)
newFrame.size.width = kMinButtonWidth;
// Preserve the right edge location.
newFrame.origin.x = NSMaxX(oldFrame) - NSWidth(newFrame);
[self setFrame:newFrame];
return NSWidth(newFrame) - NSWidth(oldFrame);
}
@end
#pragma mark -
@interface Reporter(PrivateMethods)
- (id)initWithConfigFile:(const char *)configFile;
// Returns YES if it has been long enough since the last report that we should
// submit a report for this crash.
- (BOOL)reportIntervalElapsed;
// Returns YES if we should send the report without asking the user first.
- (BOOL)shouldSubmitSilently;
// Returns YES if the minidump was generated on demand.
- (BOOL)isOnDemand;
// Returns YES if we should ask the user to provide comments.
- (BOOL)shouldRequestComments;
// Returns YES if we should ask the user to provide an email address.
- (BOOL)shouldRequestEmail;
// Shows UI to the user to ask for permission to send and any extra information
// we've been instructed to request. Returns YES if the user allows the report
// to be sent.
- (BOOL)askUserPermissionToSend;
// Returns the short description of the crash, suitable for use as a dialog
// title (e.g., "The application Foo has quit unexpectedly").
- (NSString*)shortDialogMessage;
// Return explanatory text about the crash and the reporter, suitable for the
// body text of a dialog.
- (NSString*)explanatoryDialogText;
// Returns the amount of time the UI should be shown before timing out.
- (NSTimeInterval)messageTimeout;
// Preps the comment-prompting alert window for display:
// * localizes all the elements
// * resizes and adjusts layout as necessary for localization
// * removes the email section if includeEmail is NO
- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail;
// Rmevoes the email section of the dialog, adjusting the rest of the window
// as necessary.
- (void)removeEmailPrompt;
// Run an alert window with the given timeout. Returns
// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0
// queues the message immediately in the modal run loop.
- (NSInteger)runModalWindow:(NSWindow*)window
withTimeout:(NSTimeInterval)timeout;
// This method is used to periodically update the UI with how many
// seconds are left in the dialog display.
- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer;
// When we receive this notification, it means that the user has
// begun editing the email address or comments field, and we disable
// the timers so that the user has as long as they want to type
// in their comments/email.
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification;
- (void)report;
@end
@implementation Reporter
//=============================================================================
- (id)initWithConfigFile:(const char *)configFile {
if ((self = [super init])) {
remainingDialogTime_ = 0;
uploader_ = [[Uploader alloc] initWithConfigFile:configFile];
if (!uploader_) {
[self release];
return nil;
}
}
return self;
}
//=============================================================================
- (BOOL)askUserPermissionToSend {
// Initialize Cocoa, needed to display the alert
NSApplicationLoad();
// Get the timeout value for the notification.
NSTimeInterval timeout = [self messageTimeout];
NSInteger buttonPressed = NSAlertAlternateReturn;
// Determine whether we should create a text box for user feedback.
if ([self shouldRequestComments]) {
BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self];
if (!didLoadNib) {
return NO;
}
[self configureAlertWindowIncludingEmail:[self shouldRequestEmail]];
buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout];
// Extract info from the user into the uploader_.
if ([self commentsValue]) {
[[uploader_ parameters] setObject:[self commentsValue]
forKey:@BREAKPAD_COMMENTS];
}
if ([self emailValue]) {
[[uploader_ parameters] setObject:[self emailValue]
forKey:@BREAKPAD_EMAIL];
}
} else {
// Create an alert panel to tell the user something happened
NSPanel* alert = NSGetAlertPanel([self shortDialogMessage],
[self explanatoryDialogText],
NSLocalizedString(@"sendReportButton", @""),
NSLocalizedString(@"cancelButton", @""),
nil);
// Pop the alert with an automatic timeout, and wait for the response
buttonPressed = [self runModalWindow:alert withTimeout:timeout];
// Release the panel memory
NSReleaseAlertPanel(alert);
}
return buttonPressed == NSAlertDefaultReturn;
}
- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail {
// Swap in localized values, making size adjustments to impacted elements as
// we go. Remember that the origin is in the bottom left, so elements above
// "fall" as text areas are shrunk from their overly-large IB sizes.
// Localize the header. No resizing needed, as it has plenty of room.
[dialogTitle_ setStringValue:[self shortDialogMessage]];
// Localize the explanatory text field.
[commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@",
[self explanatoryDialogText],
NSLocalizedString(@"commentsMsg", @"")]];
CGFloat commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit];
[headerBox_ breakpad_shiftVertically:commentHeightDelta];
[alertWindow_ breakpad_adjustHeight:commentHeightDelta];
// Either localize the email explanation field or remove the whole email
// section depending on whether or not we are asking for email.
if (includeEmail) {
[emailMessage_ setStringValue:NSLocalizedString(@"emailMsg", @"")];
CGFloat emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit];
[preEmailBox_ breakpad_shiftVertically:emailHeightDelta];
[alertWindow_ breakpad_adjustHeight:emailHeightDelta];
} else {
[self removeEmailPrompt]; // Handles necessary resizing.
}
// Localize the email label, and shift the associated text field.
[emailLabel_ setStringValue:NSLocalizedString(@"emailLabel", @"")];
CGFloat emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
[emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta];
// Localize the privacy policy label, and keep it right-aligned to the arrow.
[privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")];
CGFloat privacyLabelWidthDelta =
[privacyLinkLabel_ breakpad_adjustWidthToFit];
[privacyLinkLabel_ breakpad_shiftHorizontally:(-privacyLabelWidthDelta)];
// Ensure that the email field and the privacy policy link don't overlap.
CGFloat kMinControlPadding = 8;
CGFloat maxEmailFieldWidth = NSMinX([privacyLinkLabel_ frame]) -
NSMinX([emailEntryField_ frame]) -
kMinControlPadding;
if (NSWidth([emailEntryField_ bounds]) > maxEmailFieldWidth &&
maxEmailFieldWidth > 0) {
NSSize emailSize = [emailEntryField_ frame].size;
emailSize.width = maxEmailFieldWidth;
[emailEntryField_ setFrameSize:emailSize];
}
// Localize the placeholder text.
[[commentsEntryField_ cell]
setPlaceholderString:NSLocalizedString(@"commentsPlaceholder", @"")];
[[emailEntryField_ cell]
setPlaceholderString:NSLocalizedString(@"emailPlaceholder", @"")];
// Localize the buttons, and keep the cancel button at the right distance.
[sendButton_ setTitle:NSLocalizedString(@"sendReportButton", @"")];
CGFloat sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit];
[cancelButton_ breakpad_shiftHorizontally:(-sendButtonWidthDelta)];
[cancelButton_ setTitle:NSLocalizedString(@"cancelButton", @"")];
[cancelButton_ breakpad_smartSizeToFit];
}
- (void)removeEmailPrompt {
[emailSectionBox_ setHidden:YES];
CGFloat emailSectionHeight = NSHeight([emailSectionBox_ frame]);
[preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)];
[alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)];
}
- (NSInteger)runModalWindow:(NSWindow*)window
withTimeout:(NSTimeInterval)timeout {
// Queue a |stopModal| message to be performed in |timeout| seconds.
if (timeout > 0.001) {
remainingDialogTime_ = timeout;
SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:);
messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:updateSelector
userInfo:nil
repeats:YES];
}
// Run the window modally and wait for either a |stopModal| message or a
// button click.
[NSApp activateIgnoringOtherApps:YES];
NSInteger returnMethod = [NSApp runModalForWindow:window];
return returnMethod;
}
- (IBAction)sendReport:(id)sender {
// Force the text fields to end editing so text for the currently focused
// field will be commited.
[alertWindow_ makeFirstResponder:alertWindow_];
[alertWindow_ orderOut:self];
// Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
// matches the AppKit function NSRunAlertPanel()
[NSApp stopModalWithCode:NSAlertDefaultReturn];
}
// UI Button Actions
//=============================================================================
- (IBAction)cancel:(id)sender {
[alertWindow_ orderOut:self];
// Use NSAlertDefaultReturn so that the return value of |runModalWithWindow|
// matches the AppKit function NSRunAlertPanel()
[NSApp stopModalWithCode:NSAlertAlternateReturn];
}
- (IBAction)showPrivacyPolicy:(id)sender {
// Get the localized privacy policy URL and open it in the default browser.
NSURL* privacyPolicyURL =
[NSURL URLWithString:NSLocalizedString(@"privacyPolicyURL", @"")];
[[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL];
}
// Text Field Delegate Methods
//=============================================================================
- (BOOL) control:(NSControl*)control
textView:(NSTextView*)textView
doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
// If the user has entered text on the comment field, don't end
// editing on "return".
if (control == commentsEntryField_ &&
commandSelector == @selector(insertNewline:)
&& [[textView string] length] > 0) {
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
return result;
}
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification {
[messageTimer_ invalidate];
[self setCountdownMessage:@""];
}
- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer {
remainingDialogTime_ -= 1;
NSString *countdownMessage;
NSString *formatString;
int displayedTimeLeft; // This can be either minutes or seconds.
if (remainingDialogTime_ > 59) {
// calculate minutes remaining for UI purposes
displayedTimeLeft = (int)(remainingDialogTime_ / 60);
if (displayedTimeLeft == 1) {
formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @"");
} else {
formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @"");
}
} else {
displayedTimeLeft = (int)remainingDialogTime_;
if (displayedTimeLeft == 1) {
formatString = NSLocalizedString(@"countdownMsgSecondSingular", @"");
} else {
formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @"");
}
}
countdownMessage = [NSString stringWithFormat:formatString,
displayedTimeLeft];
if (remainingDialogTime_ <= 30) {
[countdownLabel_ setTextColor:[NSColor redColor]];
}
[self setCountdownMessage:countdownMessage];
if (remainingDialogTime_ <= 0) {
[messageTimer_ invalidate];
[NSApp stopModal];
}
}
#pragma mark Accessors
#pragma mark -
//=============================================================================
- (NSString *)commentsValue {
return [[commentsValue_ retain] autorelease];
}
- (void)setCommentsValue:(NSString *)value {
if (commentsValue_ != value) {
[commentsValue_ release];
commentsValue_ = [value copy];
}
}
- (NSString *)emailValue {
return [[emailValue_ retain] autorelease];
}
- (void)setEmailValue:(NSString *)value {
if (emailValue_ != value) {
[emailValue_ release];
emailValue_ = [value copy];
}
}
- (NSString *)countdownMessage {
return [[countdownMessage_ retain] autorelease];
}
- (void)setCountdownMessage:(NSString *)value {
if (countdownMessage_ != value) {
[countdownMessage_ release];
countdownMessage_ = [value copy];
}
}
#pragma mark -
//=============================================================================
- (BOOL)reportIntervalElapsed {
float interval = [[[uploader_ parameters]
objectForKey:@BREAKPAD_REPORT_INTERVAL] floatValue];
NSString *program = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *programDict =
[NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]];
NSNumber *lastTimeNum = [programDict objectForKey:kLastSubmission];
NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0;
NSTimeInterval now = CFAbsoluteTimeGetCurrent();
NSTimeInterval spanSeconds = (now - lastTime);
[programDict setObject:[NSNumber numberWithDouble:now]
forKey:kLastSubmission];
[ud setObject:programDict forKey:program];
[ud synchronize];
// If we've specified an interval and we're within that time, don't ask the
// user if we should report
GTMLoggerDebug(@"Reporter Interval: %f", interval);
if (interval > spanSeconds) {
GTMLoggerDebug(@"Within throttling interval, not sending report");
return NO;
}
return YES;
}
- (BOOL)isOnDemand {
return [[[uploader_ parameters] objectForKey:@BREAKPAD_ON_DEMAND]
isEqualToString:@"YES"];
}
- (BOOL)shouldSubmitSilently {
return [[[uploader_ parameters] objectForKey:@BREAKPAD_SKIP_CONFIRM]
isEqualToString:@"YES"];
}
- (BOOL)shouldRequestComments {
return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_COMMENTS]
isEqualToString:@"YES"];
}
- (BOOL)shouldRequestEmail {
return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_EMAIL]
isEqualToString:@"YES"];
}
- (NSString*)shortDialogMessage {
NSString *displayName =
[[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![displayName length])
displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
if ([self isOnDemand]) {
return [NSString
stringWithFormat:NSLocalizedString(@"noCrashDialogHeader", @""),
displayName];
} else {
return [NSString
stringWithFormat:NSLocalizedString(@"crashDialogHeader", @""),
displayName];
}
}
- (NSString*)explanatoryDialogText {
NSString *displayName =
[[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY];
if (![displayName length])
displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT];
NSString *vendor = [[uploader_ parameters] objectForKey:@BREAKPAD_VENDOR];
if (![vendor length])
vendor = @"unknown vendor";
if ([self isOnDemand]) {
return [NSString
stringWithFormat:NSLocalizedString(@"noCrashDialogMsg", @""),
vendor, displayName];
} else {
return [NSString
stringWithFormat:NSLocalizedString(@"crashDialogMsg", @""),
vendor];
}
}
- (NSTimeInterval)messageTimeout {
// Get the timeout value for the notification.
NSTimeInterval timeout = [[[uploader_ parameters]
objectForKey:@BREAKPAD_CONFIRM_TIMEOUT] floatValue];
// Require a timeout of at least a minute (except 0, which means no timeout).
if (timeout > 0.001 && timeout < 60.0) {
timeout = 60.0;
}
return timeout;
}
- (void)report {
[uploader_ report];
}
//=============================================================================
- (void)dealloc {
[uploader_ release];
[super dealloc];
}
- (void)awakeFromNib {
[emailEntryField_ setMaximumLength:kEmailMaxLength];
[commentsEntryField_ setMaximumLength:kUserCommentsMaxLength];
}
@end
//=============================================================================
@implementation LengthLimitingTextField
- (void)setMaximumLength:(NSUInteger)maxLength {
maximumLength_ = maxLength;
}
// This is the method we're overriding in NSTextField, which lets us
// limit the user's input if it makes the string too long.
- (BOOL) textView:(NSTextView *)textView
shouldChangeTextInRange:(NSRange)affectedCharRange
replacementString:(NSString *)replacementString {
// Sometimes the range comes in invalid, so reject if we can't
// figure out if the replacement text is too long.
if (affectedCharRange.location == NSNotFound) {
return NO;
}
// Figure out what the new string length would be, taking into
// account user selections.
NSUInteger newStringLength =
[[textView string] length] - affectedCharRange.length +
[replacementString length];
if (newStringLength > maximumLength_) {
return NO;
} else {
return YES;
}
}
// Cut, copy, and paste have to be caught specifically since there is no menu.
- (BOOL)performKeyEquivalent:(NSEvent*)event {
// Only handle the key equivalent if |self| is the text field with focus.
NSText* fieldEditor = [self currentEditor];
if (fieldEditor != nil) {
// Check for a single "Command" modifier
NSUInteger modifiers = [event modifierFlags];
modifiers &= NSDeviceIndependentModifierFlagsMask;
if (modifiers == NSCommandKeyMask) {
// Now, check for Select All, Cut, Copy, or Paste key equivalents.
NSString* characters = [event characters];
// Select All is Command-A.
if ([characters isEqualToString:@"a"]) {
[fieldEditor selectAll:self];
return YES;
// Cut is Command-X.
} else if ([characters isEqualToString:@"x"]) {
[fieldEditor cut:self];
return YES;
// Copy is Command-C.
} else if ([characters isEqualToString:@"c"]) {
[fieldEditor copy:self];
return YES;
// Paste is Command-V.
} else if ([characters isEqualToString:@"v"]) {
[fieldEditor paste:self];
return YES;
}
}
}
// Let the super class handle the rest (e.g. Command-Period will cancel).
return [super performKeyEquivalent:event];
}
@end
//=============================================================================
int main(int argc, const char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#if DEBUG
// Log to stderr in debug builds.
[GTMLogger setSharedLogger:[GTMLogger standardLoggerWithStderr]];
#endif
GTMLoggerDebug(@"Reporter Launched, argc=%d", argc);
// The expectation is that there will be one argument which is the path
// to the configuration file
if (argc != 2) {
exit(1);
}
Reporter *reporter = [[Reporter alloc] initWithConfigFile:argv[1]];
if (!reporter) {
GTMLoggerDebug(@"reporter initialization failed");
exit(1);
}
// only submit a report if we have not recently crashed in the past
BOOL shouldSubmitReport = [reporter reportIntervalElapsed];
BOOL okayToSend = NO;
// ask user if we should send
if (shouldSubmitReport) {
if ([reporter shouldSubmitSilently]) {
GTMLoggerDebug(@"Skipping confirmation and sending report");
okayToSend = YES;
} else {
okayToSend = [reporter askUserPermissionToSend];
}
}
// If we're running as root, switch over to nobody
if (getuid() == 0 || geteuid() == 0) {
struct passwd *pw = getpwnam("nobody");
// If we can't get a non-root uid, don't send the report
if (!pw) {
GTMLoggerDebug(@"!pw - %s", strerror(errno));
exit(0);
}
if (setgid(pw->pw_gid) == -1) {
GTMLoggerDebug(@"setgid(pw->pw_gid) == -1 - %s", strerror(errno));
exit(0);
}
if (setuid(pw->pw_uid) == -1) {
GTMLoggerDebug(@"setuid(pw->pw_uid) == -1 - %s", strerror(errno));
exit(0);
}
}
else {
GTMLoggerDebug(@"getuid() !=0 || geteuid() != 0");
}
if (okayToSend && shouldSubmitReport) {
GTMLoggerDebug(@"Sending Report");
[reporter report];
GTMLoggerDebug(@"Report Sent!");
} else {
GTMLoggerDebug(@"Not sending crash report okayToSend=%d, "\
"shouldSubmitReport=%d", okayToSend, shouldSubmitReport);
}
GTMLoggerDebug(@"Exiting with no errors");
// Cleanup
[reporter release];
[pool release];
return 0;
}

Some files were not shown because too many files have changed in this diff Show More