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 (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 
59 #include "XProtocol/XProtocol.hh"
62 #include "XrdSys/XrdSysAtomics.hh"
63 #include "XrdSys/XrdSysPthread.hh"
64 
65 /******************************************************************************/
66 /* L o c a l S t a t i c s */
67 /******************************************************************************/
68 
69 namespace
70 {
71 static const int iniDepth =15; // The default
72 static const int maxDepth =30; // The app maximum
73 static const int xeqDepth =32; // The one we use internally
74 
75 static const int ptrXORFilter = 1;
76 
77 XrdSysMutex btMutex;
78 std::vector<void *> *ptrFilter[2] = {0, 0};
79 int xeqPtrFilter = 0;
80 int reqFilter = 0;
81 int rspFilter = 0;
82 }
83 
84 /******************************************************************************/
85 /* C v t R e q */
86 /******************************************************************************/
87 
88 namespace
89 {
90 struct XrdInfo {const char *name; int code; int mask;};
91 
92 XrdInfo *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 
147 namespace
148 {
149 XrdInfo *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
184 namespace
185 {
186 int 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 
213 namespace
214 {
215 int 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 
231 namespace
232 {
233 void 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 
271 namespace
272 {
273 
274 bool 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 
313 void 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 
353 bool 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)
411  {if (how != XrdOucBackTrace::clrIt && how != XrdOucBackTrace::delIt)
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);
450 std::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 
467 void 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)