Merge lp:~strauman/epics-base/stacktrace-1 into lp:~epics-core/epics-base/3.15
- stacktrace-1
- Merge into 3.15
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 12544 | ||||
Proposed branch: | lp:~strauman/epics-base/stacktrace-1 | ||||
Merge into: | lp:~epics-core/epics-base/3.15 | ||||
Diff against target: |
1524 lines (+1356/-3) 21 files modified
configure/os/CONFIG.Common.linuxCommon (+3/-1) configure/os/CONFIG.win32-x86.win32-x86 (+2/-2) src/libCom/misc/cantProceed.c (+4/-0) src/libCom/osi/Makefile (+6/-0) src/libCom/osi/epicsStackTrace.c (+124/-0) src/libCom/osi/epicsStackTrace.h (+43/-0) src/libCom/osi/epicsStackTracePvt.h (+48/-0) src/libCom/osi/os/Darwin/osdBackTrace.cpp (+10/-0) src/libCom/osi/os/Darwin/osdFindAddr.cpp (+43/-0) src/libCom/osi/os/Linux/osdBackTrace.cpp (+10/-0) src/libCom/osi/os/Linux/osdFindAddr.cpp (+10/-0) src/libCom/osi/os/WIN32/osdBackTrace.cpp (+23/-0) src/libCom/osi/os/default/osdAssert.c (+4/-0) src/libCom/osi/os/default/osdBackTrace.cpp (+16/-0) src/libCom/osi/os/default/osdFindAddr.cpp (+25/-0) src/libCom/osi/os/posix/osdElfFindAddr.cpp (+664/-0) src/libCom/osi/os/posix/osdExecinfoBackTrace.cpp (+18/-0) src/libCom/osi/os/solaris/osdBackTrace.cpp (+46/-0) src/libCom/osi/os/solaris/osdFindAddr.cpp (+10/-0) src/libCom/test/Makefile (+5/-0) src/libCom/test/epicsStackTraceTest.c (+242/-0) |
||||
To merge this branch: | bzr merge lp:~strauman/epics-base/stacktrace-1 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Approve | ||
mdavidsaver | Approve | ||
Review via email: mp+232618@code.launchpad.net |
Commit message
Description of the change
Added a unit test in the standard location.
- 12528. By Till Straumann
-
- let default (no-op) version of stack trace print informative message
that stack traces are not supported.
Andrew Johnson (anj) wrote : | # |
- 12529. By Till Straumann
-
- removed obsolete rule (used during development)
- 12530. By Till Straumann
-
- removed message about a stack trace going to be printed
- 12531. By Till Straumann
-
- removed message; if there is no support remain silent
- 12532. By Till Straumann
-
- print header message
Till Straumann (strauman) wrote : | # |
Hi Andrew.
Thanks for your comments - I reply in-line...
On 08/29/2014 12:38 PM, Andrew Johnson wrote:
> A few disjoint comments in-line, hopefully others will add to this.
>
> Diff comments:
>
>> === modified file 'configure/
>> --- configure/
>> +++ configure/
>> @@ -20,7 +20,9 @@
>> # -D_BSD_SOURCE for gethostname() in unistd.h as needed by cacChannelIO.cpp.
>> OP_SYS_CPPFLAGS += -D_BSD_SOURCE
>> OP_SYS_CPPFLAGS += -Dlinux
>> -OP_SYS_LDLIBS += -lrt -ldl
>> +# add -rdynamic so that more symbols (all globals, not only
>> +# cross-referenced ones) are available to 'backtrace'.
>> +OP_SYS_LDLIBS += -lrt -ldl -rdynamic
>>
>> # Added here for cross-target builds which include this file
>> STATIC_LDFLAGS_YES= -Wl,-Bstatic
>>
>> === modified file 'src/libCom/
>> --- src/libCom/
>> +++ src/libCom/
>> @@ -19,6 +19,7 @@
>> #include "errlog.h"
>> #include "cantProceed.h"
>> #include "epicsThread.h"
>> +#include "epicsStackTrace.h"
>>
>> epicsShareFunc void * callocMustSucce
>> {
>> @@ -62,6 +63,11 @@
>>
>> errlogPrintf(
>> epicsThreadGetN
>> +
>> + errlogPrintf(
>> + epicsStackTrace();
>> + errlogPrintf("\n");
>> +
> If an OS doesn't support stack traces I would prefer that we not print the other stuff on every cantProceed() call. Maybe move those errlogPrintf() calls into the epicsStackTrace() routine?
Fair enough. Done.
>
>> errlogFlush();
>>
>> epicsThreadSlee
>>
>> === modified file 'src/libCom/
>> --- src/libCom/
>> +++ src/libCom/
>> @@ -56,6 +56,7 @@
>> INC += epicsStdioRedir
>> INC += epicsTempFile.h
>> INC += epicsGetopt.h
>> +INC += epicsStackTrace.h
>>
>> INC += devLib.h
>> INC += devLibVME.h
>> @@ -140,3 +141,11 @@
>> Com_SRCS_WIN32 += setThreadName.cpp
>> #Com_SRCS_WIN32 += dllmain.cpp
>> Com_SRCS_WIN32 += forceBadAllocEx
>> +
>> +#Stack trace support
>> +Com_SRCS += osdStackTrace.c
>> +Com_SRCS_Linux += execinfoStackTr
>> +Com_SRCS_Darwin += execinfoStackTr
>> +#we could use execinfoStackTr
>> +#you need libexecinfo.a and execinfo.h. I don't know if that
>> +#is routinely available so we don't use it for now.
>>
>> === modified file 'src/libCom/
>> --- src/libCom/
>> +++ src/libCom/
>> @@ -13,3 +13,7 @@
>> osdNetIntf$(DEP): $(COMMON_
>> osdSock$(DEP): $(COMMON_
>>
>> +execinfoConfig.h:
>> + touch $@
>> +
>> +execinfoStackT
> What is execinfoConfig.h for?
Leftover from development. Removed, thanks.
>
>> === added file 'src/libCo...
mdavidsaver (mdavidsaver) wrote : | # |
Do you plan to remove the "#if 0" code?
mdavidsaver (mdavidsaver) wrote : | # |
Would it be possible to rework elfLookupAddr() into a form equivalent to backtrace_symbols() and move it to a separate file? I think this would help to compartmentalize things as the bulk of the code in this branch sits behind elfLookupAddr.
Andrew Johnson (anj) wrote : | # |
Since both implementations of epicsStackTrace
I agree with Michael's comment about reworking elfLookupAddr(); we have been trying to remove architecture-
Which of our platforms will use the elfLookupAddr() implementation? There would seem to be some conceptual overlap between this and the epicsFindSymbol() API (in epicsFindSymbol.h and os/*/osdFindSym
VxWorks provides a routine tt(taskId) for printing stack-traces but the output is sent directly to the OS's stdout or stderr, and the internals of the implementation are not accessible. Oh, and tt() can't trace its own thread either, so it's not much use to us for this anyway.
Till Straumann (strauman) wrote : | # |
How do you feel about my adding the reverse lookup:
epicsFindAddress()
to the epicsFindSymbol API. The stacktrace could also be added - this
requires only little (but important) code in addition to epicsFindAddress().
This is the 'backtrace' call from <execinfo.h> which is available on
linux/freebsd/
IMHO it is not satisfactory that the implementations for darwin, linux and
solaris (freebsd could be added) are identical. Since they all use
dlopen/
which *are* posix -- this implementation should clearly go into a single
file
in the 'posix' directory.
The reverse lookup requires 'dladdr' which is not posix but an addition
which is available under BSD, darwin, gnu/linux and solaris - therefore
it should not be a problem.
The story with the ELF stuff is that 'dladdr' gives incomplete information
on some systems (most importantly: linux) which is why we then look
at the ELF data. The ELF business could be moved to a different file
used only by linux (ATM).
RFC
- Till
On 09/03/2014 02:42 PM, Andrew Johnson wrote:
> Since both implementations of epicsStackTrace
>
> I agree with Michael's comment about reworking elfLookupAddr(); we have been trying to remove architecture-
>
> Which of our platforms will use the elfLookupAddr() implementation? There would seem to be some conceptual overlap between this and the epicsFindSymbol() API (in epicsFindSymbol.h and os/*/osdFindSym
>
> VxWorks provides a routine tt(taskId) for printing stack-traces but the output is sent directly to the OS's stdout or stderr, and the internals of the implementation are not accessible. Oh, and tt() can't trace its own thread either, so it's not much use to us for this anyway.
>
Till Straumann (strauman) wrote : | # |
On a related note:
I just looked at epicsFindSymbol() and wanted to point out that this API
is not implemented
in a thread-safe way (the posix variant, that is). The standard says
"It is implementation-
thread-safe.
A thread-safe implementation shall return only errors that occur on
the current thread."
(http://
A little test reveals that (at least: recent, i.e., ubuntu 14.04, osx
10.9) linux + osx are
thread-safe but solaris is not.
- T.
On 09/03/2014 02:42 PM, Andrew Johnson wrote:
> Since both implementations of epicsStackTrace
>
> I agree with Michael's comment about reworking elfLookupAddr(); we have been trying to remove architecture-
>
> Which of our platforms will use the elfLookupAddr() implementation? There would seem to be some conceptual overlap between this and the epicsFindSymbol() API (in epicsFindSymbol.h and os/*/osdFindSym
>
> VxWorks provides a routine tt(taskId) for printing stack-traces but the output is sent directly to the OS's stdout or stderr, and the internals of the implementation are not accessible. Oh, and tt() can't trace its own thread either, so it's not much use to us for this anyway.
>
Andrew Johnson (anj) wrote : | # |
I was just reading an article about the GLIBC feature test macros on LWN http://
Till Straumann (strauman) wrote : | # |
Yeah, I had already noticed and fixed that one.
On 09/08/2014 08:52 AM, Andrew Johnson wrote:
> I was just reading an article about the GLIBC feature test macros on LWN http://
>
- 12533. By Till Straumann
-
- replaced TABs by four blanks
- 12534. By Till Straumann
-
- replaced TABs by four blanks
- 12535. By Till Straumann
-
- added test case for library symbols
- replaced TABs by spaces - 12536. By Till Straumann
-
- added EPICS_STACKTRAC
E_DYN_SYMBOL - 12537. By Till Straumann
-
- canonicalized printout formatting
- added EPICS_STACKTRACE_DYN_SYMBOL
- added USE_DLADDR branch (which is a 'super' of USE_ELF, i.e., the latter
depends on the former) - 12538. By Till Straumann
-
- call first epicsStackTrace
RecurseGbl( ) through function pointer -- clang
(darwin) had optimized it away. - 12539. By Till Straumann
-
- forget about backtrace_symbols() -- always use 'dladdr'. (The former
is non-standard either and AFAIK always based on the latter.) Always
using dladdr allows us to precisely control the printout formatting
and keep it consistent. - 12540. By Till Straumann
-
- use unistd's _POSIX_MAPPED_FILES to determine if a system has mmap.
- 12541. By Till Straumann
-
- first stab at breaking stack trace facility into separate files
- 12542. By Till Straumann
-
- (hopefully) correct usage of sharedLib.h...
- 12543. By Till Straumann
-
- fixed illegal void* pointer arithmetic by casting to char*
- removed more unused stuff - 12544. By Till Straumann
-
- fixed illegal void* pointer arithmetic (by casting to char*)
- 12545. By Till Straumann
-
- added windows versions of osdBackTrace.c, osdFindAddr.c; the latter
does not implement addr->symbol conversion yet. - 12546. By Till Straumann
-
- turn off frame-pointer optimization (otherwise stack trace capture
does not work). - 12547. By Till Straumann <strauman@rhel6-64f>
-
- solaris implementation of epicsBackTrace() via walkcontext()
- 12548. By Till Straumann <strauman@rhel6-64f>
-
- renamed symbol that clashed on solaris
- 12549. By Till Straumann <strauman@rhel6-64f>
-
- fixed illegal void* pointer arithmetic by casting to char*
- 12550. By Till Straumann
-
- renamed:
src/libCom/osi/os/ posix/osdBackTr ace.c => src/libCom/ osi/os/ posix/osdExecin foBackTrace. c
src/libCom/osi/os/ posix/osdFindAd dr.c => src/libCom/ osi/os/ posix/osdElfFin dAddr.c not all 'posix' platforms can use the above versions (since the used APIs are not really POSIX)
but a subset can. The platforms which can use either version #include it from 'their'
osdBackTrace.c/osdFindAddr. c. - 12551. By Till Straumann
-
- updated copyright info
- 12552. By Till Straumann
-
- cleanup
- reduced epicsStackTraceGetFeatures to epicsFindAddrGe tFeatures - 12553. By Till Straumann
-
- updated copyright info
- let epicsFindAddr clear out returned symbol info
- provide epicsFindAddrGetFeatures instead of epicsStackTrace GetFeatures - 12554. By Till Straumann
-
- WIN32 can now use default osdFindAddr (until symbol lookup is implemented)
- 12555. By Till Straumann
-
- simplified code
- provide epicsFindAddrGetFeatures( ) instead of epicsStackTrace GetFeatures( ) - 12556. By Till Straumann
-
- simplified some code
- epicsStackTrace.c provides generic epicsStackTraceGetFeatures( ) testing
the 'epicsBackTrace()' functionality. Call out to epicsFindAddrGetFeatures( )
to inquire about symbol lookup support.
Till Straumann (strauman) wrote : | # |
I restructured the code and hope to have addressed your concerns.
Functionally, dumping a stack trace can be split into two major
tasks:
- obtaining a backtrace (array of numerical PC addresses)
- finding a text symbol close to the addresses
epicsStackTrace() is now a generic piece of code which calls out to
epicsBackTrace() for obtaining the numerical backtrace
epicsFindAddr() for obtaining symbol information
It makes sense to break the two OSD functions up into separate files
because sometimes the implementation of one of them can be shared
between OSes but the other one can't. E.g., linux and solaris can
use the same symbol lookup (ELF) but require different backtrace
or e.g., OSX and linux can use the same backtrace but require
different symbol lookup.
There are variants for
os/default: provides no-op osdBackTrace.c, osdFindAddr.c
os/WIN32: implements osdBackTrace.c, inherits default osdFindAddr.c
os/solaris: implements osdBackTrace.c, inherits posix osdElfFindAddr.c
os/linux: inherits posix osdExecinfoBack
os/darwin: inherits posix osdExecinfoBack
The reason for special names of the 'posix' variants is that while there
are no true posix APIs that could help the implementation the 'posix'
version can be expected to work on multiple (but not all!) platforms that
EPICS treats as 'posix' (e.g., backtrace should work on any system with execinfo.h).
Since there could be other flavors of shareable implementations in the future
(and since not all 'posix' OSes should inherit the above variants) more
specific names were used.
All of the above have been built and tested. I have no access to freebsd
but osdBackTrace.
the same algorithms should work.
Conditional compilation (except for debugging statements) has been almost
completely eliminated except for:
- unistd/POSIX feature test for presence of mmap() on a platform
- handle a small, special quirk under solaris
Symbol lookup for WIN32 should not be hard to add but I don't want to spend
more time until a positive decision to merge this facility has been made.
mdavidsaver (mdavidsaver) wrote : | # |
With the reorganization this code is much more understandable to me. I see that the ELF code reads the on-disk executable/library at the time the stack trace is generated (the first time). IOCs can run for a long time, and it isn't out of the question that the code has been re-compiled without a restart.
Is there some way we can detect if the file read from disk really corresponds to the running code? Perhaps comparing file modification time with process start time?
- 12557. By Till Straumann
-
- made osdBackTrace/
osdFindAddr c++ sources to be more flexible for
implementations. Fixed compiler warnings (c++ more picky than cc). - 12558. By Till Straumann
-
- implemented suggestion by Michael Davidsaver: when reading ELF files
check modifification time against program start time and warn if the
file was modified more recently since it could be out of date. - 12559. By Till Straumann <strauman@rhel6-64f>
-
- fixed c++ warnings
- 12560. By Till Straumann <strauman@rhel6-64f>
-
- fixed c++ warnings
Till Straumann (strauman) wrote : | # |
OK. I have added code to the ELF reader which compares the file
modification time
to the time when the process started (I simply use a c++ initializer to
obtain the
process start time which is more portable than scanning /proc entries).
A warning is printed if the file has been modified more recently than
when the
process started. I feel that a warning is better than refusal to use the
file since
it could still be valid or at least better than nothing.
- T.
On 09/15/2014 01:02 PM, mdavidsaver wrote:
> With the reorganization this code is much more understandable to me. I see that the ELF code reads the on-disk executable/library at the time the stack trace is generated (the first time). IOCs can run for a long time, and it isn't out of the question that the code has been re-compiled without a restart.
>
> Is there some way we can detect if the file read from disk really corresponds to the running code? Perhaps comparing file modification time with process start time?
>
mdavidsaver (mdavidsaver) wrote : | # |
A warning seem adaquate.
Can we get away without assuming that time_t has 1 second resolution? Perhaps time() instead of clock_gettime()?
Given a 1 second resolution, any reason for "stat_b.st_mtime > prog_start_time" vs. ">="?
- 12561. By Till Straumann
-
- use time() instead of clock_gettime();
- when comparing modification to program start time use >= instead of >
Till Straumann (strauman) wrote : | # |
On 09/19/2014 09:31 AM, mdavidsaver wrote:
> A warning seem adaquate.
>
> Can we get away without assuming that time_t has 1 second resolution?
No - because that is how it is defined :-)
> Perhaps time() instead of clock_gettime()?
Fine with me but there is no difference - tv_sec is also of type time_t.
However, using time() does simplify the code a little bit, thanks.
>
> Given a 1 second resolution, any reason for "stat_b.st_mtime > prog_start_time" vs. ">="?
Fine with me, too. I pushed the changes...
However, I can't help getting the impression that this code is
subject to extraordinary scrutiny which seems exaggerated
given that its only purpose is adding information to a post-mortem
situation.
I don't see comparable precautions being taken when it comes e.g.,
to loading 'dbd' or 'db' files which may also have been modified after
an IOC starts running - usually there is a time window of many seconds
to minutes after an IOC starts until it gets to read dbds and dbs...
Best
- T.
mdavidsaver (mdavidsaver) wrote : | # |
>> Can we get away without assuming that time_t has 1 second resolution?
> No - because that is how it is defined :-)
Ok, for POSIX you are correct. Would be nice if this was always true, then we could simplify epicsTime.cpp.
mdavidsaver (mdavidsaver) wrote : | # |
Just realized that you have in fact made the two changes I suggested. At this point I don't see any further issues.
Andrew Johnson (anj) wrote : | # |
Making a few minor changes during merge process:
* Moved -rdynamic flag into OP_SYS_LDFLAGS
* Documented MSVS -Oy- flag
* Added epicsStackTrace
* Changed epicsStackTrace
Preview Diff
1 | === modified file 'configure/os/CONFIG.Common.linuxCommon' |
2 | --- configure/os/CONFIG.Common.linuxCommon 2014-09-08 22:57:18 +0000 |
3 | +++ configure/os/CONFIG.Common.linuxCommon 2014-09-19 17:20:51 +0000 |
4 | @@ -17,7 +17,9 @@ |
5 | POSIX_LDLIBS = -lpthread |
6 | |
7 | OP_SYS_CPPFLAGS += -Dlinux |
8 | -OP_SYS_LDLIBS += -lrt -ldl |
9 | +# add -rdynamic so that more symbols (all globals, not only |
10 | +# cross-referenced ones) are available to 'backtrace'. |
11 | +OP_SYS_LDLIBS += -lrt -ldl -rdynamic |
12 | |
13 | # Linker flags for static & shared-library builds |
14 | STATIC_LDFLAGS_YES= -Wl,-Bstatic |
15 | |
16 | === modified file 'configure/os/CONFIG.win32-x86.win32-x86' |
17 | --- configure/os/CONFIG.win32-x86.win32-x86 2013-07-26 19:37:55 +0000 |
18 | +++ configure/os/CONFIG.win32-x86.win32-x86 2014-09-19 17:20:51 +0000 |
19 | @@ -43,7 +43,7 @@ |
20 | # -MD use MSVCRT (run-time as DLL, multi-thread support) |
21 | # -GL whole program optimization |
22 | # -Zi generate program database for debugging information |
23 | -OPT_CFLAGS_YES = -Ox -GL |
24 | +OPT_CFLAGS_YES = -Ox -GL -Oy- |
25 | |
26 | # |
27 | # -Zi generate program database for debugging information |
28 | @@ -107,7 +107,7 @@ |
29 | # -Ox maximum optimizations |
30 | # -GL whole program optimization |
31 | # -Zi generate program database for debugging information |
32 | -OPT_CXXFLAGS_YES = -Ox -GL |
33 | +OPT_CXXFLAGS_YES = -Ox -GL -Oy- |
34 | |
35 | # |
36 | # -Zi generate program database for debugging information |
37 | |
38 | === modified file 'src/libCom/misc/cantProceed.c' |
39 | --- src/libCom/misc/cantProceed.c 2010-10-05 19:27:37 +0000 |
40 | +++ src/libCom/misc/cantProceed.c 2014-09-19 17:20:51 +0000 |
41 | @@ -19,6 +19,7 @@ |
42 | #include "errlog.h" |
43 | #include "cantProceed.h" |
44 | #include "epicsThread.h" |
45 | +#include "epicsStackTrace.h" |
46 | |
47 | epicsShareFunc void * callocMustSucceed(size_t count, size_t size, const char *msg) |
48 | { |
49 | @@ -62,6 +63,9 @@ |
50 | |
51 | errlogPrintf("Thread %s (%p) can't proceed, suspending.\n", |
52 | epicsThreadGetNameSelf(), (void *)epicsThreadGetIdSelf()); |
53 | + |
54 | + epicsStackTrace(); |
55 | + |
56 | errlogFlush(); |
57 | |
58 | epicsThreadSleep(1.0); |
59 | |
60 | === modified file 'src/libCom/osi/Makefile' |
61 | --- src/libCom/osi/Makefile 2014-05-23 19:14:49 +0000 |
62 | +++ src/libCom/osi/Makefile 2014-09-19 17:20:51 +0000 |
63 | @@ -56,6 +56,7 @@ |
64 | INC += epicsStdioRedirect.h |
65 | INC += epicsTempFile.h |
66 | INC += epicsGetopt.h |
67 | +INC += epicsStackTrace.h |
68 | |
69 | INC += devLib.h |
70 | INC += devLibVME.h |
71 | @@ -140,3 +141,8 @@ |
72 | Com_SRCS_WIN32 += setThreadName.cpp |
73 | #Com_SRCS_WIN32 += dllmain.cpp |
74 | Com_SRCS_WIN32 += forceBadAllocException.cpp |
75 | + |
76 | +#Stack trace support |
77 | +Com_SRCS += epicsStackTrace.c |
78 | +Com_SRCS += osdBackTrace.cpp |
79 | +Com_SRCS += osdFindAddr.cpp |
80 | |
81 | === added file 'src/libCom/osi/epicsStackTrace.c' |
82 | --- src/libCom/osi/epicsStackTrace.c 1970-01-01 00:00:00 +0000 |
83 | +++ src/libCom/osi/epicsStackTrace.c 2014-09-19 17:20:51 +0000 |
84 | @@ -0,0 +1,124 @@ |
85 | +/* |
86 | + * Copyright: Stanford University / SLAC National Laboratory. |
87 | + * |
88 | + * EPICS BASE is distributed subject to a Software License Agreement found |
89 | + * in file LICENSE that is included with this distribution. |
90 | + * |
91 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
92 | + */ |
93 | + |
94 | +#include <stdlib.h> |
95 | + |
96 | +#include "epicsStackTracePvt.h" |
97 | +#include "epicsThread.h" |
98 | +#include "epicsMutex.h" |
99 | +#include "errlog.h" |
100 | + |
101 | +#define epicsExportSharedSymbols |
102 | +#include "epicsStackTrace.h" |
103 | + |
104 | +/* How many stack frames to capture */ |
105 | +#define MAXDEPTH 100 |
106 | + |
107 | +static epicsThreadOnceId stackTraceInitId = EPICS_THREAD_ONCE_INIT; |
108 | +static epicsMutexId stackTraceMtx; |
109 | + |
110 | +static void stackTraceInit(void *unused) |
111 | +{ |
112 | + stackTraceMtx = epicsMutexMustCreate(); |
113 | +} |
114 | + |
115 | +static void stackTraceLock(void) |
116 | +{ |
117 | + epicsThreadOnce( &stackTraceInitId, stackTraceInit, 0 ); |
118 | + epicsMutexLock( stackTraceMtx ); |
119 | +} |
120 | + |
121 | +static void stackTraceUnlock(void) |
122 | +{ |
123 | + epicsMutexUnlock( stackTraceMtx ); |
124 | +} |
125 | + |
126 | +static int |
127 | +dumpInfo(void *addr, epicsSymbol *sym_p) |
128 | +{ |
129 | +int rval = 0; |
130 | + |
131 | + rval += errlogPrintf("[%*p]", (int)(sizeof(addr)*2 + 2), addr); |
132 | + if ( sym_p ) { |
133 | + if ( sym_p->f_nam ) { |
134 | + rval += errlogPrintf(": %s", sym_p->f_nam); |
135 | + } |
136 | + if ( sym_p->s_nam ) { |
137 | + rval += errlogPrintf("(%s+0x%lx)", sym_p->s_nam, (unsigned long)((char*)addr - (char*)sym_p->s_val)); |
138 | + } else { |
139 | + rval += errlogPrintf("(<no symbol information>)"); |
140 | + } |
141 | + } |
142 | + rval += errlogPrintf("\n"); |
143 | + errlogFlush(); |
144 | + |
145 | + return rval; |
146 | +} |
147 | + |
148 | +void epicsStackTrace(void) |
149 | +{ |
150 | +void **buf; |
151 | +int i,n; |
152 | +epicsSymbol sym; |
153 | + |
154 | + if ( 0 == epicsStackTraceGetFeatures() ) { |
155 | + /* unsupported on this platform */ |
156 | + return; |
157 | + } |
158 | + |
159 | + if ( ! (buf = malloc(sizeof(*buf) * MAXDEPTH))) { |
160 | + free(buf); |
161 | + errlogPrintf("epicsStackTrace(): not enough memory for backtrace\n"); |
162 | + return; |
163 | + } |
164 | + |
165 | + n = epicsBackTrace(buf, MAXDEPTH); |
166 | + |
167 | + if ( n > 0 ) { |
168 | + |
169 | + stackTraceLock(); |
170 | + |
171 | + errlogPrintf("Dumping a stack trace of thread '%s':\n", epicsThreadGetNameSelf()); |
172 | + |
173 | + errlogFlush(); |
174 | + |
175 | + for ( i=0; i<n; i++ ) { |
176 | + if ( 0 == epicsFindAddr(buf[i], &sym) ) |
177 | + dumpInfo(buf[i], &sym); |
178 | + else |
179 | + dumpInfo(buf[i], 0); |
180 | + } |
181 | + |
182 | + errlogFlush(); |
183 | + |
184 | + stackTraceUnlock(); |
185 | + |
186 | + } |
187 | + |
188 | + free(buf); |
189 | +} |
190 | + |
191 | +int epicsStackTraceGetFeatures() |
192 | +{ |
193 | +void *test[2]; |
194 | + |
195 | +static int initflag = 10; /* init to a value larger than the test snapshot */ |
196 | + |
197 | + /* don't bother about epicsOnce -- if there should be a race and |
198 | + * the detection code is executed multiple times that is no big deal. |
199 | + */ |
200 | + if ( 10 == initflag ) { |
201 | + initflag = epicsBackTrace(test, sizeof(test)/sizeof(test[0])); |
202 | + } |
203 | + |
204 | + if ( initflag <= 0 ) |
205 | + return 0; /* backtrace doesn't work or is not supported */ |
206 | + |
207 | + return ( EPICS_STACKTRACE_ADDRESSES | epicsFindAddrGetFeatures() ); |
208 | +} |
209 | |
210 | === added file 'src/libCom/osi/epicsStackTrace.h' |
211 | --- src/libCom/osi/epicsStackTrace.h 1970-01-01 00:00:00 +0000 |
212 | +++ src/libCom/osi/epicsStackTrace.h 2014-09-19 17:20:51 +0000 |
213 | @@ -0,0 +1,43 @@ |
214 | +/* |
215 | + * Copyright: Stanford University / SLAC National Laboratory. |
216 | + * |
217 | + * EPICS BASE is distributed subject to a Software License Agreement found |
218 | + * in file LICENSE that is included with this distribution. |
219 | + * |
220 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
221 | + */ |
222 | + |
223 | +#ifndef INC_epicsStackTrace_H |
224 | +#define INC_epicsStackTrace_H |
225 | + |
226 | +#include "shareLib.h" |
227 | + |
228 | +#ifdef __cplusplus |
229 | +extern "C" { |
230 | +#endif |
231 | + |
232 | +/* Dump a stack trace to the errlog */ |
233 | +epicsShareFunc void epicsStackTrace(void); |
234 | + |
235 | +/* Inquire about functionality implemented on your system */ |
236 | + |
237 | +/* StackTrace provides numerical addresses */ |
238 | +#define EPICS_STACKTRACE_ADDRESSES (1<<0) |
239 | + |
240 | +/* StackTrace is able to lookup dynamic symbols */ |
241 | +#define EPICS_STACKTRACE_DYN_SYMBOLS (1<<1) |
242 | + |
243 | +/* StackTrace is able to lookup global symbols */ |
244 | +#define EPICS_STACKTRACE_GBL_SYMBOLS (1<<2) |
245 | + |
246 | +/* StackTrace is able to lookup local symbols */ |
247 | +#define EPICS_STACKTRACE_LCL_SYMBOLS (1<<3) |
248 | + |
249 | +/* returns ORed bitset of supported features */ |
250 | +epicsShareFunc int epicsStackTraceGetFeatures(void); |
251 | + |
252 | +#ifdef __cplusplus |
253 | +} |
254 | +#endif |
255 | + |
256 | +#endif |
257 | |
258 | === added file 'src/libCom/osi/epicsStackTracePvt.h' |
259 | --- src/libCom/osi/epicsStackTracePvt.h 1970-01-01 00:00:00 +0000 |
260 | +++ src/libCom/osi/epicsStackTracePvt.h 2014-09-19 17:20:51 +0000 |
261 | @@ -0,0 +1,48 @@ |
262 | +/* |
263 | + * Copyright: Stanford University / SLAC National Laboratory. |
264 | + * |
265 | + * EPICS BASE is distributed subject to a Software License Agreement found |
266 | + * in file LICENSE that is included with this distribution. |
267 | + * |
268 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
269 | + */ |
270 | + |
271 | +#ifndef INC_epicsStackTracePvt_H |
272 | +#define INC_epicsStackTracePvt_H |
273 | + |
274 | +#include "shareLib.h" |
275 | + |
276 | +typedef struct epicsSymbol { |
277 | + const char *f_nam; /* file where the symbol is defined */ |
278 | + const char *s_nam; /* symbol name */ |
279 | + void *s_val; /* symbol value */ |
280 | +} epicsSymbol; |
281 | + |
282 | +#ifdef __cplusplus |
283 | +extern "C" { |
284 | +#endif |
285 | + |
286 | +/* Take a snapshot of the stack into 'buf' (limited to buf_sz entries) |
287 | + * RETURNS: actual number of entries in 'buf' |
288 | + */ |
289 | +epicsShareFunc int epicsBackTrace(void **buf, int buf_sz); |
290 | + |
291 | +/* Find symbol closest to 'addr'. |
292 | + * |
293 | + * If successful the routine fills in the members of *sym_p but |
294 | + * note that 'f_nam' and/or 's_nam' may be NULL if the address |
295 | + * cannot be resolved. |
296 | + * |
297 | + * RETURNS: 0 on success, nonzero on failure (not finding an address |
298 | + * is not considered an error). |
299 | + */ |
300 | +epicsShareFunc int epicsFindAddr(void *addr, epicsSymbol *sym_p); |
301 | + |
302 | +/* report supported features (as reported by epicsStackTraceGetFeatures) */ |
303 | +epicsShareFunc int epicsFindAddrGetFeatures(); |
304 | + |
305 | +#ifdef __cplusplus |
306 | +} |
307 | +#endif |
308 | + |
309 | +#endif |
310 | |
311 | === added file 'src/libCom/osi/os/Darwin/osdBackTrace.cpp' |
312 | --- src/libCom/osi/os/Darwin/osdBackTrace.cpp 1970-01-01 00:00:00 +0000 |
313 | +++ src/libCom/osi/os/Darwin/osdBackTrace.cpp 2014-09-19 17:20:51 +0000 |
314 | @@ -0,0 +1,10 @@ |
315 | +/* |
316 | + * Copyright: Stanford University / SLAC National Laboratory. |
317 | + * |
318 | + * EPICS BASE is distributed subject to a Software License Agreement found |
319 | + * in file LICENSE that is included with this distribution. |
320 | + * |
321 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
322 | + */ |
323 | + |
324 | +#include "osdExecinfoBackTrace.cpp" |
325 | |
326 | === added file 'src/libCom/osi/os/Darwin/osdFindAddr.cpp' |
327 | --- src/libCom/osi/os/Darwin/osdFindAddr.cpp 1970-01-01 00:00:00 +0000 |
328 | +++ src/libCom/osi/os/Darwin/osdFindAddr.cpp 2014-09-19 17:20:51 +0000 |
329 | @@ -0,0 +1,43 @@ |
330 | +/* |
331 | + * Copyright: Stanford University / SLAC National Laboratory. |
332 | + * |
333 | + * EPICS BASE is distributed subject to a Software License Agreement found |
334 | + * in file LICENSE that is included with this distribution. |
335 | + * |
336 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
337 | + */ |
338 | + |
339 | +/* Make sure dladdr() is visible */ |
340 | +#define _DARWIN_C_SOURCE |
341 | + |
342 | +#include <dlfcn.h> |
343 | + |
344 | +#define epicsExportSharedSymbols |
345 | +#include "epicsStackTrace.h" |
346 | +#include "epicsStackTracePvt.h" |
347 | + |
348 | +/* Darwin's finds local symbols, too :-) */ |
349 | + |
350 | +int epicsFindAddr(void *addr, epicsSymbol *sym_p) |
351 | +{ |
352 | +Dl_info inf; |
353 | + |
354 | + if ( ! dladdr(addr, &inf) ) { |
355 | + sym_p->f_nam = 0; |
356 | + sym_p->s_nam = 0; |
357 | + sym_p->s_val = 0; |
358 | + } else { |
359 | + sym_p->f_nam = inf.dli_fname; |
360 | + sym_p->s_nam = inf.dli_sname; |
361 | + sym_p->s_val = inf.dli_saddr; |
362 | + } |
363 | + |
364 | + return 0; |
365 | +} |
366 | + |
367 | +int epicsFindAddrGetFeatures(void) |
368 | +{ |
369 | + return EPICS_STACKTRACE_LCL_SYMBOLS |
370 | + | EPICS_STACKTRACE_GBL_SYMBOLS |
371 | + | EPICS_STACKTRACE_DYN_SYMBOLS; |
372 | +} |
373 | |
374 | === added file 'src/libCom/osi/os/Linux/osdBackTrace.cpp' |
375 | --- src/libCom/osi/os/Linux/osdBackTrace.cpp 1970-01-01 00:00:00 +0000 |
376 | +++ src/libCom/osi/os/Linux/osdBackTrace.cpp 2014-09-19 17:20:51 +0000 |
377 | @@ -0,0 +1,10 @@ |
378 | +/* |
379 | + * Copyright: Stanford University / SLAC National Laboratory. |
380 | + * |
381 | + * EPICS BASE is distributed subject to a Software License Agreement found |
382 | + * in file LICENSE that is included with this distribution. |
383 | + * |
384 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
385 | + */ |
386 | + |
387 | +#include "osdExecinfoBackTrace.cpp" |
388 | |
389 | === added file 'src/libCom/osi/os/Linux/osdFindAddr.cpp' |
390 | --- src/libCom/osi/os/Linux/osdFindAddr.cpp 1970-01-01 00:00:00 +0000 |
391 | +++ src/libCom/osi/os/Linux/osdFindAddr.cpp 2014-09-19 17:20:51 +0000 |
392 | @@ -0,0 +1,10 @@ |
393 | +/* |
394 | + * Copyright: Stanford University / SLAC National Laboratory. |
395 | + * |
396 | + * EPICS BASE is distributed subject to a Software License Agreement found |
397 | + * in file LICENSE that is included with this distribution. |
398 | + * |
399 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
400 | + */ |
401 | + |
402 | +#include "osdElfFindAddr.cpp" |
403 | |
404 | === added file 'src/libCom/osi/os/WIN32/osdBackTrace.cpp' |
405 | --- src/libCom/osi/os/WIN32/osdBackTrace.cpp 1970-01-01 00:00:00 +0000 |
406 | +++ src/libCom/osi/os/WIN32/osdBackTrace.cpp 2014-09-19 17:20:51 +0000 |
407 | @@ -0,0 +1,23 @@ |
408 | +/* |
409 | + * Copyright: Stanford University / SLAC National Laboratory. |
410 | + * |
411 | + * EPICS BASE is distributed subject to a Software License Agreement found |
412 | + * in file LICENSE that is included with this distribution. |
413 | + * |
414 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2014 |
415 | + */ |
416 | + |
417 | +#include <Windows.h> |
418 | + |
419 | +#define epicsExportSharedSymbols |
420 | +#include "epicsStackTracePvt.h" |
421 | + |
422 | +int epicsBackTrace(void **buf, int buf_sz) |
423 | +{ |
424 | + /* Docs say that (for some windows versions) the sum of |
425 | + * skipped + captured frames must be less than 63 |
426 | + */ |
427 | + if ( buf_sz >= 63 ) |
428 | + buf_sz = 62; |
429 | + return CaptureStackBackTrace(0, buf_sz, buf, 0); |
430 | +} |
431 | |
432 | === modified file 'src/libCom/osi/os/default/osdAssert.c' |
433 | --- src/libCom/osi/os/default/osdAssert.c 2010-10-05 19:27:37 +0000 |
434 | +++ src/libCom/osi/os/default/osdAssert.c 2014-09-19 17:20:51 +0000 |
435 | @@ -20,6 +20,7 @@ |
436 | #include "epicsThread.h" |
437 | #include "epicsTime.h" |
438 | #include "cantProceed.h" |
439 | +#include "epicsStackTrace.h" |
440 | |
441 | |
442 | void epicsAssert (const char *pFile, const unsigned line, |
443 | @@ -31,6 +32,9 @@ |
444 | "A call to 'assert(%s)'\n" |
445 | " by thread '%s' failed in %s line %u.\n", |
446 | pExp, epicsThreadGetNameSelf(), pFile, line); |
447 | + |
448 | + epicsStackTrace(); |
449 | + |
450 | errlogPrintf("EPICS Release %s.\n", epicsReleaseVersion); |
451 | |
452 | if (epicsTimeGetCurrent(¤t) == 0) { |
453 | |
454 | === added file 'src/libCom/osi/os/default/osdBackTrace.cpp' |
455 | --- src/libCom/osi/os/default/osdBackTrace.cpp 1970-01-01 00:00:00 +0000 |
456 | +++ src/libCom/osi/os/default/osdBackTrace.cpp 2014-09-19 17:20:51 +0000 |
457 | @@ -0,0 +1,16 @@ |
458 | +/* |
459 | + * Copyright: Stanford University / SLAC National Laboratory. |
460 | + * |
461 | + * EPICS BASE is distributed subject to a Software License Agreement found |
462 | + * in file LICENSE that is included with this distribution. |
463 | + * |
464 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
465 | + */ |
466 | + |
467 | +#define epicsExportSharedSymbols |
468 | +#include "epicsStackTracePvt.h" |
469 | + |
470 | +int epicsBackTrace(void **buf, int buf_sz) |
471 | +{ |
472 | + return -1; |
473 | +} |
474 | |
475 | === added file 'src/libCom/osi/os/default/osdFindAddr.cpp' |
476 | --- src/libCom/osi/os/default/osdFindAddr.cpp 1970-01-01 00:00:00 +0000 |
477 | +++ src/libCom/osi/os/default/osdFindAddr.cpp 2014-09-19 17:20:51 +0000 |
478 | @@ -0,0 +1,25 @@ |
479 | +/* |
480 | + * Copyright: Stanford University / SLAC National Laboratory. |
481 | + * |
482 | + * EPICS BASE is distributed subject to a Software License Agreement found |
483 | + * in file LICENSE that is included with this distribution. |
484 | + * |
485 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
486 | + */ |
487 | + |
488 | +#define epicsExportSharedSymbols |
489 | +#include "epicsStackTracePvt.h" |
490 | +#include "epicsStackTrace.h" |
491 | + |
492 | +int epicsFindAddr(void *addr, epicsSymbol *sym_p) |
493 | +{ |
494 | + sym_p->f_nam = 0; |
495 | + sym_p->s_nam = 0; |
496 | + sym_p->s_val = 0; |
497 | + return -1; |
498 | +} |
499 | + |
500 | +int epicsFindAddrGetFeatures(void) |
501 | +{ |
502 | + return 0; |
503 | +} |
504 | |
505 | === added file 'src/libCom/osi/os/posix/osdElfFindAddr.cpp' |
506 | --- src/libCom/osi/os/posix/osdElfFindAddr.cpp 1970-01-01 00:00:00 +0000 |
507 | +++ src/libCom/osi/os/posix/osdElfFindAddr.cpp 2014-09-19 17:20:51 +0000 |
508 | @@ -0,0 +1,664 @@ |
509 | +/* |
510 | + * Copyright: Stanford University / SLAC National Laboratory. |
511 | + * |
512 | + * EPICS BASE is distributed subject to a Software License Agreement found |
513 | + * in file LICENSE that is included with this distribution. |
514 | + * |
515 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
516 | + */ |
517 | + |
518 | +/* Make sure dladdr() is visible on linux/solaris */ |
519 | +#ifndef _GNU_SOURCE |
520 | +#define _GNU_SOURCE |
521 | +#endif |
522 | +/* get dladdr under solaris */ |
523 | +#ifndef __EXTENSIONS__ |
524 | +#define __EXTENSIONS__ |
525 | +#endif |
526 | + |
527 | +#include <unistd.h> |
528 | +#include <dlfcn.h> |
529 | +#include <stdlib.h> |
530 | +#include <fcntl.h> |
531 | +#include <string.h> |
532 | +#include <elf.h> |
533 | +#include <errno.h> |
534 | +#include <inttypes.h> |
535 | +#include <sys/stat.h> |
536 | +#include <sys/types.h> |
537 | +#include <time.h> |
538 | + |
539 | +#ifdef _POSIX_MAPPED_FILES |
540 | +#include <sys/mman.h> |
541 | +#endif |
542 | + |
543 | +#include "epicsMutex.h" |
544 | +#include "epicsThread.h" |
545 | +#include <errlog.h> |
546 | + |
547 | +#define epicsExportSharedSymbols |
548 | +#include "epicsStackTrace.h" |
549 | +#include "epicsStackTracePvt.h" |
550 | + |
551 | +#define FIND_ADDR_DEBUG 0 |
552 | + |
553 | +/* |
554 | + * On some systems (linux, solaris) dladdr doesn't find local symbols |
555 | + * or symbols in the main executable. |
556 | + * Hence, we want to use dladdr() to find the file name |
557 | + * where a symbol is defined and if not more information is available |
558 | + * then proceed to lookup symbols in the ELF symbol tables. |
559 | + */ |
560 | + |
561 | +/* Macros to handle elf32 vs. elf64 access to unions etc. */ |
562 | + |
563 | +#define FLD(c,s,f) (ELFCLASS32==c ? s.e32.f : s.e64.f ) |
564 | +#define ARR(c,s,i,f) (ELFCLASS32==c ? s.e32[i].f : s.e64[i].f) |
565 | + |
566 | +/* Elf header */ |
567 | +typedef union Ehdr_ { |
568 | + Elf32_Ehdr e32; |
569 | + Elf64_Ehdr e64; |
570 | +} Ehdr; |
571 | + |
572 | +/* Section header */ |
573 | +typedef union Shdr_ { |
574 | + Elf32_Shdr e32; |
575 | + Elf64_Shdr e64; |
576 | +} Shdr; |
577 | + |
578 | +/* Elf symbol */ |
579 | +typedef union Sym_ { |
580 | + void *raw; |
581 | + Elf32_Sym *e32; |
582 | + Elf64_Sym *e64; |
583 | +} Sym; |
584 | + |
585 | +/* Memory mapped portion of a file; we must |
586 | + * keep additional information because the |
587 | + * map's starting address + length must be |
588 | + * page-aligned (man mmap). |
589 | + */ |
590 | +typedef struct MMap_ { |
591 | + void *addr; |
592 | + off_t off; /* offset into the map where 'real' data start */ |
593 | + size_t len; |
594 | + size_t max; /* max offset: legal data from addr+off .. addr+off+max-1 */ |
595 | + void (*freeMap)(struct MMap_*); /* 'method' to destroy the mapping */ |
596 | +} *MMap; |
597 | + |
598 | +/* Structure describing symbol information |
599 | + * contained in a file. |
600 | + * We keep these around (so that the file |
601 | + * doesn't have to be opened + parsed every |
602 | + * time we do a lookup). |
603 | + */ |
604 | +typedef struct ESyms_ { |
605 | + struct ESyms_ *next; /* linked list; one struct per executable */ |
606 | + const char *fname; /* file name */ |
607 | + int fd; /* file descriptor */ |
608 | + uintptr_t addr; /* address where file is loaded */ |
609 | + MMap symMap; |
610 | + MMap strMap; |
611 | + size_t nsyms; |
612 | + uint8_t eclss; |
613 | +} *ESyms; |
614 | + |
615 | +/* LOCKING NOTE: if the ELF symbol facility is ever expanded to be truly used |
616 | + * in a multithreaded way then proper multiple-readers, single-writer locking |
617 | + * should be implemented. |
618 | + */ |
619 | + |
620 | +/* Linked list where we keep all our ESyms */ |
621 | +static ESyms elfs = 0; |
622 | + |
623 | +static epicsMutexId listMtx; |
624 | +static epicsThreadOnceId listMtxInitId = EPICS_THREAD_ONCE_INIT; |
625 | + |
626 | +static time_t prog_start_time = time(0); |
627 | + |
628 | +extern "C" { |
629 | + |
630 | +static void listMtxInit(void *unused) |
631 | +{ |
632 | + listMtx = epicsMutexMustCreate(); |
633 | +} |
634 | + |
635 | +} |
636 | + |
637 | +static void |
638 | +elfsLockWrite() |
639 | +{ |
640 | + epicsThreadOnce(&listMtxInitId, listMtxInit, 0); |
641 | + epicsMutexMustLock(listMtx); |
642 | +} |
643 | + |
644 | +static void |
645 | +elfsUnlockWrite() |
646 | +{ |
647 | + epicsMutexUnlock(listMtx); |
648 | +} |
649 | + |
650 | +static void |
651 | +freeMap(MMap m) |
652 | +{ |
653 | + if ( m ) { |
654 | + m->freeMap(m); |
655 | + free(m); |
656 | + } |
657 | +} |
658 | + |
659 | +/* Helper to read exactly 'sz' bytes into 'buf' |
660 | + * RETURNS: # chars read or negative value on error. |
661 | + */ |
662 | +static ssize_t |
663 | +do_read(int fd, void *buf, ssize_t sz) |
664 | +{ |
665 | +ssize_t got; |
666 | +char *ptr=(char*)buf; |
667 | + while ( sz > 0 ) { |
668 | + if ( (got=read(fd,ptr,sz)) <= 0 ) { |
669 | + return got; |
670 | + } |
671 | + ptr+=got; |
672 | + sz -=got; |
673 | + } |
674 | + return ptr-(char*)buf; |
675 | +} |
676 | + |
677 | +/* Elf file access -- can either be with mmap or by sequential read */ |
678 | + |
679 | +#ifdef _POSIX_MAPPED_FILES |
680 | +/* Destructor for data that is mmap()ed */ |
681 | +static void |
682 | +freeMapMmap(MMap m) |
683 | +{ |
684 | + if ( MAP_FAILED != m->addr ) |
685 | + munmap( m->addr, m->len ); |
686 | +} |
687 | + |
688 | +/* Obtain section data with mmap() */ |
689 | +static MMap |
690 | +getscn_mmap(int fd, uint8_t c, Shdr *shdr_p) |
691 | +{ |
692 | +off_t n; |
693 | +MMap rval = 0; |
694 | +size_t pgsz = sysconf(_SC_PAGESIZE); |
695 | + |
696 | + if ( 0 == (n = (off_t)FLD(c,(*shdr_p),sh_size)) ) { |
697 | + errlogPrintf("elfRead - getscn() -- no section data\n"); |
698 | + goto bail; |
699 | + } |
700 | + |
701 | + if ( ! (rval = (MMap) malloc(sizeof(*rval))) ) { |
702 | + errlogPrintf("elfRead - getscn() -- no memory for section map\n"); |
703 | + goto bail; |
704 | + } |
705 | + |
706 | + rval->freeMap = freeMapMmap; |
707 | + |
708 | + rval->off = (off_t) (FLD(c,(*shdr_p),sh_offset) & (pgsz-1)); |
709 | + rval->len = (n + rval->off + (pgsz - 1)) & ~(pgsz - 1); |
710 | + rval->max = rval->len - rval->off; |
711 | + |
712 | + if ( MAP_FAILED == (rval->addr = mmap(0, rval->len, PROT_READ, MAP_SHARED, fd, (off_t) (FLD(c,(*shdr_p),sh_offset) & ~(pgsz-1)))) ) { |
713 | + errlogPrintf("elfRead - getscn() -- mapping section contents: %s\n", strerror(errno)); |
714 | + goto bail; |
715 | + } |
716 | + |
717 | + return rval; |
718 | + |
719 | +bail: |
720 | + freeMap(rval); |
721 | + return 0; |
722 | +} |
723 | +#else |
724 | +static MMap getscn_mmap(int fd, uint8_t c, Shrd *shdr_p) |
725 | +{ |
726 | + return 0; |
727 | +} |
728 | +#endif |
729 | + |
730 | +/* Destructor for data that is read into a malloc()ed buffer */ |
731 | +static void |
732 | +freeMapMalloc(MMap m) |
733 | +{ |
734 | + free(m->addr); |
735 | +} |
736 | + |
737 | +/* Read section data into a malloc()ed buffer */ |
738 | +static MMap |
739 | +getscn_read(int fd, uint8_t c, Shdr *shdr_p) |
740 | +{ |
741 | +ssize_t n; |
742 | +MMap rval = 0; |
743 | + |
744 | + if ( 0 == (n = (ssize_t) FLD(c,(*shdr_p),sh_size)) ) { |
745 | + errlogPrintf("elfRead - getscn() -- no section data\n"); |
746 | + goto bail; |
747 | + } |
748 | + |
749 | + if ( ! (rval = (MMap) malloc(sizeof(*rval))) ) { |
750 | + errlogPrintf("elfRead - getscn() -- no memory for section map\n"); |
751 | + goto bail; |
752 | + } |
753 | + |
754 | + rval->freeMap = freeMapMalloc; |
755 | + |
756 | + if ( ! (rval->addr = malloc(n)) ) { |
757 | + errlogPrintf("elfRead - getscn() -- no memory for section data\n"); |
758 | + goto bail; |
759 | + } |
760 | + |
761 | + rval->off = 0; |
762 | + rval->len = n; |
763 | + rval->max = rval->len - rval->off; |
764 | + |
765 | + /* seek to symbol table contents */ |
766 | + if ( (off_t)-1 == lseek(fd, (off_t) FLD(c,(*shdr_p),sh_offset), SEEK_SET) ) { |
767 | + errlogPrintf("elfRead - getscn() -- seeking to sh_offset: %s\n", strerror(errno)); |
768 | + goto bail; |
769 | + } |
770 | + |
771 | + if ( n != do_read(fd, rval->addr, n) ) { |
772 | + errlogPrintf("elfRead - getscn() -- reading section contents: %s\n", strerror(errno)); |
773 | + goto bail; |
774 | + } |
775 | + |
776 | + return rval; |
777 | + |
778 | +bail: |
779 | + freeMap(rval); |
780 | + return 0; |
781 | +} |
782 | + |
783 | +static MMap |
784 | +getscn(int fd, uint8_t c, Shdr *shdr_p) |
785 | +{ |
786 | +MMap rval = getscn_mmap(fd, c, shdr_p); |
787 | + |
788 | + if ( ! rval ) |
789 | + rval = getscn_read(fd, c, shdr_p); |
790 | + |
791 | + return rval; |
792 | +} |
793 | + |
794 | +/* Release resources but keep filename so that |
795 | + * a file w/o symbol table is not read over and over again. |
796 | + */ |
797 | +static void |
798 | +elfSymsRelease(ESyms es) |
799 | +{ |
800 | + if ( es ) { |
801 | + freeMap(es->symMap); |
802 | + es->symMap = 0; |
803 | + freeMap(es->strMap); |
804 | + es->strMap = 0; |
805 | + if ( es->fd >= 0 ) |
806 | + close(es->fd); |
807 | + es->fd = -1; |
808 | + es->nsyms = 0; |
809 | + } |
810 | +} |
811 | + |
812 | +static ESyms |
813 | +elfRead(const char *fname, uintptr_t fbase) |
814 | +{ |
815 | +int i; |
816 | +Ehdr ehdr; |
817 | +Shdr shdr; |
818 | +uint8_t c; |
819 | +ESyms es; |
820 | +ssize_t idx,n; |
821 | +const char *cp; |
822 | +struct stat stat_b; |
823 | + |
824 | + if ( !(es = (ESyms) malloc(sizeof(*es))) ) { |
825 | + /* no memory -- give up */ |
826 | + return 0; |
827 | + } |
828 | + |
829 | + memset(es, 0, sizeof(*es)); |
830 | + es->fd = -1; |
831 | + es->fname = fname; |
832 | + |
833 | + if ( (es->fd = open(fname, O_RDONLY)) < 0 ) { |
834 | + errlogPrintf("elfRead() -- unable to open file: %s\n", strerror(errno)); |
835 | + goto bail; |
836 | + } |
837 | + |
838 | + if ( EI_NIDENT != do_read(es->fd, &ehdr, EI_NIDENT) ) { |
839 | + errlogPrintf("elfRead() -- unable to read ELF e_ident: %s\n", strerror(errno)); |
840 | + goto bail; |
841 | + } |
842 | + |
843 | + if ( ELFMAG0 != ehdr.e32.e_ident[EI_MAG0] |
844 | + || ELFMAG1 != ehdr.e32.e_ident[EI_MAG1] |
845 | + || ELFMAG2 != ehdr.e32.e_ident[EI_MAG2] |
846 | + || ELFMAG3 != ehdr.e32.e_ident[EI_MAG3] ) { |
847 | + errlogPrintf("bad ELF magic number\n"); |
848 | + goto bail; |
849 | + } |
850 | + |
851 | + if ( EV_CURRENT != ehdr.e32.e_ident[EI_VERSION] ) { |
852 | + errlogPrintf("bad ELF version\n"); |
853 | + goto bail; |
854 | + } |
855 | + |
856 | + switch ( (es->eclss = c = ehdr.e32.e_ident[EI_CLASS]) ) { |
857 | + default: |
858 | + errlogPrintf("bad ELF class\n"); |
859 | + goto bail; |
860 | + |
861 | + case ELFCLASS32: |
862 | + n = sizeof(Elf32_Ehdr); |
863 | + break; |
864 | + case ELFCLASS64: |
865 | + n = sizeof(Elf64_Ehdr); |
866 | + break; |
867 | + } |
868 | + n -= EI_NIDENT; |
869 | + |
870 | + if ( 0 == fstat(es->fd, &stat_b) ) { |
871 | + if ( stat_b.st_mtime >= prog_start_time ) { |
872 | + errlogPrintf("elfRead() -- WARNING: '%s' was modified after program start -- symbol information may be inaccurate or invalid\n", fname); |
873 | + } |
874 | + } |
875 | + |
876 | + /* read rest */ |
877 | + if ( n != do_read(es->fd, ehdr.e32.e_ident + EI_NIDENT, n) ) { |
878 | + errlogPrintf("elfRead() -- unable to read ELF ehdr: %s\n", strerror(errno)); |
879 | + goto bail; |
880 | + } |
881 | + |
882 | + /* seek to section header table */ |
883 | + if ( (off_t)-1 == lseek(es->fd, (off_t) FLD(c,ehdr,e_shoff), SEEK_SET) ) { |
884 | + errlogPrintf("elfRead() -- unable to seek to shoff: %s\n", strerror(errno)); |
885 | + goto bail; |
886 | + } |
887 | + |
888 | + n = ELFCLASS32 == c ? sizeof(shdr.e32) : sizeof(shdr.e64); |
889 | + |
890 | + for ( i = 0; i<FLD(c,ehdr,e_shnum); i++ ) { |
891 | + if ( n != do_read(es->fd, &shdr, n) ) { |
892 | + errlogPrintf("elfRead() -- unable to read section header: %s\n", strerror(errno)); |
893 | + goto bail; |
894 | + } |
895 | + if ( SHT_SYMTAB == FLD(c,shdr,sh_type) ) |
896 | + break; |
897 | + } |
898 | + |
899 | + if ( i>=FLD(c,ehdr,e_shnum) ) { |
900 | + /* no SYMTAB -- try dynamic symbols */ |
901 | + |
902 | + if ( (off_t)-1 == lseek(es->fd, (off_t) FLD(c,ehdr,e_shoff), SEEK_SET) ) { |
903 | + errlogPrintf("elfRead() -- unable to seek to shoff: %s\n", strerror(errno)); |
904 | + goto bail; |
905 | + } |
906 | + |
907 | + for ( i = 0; i<FLD(c,ehdr,e_shnum); i++ ) { |
908 | + if ( n != do_read(es->fd, &shdr, n) ) { |
909 | + errlogPrintf("elfRead() -- unable to read section header: %s\n", strerror(errno)); |
910 | + goto bail; |
911 | + } |
912 | + if ( SHT_DYNSYM == FLD(c,shdr,sh_type) ) |
913 | + break; |
914 | + } |
915 | + } |
916 | + |
917 | + if ( i>=FLD(c,ehdr,e_shnum) ) { |
918 | + errlogPrintf("elfRead() -- no symbol table found\n"); |
919 | + goto bail; |
920 | + } |
921 | + |
922 | + if ( 0 == (n = (ssize_t) FLD(c,shdr,sh_size)) ) { |
923 | + errlogPrintf("elfRead() -- no symbol table data\n"); |
924 | + goto bail; |
925 | + } |
926 | + |
927 | + if ( !(es->symMap = getscn(es->fd, c, &shdr)) ) { |
928 | + errlogPrintf("elfRead() -- unable to read ELF symtab\n"); |
929 | + goto bail; |
930 | + } |
931 | + |
932 | + es->nsyms = n / (ELFCLASS32==c ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym)); |
933 | + |
934 | + /* find and read string table */ |
935 | + |
936 | + n = ELFCLASS32 == c ? sizeof(shdr.e32) : sizeof(shdr.e64); |
937 | + |
938 | + /* seek to section header table */ |
939 | + if ( (off_t)-1 == lseek(es->fd, (off_t) (FLD(c,ehdr,e_shoff) + n * FLD(c,shdr,sh_link)), SEEK_SET) ) { |
940 | + errlogPrintf("elfRead() -- unable to lseek to ELF e_shoff: %s\n", strerror(errno)); |
941 | + goto bail; |
942 | + } |
943 | + |
944 | + if ( n != do_read(es->fd, &shdr, n) ) { |
945 | + errlogPrintf("elfRead() -- unable to read ELF strtab section header: %s\n", strerror(errno)); |
946 | + goto bail; |
947 | + } |
948 | + |
949 | + if ( !(es->strMap = getscn(es->fd,c,&shdr)) ) { |
950 | + errlogPrintf("elfRead() -- unable to read ELF strtab\n"); |
951 | + goto bail; |
952 | + } |
953 | + |
954 | + /* Make sure there is a terminating NUL - unfortunately, memrchr is not portable */ |
955 | + cp = (char*)es->strMap->addr + es->strMap->off; |
956 | + for ( idx = es->strMap->max - 1; i >= 0; i-- ) { |
957 | + if ( !cp[i] ) |
958 | + break; |
959 | + } |
960 | + es->strMap->max = idx + 1; |
961 | + |
962 | + switch ( FLD(c,ehdr,e_type) ) { |
963 | + case ET_EXEC: |
964 | + /* Symbols in an executable already has absolute addresses */ |
965 | + es->addr = 0; |
966 | + break; |
967 | + case ET_DYN: |
968 | + /* Symbols in an shared library are relative to base address */ |
969 | + es->addr = fbase; |
970 | + break; |
971 | + default: |
972 | + errlogPrintf("dlLookupAddr(): Unexpected ELF object file type %u\n", FLD(c,ehdr,e_type)); |
973 | + goto bail; |
974 | + } |
975 | + |
976 | + return es; |
977 | + |
978 | +bail: |
979 | + elfSymsRelease(es); |
980 | + return es; |
981 | +} |
982 | + |
983 | +/* Destroy a cached ELF symbol table */ |
984 | +static void |
985 | +elfSymsDestroy(ESyms es) |
986 | +{ |
987 | + if ( es ) { |
988 | + elfSymsRelease(es); |
989 | + free(es); |
990 | + } |
991 | +} |
992 | + |
993 | +/* Destroy all cached ELF symbol tables |
994 | + * |
995 | + * However - w/o proper locking for read access |
996 | + * this must not be used. Otherwise, readers |
997 | + * will hold stale pointers... |
998 | + * |
999 | + * We leave the commented code here to show |
1000 | + * how the tables can be torn down. |
1001 | + |
1002 | +void |
1003 | +elfSymTblFlush() |
1004 | +{ |
1005 | +ESyms es; |
1006 | + |
1007 | + elfsLockWrite(); |
1008 | + while ( (es = elfs) ) { |
1009 | + elfs = es->next; |
1010 | + es->next = 0; |
1011 | + elfsUnlockWrite(); |
1012 | + elfSymsDestroy(es); |
1013 | + elfsLockWrite(); |
1014 | + } |
1015 | + elfsUnlockWrite(); |
1016 | +} |
1017 | + |
1018 | +*/ |
1019 | + |
1020 | + |
1021 | +/* This routine must be called with the write-lock held */ |
1022 | +static ESyms |
1023 | +elfSymsFind(const char *fname) |
1024 | +{ |
1025 | +ESyms es; |
1026 | + for ( es=elfs; es && strcmp(fname, es->fname); es = es->next ) |
1027 | + /* nothing else to do */; |
1028 | + return es; |
1029 | +} |
1030 | + |
1031 | +int |
1032 | +epicsFindAddr(void *addr, epicsSymbol *sym_p) |
1033 | +{ |
1034 | +Dl_info inf; |
1035 | +ESyms es,nes = 0; |
1036 | +uintptr_t minoff,off; |
1037 | +size_t i; |
1038 | +Sym sym; |
1039 | +Sym nearest; |
1040 | +const char *strtab; |
1041 | +uint8_t c; |
1042 | +size_t idx; |
1043 | + |
1044 | + if ( ! dladdr(addr, &inf) || (!inf.dli_fname && !inf.dli_sname) ) { |
1045 | + sym_p->f_nam = 0; |
1046 | + sym_p->s_nam = 0; |
1047 | + /* unable to lookup */ |
1048 | + return 0; |
1049 | + } |
1050 | + |
1051 | + sym_p->f_nam = inf.dli_fname; |
1052 | + |
1053 | + /* If the symbol is in the main executable then solaris' dladdr returns bogus info */ |
1054 | +#ifndef __sun |
1055 | + if ( (sym_p->s_nam = inf.dli_sname) ) { |
1056 | + sym_p->s_val = inf.dli_saddr; |
1057 | + /* Have a symbol name - just use it and be done */ |
1058 | + return 0; |
1059 | + } |
1060 | +#endif |
1061 | + |
1062 | + /* No symbol info; try to access ELF file and ready symbol table from there */ |
1063 | + |
1064 | + elfsLockWrite(); |
1065 | + |
1066 | + /* See if we have loaded this file already */ |
1067 | + es = elfSymsFind(inf.dli_fname); |
1068 | + |
1069 | + if ( !es ) { |
1070 | + |
1071 | + elfsUnlockWrite(); |
1072 | + |
1073 | + if ( ! (nes = elfRead(inf.dli_fname, (uintptr_t)inf.dli_fbase)) ) { |
1074 | + /* this path can only be taken if there is no memory for '*nes' */ |
1075 | + return 0; |
1076 | + } |
1077 | + |
1078 | + elfsLockWrite(); |
1079 | + |
1080 | + /* Has someone else intervened and already added this file while we were reading ? */ |
1081 | + es = elfSymsFind(inf.dli_fname); |
1082 | + |
1083 | + if ( es ) { |
1084 | + /* will undo our work in the unlikely event... */ |
1085 | + } else { |
1086 | + nes->next = elfs; |
1087 | + es = elfs = nes; |
1088 | + nes = 0; |
1089 | + } |
1090 | + } |
1091 | + |
1092 | + elfsUnlockWrite(); |
1093 | + |
1094 | + /* Undo our work in the unlikely event that it was redundant */ |
1095 | + if ( nes ) |
1096 | + elfSymsDestroy( nes ); |
1097 | + |
1098 | + nearest.raw = 0; |
1099 | + minoff = (uintptr_t)-1LL; |
1100 | + |
1101 | + if ( es->nsyms ) { |
1102 | + c = es->eclss; |
1103 | + sym.raw = (char*)es->symMap->addr + es->symMap->off; |
1104 | + strtab = (char*)es->strMap->addr + es->strMap->off; |
1105 | + |
1106 | + /* Do a brute-force search through the symbol table; if this is executed |
1107 | + * very often then it would be worthwhile constructing a sorted list of |
1108 | + * symbol addresses but for the stack trace we don't care... |
1109 | + */ |
1110 | +#if (FIND_ADDR_DEBUG & 1) |
1111 | + printf("Looking for %p\n", addr); |
1112 | +#endif |
1113 | + |
1114 | + if ( ELFCLASS32 == c ) { |
1115 | + for ( i=0; i<es->nsyms; i++ ) { |
1116 | + if ( STT_FUNC != ELF32_ST_TYPE(sym.e32[i].st_info) ) |
1117 | + continue; |
1118 | + /* don't bother about undefined symbols */ |
1119 | + if ( 0 == sym.e32[i].st_shndx ) |
1120 | + continue; |
1121 | +#if (FIND_ADDR_DEBUG & 1) |
1122 | + printf("Trying: %s (0x%lx)\n", strtab + sym.e32[i].st_name, (unsigned long)(sym.e32[i].st_value + es->addr)); |
1123 | +#endif |
1124 | + if ( (uintptr_t)addr >= (uintptr_t)sym.e32[i].st_value + es->addr ) { |
1125 | + off = (uintptr_t)addr - ((uintptr_t)sym.e32[i].st_value + es->addr); |
1126 | + if ( off < minoff ) { |
1127 | + minoff = off; |
1128 | + nearest.e32 = &sym.e32[i]; |
1129 | + } |
1130 | + } |
1131 | + } |
1132 | + } else { |
1133 | + for ( i=0; i<es->nsyms; i++ ) { |
1134 | + if ( STT_FUNC != ELF64_ST_TYPE(sym.e64[i].st_info) ) |
1135 | + continue; |
1136 | + /* don't bother about undefined symbols */ |
1137 | + if ( 0 == sym.e64[i].st_shndx ) |
1138 | + continue; |
1139 | +#if (FIND_ADDR_DEBUG & 1) |
1140 | + printf("Trying: %s (0x%llx)\n", strtab + sym.e64[i].st_name, (unsigned long long)(sym.e64[i].st_value + es->addr)); |
1141 | +#endif |
1142 | + if ( (uintptr_t)addr >= (uintptr_t)sym.e64[i].st_value + es->addr ) { |
1143 | + off = (uintptr_t)addr - ((uintptr_t)sym.e64[i].st_value + es->addr); |
1144 | + if ( off < minoff ) { |
1145 | + minoff = off; |
1146 | + nearest.e64 = &sym.e64[i]; |
1147 | + } |
1148 | + } |
1149 | + } |
1150 | + } |
1151 | + } |
1152 | + |
1153 | + if ( nearest.raw && ( (idx = ARR(c,nearest,0,st_name)) < es->strMap->max ) ) { |
1154 | + sym_p->s_nam = strtab + idx; |
1155 | + sym_p->s_val = (char*) ARR(c, nearest, 0, st_value) + es->addr; |
1156 | + } |
1157 | + |
1158 | + return 0; |
1159 | +} |
1160 | + |
1161 | +int epicsFindAddrGetFeatures(void) |
1162 | +{ |
1163 | + /* The static information given here may not be correct; |
1164 | + * it also depends on |
1165 | + * - compilation (frame pointer optimization) |
1166 | + * - linkage (static vs. dynamic) |
1167 | + * - stripping |
1168 | + */ |
1169 | + return EPICS_STACKTRACE_LCL_SYMBOLS |
1170 | + | EPICS_STACKTRACE_GBL_SYMBOLS |
1171 | + | EPICS_STACKTRACE_DYN_SYMBOLS; |
1172 | +} |
1173 | |
1174 | === added file 'src/libCom/osi/os/posix/osdExecinfoBackTrace.cpp' |
1175 | --- src/libCom/osi/os/posix/osdExecinfoBackTrace.cpp 1970-01-01 00:00:00 +0000 |
1176 | +++ src/libCom/osi/os/posix/osdExecinfoBackTrace.cpp 2014-09-19 17:20:51 +0000 |
1177 | @@ -0,0 +1,18 @@ |
1178 | +/* |
1179 | + * Copyright: Stanford University / SLAC National Laboratory. |
1180 | + * |
1181 | + * EPICS BASE is distributed subject to a Software License Agreement found |
1182 | + * in file LICENSE that is included with this distribution. |
1183 | + * |
1184 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
1185 | + */ |
1186 | + |
1187 | +#include <execinfo.h> |
1188 | + |
1189 | +#define epicsExportSharedSymbols |
1190 | +#include "epicsStackTracePvt.h" |
1191 | + |
1192 | +int epicsBackTrace(void **buf, int buf_sz) |
1193 | +{ |
1194 | + return backtrace(buf, buf_sz); |
1195 | +} |
1196 | |
1197 | === added file 'src/libCom/osi/os/solaris/osdBackTrace.cpp' |
1198 | --- src/libCom/osi/os/solaris/osdBackTrace.cpp 1970-01-01 00:00:00 +0000 |
1199 | +++ src/libCom/osi/os/solaris/osdBackTrace.cpp 2014-09-19 17:20:51 +0000 |
1200 | @@ -0,0 +1,46 @@ |
1201 | +/* |
1202 | + * Copyright: Stanford University / SLAC National Laboratory. |
1203 | + * |
1204 | + * EPICS BASE is distributed subject to a Software License Agreement found |
1205 | + * in file LICENSE that is included with this distribution. |
1206 | + * |
1207 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2014 |
1208 | + */ |
1209 | + |
1210 | +#include <ucontext.h> |
1211 | + |
1212 | +#define epicsExportSharedSymbols |
1213 | +#include "epicsStackTracePvt.h" |
1214 | + |
1215 | +struct wlk { |
1216 | + void **buf; |
1217 | + int max; |
1218 | + int cur; |
1219 | +}; |
1220 | + |
1221 | + |
1222 | +extern "C" { |
1223 | + |
1224 | +static int |
1225 | +walker(uintptr_t addr, int sig, void *arg) |
1226 | +{ |
1227 | +struct wlk *w_p = (struct wlk *)arg; |
1228 | + if ( w_p->cur < w_p->max ) |
1229 | + w_p->buf[w_p->cur++] = (void*)addr; |
1230 | + return 0; |
1231 | +} |
1232 | + |
1233 | +} |
1234 | + |
1235 | +int epicsBackTrace(void **buf, int buf_sz) |
1236 | +{ |
1237 | +ucontext_t u; |
1238 | +struct wlk d; |
1239 | + d.buf = buf; |
1240 | + d.max = buf_sz; |
1241 | + d.cur = 0; |
1242 | + if ( getcontext(&u) ) |
1243 | + return -1; |
1244 | + walkcontext( &u, walker, &d ); |
1245 | + return d.cur; |
1246 | +} |
1247 | |
1248 | === added file 'src/libCom/osi/os/solaris/osdFindAddr.cpp' |
1249 | --- src/libCom/osi/os/solaris/osdFindAddr.cpp 1970-01-01 00:00:00 +0000 |
1250 | +++ src/libCom/osi/os/solaris/osdFindAddr.cpp 2014-09-19 17:20:51 +0000 |
1251 | @@ -0,0 +1,10 @@ |
1252 | +/* |
1253 | + * Copyright: Stanford University / SLAC National Laboratory. |
1254 | + * |
1255 | + * EPICS BASE is distributed subject to a Software License Agreement found |
1256 | + * in file LICENSE that is included with this distribution. |
1257 | + * |
1258 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2011, 2014 |
1259 | + */ |
1260 | + |
1261 | +#include "osdElfFindAddr.cpp" |
1262 | |
1263 | === modified file 'src/libCom/test/Makefile' |
1264 | --- src/libCom/test/Makefile 2014-07-29 21:05:24 +0000 |
1265 | +++ src/libCom/test/Makefile 2014-09-19 17:20:51 +0000 |
1266 | @@ -184,6 +184,11 @@ |
1267 | testHarness_SRCS += epicsMessageQueueTest.cpp |
1268 | TESTS += epicsMessageQueueTest |
1269 | |
1270 | +TESTPROD_HOST += epicsStackTraceTest |
1271 | +epicsStackTraceTest_SRCS += epicsStackTraceTest.c |
1272 | +testHarness_SRCS += epicsStackTraceTest.c |
1273 | +TESTS += epicsStackTraceTest |
1274 | + |
1275 | |
1276 | # The testHarness runs all the test programs in a known working order. |
1277 | testHarness_SRCS += epicsRunLibComTests.c |
1278 | |
1279 | === added file 'src/libCom/test/epicsStackTraceTest.c' |
1280 | --- src/libCom/test/epicsStackTraceTest.c 1970-01-01 00:00:00 +0000 |
1281 | +++ src/libCom/test/epicsStackTraceTest.c 2014-09-19 17:20:51 +0000 |
1282 | @@ -0,0 +1,242 @@ |
1283 | +/* |
1284 | + * Copyright: Stanford University / SLAC National Laboratory. |
1285 | + * |
1286 | + * EPICS BASE is distributed subject to a Software License Agreement found |
1287 | + * in file LICENSE that is included with this distribution. |
1288 | + * |
1289 | + * Author: Till Straumann <strauman@slac.stanford.edu>, 2014 |
1290 | + */ |
1291 | + |
1292 | +/* |
1293 | + * Check stack trace functionality |
1294 | + */ |
1295 | + |
1296 | +#include "epicsStackTrace.h" |
1297 | +#include "errlog.h" |
1298 | +#include "epicsUnitTest.h" |
1299 | +#include "testMain.h" |
1300 | + |
1301 | +#include <string.h> |
1302 | +#include <stdlib.h> |
1303 | +#include <ctype.h> |
1304 | + |
1305 | +#define TST_BUFSZ 10000 |
1306 | + |
1307 | +#define MAXP 10 |
1308 | + |
1309 | +/* estimated size of (compiled) epicsStackTraceRecurseGbl */ |
1310 | +#define WINDOW_SZ 400 |
1311 | + |
1312 | +static int test_debug = 0; |
1313 | + |
1314 | +typedef struct TestDataRec_ { |
1315 | + char buf[TST_BUFSZ]; |
1316 | + int pos; |
1317 | +} TestDataRec, *TestData; |
1318 | + |
1319 | +typedef void (*RecFn)(int); |
1320 | + |
1321 | +/* We want a stack trace and need a few nested routines. |
1322 | + * The whole magic here is intended to prevent a compiler |
1323 | + * from optimizing the call stack away: |
1324 | + * - call via a volatile pointer |
1325 | + * - add a call to a no-op function at the end so that |
1326 | + * tail-call optimization doesn't eliminate the call |
1327 | + * stack. |
1328 | + * |
1329 | + * We use a local (static) and a global routine to test |
1330 | + * if the stacktrace supports either flavor. |
1331 | + */ |
1332 | + |
1333 | +void epicsStackTraceRecurseGbl(int lvl); |
1334 | +static void epicsStackTraceRecurseLcl(int lvl); |
1335 | + |
1336 | +void nopFn(int lvl) |
1337 | +{ |
1338 | +} |
1339 | + |
1340 | +RecFn volatile lfp = epicsStackTraceRecurseLcl; |
1341 | +RecFn volatile gfp = epicsStackTraceRecurseGbl; |
1342 | +RecFn volatile nfp = nopFn; |
1343 | + |
1344 | +static void |
1345 | +epicsStackTraceRecurseLcl(int lvl) |
1346 | +{ |
1347 | + if ( lvl ) |
1348 | + gfp(lvl-1); |
1349 | + else |
1350 | + epicsStackTrace(); |
1351 | + |
1352 | + /* call something so that the call through gfp() doesn't |
1353 | + * get optimized into a jump (tail-call optimization) |
1354 | + */ |
1355 | + nfp(0); |
1356 | +} |
1357 | + |
1358 | +void epicsStackTraceRecurseGbl(int lvl) |
1359 | +{ |
1360 | + if ( lvl ) |
1361 | + lfp(lvl-1); |
1362 | + else |
1363 | + epicsStackTrace(); |
1364 | + |
1365 | + /* call something so that the call through gfp() doesn't |
1366 | + * get optimized into a jump (tail-call optimization) |
1367 | + */ |
1368 | + nfp(0); |
1369 | +} |
1370 | + |
1371 | +static void logClient(void *ptr, const char *msg) |
1372 | +{ |
1373 | +TestData td = ptr; |
1374 | +size_t sz = strlen(msg); |
1375 | +size_t mx = sizeof(td->buf) - td->pos - 1; |
1376 | + |
1377 | + if ( sz > mx ) |
1378 | + sz = mx; |
1379 | + strncpy( td->buf+td->pos, msg, sz ); |
1380 | + td->pos += sz; |
1381 | +} |
1382 | + |
1383 | +static int |
1384 | +findStringOcc(const char *buf, const char *what) |
1385 | +{ |
1386 | +int rval; |
1387 | +size_t l = strlen(what); |
1388 | +int ch; |
1389 | + |
1390 | + rval = 0; |
1391 | + while ( (buf = strstr(buf, what)) ) { |
1392 | + /* Is it just a prefix? */ |
1393 | + ch = buf[l]; |
1394 | + if ( ! isalnum(ch) && '_' != ch ) { |
1395 | + rval++; |
1396 | + } |
1397 | + buf += l; |
1398 | + } |
1399 | + |
1400 | + if ( test_debug ) |
1401 | + testDiag("found %i x %s\n", rval, what); |
1402 | + |
1403 | + return rval; |
1404 | +} |
1405 | + |
1406 | +static int |
1407 | +findNumOcc(const char *buf) |
1408 | +{ |
1409 | +void *ptrs[MAXP]; |
1410 | +int n_ptrs = 0; |
1411 | +int i,j; |
1412 | +int rval = 0; |
1413 | + |
1414 | + while ( n_ptrs < sizeof(ptrs)/sizeof(ptrs[0]) && (buf=strchr(buf,'[')) ) { |
1415 | + if ( 1 == sscanf(buf+1,"%p", &ptrs[n_ptrs]) ) |
1416 | + n_ptrs++; |
1417 | + buf++; |
1418 | + } |
1419 | + /* We should find an address close to epicsStackTraceRecurseGbl twice */ |
1420 | + for (i=0; i<n_ptrs-1; i++) { |
1421 | + /* I got a (unjustified) index-out-of-bound warning |
1422 | + * when setting j=i+1 here. Thus the weird j!= i check... |
1423 | + */ |
1424 | + j = i; |
1425 | + while ( j < n_ptrs ) { |
1426 | + if ( j != i && ptrs[j] == ptrs[i] ) { |
1427 | + if ( (char*)ptrs[i] >= (char*)epicsStackTraceRecurseGbl && (char*)ptrs[i] < (char*)epicsStackTraceRecurseGbl + WINDOW_SZ ) { |
1428 | + rval ++; |
1429 | + if ( test_debug ) |
1430 | + testDiag("found address %p again\n", ptrs[i]); |
1431 | + } |
1432 | + } |
1433 | + j++; |
1434 | + } |
1435 | + } |
1436 | + return rval; |
1437 | +} |
1438 | + |
1439 | +MAIN(epicsStackTraceTest) |
1440 | +{ |
1441 | +int features, all_features; |
1442 | +TestDataRec testData; |
1443 | +int gblFound, lclFound, numFound, dynFound; |
1444 | +char *nl, *p; |
1445 | + |
1446 | + if ( getenv("EPICS_STACK_TRACE_TEST_DEBUG") ) |
1447 | + test_debug = 1; |
1448 | + |
1449 | + testData.pos = 0; |
1450 | + |
1451 | + testPlan(5); |
1452 | + |
1453 | + features = epicsStackTraceGetFeatures(); |
1454 | + |
1455 | + all_features = EPICS_STACKTRACE_LCL_SYMBOLS |
1456 | + | EPICS_STACKTRACE_GBL_SYMBOLS |
1457 | + | EPICS_STACKTRACE_DYN_SYMBOLS |
1458 | + | EPICS_STACKTRACE_ADDRESSES; |
1459 | + |
1460 | + if ( ! testOk( (features & ~all_features) == 0, |
1461 | + "epicsStackTraceGetFeatures() obtains features") ) |
1462 | + testAbort("epicsStackTraceGetFeatures() not working as expected"); |
1463 | + |
1464 | + testData.pos = 0; |
1465 | + |
1466 | + testDiag("calling a few nested routines and eventually dump a stack trace"); |
1467 | + |
1468 | + eltc(0); |
1469 | + errlogAddListener( logClient, &testData ); |
1470 | + gfp(3); |
1471 | + errlogRemoveListeners( logClient, &testData ); |
1472 | + eltc(1); |
1473 | + |
1474 | + /* ensure there's a terminating NUL -- we have reserved space for it */ |
1475 | + testData.buf[testData.pos] = 0; |
1476 | + |
1477 | + testDiag("now scan the result for what we expect"); |
1478 | + |
1479 | + dynFound = findStringOcc( testData.buf, "epicsStackTrace" ); |
1480 | + gblFound = findStringOcc( testData.buf, "epicsStackTraceRecurseGbl" ); |
1481 | + lclFound = findStringOcc( testData.buf, "epicsStackTraceRecurseLcl" ); |
1482 | + numFound = findNumOcc ( testData.buf ); |
1483 | + |
1484 | + if ( (features & EPICS_STACKTRACE_DYN_SYMBOLS) ) { |
1485 | + testOk( dynFound == 1, "dumping symbol from library" ); |
1486 | + } else { |
1487 | + testOk( 1 , "no support for dumping library symbols on this platform"); |
1488 | + } |
1489 | + |
1490 | + |
1491 | + if ( (features & EPICS_STACKTRACE_GBL_SYMBOLS) ) { |
1492 | + testOk( gblFound == 2, "dumping global symbols" ); |
1493 | + } else { |
1494 | + testOk( 1 , "no support for dumping global symbols on this platform"); |
1495 | + } |
1496 | + |
1497 | + if ( (features & EPICS_STACKTRACE_LCL_SYMBOLS) ) { |
1498 | + testOk( lclFound == 2, "dumping local symbols" ); |
1499 | + } else { |
1500 | + testOk( 1 , "no support for dumping local symbols on this platform"); |
1501 | + } |
1502 | + |
1503 | + if ( (features & EPICS_STACKTRACE_ADDRESSES) ) { |
1504 | + testOk( numFound > 0, "dumping addresses" ); |
1505 | + } else { |
1506 | + testOk( 1 , "no support for dumping addresses on this platform"); |
1507 | + } |
1508 | + |
1509 | + |
1510 | + if ( test_debug ) { |
1511 | + p = testData.buf; |
1512 | + while ( (nl = strchr(p,'\n')) ) { |
1513 | + *nl = 0; |
1514 | + testDiag("%s",p); |
1515 | + *nl = '\n'; |
1516 | + p = nl+1; |
1517 | + } |
1518 | + testDiag("%s", p); |
1519 | + } |
1520 | + |
1521 | + testDone(); |
1522 | + |
1523 | + return 0; |
1524 | +} |
A few disjoint comments in-line, hopefully others will add to this.