XRootD
Loading...
Searching...
No Matches
XrdOucBackTrace.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d O u c B a c k T r a c e . c c */
4/* */
5/*(c) 2015 by the Board of Trustees of the Leland Stanford, Jr., University */
6/*Produced by Andrew Hanushevsky for Stanford University under contract */
7/* DE-AC02-76-SFO0515 with the Deprtment of Energy */
8/* */
9/* This file is part of the XRootD software suite. */
10/* */
11/* XRootD is free software: you can redistribute it and/or modify it under */
12/* the terms of the GNU Lesser General Public License as published by the */
13/* Free Software Foundation, either version 3 of the License, or (at your */
14/* option) any later version. */
15/* */
16/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19/* License for more details. */
20/* */
21/* You should have received a copy of the GNU Lesser General Public License */
22/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24/* */
25/* The copyright holder's institutional names and contributor's names may not */
26/* be used to endorse or promote products derived from this software without */
27/* specific prior written permission of the institution or contributor. */
28/******************************************************************************/
29
30#include <iostream>
31#include <cstdio>
32#include <cstdlib>
33#include <unistd.h>
34#include <vector>
35#include <sys/syscall.h>
36
37#ifndef MUSL /* glibc, uclibc, and macOS all have execinfo.h */
38#include <execinfo.h>
39#include <cxxabi.h>
40#endif
41
42// Linux and MacOS provide actual thread number, others a thread pointer.
43//
44#if defined(__linux__) || defined(__APPLE__)
45#define TidType long long
46#define TidFmt "%lld"
47#elif defined(__GNU__)
48#define TidType pthread_t // int
49#define TidFmt "%d"
50#else
51#define TidType pthread_t
52#define TidFmt "%p"
53#endif
54
60
61/******************************************************************************/
62/* L o c a l S t a t i c s */
63/******************************************************************************/
64
65namespace
66{
67static const int iniDepth =15; // The default
68static const int maxDepth =30; // The app maximum
69static const int xeqDepth =32; // The one we use internally
70
71static const int ptrXORFilter = 1;
72
73XrdSysMutex btMutex;
74std::vector<void *> *ptrFilter[2] = {0, 0};
75int xeqPtrFilter = 0;
76int reqFilter = 0;
77int rspFilter = 0;
78}
79
80/******************************************************************************/
81/* C v t R e q */
82/******************************************************************************/
83
84namespace
85{
86struct XrdInfo {const char *name; int code; int mask;};
87
88XrdInfo *CvtReq(const char *name, int rnum)
89{
90 static XrdInfo reqTab[] = {{"auth", kXR_auth, 1 },
91 {"query", kXR_query, 1<< 1},
92 {"chmod", kXR_chmod, 1<< 2},
93 {"close", kXR_close, 1<< 3},
94 {"dirlist", kXR_dirlist, 1<< 4},
95 {"gpfile", kXR_gpfile, 1<< 5},
96 {"protocol", kXR_protocol,1<< 6},
97 {"login", kXR_login, 1<< 7},
98 {"mkdir", kXR_mkdir, 1<< 8},
99 {"mv", kXR_mv, 1<< 9},
100 {"open", kXR_open, 1<<10},
101 {"ping", kXR_ping, 1<<11},
102 {"chkpoint", kXR_chkpoint,1<<12},
103 {"read", kXR_read, 1<<13},
104 {"rm", kXR_rm, 1<<14},
105 {"rmdir", kXR_rmdir, 1<<15},
106 {"sync", kXR_sync, 1<<16},
107 {"stat", kXR_stat, 1<<17},
108 {"set", kXR_set, 1<<18},
109 {"write", kXR_write, 1<<19},
110 {"fattr", kXR_fattr, 1<<20},
111 {"prepare", kXR_prepare, 1<<21},
112 {"statx", kXR_statx, 1<<22},
113 {"endess", kXR_endsess, 1<<23},
114 {"bind", kXR_bind, 1<<24},
115 {"readv", kXR_readv, 1<<25},
116 {"pgwrite", kXR_pgwrite, 1<<26},
117 {"locate", kXR_locate, 1<<27},
118 {"truncate", kXR_truncate,1<<28}
119 };
120
121 static XrdInfo unkTab = {"n/a",-1,-1};
122 static const int reqNum = kXR_truncate-kXR_auth+1;
123
124// Check if we only need to translate a code to a name
125//
126 if (!name)
127 {if (rnum < kXR_auth || rnum > kXR_truncate) return &unkTab;
128 return &reqTab[rnum-kXR_auth];
129 }
130
131// Find the name in the table
132//
133 for (int i = 0; i < reqNum; i++)
134 {if (!strcmp(name, reqTab[i].name)) return &reqTab[i];}
135 return &unkTab;
136}
137}
138
139/******************************************************************************/
140/* C v t R s p */
141/******************************************************************************/
142
143namespace
144{
145XrdInfo *CvtRsp(const char *name, int snum)
146{
147 static XrdInfo rspTab[] = {{"oksofar", kXR_oksofar, 1<< 1},
148 {"attn", kXR_attn, 1<< 2},
149 {"authmore", kXR_authmore, 1<< 3},
150 {"error", kXR_error, 1<< 4},
151 {"redirect", kXR_redirect, 1<< 5},
152 {"wait", kXR_wait, 1<< 6},
153 {"waitresp", kXR_waitresp, 1<< 7}
154 };
155 static XrdInfo aokTab = {"ok", 0, 1};
156 static XrdInfo unkTab = {"n/a", -1,-1};
157 static const int rspNum = kXR_waitresp-kXR_oksofar+1;
158
159// Check if we only need to translate a code to a name
160//
161 if (!name)
162 {if (!snum) return &aokTab;
163 if (snum < kXR_oksofar || snum > kXR_waitresp) return &unkTab;
164 return &rspTab[snum-kXR_oksofar];
165 }
166
167// Find the name in the table
168//
169 for (int i = 0; i < rspNum; i++)
170 {if (!strcmp(name, rspTab[i].name)) return &rspTab[i];}
171 return &unkTab;
172}
173}
174
175/******************************************************************************/
176/* D e m a n g l e */
177/******************************************************************************/
178
179#ifndef MUSL
180namespace
181{
182int Demangle(char *cSym, char *buff, int blen)
183{
184 int status;
185 char *plus = index(cSym, '+');
186 char *brak = (plus ? index(plus, '[') : 0);
187 char *cpar = (plus ? index(plus, ')') : 0);
188 char *realname;
189
190 if (*cSym != '(' || !plus || !cpar || !brak)
191 return snprintf(buff, blen, "%s\n", cSym);
192 *plus = 0;
193
194 realname = abi::__cxa_demangle(cSym+1, 0, 0, &status);
195
196 if (status) {*plus = '+'; return snprintf(buff, blen, "%s\n", cSym);}
197
198 *cpar = 0;
199 status = snprintf(buff, blen, "%s %s+%s\n", brak, realname, plus+1);
200 free(realname);
201 return status;
202}
203}
204
205/******************************************************************************/
206/* D u m p D e p t h */
207/******************************************************************************/
208
209namespace
210{
211int DumpDepth()
212{
213 char *theDepth = getenv("XRDBT_DEPTH");
214 int depth = iniDepth;
215
216 if (theDepth && (depth = atoi(theDepth)) <= 0) depth = iniDepth;
217
218 return (depth <= maxDepth ? depth : maxDepth);
219}
220}
221#endif
222
223/******************************************************************************/
224/* D u m p S t a c k */
225/******************************************************************************/
226
227namespace
228{
229void DumpStack(char *bP, int bL, TidType tid)
230{
231#ifdef MUSL
232 snprintf(bP, bL, "TBT " TidFmt " No stack information available with musl libc.", tid);
233 return;
234#else
235 static int btDepth = DumpDepth(); // One time MT-safe call
236 char **cSyms=0;
237 char *cStack[xeqDepth];
238 int k, n = backtrace((void **)cStack, xeqDepth);
239
240// Get call symbols if we have any of them here
241//
242 if (n > 1) cSyms = backtrace_symbols((void **)cStack, n);
243 else {snprintf(bP, bL, "TBT " TidFmt " No stack information available.", tid);
244 return;
245 }
246
247// Dump the stack into the buffer
248//
249 if (n > btDepth) n = btDepth+1;
250 for (int i = 2; i < n && bL > 24; i++)
251 {char *paren = index(cSyms[i], '(');
252 if (!paren) k = snprintf(bP, bL, "TBT " TidFmt " %s\n", tid, cSyms[i]);
253 else {k = snprintf(bP, bL, "TBT " TidFmt " ", tid);
254 bL -= k; bP += k;
255 k = Demangle(paren, bP, bL);
256 }
257 bL -= k; bP += k;
258 }
259#endif
260}
261}
262
263/******************************************************************************/
264/* S c r e e n */
265/******************************************************************************/
266
267namespace
268{
269
270bool Screen(void *thisP, void *objP, bool rrOK)
271{
272 XrdSysMutexHelper btHelp(btMutex);
273 std::vector<void *>::const_iterator it;
274 std::vector<void *> *objV, *thsV;
275
276// Filter by object pointer
277//
278 objV = ptrFilter[XrdOucBackTrace::isObject];
279 if (objV)
280 {for (it = objV->begin(); it!= objV->end(); ++it)
281 if (objP == *it) return true;
282 }
283
284// Filter by this pointer
285//
286 thsV = ptrFilter[XrdOucBackTrace::isThis];
287 if (thsV)
288 {for (it = thsV->begin(); it!= thsV->end(); ++it)
289 if (thisP == *it) return true;
290 }
291
292// If we something was in both lists then we have failed
293//
294 if ((objV && objV->size()) && (thsV && thsV->size())) return false;
295
296// The results if the result is the req/rsp filter
297//
298 return rrOK;
299}
300}
301
302/******************************************************************************/
303/* X r d O u c B a c k T r a c e M e t h o d s */
304/******************************************************************************/
305/******************************************************************************/
306/* D o B T */
307/******************************************************************************/
308
309void XrdOucBackTrace::DoBT(const char *head, void *thisP, void *objP,
310 const char *tail, bool force)
311{
312 TidType tid;
313 int k;
314 char btBuff[4096];
315
316// Apply any necessary filters
317//
318 if (!force)
319 {if (AtomicGet(xeqPtrFilter) && !Screen(thisP, objP, false)) return;}
320
321// Prepare for formatting
322//
323 if (!head) head = "";
324 if (!tail) tail = "";
325#if defined(__linux__) || defined(__APPLE__)
326 tid = syscall(SYS_gettid);
327#else
328 tid = XrdSysThread::ID();
329#endif
330
331// Format the header
332//
333 k = snprintf(btBuff,sizeof(btBuff),"\nTBT " TidFmt " %p %s obj %p %s\n",
334 tid, thisP, head, objP, tail);
335
336// Now dump the stack
337//
338 DumpStack(btBuff+k, sizeof(btBuff)-k-8, tid);
339
340// Output the information
341//
342 std::cerr <<btBuff <<std::flush;
343}
344
345/******************************************************************************/
346/* I n i t */
347/******************************************************************************/
348
349bool XrdOucBackTrace::Init(const char *reqs, const char *rsps)
350{
351 XrdOucTokenizer tokLine(0);
352 XrdInfo *infoP;
353 char *line, *token;
354 bool aOK = true;
355
356// Check if we have a request filter
357//
358 if (reqs || (reqs = getenv("XRDBT_REQFILTER")))
359 {line = strdup(reqs);
360 tokLine.Attach(line);
361 token = tokLine.GetLine();
362 while((token = tokLine.GetToken()))
363 {infoP = CvtReq(token, 0);
364 if (infoP->code > 0) reqFilter |= infoP->mask;
365 else aOK = false;
366 }
367 free(line);
368 }
369
370// Check if we have a response filter
371//
372 if (rsps || (rsps = getenv("XRDBT_RSPFILTER")))
373 {line = strdup(rsps);
374 tokLine.Attach(line);
375 token = tokLine.GetLine();
376 while((token = tokLine.GetToken()))
377 {infoP = CvtRsp(token, 0);
378 if (infoP->code > 0) rspFilter |= infoP->mask;
379 else aOK = false;
380 }
381 free(line);
382 }
383
384// All done
385//
386 return aOK;
387}
388
389/******************************************************************************/
390/* F i l t e r */
391/******************************************************************************/
392
395{
396 XrdSysMutexHelper btHelp(btMutex);
397 std::vector<void *>::iterator it;
398 std::vector<void *> *filtP;
399
400// Get the filter, we have the mutex so no need to atomically fetch it
401//
402 filtP = ptrFilter[pType];
403
404// Perfome action when we don't already have a filter
405//
406 if (!filtP)
408 {filtP = new std::vector<void *>();
409 filtP->push_back(ptr);
410 ptrFilter[pType] = filtP;
411 AtomicInc(xeqPtrFilter); // This forces the above to complete
412 }
413 return;
414 }
415
416// We have a filter, see it we need to clear it
417//
418 if (how == XrdOucBackTrace::clrIt)
419 {int i = pType ^ ptrXORFilter;
420 filtP->clear();
421 if (!ptrFilter[i] || ptrFilter[i]->size() == 0) AtomicZAP(xeqPtrFilter);
422 return;
423 }
424
425// We have a filter, see it we need to replace it
426//
427 if (how == XrdOucBackTrace::repIt)
428 {filtP->clear();
429 filtP->push_back(ptr);
430 AtomicInc(xeqPtrFilter);
431 return;
432 }
433
434// We only have add and delete left and these require us to find the pointer
435//
436 for (it = filtP->begin(); it!= filtP->end(); ++it) if (ptr == *it) break;
437
438// Handle the case where we found the element
439//
440 if (it != filtP->end())
441 {if (how == XrdOucBackTrace::delIt)
442 {int i = pType ^ ptrXORFilter;
443 filtP->erase(it);
444 if (filtP->size() == 0 && (!ptrFilter[i] || !(ptrFilter[i]->size())))
445 AtomicZAP(xeqPtrFilter);
446std::cerr <<"delIt: " <<xeqPtrFilter <<std::endl;
447 }
448 return;
449 }
450
451// We did not find the element, add it if we must
452//
453 if (how == XrdOucBackTrace::addIt)
454 {filtP->push_back(ptr);
455 AtomicInc(xeqPtrFilter);
456 }
457}
458
459/******************************************************************************/
460/* X r d B T */
461/******************************************************************************/
462
463void XrdOucBackTrace::XrdBT(const char *head, void *thisP, void *objP,
464 int rspN, int reqN,
465 const char *tail, bool force)
466{
467 XrdInfo *infoP, *reqInfo, *rspInfo;
468 TidType tid;
469 int k;
470 char btBuff[4096];
471 bool rrOK;
472
473// Apply any necessary filters
474//
475 if (!force)
476 { if (!reqFilter && !rspFilter) rrOK = false;
477 else if (reqFilter && (infoP=CvtReq(0, reqN))
478 && !(reqFilter & infoP->mask)) rrOK = false;
479 else if (rspFilter && (infoP=CvtRsp(0, rspN))
480 && !(rspFilter & infoP->mask)) rrOK = false;
481 else rrOK = true;
482 if (AtomicGet(xeqPtrFilter)) {if (!Screen(thisP, objP, rrOK)) return;}
483 else if (!rrOK) return;
484 }
485
486// Prepare for formatting
487//
488 if (!head) head = "";
489 if (!tail) tail = "";
490 reqInfo = CvtReq(0, reqN);
491 rspInfo = CvtRsp(0, rspN);
492#if defined(__linux__) || defined(__APPLE__)
493 tid = syscall(SYS_gettid);
494#else
495 tid = XrdSysThread::ID();
496#endif
497
498// Format the header
499//
500 k = snprintf(btBuff, sizeof(btBuff),
501 "\nTBT " TidFmt " %p %s obj %p req %s rsp %s %s\n",
502 tid, thisP, head, objP, reqInfo->name, rspInfo->name, tail);
503
504// Now dump the stack
505//
506 DumpStack(btBuff+k, sizeof(btBuff)-k-8, tid);
507
508// Output the information
509//
510 std::cerr <<btBuff <<std::flush;
511}
@ kXR_waitresp
Definition XProtocol.hh:904
@ kXR_redirect
Definition XProtocol.hh:902
@ kXR_oksofar
Definition XProtocol.hh:898
@ kXR_authmore
Definition XProtocol.hh:900
@ kXR_attn
Definition XProtocol.hh:899
@ kXR_wait
Definition XProtocol.hh:903
@ kXR_error
Definition XProtocol.hh:901
@ kXR_read
Definition XProtocol.hh:125
@ kXR_open
Definition XProtocol.hh:122
@ kXR_readv
Definition XProtocol.hh:137
@ kXR_mkdir
Definition XProtocol.hh:120
@ kXR_sync
Definition XProtocol.hh:128
@ kXR_chmod
Definition XProtocol.hh:114
@ kXR_bind
Definition XProtocol.hh:136
@ kXR_dirlist
Definition XProtocol.hh:116
@ kXR_fattr
Definition XProtocol.hh:132
@ kXR_rm
Definition XProtocol.hh:126
@ kXR_query
Definition XProtocol.hh:113
@ kXR_write
Definition XProtocol.hh:131
@ kXR_gpfile
Definition XProtocol.hh:117
@ kXR_login
Definition XProtocol.hh:119
@ kXR_auth
Definition XProtocol.hh:112
@ kXR_endsess
Definition XProtocol.hh:135
@ kXR_set
Definition XProtocol.hh:130
@ kXR_rmdir
Definition XProtocol.hh:127
@ kXR_statx
Definition XProtocol.hh:134
@ kXR_truncate
Definition XProtocol.hh:140
@ kXR_protocol
Definition XProtocol.hh:118
@ kXR_mv
Definition XProtocol.hh:121
@ kXR_ping
Definition XProtocol.hh:123
@ kXR_stat
Definition XProtocol.hh:129
@ kXR_chkpoint
Definition XProtocol.hh:124
@ kXR_locate
Definition XProtocol.hh:139
@ kXR_close
Definition XProtocol.hh:115
@ kXR_pgwrite
Definition XProtocol.hh:138
@ kXR_prepare
Definition XProtocol.hh:133
#define TidType
#define TidFmt
#define AtomicInc(x)
#define AtomicGet(x)
#define AtomicZAP(x)
PtrType
Define filter types and actions.
@ isObject
Pointer is an object pointer.
@ isThis
Pointer is a this pointer.
static void Filter(void *ptr, PtrType pType, Action how=addIt)
static bool Init(const char *reqs=0, const char *rsps=0)
@ repIt
Replace all PtrTypes items filtered with this item.
@ delIt
Delete this item from the list of PtrTypes filtered.
@ addIt
Add item to the list of PtrTypes being filtered.
@ clrIt
Delete all PtrType filtered items (1st arg ignored).
static void XrdBT(const char *head=0, void *thisP=0, void *objP=0, int rspN=0, int reqN=0, const char *tail=0, bool force=false)
static void DoBT(const char *head=0, void *thisP=0, void *objP=0, const char *tail=0, bool force=false)
char * GetToken(char **rest=0, int lowcase=0)
void Attach(char *bp)
static pthread_t ID(void)