XRootD
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 
55 #include "XProtocol/XProtocol.hh"
58 #include "XrdSys/XrdSysAtomics.hh"
59 #include "XrdSys/XrdSysPthread.hh"
60 
61 /******************************************************************************/
62 /* L o c a l S t a t i c s */
63 /******************************************************************************/
64 
65 namespace
66 {
67 static const int iniDepth =15; // The default
68 static const int maxDepth =30; // The app maximum
69 static const int xeqDepth =32; // The one we use internally
70 
71 static const int ptrXORFilter = 1;
72 
73 XrdSysMutex btMutex;
74 std::vector<void *> *ptrFilter[2] = {0, 0};
75 int xeqPtrFilter = 0;
76 int reqFilter = 0;
77 int rspFilter = 0;
78 }
79 
80 /******************************************************************************/
81 /* C v t R e q */
82 /******************************************************************************/
83 
84 namespace
85 {
86 struct XrdInfo {const char *name; int code; int mask;};
87 
88 XrdInfo *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 
143 namespace
144 {
145 XrdInfo *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
180 namespace
181 {
182 int 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 
209 namespace
210 {
211 int 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 
227 namespace
228 {
229 void 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 
267 namespace
268 {
269 
270 bool 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 
309 void 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 
349 bool 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)
407  {if (how != XrdOucBackTrace::clrIt && how != XrdOucBackTrace::delIt)
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);
446 std::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 
463 void 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: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)