Loading...
Searching...
No Matches
XrdOucBackTrace.cc
Go to the documentation of this file.
/******************************************************************************/
/* */
/* X r d O u c B a c k T r a c e . c c */
/* */
/*(c) 2015 by the Board of Trustees of the Leland Stanford, Jr., University */
/*Produced by Andrew Hanushevsky for Stanford University under contract */
/* DE-AC02-76-SFO0515 with the Deprtment of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <vector>
#include <sys/syscall.h>
#ifndef MUSL /* glibc, uclibc, and macOS all have execinfo.h */
#include <execinfo.h>
#include <cxxabi.h>
#endif
// Linux and MacOS provide actual thread number, others a thread pointer.
//
#if defined(__linux__) || defined(__APPLE__)
#define TidType long long
#define TidFmt "%lld"
#elif defined(__GNU__)
#define TidType pthread_t // int (ix86), long (x86_64)
#ifdef __x86_64__
#define TidFmt "%ld"
#else
#define TidFmt "%d"
#endif
#else
#define TidType pthread_t
#define TidFmt "%p"
#endif
#include "XProtocol/XProtocol.hh"
#include "XrdOuc/XrdOucBackTrace.hh"
#include "XrdOuc/XrdOucTokenizer.hh"
#include "XrdSys/XrdSysAtomics.hh"
#include "XrdSys/XrdSysPthread.hh"
/******************************************************************************/
/* L o c a l S t a t i c s */
/******************************************************************************/
namespace
{
static const int iniDepth =15; // The default
static const int maxDepth =30; // The app maximum
static const int xeqDepth =32; // The one we use internally
static const int ptrXORFilter = 1;
XrdSysMutex btMutex;
std::vector<void *> *ptrFilter[2] = {0, 0};
int xeqPtrFilter = 0;
int reqFilter = 0;
int rspFilter = 0;
}
/******************************************************************************/
/* C v t R e q */
/******************************************************************************/
namespace
{
struct XrdInfo {const char *name; int code; int mask;};
XrdInfo *CvtReq(const char *name, int rnum)
{
static XrdInfo reqTab[] = {{"auth", kXR_auth, 1 },
{"query", kXR_query, 1<< 1},
{"chmod", kXR_chmod, 1<< 2},
{"close", kXR_close, 1<< 3},
{"dirlist", kXR_dirlist, 1<< 4},
{"gpfile", kXR_gpfile, 1<< 5},
{"protocol", kXR_protocol,1<< 6},
{"login", kXR_login, 1<< 7},
{"mkdir", kXR_mkdir, 1<< 8},
{"mv", kXR_mv, 1<< 9},
{"open", kXR_open, 1<<10},
{"ping", kXR_ping, 1<<11},
{"chkpoint", kXR_chkpoint,1<<12},
{"read", kXR_read, 1<<13},
{"rm", kXR_rm, 1<<14},
{"rmdir", kXR_rmdir, 1<<15},
{"sync", kXR_sync, 1<<16},
{"stat", kXR_stat, 1<<17},
{"set", kXR_set, 1<<18},
{"write", kXR_write, 1<<19},
{"fattr", kXR_fattr, 1<<20},
{"prepare", kXR_prepare, 1<<21},
{"statx", kXR_statx, 1<<22},
{"endess", kXR_endsess, 1<<23},
{"bind", kXR_bind, 1<<24},
{"readv", kXR_readv, 1<<25},
{"pgwrite", kXR_pgwrite, 1<<26},
{"locate", kXR_locate, 1<<27},
{"truncate", kXR_truncate,1<<28}
};
static XrdInfo unkTab = {"n/a",-1,-1};
static const int reqNum = kXR_truncate-kXR_auth+1;
// Check if we only need to translate a code to a name
//
if (!name)
{if (rnum < kXR_auth || rnum > kXR_truncate) return &unkTab;
return &reqTab[rnum-kXR_auth];
}
// Find the name in the table
//
for (int i = 0; i < reqNum; i++)
{if (!strcmp(name, reqTab[i].name)) return &reqTab[i];}
return &unkTab;
}
}
/******************************************************************************/
/* C v t R s p */
/******************************************************************************/
namespace
{
XrdInfo *CvtRsp(const char *name, int snum)
{
static XrdInfo rspTab[] = {{"oksofar", kXR_oksofar, 1<< 1},
{"attn", kXR_attn, 1<< 2},
{"authmore", kXR_authmore, 1<< 3},
{"error", kXR_error, 1<< 4},
{"redirect", kXR_redirect, 1<< 5},
{"wait", kXR_wait, 1<< 6},
{"waitresp", kXR_waitresp, 1<< 7}
};
static XrdInfo aokTab = {"ok", 0, 1};
static XrdInfo unkTab = {"n/a", -1,-1};
static const int rspNum = kXR_waitresp-kXR_oksofar+1;
// Check if we only need to translate a code to a name
//
if (!name)
{if (!snum) return &aokTab;
if (snum < kXR_oksofar || snum > kXR_waitresp) return &unkTab;
return &rspTab[snum-kXR_oksofar];
}
// Find the name in the table
//
for (int i = 0; i < rspNum; i++)
{if (!strcmp(name, rspTab[i].name)) return &rspTab[i];}
return &unkTab;
}
}
/******************************************************************************/
/* D e m a n g l e */
/******************************************************************************/
#ifndef MUSL
namespace
{
int Demangle(char *cSym, char *buff, int blen)
{
int status;
char *plus = index(cSym, '+');
char *brak = (plus ? index(plus, '[') : 0);
char *cpar = (plus ? index(plus, ')') : 0);
char *realname;
if (*cSym != '(' || !plus || !cpar || !brak)
return snprintf(buff, blen, "%s\n", cSym);
*plus = 0;
realname = abi::__cxa_demangle(cSym+1, 0, 0, &status);
if (status) {*plus = '+'; return snprintf(buff, blen, "%s\n", cSym);}
*cpar = 0;
status = snprintf(buff, blen, "%s %s+%s\n", brak, realname, plus+1);
free(realname);
return status;
}
}
/******************************************************************************/
/* D u m p D e p t h */
/******************************************************************************/
namespace
{
int DumpDepth()
{
char *theDepth = getenv("XRDBT_DEPTH");
int depth = iniDepth;
if (theDepth && (depth = atoi(theDepth)) <= 0) depth = iniDepth;
return (depth <= maxDepth ? depth : maxDepth);
}
}
#endif
/******************************************************************************/
/* D u m p S t a c k */
/******************************************************************************/
namespace
{
void DumpStack(char *bP, int bL, TidType tid)
{
#ifdef MUSL
snprintf(bP, bL, "TBT " TidFmt " No stack information available with musl libc.", tid);
return;
#else
static int btDepth = DumpDepth(); // One time MT-safe call
char **cSyms=0;
char *cStack[xeqDepth];
int k, n = backtrace((void **)cStack, xeqDepth);
// Get call symbols if we have any of them here
//
if (n > 1) cSyms = backtrace_symbols((void **)cStack, n);
else {snprintf(bP, bL, "TBT " TidFmt " No stack information available.", tid);
return;
}
// Dump the stack into the buffer
//
if (n > btDepth) n = btDepth+1;
for (int i = 2; i < n && bL > 24; i++)
{char *paren = index(cSyms[i], '(');
if (!paren) k = snprintf(bP, bL, "TBT " TidFmt " %s\n", tid, cSyms[i]);
else {k = snprintf(bP, bL, "TBT " TidFmt " ", tid);
bL -= k; bP += k;
k = Demangle(paren, bP, bL);
}
bL -= k; bP += k;
}
#endif
}
}
/******************************************************************************/
/* S c r e e n */
/******************************************************************************/
namespace
{
bool Screen(void *thisP, void *objP, bool rrOK)
{
XrdSysMutexHelper btHelp(btMutex);
std::vector<void *>::const_iterator it;
std::vector<void *> *objV, *thsV;
// Filter by object pointer
//
objV = ptrFilter[XrdOucBackTrace::isObject];
if (objV)
{for (it = objV->begin(); it!= objV->end(); ++it)
if (objP == *it) return true;
}
// Filter by this pointer
//
thsV = ptrFilter[XrdOucBackTrace::isThis];
if (thsV)
{for (it = thsV->begin(); it!= thsV->end(); ++it)
if (thisP == *it) return true;
}
// If we something was in both lists then we have failed
//
if ((objV && objV->size()) && (thsV && thsV->size())) return false;
// The results if the result is the req/rsp filter
//
return rrOK;
}
}
/******************************************************************************/
/* X r d O u c B a c k T r a c e M e t h o d s */
/******************************************************************************/
/******************************************************************************/
/* D o B T */
/******************************************************************************/
void XrdOucBackTrace::DoBT(const char *head, void *thisP, void *objP,
const char *tail, bool force)
{
TidType tid;
int k;
char btBuff[4096];
// Apply any necessary filters
//
if (!force)
{if (AtomicGet(xeqPtrFilter) && !Screen(thisP, objP, false)) return;}
// Prepare for formatting
//
if (!head) head = "";
if (!tail) tail = "";
#if defined(__linux__) || defined(__APPLE__)
tid = syscall(SYS_gettid);
#else
tid = XrdSysThread::ID();
#endif
// Format the header
//
k = snprintf(btBuff,sizeof(btBuff),"\nTBT " TidFmt " %p %s obj %p %s\n",
tid, thisP, head, objP, tail);
// Now dump the stack
//
DumpStack(btBuff+k, sizeof(btBuff)-k-8, tid);
// Output the information
//
std::cerr <<btBuff <<std::flush;
}
/******************************************************************************/
/* I n i t */
/******************************************************************************/
bool XrdOucBackTrace::Init(const char *reqs, const char *rsps)
{
XrdOucTokenizer tokLine(0);
XrdInfo *infoP;
char *line, *token;
bool aOK = true;
// Check if we have a request filter
//
if (reqs || (reqs = getenv("XRDBT_REQFILTER")))
{line = strdup(reqs);
tokLine.Attach(line);
token = tokLine.GetLine();
while((token = tokLine.GetToken()))
{infoP = CvtReq(token, 0);
if (infoP->code > 0) reqFilter |= infoP->mask;
else aOK = false;
}
free(line);
}
// Check if we have a response filter
//
if (rsps || (rsps = getenv("XRDBT_RSPFILTER")))
{line = strdup(rsps);
tokLine.Attach(line);
token = tokLine.GetLine();
while((token = tokLine.GetToken()))
{infoP = CvtRsp(token, 0);
if (infoP->code > 0) rspFilter |= infoP->mask;
else aOK = false;
}
free(line);
}
// All done
//
return aOK;
}
/******************************************************************************/
/* F i l t e r */
/******************************************************************************/
void XrdOucBackTrace::Filter(void *ptr, XrdOucBackTrace::PtrType pType,
XrdOucBackTrace::Action how)
{
XrdSysMutexHelper btHelp(btMutex);
std::vector<void *>::iterator it;
std::vector<void *> *filtP;
// Get the filter, we have the mutex so no need to atomically fetch it
//
filtP = ptrFilter[pType];
// Perfome action when we don't already have a filter
//
if (!filtP)
{if (how != XrdOucBackTrace::clrIt && how != XrdOucBackTrace::delIt)
{filtP = new std::vector<void *>();
filtP->push_back(ptr);
ptrFilter[pType] = filtP;
AtomicInc(xeqPtrFilter); // This forces the above to complete
}
return;
}
// We have a filter, see it we need to clear it
//
if (how == XrdOucBackTrace::clrIt)
{int i = pType ^ ptrXORFilter;
filtP->clear();
if (!ptrFilter[i] || ptrFilter[i]->size() == 0) AtomicZAP(xeqPtrFilter);
return;
}
// We have a filter, see it we need to replace it
//
if (how == XrdOucBackTrace::repIt)
{filtP->clear();
filtP->push_back(ptr);
AtomicInc(xeqPtrFilter);
return;
}
// We only have add and delete left and these require us to find the pointer
//
for (it = filtP->begin(); it!= filtP->end(); ++it) if (ptr == *it) break;
// Handle the case where we found the element
//
if (it != filtP->end())
{if (how == XrdOucBackTrace::delIt)
{int i = pType ^ ptrXORFilter;
filtP->erase(it);
if (filtP->size() == 0 && (!ptrFilter[i] || !(ptrFilter[i]->size())))
AtomicZAP(xeqPtrFilter);
std::cerr <<"delIt: " <<xeqPtrFilter <<std::endl;
}
return;
}
// We did not find the element, add it if we must
//
if (how == XrdOucBackTrace::addIt)
{filtP->push_back(ptr);
AtomicInc(xeqPtrFilter);
}
}
/******************************************************************************/
/* X r d B T */
/******************************************************************************/
void XrdOucBackTrace::XrdBT(const char *head, void *thisP, void *objP,
int rspN, int reqN,
const char *tail, bool force)
{
XrdInfo *infoP, *reqInfo, *rspInfo;
TidType tid;
int k;
char btBuff[4096];
bool rrOK;
// Apply any necessary filters
//
if (!force)
{ if (!reqFilter && !rspFilter) rrOK = false;
else if (reqFilter && (infoP=CvtReq(0, reqN))
&& !(reqFilter & infoP->mask)) rrOK = false;
else if (rspFilter && (infoP=CvtRsp(0, rspN))
&& !(rspFilter & infoP->mask)) rrOK = false;
else rrOK = true;
if (AtomicGet(xeqPtrFilter)) {if (!Screen(thisP, objP, rrOK)) return;}
else if (!rrOK) return;
}
// Prepare for formatting
//
if (!head) head = "";
if (!tail) tail = "";
reqInfo = CvtReq(0, reqN);
rspInfo = CvtRsp(0, rspN);
#if defined(__linux__) || defined(__APPLE__)
tid = syscall(SYS_gettid);
#else
tid = XrdSysThread::ID();
#endif
// Format the header
//
k = snprintf(btBuff, sizeof(btBuff),
"\nTBT " TidFmt " %p %s obj %p req %s rsp %s %s\n",
tid, thisP, head, objP, reqInfo->name, rspInfo->name, tail);
// Now dump the stack
//
DumpStack(btBuff+k, sizeof(btBuff)-k-8, tid);
// Output the information
//
std::cerr <<btBuff <<std::flush;
}
XProtocol.hh
kXR_waitresp
@ kXR_waitresp
Definition XProtocol.hh:906
kXR_redirect
@ kXR_redirect
Definition XProtocol.hh:904
kXR_oksofar
@ kXR_oksofar
Definition XProtocol.hh:900
kXR_authmore
@ kXR_authmore
Definition XProtocol.hh:902
kXR_attn
@ kXR_attn
Definition XProtocol.hh:901
kXR_wait
@ kXR_wait
Definition XProtocol.hh:905
kXR_error
@ kXR_error
Definition XProtocol.hh:903
kXR_read
@ kXR_read
Definition XProtocol.hh:125
kXR_open
@ kXR_open
Definition XProtocol.hh:122
kXR_readv
@ kXR_readv
Definition XProtocol.hh:137
kXR_mkdir
@ kXR_mkdir
Definition XProtocol.hh:120
kXR_sync
@ kXR_sync
Definition XProtocol.hh:128
kXR_chmod
@ kXR_chmod
Definition XProtocol.hh:114
kXR_bind
@ kXR_bind
Definition XProtocol.hh:136
kXR_dirlist
@ kXR_dirlist
Definition XProtocol.hh:116
kXR_fattr
@ kXR_fattr
Definition XProtocol.hh:132
kXR_rm
@ kXR_rm
Definition XProtocol.hh:126
kXR_query
@ kXR_query
Definition XProtocol.hh:113
kXR_write
@ kXR_write
Definition XProtocol.hh:131
kXR_gpfile
@ kXR_gpfile
Definition XProtocol.hh:117
kXR_login
@ kXR_login
Definition XProtocol.hh:119
kXR_auth
@ kXR_auth
Definition XProtocol.hh:112
kXR_endsess
@ kXR_endsess
Definition XProtocol.hh:135
kXR_set
@ kXR_set
Definition XProtocol.hh:130
kXR_rmdir
@ kXR_rmdir
Definition XProtocol.hh:127
kXR_statx
@ kXR_statx
Definition XProtocol.hh:134
kXR_truncate
@ kXR_truncate
Definition XProtocol.hh:140
kXR_protocol
@ kXR_protocol
Definition XProtocol.hh:118
kXR_mv
@ kXR_mv
Definition XProtocol.hh:121
kXR_ping
@ kXR_ping
Definition XProtocol.hh:123
kXR_stat
@ kXR_stat
Definition XProtocol.hh:129
kXR_chkpoint
@ kXR_chkpoint
Definition XProtocol.hh:124
kXR_locate
@ kXR_locate
Definition XProtocol.hh:139
kXR_close
@ kXR_close
Definition XProtocol.hh:115
kXR_pgwrite
@ kXR_pgwrite
Definition XProtocol.hh:138
kXR_prepare
@ kXR_prepare
Definition XProtocol.hh:133
TidType
#define TidType
Definition XrdOucBackTrace.cc:55
TidFmt
#define TidFmt
Definition XrdOucBackTrace.cc:56
XrdOucBackTrace.hh
XrdOucTokenizer.hh
XrdSysAtomics.hh
AtomicInc
#define AtomicInc(x)
Definition XrdSysAtomics.hh:72
AtomicGet
#define AtomicGet(x)
Definition XrdSysAtomics.hh:71
AtomicZAP
#define AtomicZAP(x)
Definition XrdSysAtomics.hh:75
XrdSysPthread.hh
XrdOucBackTrace::PtrType
PtrType
Define filter types and actions.
Definition XrdOucBackTrace.hh:128
XrdOucBackTrace::isObject
@ isObject
Pointer is an object pointer.
Definition XrdOucBackTrace.hh:129
XrdOucBackTrace::isThis
@ isThis
Pointer is a this pointer.
Definition XrdOucBackTrace.hh:128
XrdOucBackTrace::Filter
static void Filter(void *ptr, PtrType pType, Action how=addIt)
Definition XrdOucBackTrace.cc:397
XrdOucBackTrace::Init
static bool Init(const char *reqs=0, const char *rsps=0)
Definition XrdOucBackTrace.cc:353
XrdOucBackTrace::Action
Action
Definition XrdOucBackTrace.hh:132
XrdOucBackTrace::repIt
@ repIt
Replace all PtrTypes items filtered with this item.
Definition XrdOucBackTrace.hh:135
XrdOucBackTrace::delIt
@ delIt
Delete this item from the list of PtrTypes filtered.
Definition XrdOucBackTrace.hh:134
XrdOucBackTrace::addIt
@ addIt
Add item to the list of PtrTypes being filtered.
Definition XrdOucBackTrace.hh:132
XrdOucBackTrace::clrIt
@ clrIt
Delete all PtrType filtered items (1st arg ignored).
Definition XrdOucBackTrace.hh:133
XrdOucBackTrace::XrdBT
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)
Definition XrdOucBackTrace.cc:467
XrdOucBackTrace::DoBT
static void DoBT(const char *head=0, void *thisP=0, void *objP=0, const char *tail=0, bool force=false)
Definition XrdOucBackTrace.cc:313
XrdOucTokenizer
Definition XrdOucTokenizer.hh:33
XrdOucTokenizer::GetToken
char * GetToken(char **rest=0, int lowcase=0)
Definition XrdOucTokenizer.cc:93
XrdOucTokenizer::GetLine
char * GetLine()
Definition XrdOucTokenizer.cc:55
XrdOucTokenizer::Attach
void Attach(char *bp)
Definition XrdOucTokenizer.cc:43
XrdSysMutexHelper
Definition XrdSysPthread.hh:263
XrdSysMutex
Definition XrdSysPthread.hh:165
XrdSysThread::ID
static pthread_t ID(void)
Definition XrdSysPthread.hh:584