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