1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2    	// vim: ts=8 sw=2 smarttab
3    	/*
4    	 * Ceph - scalable distributed file system
5    	 *
6    	 * Copyright (C) 2008-2011 New Dream Network
7    	 *
8    	 * This is free software; you can redistribute it and/or
9    	 * modify it under the terms of the GNU Lesser General Public
10   	 * License version 2.1, as published by the Free Software
11   	 * Foundation.  See file COPYING.
12   	 *
13   	 */
14   	
15   	#include "include/compat.h"
16   	#include "common/debug.h"
17   	
18   	namespace ceph {
19   	  static CephContext *g_assert_context = NULL;
20   	
21   	  /* If you register an assert context, ceph_assert() will try to lock the dout
22   	   * stream of that context before starting an assert. This is nice because the
23   	   * output looks better. Your assert will not be interleaved with other dout
24   	   * statements.
25   	   *
26   	   * However, this is strictly optional and library code currently does not
27   	   * register an assert context. The extra complexity of supporting this
28   	   * wouldn't really be worth it.
29   	   */
30   	  void register_assert_context(CephContext *cct)
31   	  {
32   	    ceph_assert(!g_assert_context);
33   	    g_assert_context = cct;
34   	  }
35   	
36   	  [[gnu::cold]] void __ceph_assert_fail(const char *assertion,
37   						const char *file, int line,
38   						const char *func)
39   	  {
40   	    g_assert_condition = assertion;
41   	    g_assert_file = file;
42   	    g_assert_line = line;
43   	    g_assert_func = func;
44   	    g_assert_thread = (unsigned long long)pthread_self();
45   	    ceph_pthread_getname(pthread_self(), g_assert_thread_name,
46   			       sizeof(g_assert_thread_name));
47   	
48   	    ostringstream tss;
49   	    tss << ceph_clock_now();
50   	
51   	    snprintf(g_assert_msg, sizeof(g_assert_msg),
52   		     "%s: In function '%s' thread %llx time %s\n"
53   		     "%s: %d: FAILED ceph_assert(%s)\n",
54   		     file, func, (unsigned long long)pthread_self(), tss.str().c_str(),
55   		     file, line, assertion);
56   	    dout_emergency(g_assert_msg);
57   	
58   	    // TODO: get rid of this memory allocation.
59   	    ostringstream oss;
60   	    oss << BackTrace(1);
61   	    dout_emergency(oss.str());
62   	
63   	    if (g_assert_context) {
64   	      lderr(g_assert_context) << g_assert_msg << std::endl;
65   	      *_dout << oss.str() << dendl;
66   	
67   	      // dump recent only if the abort signal handler won't do it for us
68   	      if (!g_assert_context->_conf->fatal_signal_handlers) {
(1) Event fun_call_w_exception: Called function throws an exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE". [details]
69   		g_assert_context->_log->dump_recent();
70   	      }
71   	    }
72   	
73   	    abort();
74   	  }
75   	
76   	  [[gnu::cold]] void __ceph_assert_fail(const assert_data &ctx)
77   	  {
(1) Event fun_call_w_exception: Called function throws an exception of type "_ZN5boost16exception_detail10clone_implINS0_19error_info_injectorINSt8ios_base7failureB5cxx11EEEEE". [details]
78   	    __ceph_assert_fail(ctx.assertion, ctx.file, ctx.line, ctx.function);
79   	  }
80   	
81   	  class BufAppender {
82   	  public:
83   	    BufAppender(char* buf, int size) : bufptr(buf), remaining(size) {}
84   	
85   	    void printf(const char * format, ...) {
86   	      va_list args;
87   	      va_start(args, format);
88   	      this->vprintf(format, args);
89   	      va_end(args);
90   	    }
91   	
92   	    void vprintf(const char * format, va_list args) {
93   	      int n = vsnprintf(bufptr, remaining, format, args);
94   	      if (n >= 0) {
95   		if (n < remaining) {
96   		  remaining -= n;
97   		  bufptr += n;
98   		} else {
99   		  remaining = 0;
100  		}
101  	      }
102  	    }
103  	
104  	  private:
105  	    char* bufptr;
106  	    int remaining;
107  	  };
108  	
109  	
110  	  [[gnu::cold]] void __ceph_assertf_fail(const char *assertion,
111  						 const char *file, int line,
112  						 const char *func, const char* msg,
113  						 ...)
114  	  {
115  	    ostringstream tss;
116  	    tss << ceph_clock_now();
117  	
118  	    g_assert_condition = assertion;
119  	    g_assert_file = file;
120  	    g_assert_line = line;
121  	    g_assert_func = func;
122  	    g_assert_thread = (unsigned long long)pthread_self();
123  	    ceph_pthread_getname(pthread_self(), g_assert_thread_name,
124  			       sizeof(g_assert_thread_name));
125  	
126  	    BufAppender ba(g_assert_msg, sizeof(g_assert_msg));
127  	    BackTrace *bt = new BackTrace(1);
128  	    ba.printf("%s: In function '%s' thread %llx time %s\n"
129  		     "%s: %d: FAILED ceph_assert(%s)\n",
130  		     file, func, (unsigned long long)pthread_self(), tss.str().c_str(),
131  		     file, line, assertion);
132  	    ba.printf("Assertion details: ");
133  	    va_list args;
134  	    va_start(args, msg);
135  	    ba.vprintf(msg, args);
136  	    va_end(args);
137  	    ba.printf("\n");
138  	    dout_emergency(g_assert_msg);
139  	
140  	    // TODO: get rid of this memory allocation.
141  	    ostringstream oss;
142  	    oss << *bt;
143  	    dout_emergency(oss.str());
144  	
145  	    if (g_assert_context) {
146  	      lderr(g_assert_context) << g_assert_msg << std::endl;
147  	      *_dout << oss.str() << dendl;
148  	
149  	      // dump recent only if the abort signal handler won't do it for us
150  	      if (!g_assert_context->_conf->fatal_signal_handlers) {
151  		g_assert_context->_log->dump_recent();
152  	      }
153  	    }
154  	
155  	    abort();
156  	  }
157  	
158  	  [[gnu::cold]] void __ceph_abort(const char *file, int line,
159  					  const char *func, const string& msg)
160  	  {
161  	    ostringstream tss;
162  	    tss << ceph_clock_now();
163  	
164  	    g_assert_condition = "abort";
165  	    g_assert_file = file;
166  	    g_assert_line = line;
167  	    g_assert_func = func;
168  	    g_assert_thread = (unsigned long long)pthread_self();
169  	    ceph_pthread_getname(pthread_self(), g_assert_thread_name,
170  			       sizeof(g_assert_thread_name));
171  	
172  	    BackTrace *bt = new BackTrace(1);
173  	    snprintf(g_assert_msg, sizeof(g_assert_msg),
174  	             "%s: In function '%s' thread %llx time %s\n"
175  		     "%s: %d: ceph_abort_msg(\"%s\")\n", file, func,
176  		     (unsigned long long)pthread_self(),
177  		     tss.str().c_str(), file, line,
178  		     msg.c_str());
179  	    dout_emergency(g_assert_msg);
180  	
181  	    // TODO: get rid of this memory allocation.
182  	    ostringstream oss;
183  	    oss << *bt;
184  	    dout_emergency(oss.str());
185  	
186  	    if (g_assert_context) {
187  	      lderr(g_assert_context) << g_assert_msg << std::endl;
188  	      *_dout << oss.str() << dendl;
189  	
190  	      // dump recent only if the abort signal handler won't do it for us
191  	      if (!g_assert_context->_conf->fatal_signal_handlers) {
192  		g_assert_context->_log->dump_recent();
193  	      }
194  	    }
195  	
196  	    abort();
197  	  }
198  	
199  	  [[gnu::cold]] void __ceph_abortf(const char *file, int line,
200  					   const char *func, const char* msg,
201  					   ...)
202  	  {
203  	    ostringstream tss;
204  	    tss << ceph_clock_now();
205  	
206  	    g_assert_condition = "abort";
207  	    g_assert_file = file;
208  	    g_assert_line = line;
209  	    g_assert_func = func;
210  	    g_assert_thread = (unsigned long long)pthread_self();
211  	    ceph_pthread_getname(pthread_self(), g_assert_thread_name,
212  			       sizeof(g_assert_thread_name));
213  	
214  	    BufAppender ba(g_assert_msg, sizeof(g_assert_msg));
215  	    BackTrace *bt = new BackTrace(1);
216  	    ba.printf("%s: In function '%s' thread %llx time %s\n"
217  		      "%s: %d: abort()\n",
218  		      file, func, (unsigned long long)pthread_self(), tss.str().c_str(),
219  		      file, line);
220  	    ba.printf("Abort details: ");
221  	    va_list args;
222  	    va_start(args, msg);
223  	    ba.vprintf(msg, args);
224  	    va_end(args);
225  	    ba.printf("\n");
226  	    dout_emergency(g_assert_msg);
227  	
228  	    // TODO: get rid of this memory allocation.
229  	    ostringstream oss;
230  	    oss << *bt;
231  	    dout_emergency(oss.str());
232  	
233  	    if (g_assert_context) {
234  	      lderr(g_assert_context) << g_assert_msg << std::endl;
235  	      *_dout << oss.str() << dendl;
236  	
237  	      // dump recent only if the abort signal handler won't do it for us
238  	      if (!g_assert_context->_conf->fatal_signal_handlers) {
239  		g_assert_context->_log->dump_recent();
240  	      }
241  	    }
242  	
243  	    abort();
244  	  }
245  	
246  	  [[gnu::cold]] void __ceph_assert_warn(const char *assertion,
247  						const char *file,
248  						int line, const char *func)
249  	  {
250  	    char buf[8096];
251  	    snprintf(buf, sizeof(buf),
252  		     "WARNING: ceph_assert(%s) at: %s: %d: %s()\n",
253  		     assertion, file, line, func);
254  	    dout_emergency(buf);
255  	  }
256  	}
257