PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/ResInsight-2018.01.1/ThirdParty/Ert/lib/util/util_abort_gnu.c

https://bitbucket.org/terraai/resinsight
C | 324 lines | 224 code | 50 blank | 50 comment | 32 complexity | 3c0151fe55bdc82be058c21a8189007a MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1
  1. /*
  2. This file implements the fully fledged util_abort() function which
  3. assumes that the current build has the following features:
  4. fork() : To support calling external program addr2line().
  5. pthread : To serialize the use of util_abort() - not very important.
  6. execinfo.h : The backtrace functions backtrace() and backtrace_symbols().
  7. _GNU_SOURCE : To get the dladdr() function.
  8. If not all these features are availbale the simpler version in
  9. util_abort_simple.c is built instead.
  10. */
  11. /**
  12. This function uses the external program addr2line to convert the
  13. hexadecimal adress given by the libc function backtrace() into a
  14. function name and file:line.
  15. Observe that the function is quite involved, so if util_abort() is
  16. called because something is seriously broken, it might very well fail.
  17. The executable should be found from one line in the backtrace with
  18. the function util_bt_alloc_current_executable(), the argument
  19. bt_symbol is the lines generated by the bt_symbols() function.
  20. This function is purely a helper function for util_abort().
  21. */
  22. #define __USE_GNU // Must be defined to get access to the dladdr() function; Man page says the symbol should be: _GNU_SOURCE but that does not seem to work?
  23. #define _GNU_SOURCE // Must be defined _before_ #include <errno.h> to get the symbol 'program_invocation_name'.
  24. #include <errno.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <ert/util/util.h>
  28. #include <ert/util/test_util.h>
  29. #include <stdbool.h>
  30. #include <dlfcn.h>
  31. #include <execinfo.h>
  32. #include <pthread.h>
  33. #include <pwd.h>
  34. #include <signal.h>
  35. #include <unistd.h>
  36. #if !defined(__GLIBC__) /* note: not same as __GNUC__ */
  37. # if defined (__APPLE__)
  38. # include <stdlib.h> /* alloca */
  39. # include <sys/syslimits.h> /* PATH_MAX */
  40. # include <mach-o/dyld.h> /* _NSGetExecutablePath */
  41. # elif defined (__LINUX__)
  42. # include <stdlib.h> /* alloca */
  43. # include <limits.h> /* PATH_MAX */
  44. # include <unistd.h> /* readlink */
  45. # else
  46. # error No known program_invocation_name in runtime library
  47. # endif
  48. #endif
  49. #define UNDEFINED_FUNCTION "??"
  50. static bool util_addr2line_lookup__(const void * bt_addr , char ** func_name , char ** file_name , int * line_nr, bool subtract_base_adress) {
  51. *func_name = NULL; // If dladdr() succeeds, but addr2line fails the func_name pointer will be set, but the function will return false anyway.
  52. *file_name = NULL;
  53. *line_nr = 0;
  54. {
  55. bool address_found = false;
  56. #if defined(__APPLE__)
  57. return address_found;
  58. #else
  59. Dl_info dl_info;
  60. if (dladdr(bt_addr , &dl_info)) {
  61. const char * executable = dl_info.dli_fname;
  62. *func_name = util_alloc_string_copy( dl_info.dli_sname );
  63. if (util_file_exists( executable )) {
  64. char *stdout_file = util_alloc_tmp_file("/tmp" , "addr2line" , true);
  65. /* 1: Run addr2line application */
  66. {
  67. char ** argv = (char**)util_calloc(3 , sizeof * argv );
  68. argv[0] = util_alloc_string_copy("--functions");
  69. argv[1] = util_alloc_sprintf("--exe=%s" , executable );
  70. {
  71. char * rel_address = (char *) bt_addr;
  72. if (subtract_base_adress)
  73. rel_address -= (size_t) dl_info.dli_fbase;
  74. argv[2] = util_alloc_sprintf("%p" , (void *) rel_address);
  75. }
  76. util_spawn_blocking("addr2line", 3, (const char **) argv, stdout_file, NULL);
  77. util_free_stringlist(argv , 3);
  78. }
  79. /* 2: Parse stdout output */
  80. if (util_file_exists( stdout_file )) {
  81. bool at_eof;
  82. FILE * stream = util_fopen(stdout_file , "r");
  83. char * tmp_fname = util_fscanf_alloc_line(stream , &at_eof);
  84. if (strcmp(tmp_fname , UNDEFINED_FUNCTION) != 0) {
  85. char * stdout_file_name = util_fscanf_alloc_line(stream , &at_eof);
  86. char * line_string = NULL;
  87. util_binary_split_string( stdout_file_name , ":" , false , file_name , &line_string);
  88. if (line_string && util_sscanf_int( line_string , line_nr))
  89. address_found = true;
  90. free( stdout_file_name );
  91. util_safe_free( line_string );
  92. }
  93. free( tmp_fname );
  94. fclose(stream);
  95. }
  96. util_unlink_existing(stdout_file);
  97. free( stdout_file );
  98. }
  99. }
  100. return address_found;
  101. #endif
  102. }
  103. }
  104. bool util_addr2line_lookup(const void * bt_addr , char ** func_name , char ** file_name , int * line_nr) {
  105. if (util_addr2line_lookup__(bt_addr , func_name , file_name , line_nr , false))
  106. return true;
  107. else
  108. return util_addr2line_lookup__(bt_addr , func_name , file_name , line_nr , true);
  109. }
  110. /**
  111. This function prints a message to stream and aborts. The function is
  112. implemented with the help of a variable length argument list - just
  113. like printf(fmt , arg1, arg2 , arg3 ...);
  114. Observe that it is __VERY__ important that the arguments and the
  115. format string match up, otherwise the util_abort() routine will hang
  116. indefinetely; without printing anything to stream.
  117. A backtrace is also included, with the help of the exernal utility
  118. addr2line, this backtrace is converted into usable
  119. function/file/line information (provided the required debugging
  120. information is compiled in).
  121. */
  122. static pthread_mutex_t __abort_mutex = PTHREAD_MUTEX_INITIALIZER; /* Used purely to serialize the util_abort() routine. */
  123. static char * realloc_padding(char * pad_ptr , int pad_length) {
  124. int i;
  125. pad_ptr = (char*)util_realloc( pad_ptr , (pad_length + 1) * sizeof * pad_ptr );
  126. for (i=0; i < pad_length; i++)
  127. pad_ptr[i] = ' ';
  128. pad_ptr[pad_length] = '\0';
  129. return pad_ptr;
  130. }
  131. static void util_fprintf_backtrace(FILE * stream) {
  132. const char * with_linenr_format = " #%02d %s(..) %s in %s:%d\n";
  133. const char * func_format = " #%02d %s(..) %s in ???\n";
  134. const char * unknown_format = " #%02d ???? \n";
  135. const int max_bt = 100;
  136. const int max_func_length = 70;
  137. void *bt_addr[max_bt];
  138. int size,i;
  139. size = backtrace(bt_addr , max_bt);
  140. fprintf(stream , "--------------------------------------------------------------------------------\n");
  141. for (i=0; i < size; i++) {
  142. int line_nr;
  143. char * func_name;
  144. char * file_name;
  145. char * padding = NULL;
  146. if (util_addr2line_lookup(bt_addr[i], &func_name , &file_name , &line_nr)) {
  147. int pad_length;
  148. char * function;
  149. // Seems it can return true - but with func_name == NULL?! Static/inlinded functions?
  150. if (func_name)
  151. function = func_name;
  152. else
  153. function = "???";
  154. pad_length = util_int_max (2, 2 + max_func_length - strlen(function));
  155. padding = realloc_padding( padding , pad_length);
  156. fprintf(stream , with_linenr_format , i , function , padding , file_name , line_nr);
  157. } else {
  158. if (func_name != NULL) {
  159. int pad_length = util_int_max( 2 , 2 + max_func_length - strlen(func_name));
  160. padding = realloc_padding( padding , pad_length);
  161. fprintf(stream , func_format , i , func_name , padding);
  162. } else {
  163. padding = realloc_padding( padding , 2 + max_func_length );
  164. fprintf(stream , unknown_format , i , padding);
  165. }
  166. }
  167. util_safe_free( func_name );
  168. util_safe_free( file_name );
  169. util_safe_free( padding );
  170. }
  171. fprintf(stream , "--------------------------------------------------------------------------------\n");
  172. }
  173. char * util_alloc_dump_filename() {
  174. time_t timestamp = time(NULL);
  175. char day[32];
  176. strftime(day, 32, "%Y%m%d-%H%M%S", localtime(&timestamp));
  177. {
  178. uid_t uid = getuid();
  179. struct passwd *pwd = getpwuid(uid);
  180. char * filename;
  181. if (pwd)
  182. filename = util_alloc_sprintf("/tmp/ert_abort_dump.%s.%s.log", pwd->pw_name, day);
  183. else
  184. filename = util_alloc_sprintf("/tmp/ert_abort_dump.%d.%s.log", uid , day);
  185. return filename;
  186. }
  187. }
  188. #include <setjmp.h>
  189. static jmp_buf jump_buffer;
  190. static char * intercept_function = NULL;
  191. static void util_abort_test_intercept( const char * function ) {
  192. if (intercept_function && (strcmp(function , intercept_function) == 0)) {
  193. longjmp(jump_buffer , 0 );
  194. }
  195. }
  196. jmp_buf * util_abort_test_jump_buffer() {
  197. return &jump_buffer;
  198. }
  199. void util_abort_test_set_intercept_function(const char * function) {
  200. intercept_function = util_realloc_string_copy( intercept_function , function );
  201. }
  202. static char* __abort_program_message;
  203. void util_abort__(const char * file , const char * function , int line , const char * fmt , ...) {
  204. util_abort_test_intercept( function );
  205. pthread_mutex_lock( &__abort_mutex ); /* Abort before unlock() */
  206. {
  207. char * filename = NULL;
  208. FILE * abort_dump = NULL;
  209. if (!getenv("ERT_SHOW_BACKTRACE"))
  210. filename = util_alloc_dump_filename();
  211. if (filename)
  212. abort_dump = fopen(filename, "w");
  213. if (abort_dump == NULL)
  214. abort_dump = stderr;
  215. va_list ap;
  216. va_start(ap , fmt);
  217. fprintf(abort_dump , "\n\n");
  218. fprintf(abort_dump , "Abort called from: %s (%s:%d) \n\n",function , file , line);
  219. fprintf(abort_dump , "Error message: ");
  220. vfprintf(abort_dump , fmt , ap);
  221. fprintf(abort_dump , "\n\n");
  222. va_end(ap);
  223. /*
  224. The backtrace is based on calling the external program
  225. addr2line; the call is based on util_spawn() which is
  226. currently only available on POSIX.
  227. */
  228. const bool include_backtrace = true;
  229. if (include_backtrace) {
  230. if (__abort_program_message != NULL) {
  231. #if !defined(__GLIBC__)
  232. /* allocate a temporary buffer to hold the path */
  233. char* program_invocation_name = alloca (PATH_MAX);
  234. # if defined(__APPLE__)
  235. uint32_t buflen = PATH_MAX;
  236. _NSGetExecutablePath (program_invocation_name, &buflen);
  237. # elif defined(__LINUX__)
  238. readlink ("/proc/self/exe", program_invocation_name, PATH_MAX);
  239. # endif
  240. #endif /* !defined(__GLIBC__) */
  241. fprintf(abort_dump,"--------------------------------------------------------------------------------\n");
  242. fprintf(abort_dump,"%s",__abort_program_message);
  243. fprintf(abort_dump, "Current executable ..: %s\n" , program_invocation_name);
  244. fprintf(abort_dump,"--------------------------------------------------------------------------------\n");
  245. }
  246. fprintf(abort_dump,"\n");
  247. util_fprintf_backtrace( abort_dump );
  248. }
  249. if (abort_dump != stderr) {
  250. util_fclose(abort_dump);
  251. fprintf(stderr, "\nError message: ");
  252. {
  253. va_list args;
  254. va_start(args , fmt);
  255. vfprintf(stderr , fmt , args);
  256. va_end(args);
  257. }
  258. fprintf(stderr, "\nSee file: %s for more details of the crash.\nSetting the environment variable \"ERT_SHOW_BACKTRACE\" will show the backtrace on stderr.\n", filename);
  259. }
  260. chmod(filename, 00644); // -rw-r--r--
  261. free(filename);
  262. }
  263. pthread_mutex_unlock(&__abort_mutex);
  264. signal(SIGABRT, SIG_DFL);
  265. abort();
  266. }
  267. /*****************************************************************/