Implement wxStackWalker for wxOSX.

Use atos(1) to map address to their symbolic names.

Closes #10067.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@71513 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2012-05-20 20:29:22 +00:00
parent 816e7781b0
commit abf99aad2d
3 changed files with 94 additions and 47 deletions

View File

@@ -581,6 +581,7 @@ OSX:
- Provide native implementations of wxDatePickerCtrl and wxTimePickerCtrl. - Provide native implementations of wxDatePickerCtrl and wxTimePickerCtrl.
- Fix handling of positional parameters in wxPrintf() &c (David Connet). - Fix handling of positional parameters in wxPrintf() &c (David Connet).
- Implement wxStackWalker.
Univ: Univ:

View File

@@ -14,13 +14,6 @@
#ifndef _WX_OSX_CHKCONF_H_ #ifndef _WX_OSX_CHKCONF_H_
#define _WX_OSX_CHKCONF_H_ #define _WX_OSX_CHKCONF_H_
#if wxUSE_STACKWALKER
/* not supported under Mac */
# undef wxUSE_STACKWALKER
# define wxUSE_STACKWALKER 0
#endif /* wxUSE_STACKWALKER */
/* /*
* check graphics context option, must be on for every os x platform * check graphics context option, must be on for every os x platform
* we only use core graphics now on all builds, try to catch attempts * we only use core graphics now on all builds, try to catch attempts

View File

@@ -130,10 +130,12 @@ void wxStackFrame::OnGetName()
m_module.assign(syminfo, posOpen); m_module.assign(syminfo, posOpen);
} }
#ifndef __WXOSX__
else // not in "module(funcname+offset)" format else // not in "module(funcname+offset)" format
{ {
m_module = syminfo; m_module = syminfo;
} }
#endif // !__WXOSX__
} }
@@ -204,6 +206,28 @@ void wxStackWalker::FreeStack()
m_depth = 0; m_depth = 0;
} }
namespace
{
// Helper function to read a line from the file and return it without the
// trailing newline. Line number is only used for error reporting.
bool ReadLine(FILE* fp, unsigned long num, wxString* line)
{
if ( !fgets(g_buf, WXSIZEOF(g_buf), fp) )
{
wxLogDebug(wxS("cannot read address information for stack frame #%lu"),
num);
return false;
}
*line = wxString::FromAscii(g_buf);
line->RemoveLast();
return true;
}
} // anonymous namespace
int wxStackWalker::InitFrames(wxStackFrame *arr, size_t n, void **addresses, char **syminfo) int wxStackWalker::InitFrames(wxStackFrame *arr, size_t n, void **addresses, char **syminfo)
{ {
// we need to launch addr2line tool to get this information and we need to // we need to launch addr2line tool to get this information and we need to
@@ -220,9 +244,13 @@ int wxStackWalker::InitFrames(wxStackFrame *arr, size_t n, void **addresses, cha
} }
} }
// build the (long) command line for executing addr2line in an optimized way // build the command line for executing addr2line or atos under OS X using
// (e.g. use always chars, even in Unicode build: popen() always takes chars) // char* directly to avoid the conversions from Unicode
#ifdef __WXOSX__
int len = snprintf(g_buf, BUFSIZE, "atos -p %d", (int)getpid());
#else
int len = snprintf(g_buf, BUFSIZE, "addr2line -C -f -e \"%s\"", (const char*) exepath.mb_str()); int len = snprintf(g_buf, BUFSIZE, "addr2line -C -f -e \"%s\"", (const char*) exepath.mb_str());
#endif
len = (len <= 0) ? strlen(g_buf) : len; // in case snprintf() is broken len = (len <= 0) ? strlen(g_buf) : len; // in case snprintf() is broken
for (size_t i=0; i<n; i++) for (size_t i=0; i<n; i++)
{ {
@@ -236,54 +264,79 @@ int wxStackWalker::InitFrames(wxStackFrame *arr, size_t n, void **addresses, cha
if ( !fp ) if ( !fp )
return 0; return 0;
// parse addr2line output (should be exactly 2 lines for each address) // parse the output reusing the same buffer to avoid any big memory
// reusing the g_buf used for building the command line above // allocations which could fail if our program is in a bad state
wxString name, filename; wxString name, filename;
unsigned long line = 0, unsigned long line = 0,
curr = 0; curr = 0;
for ( size_t i = 0; i < n; i++ ) for ( size_t i = 0; i < n; i++ )
{ {
// 1st line has function name #ifdef __WXOSX__
if ( fgets(g_buf, WXSIZEOF(g_buf), fp) ) wxString buffer;
{ if ( !ReadLine(fp, i, &buffer) )
name = wxString::FromAscii(g_buf); return false;
name.RemoveLast(); // trailing newline
if ( name == wxT("??") ) line = 0;
name.clear(); filename.clear();
// We can get back either the string in the following format:
//
// func(args) (in module) (file:line)
//
// or just the same address back if it couldn't be resolved.
const size_t posIn = buffer.find("(in ");
if ( posIn != wxString::npos )
{
name.assign(buffer, 0, posIn);
size_t posAt = buffer.find(") (", posIn + 3);
if ( posAt != wxString::npos )
{
posAt += 3; // Skip ") ("
// Discard the two last characters which are ")\n"
wxString location(buffer, posAt, buffer.length() - posAt - 2);
wxString linenum;
filename = location.BeforeFirst(':', &linenum);
if ( !linenum.empty() )
linenum.ToULong(&line);
}
}
#else // !__WXOSX__
// 1st line has function name
if ( !ReadLine(fp, i, &name) )
return false;
name = wxString::FromAscii(g_buf);
name.RemoveLast(); // trailing newline
if ( name == wxT("??") )
name.clear();
// 2nd one -- the file/line info
if ( !ReadLine(fp, i, &filename) )
return false;
const size_t posColon = filename.find(wxT(':'));
if ( posColon != wxString::npos )
{
// parse line number (it's ok if it fails, this will just leave
// line at its current, invalid, 0 value)
wxString(filename, posColon + 1, wxString::npos).ToULong(&line);
// remove line number from 'filename'
filename.erase(posColon);
if ( filename == wxT("??") )
filename.clear();
} }
else else
{ {
wxLogDebug(wxT("cannot read addr2line output for stack frame #%lu"), wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
(unsigned long)i); wxT("the semicolon is missing"),
return false; filename.c_str());
}
// 2nd one -- the file/line info
if ( fgets(g_buf, WXSIZEOF(g_buf), fp) )
{
filename = wxString::FromAscii(g_buf);
filename.RemoveLast();
const size_t posColon = filename.find(wxT(':'));
if ( posColon != wxString::npos )
{
// parse line number (it's ok if it fails, this will just leave
// line at its current, invalid, 0 value)
wxString(filename, posColon + 1, wxString::npos).ToULong(&line);
// remove line number from 'filename'
filename.erase(posColon);
if ( filename == wxT("??") )
filename.clear();
}
else
{
wxLogDebug(wxT("Unexpected addr2line format: \"%s\" - ")
wxT("the semicolon is missing"),
filename.c_str());
}
} }
#endif // __WXOSX__/!__WXOSX__
// now we've got enough info to initialize curr-th stack frame // now we've got enough info to initialize curr-th stack frame
// (at worst, only addresses[i] and syminfo[i] have been initialized, // (at worst, only addresses[i] and syminfo[i] have been initialized,