1 2 /*--------------------------------------------------------------------*/ 3 /*--- Error management for Helgrind. ---*/ 4 /*--- hg_errors.c ---*/ 5 /*--------------------------------------------------------------------*/ 6 7 /* 8 This file is part of Helgrind, a Valgrind tool for detecting errors 9 in threaded programs. 10 11 Copyright (C) 2007-2010 OpenWorks Ltd 12 info (at) open-works.co.uk 13 14 This program is free software; you can redistribute it and/or 15 modify it under the terms of the GNU General Public License as 16 published by the Free Software Foundation; either version 2 of the 17 License, or (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, but 20 WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 27 02111-1307, USA. 28 29 The GNU General Public License is contained in the file COPYING. 30 */ 31 32 #include "pub_tool_basics.h" 33 #include "pub_tool_libcbase.h" 34 #include "pub_tool_libcassert.h" 35 #include "pub_tool_libcprint.h" 36 #include "pub_tool_execontext.h" 37 #include "pub_tool_errormgr.h" 38 #include "pub_tool_wordfm.h" 39 #include "pub_tool_xarray.h" 40 #include "pub_tool_debuginfo.h" 41 #include "pub_tool_threadstate.h" 42 #include "pub_tool_options.h" // VG_(clo_xml) 43 44 #include "hg_basics.h" 45 #include "hg_wordset.h" 46 #include "hg_lock_n_thread.h" 47 #include "libhb.h" 48 #include "hg_errors.h" /* self */ 49 50 51 /*----------------------------------------------------------------*/ 52 /*--- Error management -- storage ---*/ 53 /*----------------------------------------------------------------*/ 54 55 /* maps (by value) strings to a copy of them in ARENA_TOOL */ 56 57 static WordFM* string_table = NULL; 58 59 ULong HG_(stats__string_table_queries) = 0; 60 61 ULong HG_(stats__string_table_get_map_size) ( void ) { 62 return string_table ? (ULong)VG_(sizeFM)(string_table) : 0; 63 } 64 65 static Word string_table_cmp ( UWord s1, UWord s2 ) { 66 return (Word)VG_(strcmp)( (HChar*)s1, (HChar*)s2 ); 67 } 68 69 static HChar* string_table_strdup ( HChar* str ) { 70 HChar* copy = NULL; 71 HG_(stats__string_table_queries)++; 72 if (!str) 73 str = "(null)"; 74 if (!string_table) { 75 string_table = VG_(newFM)( HG_(zalloc), "hg.sts.1", 76 HG_(free), string_table_cmp ); 77 tl_assert(string_table); 78 } 79 if (VG_(lookupFM)( string_table, 80 NULL, (Word*)©, (Word)str )) { 81 tl_assert(copy); 82 if (0) VG_(printf)("string_table_strdup: %p -> %p\n", str, copy ); 83 return copy; 84 } else { 85 copy = HG_(strdup)("hg.sts.2", str); 86 tl_assert(copy); 87 VG_(addToFM)( string_table, (Word)copy, (Word)copy ); 88 return copy; 89 } 90 } 91 92 /* maps from Lock .unique fields to LockP*s */ 93 94 static WordFM* map_LockN_to_P = NULL; 95 96 ULong HG_(stats__LockN_to_P_queries) = 0; 97 98 ULong HG_(stats__LockN_to_P_get_map_size) ( void ) { 99 return map_LockN_to_P ? (ULong)VG_(sizeFM)(map_LockN_to_P) : 0; 100 } 101 102 static Word lock_unique_cmp ( UWord lk1W, UWord lk2W ) 103 { 104 Lock* lk1 = (Lock*)lk1W; 105 Lock* lk2 = (Lock*)lk2W; 106 tl_assert( HG_(is_sane_LockNorP)(lk1) ); 107 tl_assert( HG_(is_sane_LockNorP)(lk2) ); 108 if (lk1->unique < lk2->unique) return -1; 109 if (lk1->unique > lk2->unique) return 1; 110 return 0; 111 } 112 113 static Lock* mk_LockP_from_LockN ( Lock* lkn ) 114 { 115 Lock* lkp = NULL; 116 HG_(stats__LockN_to_P_queries)++; 117 tl_assert( HG_(is_sane_LockN)(lkn) ); 118 if (!map_LockN_to_P) { 119 map_LockN_to_P = VG_(newFM)( HG_(zalloc), "hg.mLPfLN.1", 120 HG_(free), lock_unique_cmp ); 121 tl_assert(map_LockN_to_P); 122 } 123 if (!VG_(lookupFM)( map_LockN_to_P, NULL, (Word*)&lkp, (Word)lkn)) { 124 lkp = HG_(zalloc)( "hg.mLPfLN.2", sizeof(Lock) ); 125 *lkp = *lkn; 126 lkp->admin = NULL; 127 lkp->magic = LockP_MAGIC; 128 /* Forget about the bag of lock holders - don't copy that. 129 Also, acquired_at should be NULL whenever heldBy is, and vice 130 versa. Also forget about the associated libhb synch object. */ 131 lkp->heldW = False; 132 lkp->heldBy = NULL; 133 lkp->acquired_at = NULL; 134 lkp->hbso = NULL; 135 VG_(addToFM)( map_LockN_to_P, (Word)lkp, (Word)lkp ); 136 } 137 tl_assert( HG_(is_sane_LockP)(lkp) ); 138 return lkp; 139 } 140 141 /* Errors: 142 143 race: program counter 144 read or write 145 data size 146 previous state 147 current state 148 149 FIXME: how does state printing interact with lockset gc? 150 Are the locksets in prev/curr state always valid? 151 Ditto question for the threadsets 152 ThreadSets - probably are always valid if Threads 153 are never thrown away. 154 LockSets - could at least print the lockset elements that 155 correspond to actual locks at the time of printing. Hmm. 156 */ 157 158 /* Error kinds */ 159 typedef 160 enum { 161 XE_Race=1101, // race 162 XE_UnlockUnlocked, // unlocking a not-locked lock 163 XE_UnlockForeign, // unlocking a lock held by some other thread 164 XE_UnlockBogus, // unlocking an address not known to be a lock 165 XE_PthAPIerror, // error from the POSIX pthreads API 166 XE_LockOrder, // lock order error 167 XE_Misc // misc other error (w/ string to describe it) 168 } 169 XErrorTag; 170 171 /* Extra contexts for kinds */ 172 typedef 173 struct { 174 XErrorTag tag; 175 union { 176 struct { 177 Addr data_addr; 178 Int szB; 179 Bool isWrite; 180 Thread* thr; 181 /* descr1/2 provide a description of stack/global locs */ 182 XArray* descr1; /* XArray* of HChar */ 183 XArray* descr2; /* XArray* of HChar */ 184 /* halloc/haddr/hszB describe the addr if it is a heap block. */ 185 ExeContext* hctxt; 186 Addr haddr; 187 SizeT hszB; 188 /* h1_* and h2_* provide some description of a previously 189 observed access with which we are conflicting. */ 190 Thread* h1_ct; /* non-NULL means h1 info present */ 191 ExeContext* h1_ct_mbsegstartEC; 192 ExeContext* h1_ct_mbsegendEC; 193 Thread* h2_ct; /* non-NULL means h2 info present */ 194 ExeContext* h2_ct_accEC; 195 Int h2_ct_accSzB; 196 Bool h2_ct_accIsW; 197 } Race; 198 struct { 199 Thread* thr; /* doing the unlocking */ 200 Lock* lock; /* lock (that is already unlocked) */ 201 } UnlockUnlocked; 202 struct { 203 Thread* thr; /* doing the unlocking */ 204 Thread* owner; /* thread that actually holds the lock */ 205 Lock* lock; /* lock (that is held by 'owner') */ 206 } UnlockForeign; 207 struct { 208 Thread* thr; /* doing the unlocking */ 209 Addr lock_ga; /* purported address of the lock */ 210 } UnlockBogus; 211 struct { 212 Thread* thr; 213 HChar* fnname; /* persistent, in tool-arena */ 214 Word err; /* pth error code */ 215 HChar* errstr; /* persistent, in tool-arena */ 216 } PthAPIerror; 217 struct { 218 Thread* thr; 219 Addr before_ga; /* always locked first in prog. history */ 220 Addr after_ga; 221 ExeContext* before_ec; 222 ExeContext* after_ec; 223 } LockOrder; 224 struct { 225 Thread* thr; 226 HChar* errstr; /* persistent, in tool-arena */ 227 HChar* auxstr; /* optional, persistent, in tool-arena */ 228 ExeContext* auxctx; /* optional */ 229 } Misc; 230 } XE; 231 } 232 XError; 233 234 static void init_XError ( XError* xe ) { 235 VG_(memset)(xe, 0, sizeof(*xe) ); 236 xe->tag = XE_Race-1; /* bogus */ 237 } 238 239 240 /* Extensions of suppressions */ 241 typedef 242 enum { 243 XS_Race=1201, /* race */ 244 XS_FreeMemLock, 245 XS_UnlockUnlocked, 246 XS_UnlockForeign, 247 XS_UnlockBogus, 248 XS_PthAPIerror, 249 XS_LockOrder, 250 XS_Misc 251 } 252 XSuppTag; 253 254 255 /* Updates the copy with address info if necessary. */ 256 UInt HG_(update_extra) ( Error* err ) 257 { 258 XError* xe = (XError*)VG_(get_error_extra)(err); 259 tl_assert(xe); 260 //if (extra != NULL && Undescribed == extra->addrinfo.akind) { 261 // describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) ); 262 //} 263 264 if (xe->tag == XE_Race) { 265 266 /* See if we can come up with a source level description of the 267 raced-upon address. This is potentially expensive, which is 268 why it's only done at the update_extra point, not when the 269 error is initially created. */ 270 static Int xxx = 0; 271 xxx++; 272 if (0) 273 VG_(printf)("HG_(update_extra): " 274 "%d conflicting-event queries\n", xxx); 275 276 tl_assert(!xe->XE.Race.hctxt); 277 tl_assert(!xe->XE.Race.descr1); 278 tl_assert(!xe->XE.Race.descr2); 279 280 /* First, see if it's in any heap block. Unfortunately this 281 means a linear search through all allocated heap blocks. The 282 assertion says that if it's detected as a heap block, then we 283 must have an allocation context for it, since all heap blocks 284 should have an allocation context. */ 285 Bool is_heapblock 286 = HG_(mm_find_containing_block)( 287 &xe->XE.Race.hctxt, &xe->XE.Race.haddr, &xe->XE.Race.hszB, 288 xe->XE.Race.data_addr 289 ); 290 tl_assert(is_heapblock == (xe->XE.Race.hctxt != NULL)); 291 292 if (!xe->XE.Race.hctxt) { 293 /* It's not in any heap block. See if we can map it to a 294 stack or global symbol. */ 295 296 xe->XE.Race.descr1 297 = VG_(newXA)( HG_(zalloc), "hg.update_extra.Race.descr1", 298 HG_(free), sizeof(HChar) ); 299 xe->XE.Race.descr2 300 = VG_(newXA)( HG_(zalloc), "hg.update_extra.Race.descr2", 301 HG_(free), sizeof(HChar) ); 302 303 (void) VG_(get_data_description)( xe->XE.Race.descr1, 304 xe->XE.Race.descr2, 305 xe->XE.Race.data_addr ); 306 307 /* If there's nothing in descr1/2, free it. Why is it safe to 308 to VG_(indexXA) at zero here? Because 309 VG_(get_data_description) guarantees to zero terminate 310 descr1/2 regardless of the outcome of the call. So there's 311 always at least one element in each XA after the call. 312 */ 313 if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Race.descr1, 0 ))) { 314 VG_(deleteXA)( xe->XE.Race.descr1 ); 315 xe->XE.Race.descr1 = NULL; 316 } 317 if (0 == VG_(strlen)( VG_(indexXA)( xe->XE.Race.descr2, 0 ))) { 318 VG_(deleteXA)( xe->XE.Race.descr2 ); 319 xe->XE.Race.descr2 = NULL; 320 } 321 } 322 323 /* And poke around in the conflicting-event map, to see if we 324 can rustle up a plausible-looking conflicting memory access 325 to show. */ 326 if (HG_(clo_history_level) >= 2) { 327 Thr* thrp = NULL; 328 ExeContext* wherep = NULL; 329 Addr acc_addr = xe->XE.Race.data_addr; 330 Int acc_szB = xe->XE.Race.szB; 331 Thr* acc_thr = xe->XE.Race.thr->hbthr; 332 Bool acc_isW = xe->XE.Race.isWrite; 333 SizeT conf_szB = 0; 334 Bool conf_isW = False; 335 tl_assert(!xe->XE.Race.h2_ct_accEC); 336 tl_assert(!xe->XE.Race.h2_ct); 337 if (libhb_event_map_lookup( 338 &wherep, &thrp, &conf_szB, &conf_isW, 339 acc_thr, acc_addr, acc_szB, acc_isW )) { 340 Thread* threadp; 341 tl_assert(wherep); 342 tl_assert(thrp); 343 threadp = libhb_get_Thr_opaque( thrp ); 344 tl_assert(threadp); 345 xe->XE.Race.h2_ct_accEC = wherep; 346 xe->XE.Race.h2_ct = threadp; 347 xe->XE.Race.h2_ct_accSzB = (Int)conf_szB; 348 xe->XE.Race.h2_ct_accIsW = conf_isW; 349 } 350 } 351 352 // both NULL or both non-NULL 353 tl_assert( (!!xe->XE.Race.h2_ct) == (!!xe->XE.Race.h2_ct_accEC) ); 354 } 355 356 return sizeof(XError); 357 } 358 359 void HG_(record_error_Race) ( Thread* thr, 360 Addr data_addr, Int szB, Bool isWrite, 361 Thread* h1_ct, 362 ExeContext* h1_ct_segstart, 363 ExeContext* h1_ct_mbsegendEC ) 364 { 365 XError xe; 366 tl_assert( HG_(is_sane_Thread)(thr) ); 367 368 # if defined(VGO_linux) 369 /* Skip any races on locations apparently in GOTPLT sections. This 370 is said to be caused by ld.so poking PLT table entries (or 371 whatever) when it writes the resolved address of a dynamically 372 linked routine, into the table (or whatever) when it is called 373 for the first time. */ 374 { 375 VgSectKind sect = VG_(DebugInfo_sect_kind)( NULL, 0, data_addr ); 376 if (0) VG_(printf)("XXXXXXXXX RACE on %#lx %s\n", 377 data_addr, VG_(pp_SectKind)(sect)); 378 /* SectPLT is required on ???-linux */ 379 if (sect == Vg_SectGOTPLT) return; 380 /* SectPLT is required on ppc32/64-linux */ 381 if (sect == Vg_SectPLT) return; 382 } 383 # endif 384 385 init_XError(&xe); 386 xe.tag = XE_Race; 387 xe.XE.Race.data_addr = data_addr; 388 xe.XE.Race.szB = szB; 389 xe.XE.Race.isWrite = isWrite; 390 xe.XE.Race.thr = thr; 391 tl_assert(isWrite == False || isWrite == True); 392 tl_assert(szB == 8 || szB == 4 || szB == 2 || szB == 1); 393 /* Skip on the detailed description of the raced-on address at this 394 point; it's expensive. Leave it for the update_extra function 395 if we ever make it that far. */ 396 tl_assert(xe.XE.Race.descr1 == NULL); 397 tl_assert(xe.XE.Race.descr2 == NULL); 398 // FIXME: tid vs thr 399 // Skip on any of the conflicting-access info at this point. 400 // It's expensive to obtain, and this error is more likely than 401 // not to be discarded. We'll fill these fields in in 402 // HG_(update_extra) just above, assuming the error ever makes 403 // it that far (unlikely). 404 xe.XE.Race.h2_ct_accSzB = 0; 405 xe.XE.Race.h2_ct_accIsW = False; 406 xe.XE.Race.h2_ct_accEC = NULL; 407 xe.XE.Race.h2_ct = NULL; 408 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); 409 tl_assert( thr->coretid != VG_INVALID_THREADID ); 410 411 xe.XE.Race.h1_ct = h1_ct; 412 xe.XE.Race.h1_ct_mbsegstartEC = h1_ct_segstart; 413 xe.XE.Race.h1_ct_mbsegendEC = h1_ct_mbsegendEC; 414 415 VG_(maybe_record_error)( thr->coretid, 416 XE_Race, data_addr, NULL, &xe ); 417 } 418 419 void HG_(record_error_UnlockUnlocked) ( Thread* thr, Lock* lk ) 420 { 421 XError xe; 422 tl_assert( HG_(is_sane_Thread)(thr) ); 423 tl_assert( HG_(is_sane_LockN)(lk) ); 424 init_XError(&xe); 425 xe.tag = XE_UnlockUnlocked; 426 xe.XE.UnlockUnlocked.thr = thr; 427 xe.XE.UnlockUnlocked.lock = mk_LockP_from_LockN(lk); 428 // FIXME: tid vs thr 429 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); 430 tl_assert( thr->coretid != VG_INVALID_THREADID ); 431 VG_(maybe_record_error)( thr->coretid, 432 XE_UnlockUnlocked, 0, NULL, &xe ); 433 } 434 435 void HG_(record_error_UnlockForeign) ( Thread* thr, 436 Thread* owner, Lock* lk ) 437 { 438 XError xe; 439 tl_assert( HG_(is_sane_Thread)(thr) ); 440 tl_assert( HG_(is_sane_Thread)(owner) ); 441 tl_assert( HG_(is_sane_LockN)(lk) ); 442 init_XError(&xe); 443 xe.tag = XE_UnlockForeign; 444 xe.XE.UnlockForeign.thr = thr; 445 xe.XE.UnlockForeign.owner = owner; 446 xe.XE.UnlockForeign.lock = mk_LockP_from_LockN(lk); 447 // FIXME: tid vs thr 448 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); 449 tl_assert( thr->coretid != VG_INVALID_THREADID ); 450 VG_(maybe_record_error)( thr->coretid, 451 XE_UnlockForeign, 0, NULL, &xe ); 452 } 453 454 void HG_(record_error_UnlockBogus) ( Thread* thr, Addr lock_ga ) 455 { 456 XError xe; 457 tl_assert( HG_(is_sane_Thread)(thr) ); 458 init_XError(&xe); 459 xe.tag = XE_UnlockBogus; 460 xe.XE.UnlockBogus.thr = thr; 461 xe.XE.UnlockBogus.lock_ga = lock_ga; 462 // FIXME: tid vs thr 463 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); 464 tl_assert( thr->coretid != VG_INVALID_THREADID ); 465 VG_(maybe_record_error)( thr->coretid, 466 XE_UnlockBogus, 0, NULL, &xe ); 467 } 468 469 void HG_(record_error_LockOrder)( 470 Thread* thr, Addr before_ga, Addr after_ga, 471 ExeContext* before_ec, ExeContext* after_ec 472 ) 473 { 474 XError xe; 475 tl_assert( HG_(is_sane_Thread)(thr) ); 476 if (!HG_(clo_track_lockorders)) 477 return; 478 init_XError(&xe); 479 xe.tag = XE_LockOrder; 480 xe.XE.LockOrder.thr = thr; 481 xe.XE.LockOrder.before_ga = before_ga; 482 xe.XE.LockOrder.before_ec = before_ec; 483 xe.XE.LockOrder.after_ga = after_ga; 484 xe.XE.LockOrder.after_ec = after_ec; 485 // FIXME: tid vs thr 486 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); 487 tl_assert( thr->coretid != VG_INVALID_THREADID ); 488 VG_(maybe_record_error)( thr->coretid, 489 XE_LockOrder, 0, NULL, &xe ); 490 } 491 492 void HG_(record_error_PthAPIerror) ( Thread* thr, HChar* fnname, 493 Word err, HChar* errstr ) 494 { 495 XError xe; 496 tl_assert( HG_(is_sane_Thread)(thr) ); 497 tl_assert(fnname); 498 tl_assert(errstr); 499 init_XError(&xe); 500 xe.tag = XE_PthAPIerror; 501 xe.XE.PthAPIerror.thr = thr; 502 xe.XE.PthAPIerror.fnname = string_table_strdup(fnname); 503 xe.XE.PthAPIerror.err = err; 504 xe.XE.PthAPIerror.errstr = string_table_strdup(errstr); 505 // FIXME: tid vs thr 506 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); 507 tl_assert( thr->coretid != VG_INVALID_THREADID ); 508 VG_(maybe_record_error)( thr->coretid, 509 XE_PthAPIerror, 0, NULL, &xe ); 510 } 511 512 void HG_(record_error_Misc_w_aux) ( Thread* thr, HChar* errstr, 513 HChar* auxstr, ExeContext* auxctx ) 514 { 515 XError xe; 516 tl_assert( HG_(is_sane_Thread)(thr) ); 517 tl_assert(errstr); 518 init_XError(&xe); 519 xe.tag = XE_Misc; 520 xe.XE.Misc.thr = thr; 521 xe.XE.Misc.errstr = string_table_strdup(errstr); 522 xe.XE.Misc.auxstr = auxstr ? string_table_strdup(auxstr) : NULL; 523 xe.XE.Misc.auxctx = auxctx; 524 // FIXME: tid vs thr 525 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) ); 526 tl_assert( thr->coretid != VG_INVALID_THREADID ); 527 VG_(maybe_record_error)( thr->coretid, 528 XE_Misc, 0, NULL, &xe ); 529 } 530 531 void HG_(record_error_Misc) ( Thread* thr, HChar* errstr ) 532 { 533 HG_(record_error_Misc_w_aux)(thr, errstr, NULL, NULL); 534 } 535 536 Bool HG_(eq_Error) ( VgRes not_used, Error* e1, Error* e2 ) 537 { 538 XError *xe1, *xe2; 539 540 tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); 541 542 xe1 = (XError*)VG_(get_error_extra)(e1); 543 xe2 = (XError*)VG_(get_error_extra)(e2); 544 tl_assert(xe1); 545 tl_assert(xe2); 546 547 switch (VG_(get_error_kind)(e1)) { 548 case XE_Race: 549 return xe1->XE.Race.szB == xe2->XE.Race.szB 550 && xe1->XE.Race.isWrite == xe2->XE.Race.isWrite 551 && (HG_(clo_cmp_race_err_addrs) 552 ? xe1->XE.Race.data_addr == xe2->XE.Race.data_addr 553 : True); 554 case XE_UnlockUnlocked: 555 return xe1->XE.UnlockUnlocked.thr == xe2->XE.UnlockUnlocked.thr 556 && xe1->XE.UnlockUnlocked.lock == xe2->XE.UnlockUnlocked.lock; 557 case XE_UnlockForeign: 558 return xe1->XE.UnlockForeign.thr == xe2->XE.UnlockForeign.thr 559 && xe1->XE.UnlockForeign.owner == xe2->XE.UnlockForeign.owner 560 && xe1->XE.UnlockForeign.lock == xe2->XE.UnlockForeign.lock; 561 case XE_UnlockBogus: 562 return xe1->XE.UnlockBogus.thr == xe2->XE.UnlockBogus.thr 563 && xe1->XE.UnlockBogus.lock_ga == xe2->XE.UnlockBogus.lock_ga; 564 case XE_PthAPIerror: 565 return xe1->XE.PthAPIerror.thr == xe2->XE.PthAPIerror.thr 566 && 0==VG_(strcmp)(xe1->XE.PthAPIerror.fnname, 567 xe2->XE.PthAPIerror.fnname) 568 && xe1->XE.PthAPIerror.err == xe2->XE.PthAPIerror.err; 569 case XE_LockOrder: 570 return xe1->XE.LockOrder.thr == xe2->XE.LockOrder.thr; 571 case XE_Misc: 572 return xe1->XE.Misc.thr == xe2->XE.Misc.thr 573 && 0==VG_(strcmp)(xe1->XE.Misc.errstr, xe2->XE.Misc.errstr); 574 default: 575 tl_assert(0); 576 } 577 578 /*NOTREACHED*/ 579 tl_assert(0); 580 } 581 582 583 /*----------------------------------------------------------------*/ 584 /*--- Error management -- printing ---*/ 585 /*----------------------------------------------------------------*/ 586 587 /* Do a printf-style operation on either the XML or normal output 588 channel, depending on the setting of VG_(clo_xml). 589 */ 590 static void emit_WRK ( HChar* format, va_list vargs ) 591 { 592 if (VG_(clo_xml)) { 593 VG_(vprintf_xml)(format, vargs); 594 } else { 595 VG_(vmessage)(Vg_UserMsg, format, vargs); 596 } 597 } 598 static void emit ( HChar* format, ... ) PRINTF_CHECK(1, 2); 599 static void emit ( HChar* format, ... ) 600 { 601 va_list vargs; 602 va_start(vargs, format); 603 emit_WRK(format, vargs); 604 va_end(vargs); 605 } 606 static void emit_no_f_c ( HChar* format, ... ) 607 { 608 va_list vargs; 609 va_start(vargs, format); 610 emit_WRK(format, vargs); 611 va_end(vargs); 612 } 613 614 615 /* Announce (that is, print the point-of-creation) of 'thr'. Only do 616 this once, as we only want to see these announcements once per 617 thread. Returned Bool indicates whether or not an announcement was 618 made. 619 */ 620 static Bool announce_one_thread ( Thread* thr ) 621 { 622 tl_assert(HG_(is_sane_Thread)(thr)); 623 tl_assert(thr->errmsg_index >= 1); 624 if (thr->announced) 625 return False; 626 627 if (VG_(clo_xml)) { 628 629 VG_(printf_xml)("<announcethread>\n"); 630 VG_(printf_xml)(" <hthreadid>%d</hthreadid>\n", thr->errmsg_index); 631 if (thr->errmsg_index == 1) { 632 tl_assert(thr->created_at == NULL); 633 VG_(printf_xml)(" <isrootthread></isrootthread>\n"); 634 } else { 635 tl_assert(thr->created_at != NULL); 636 VG_(pp_ExeContext)( thr->created_at ); 637 } 638 VG_(printf_xml)("</announcethread>\n\n"); 639 640 } else { 641 642 if (thr->errmsg_index == 1) { 643 tl_assert(thr->created_at == NULL); 644 VG_(message)(Vg_UserMsg, 645 "Thread #%d is the program's root thread\n", 646 thr->errmsg_index); 647 } else { 648 tl_assert(thr->created_at != NULL); 649 VG_(message)(Vg_UserMsg, "Thread #%d was created\n", 650 thr->errmsg_index); 651 VG_(pp_ExeContext)( thr->created_at ); 652 } 653 VG_(message)(Vg_UserMsg, "\n"); 654 655 } 656 657 thr->announced = True; 658 return True; 659 } 660 661 662 /* This is the "this error is due to be printed shortly; so have a 663 look at it any print any preamble you want" function. We use it to 664 announce any previously un-announced threads in the upcoming error 665 message. 666 */ 667 void HG_(before_pp_Error) ( Error* err ) 668 { 669 XError* xe; 670 tl_assert(err); 671 xe = (XError*)VG_(get_error_extra)(err); 672 tl_assert(xe); 673 674 switch (VG_(get_error_kind)(err)) { 675 case XE_Misc: 676 announce_one_thread( xe->XE.Misc.thr ); 677 break; 678 case XE_LockOrder: 679 announce_one_thread( xe->XE.LockOrder.thr ); 680 break; 681 case XE_PthAPIerror: 682 announce_one_thread( xe->XE.PthAPIerror.thr ); 683 break; 684 case XE_UnlockBogus: 685 announce_one_thread( xe->XE.UnlockBogus.thr ); 686 break; 687 case XE_UnlockForeign: 688 announce_one_thread( xe->XE.UnlockForeign.thr ); 689 announce_one_thread( xe->XE.UnlockForeign.owner ); 690 break; 691 case XE_UnlockUnlocked: 692 announce_one_thread( xe->XE.UnlockUnlocked.thr ); 693 break; 694 case XE_Race: 695 announce_one_thread( xe->XE.Race.thr ); 696 if (xe->XE.Race.h2_ct) 697 announce_one_thread( xe->XE.Race.h2_ct ); 698 if (xe->XE.Race.h1_ct) 699 announce_one_thread( xe->XE.Race.h1_ct ); 700 break; 701 default: 702 tl_assert(0); 703 } 704 } 705 706 void HG_(pp_Error) ( Error* err ) 707 { 708 const Bool xml = VG_(clo_xml); /* a shorthand, that's all */ 709 710 XError *xe = (XError*)VG_(get_error_extra)(err); 711 tl_assert(xe); 712 713 switch (VG_(get_error_kind)(err)) { 714 715 case XE_Misc: { 716 tl_assert( HG_(is_sane_Thread)( xe->XE.Misc.thr ) ); 717 718 if (xml) { 719 720 emit( " <kind>Misc</kind>\n"); 721 emit( " <xwhat>\n" ); 722 emit( " <text>Thread #%d: %s</text>\n", 723 (Int)xe->XE.Misc.thr->errmsg_index, 724 xe->XE.Misc.errstr ); 725 emit( " <hthreadid>%d</hthreadid>\n", 726 (Int)xe->XE.Misc.thr->errmsg_index ); 727 emit( " </xwhat>\n" ); 728 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 729 if (xe->XE.Misc.auxstr) { 730 emit(" <auxwhat>%s</auxwhat>\n", xe->XE.Misc.auxstr); 731 if (xe->XE.Misc.auxctx) 732 VG_(pp_ExeContext)( xe->XE.Misc.auxctx ); 733 } 734 735 } else { 736 737 emit( "Thread #%d: %s\n", 738 (Int)xe->XE.Misc.thr->errmsg_index, 739 xe->XE.Misc.errstr ); 740 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 741 if (xe->XE.Misc.auxstr) { 742 emit(" %s\n", xe->XE.Misc.auxstr); 743 if (xe->XE.Misc.auxctx) 744 VG_(pp_ExeContext)( xe->XE.Misc.auxctx ); 745 } 746 747 } 748 break; 749 } 750 751 case XE_LockOrder: { 752 tl_assert( HG_(is_sane_Thread)( xe->XE.LockOrder.thr ) ); 753 754 if (xml) { 755 756 emit( " <kind>LockOrder</kind>\n"); 757 emit( " <xwhat>\n" ); 758 emit( " <text>Thread #%d: lock order \"%p before %p\" " 759 "violated</text>\n", 760 (Int)xe->XE.LockOrder.thr->errmsg_index, 761 (void*)xe->XE.LockOrder.before_ga, 762 (void*)xe->XE.LockOrder.after_ga ); 763 emit( " <hthreadid>%d</hthreadid>\n", 764 (Int)xe->XE.LockOrder.thr->errmsg_index ); 765 emit( " </xwhat>\n" ); 766 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 767 if (xe->XE.LockOrder.before_ec && xe->XE.LockOrder.after_ec) { 768 emit( " <auxwhat>Required order was established by " 769 "acquisition of lock at %p</auxwhat>\n", 770 (void*)xe->XE.LockOrder.before_ga ); 771 VG_(pp_ExeContext)( xe->XE.LockOrder.before_ec ); 772 emit( " <auxwhat>followed by a later acquisition " 773 "of lock at %p</auxwhat>\n", 774 (void*)xe->XE.LockOrder.after_ga ); 775 VG_(pp_ExeContext)( xe->XE.LockOrder.after_ec ); 776 } 777 778 } else { 779 780 emit( "Thread #%d: lock order \"%p before %p\" violated\n", 781 (Int)xe->XE.LockOrder.thr->errmsg_index, 782 (void*)xe->XE.LockOrder.before_ga, 783 (void*)xe->XE.LockOrder.after_ga ); 784 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 785 if (xe->XE.LockOrder.before_ec && xe->XE.LockOrder.after_ec) { 786 emit( " Required order was established by " 787 "acquisition of lock at %p\n", 788 (void*)xe->XE.LockOrder.before_ga ); 789 VG_(pp_ExeContext)( xe->XE.LockOrder.before_ec ); 790 emit( " followed by a later acquisition of lock at %p\n", 791 (void*)xe->XE.LockOrder.after_ga ); 792 VG_(pp_ExeContext)( xe->XE.LockOrder.after_ec ); 793 } 794 795 } 796 797 break; 798 } 799 800 case XE_PthAPIerror: { 801 tl_assert( HG_(is_sane_Thread)( xe->XE.PthAPIerror.thr ) ); 802 803 if (xml) { 804 805 emit( " <kind>PthAPIerror</kind>\n"); 806 emit( " <xwhat>\n" ); 807 emit_no_f_c( 808 " <text>Thread #%d's call to %t failed</text>\n", 809 (Int)xe->XE.PthAPIerror.thr->errmsg_index, 810 xe->XE.PthAPIerror.fnname ); 811 emit( " <hthreadid>%d</hthreadid>\n", 812 (Int)xe->XE.PthAPIerror.thr->errmsg_index ); 813 emit( " </xwhat>\n" ); 814 emit( " <what>with error code %ld (%s)</what>\n", 815 xe->XE.PthAPIerror.err, xe->XE.PthAPIerror.errstr ); 816 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 817 818 } else { 819 820 emit_no_f_c( "Thread #%d's call to %t failed\n", 821 (Int)xe->XE.PthAPIerror.thr->errmsg_index, 822 xe->XE.PthAPIerror.fnname ); 823 emit( " with error code %ld (%s)\n", 824 xe->XE.PthAPIerror.err, xe->XE.PthAPIerror.errstr ); 825 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 826 827 } 828 829 break; 830 } 831 832 case XE_UnlockBogus: { 833 tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockBogus.thr ) ); 834 835 if (xml) { 836 837 emit( " <kind>UnlockBogus</kind>\n"); 838 emit( " <xwhat>\n" ); 839 emit( " <text>Thread #%d unlocked an invalid " 840 "lock at %p</text>\n", 841 (Int)xe->XE.UnlockBogus.thr->errmsg_index, 842 (void*)xe->XE.UnlockBogus.lock_ga ); 843 emit( " <hthreadid>%d</hthreadid>\n", 844 (Int)xe->XE.UnlockBogus.thr->errmsg_index ); 845 emit( " </xwhat>\n" ); 846 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 847 848 } else { 849 850 emit( "Thread #%d unlocked an invalid lock at %p\n", 851 (Int)xe->XE.UnlockBogus.thr->errmsg_index, 852 (void*)xe->XE.UnlockBogus.lock_ga ); 853 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 854 855 } 856 857 break; 858 } 859 860 case XE_UnlockForeign: { 861 tl_assert( HG_(is_sane_LockP)( xe->XE.UnlockForeign.lock ) ); 862 tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockForeign.owner ) ); 863 tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockForeign.thr ) ); 864 865 if (xml) { 866 867 emit( " <kind>UnlockForeign</kind>\n"); 868 emit( " <xwhat>\n" ); 869 emit( " <text>Thread #%d unlocked lock at %p " 870 "currently held by thread #%d</text>\n", 871 (Int)xe->XE.UnlockForeign.thr->errmsg_index, 872 (void*)xe->XE.UnlockForeign.lock->guestaddr, 873 (Int)xe->XE.UnlockForeign.owner->errmsg_index ); 874 emit( " <hthreadid>%d</hthreadid>\n", 875 (Int)xe->XE.UnlockForeign.thr->errmsg_index ); 876 emit( " <hthreadid>%d</hthreadid>\n", 877 (Int)xe->XE.UnlockForeign.owner->errmsg_index ); 878 emit( " </xwhat>\n" ); 879 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 880 881 if (xe->XE.UnlockForeign.lock->appeared_at) { 882 emit( " <auxwhat>Lock at %p was first observed</auxwhat>\n", 883 (void*)xe->XE.UnlockForeign.lock->guestaddr ); 884 VG_(pp_ExeContext)( xe->XE.UnlockForeign.lock->appeared_at ); 885 } 886 887 } else { 888 889 emit( "Thread #%d unlocked lock at %p " 890 "currently held by thread #%d\n", 891 (Int)xe->XE.UnlockForeign.thr->errmsg_index, 892 (void*)xe->XE.UnlockForeign.lock->guestaddr, 893 (Int)xe->XE.UnlockForeign.owner->errmsg_index ); 894 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 895 if (xe->XE.UnlockForeign.lock->appeared_at) { 896 emit( " Lock at %p was first observed\n", 897 (void*)xe->XE.UnlockForeign.lock->guestaddr ); 898 VG_(pp_ExeContext)( xe->XE.UnlockForeign.lock->appeared_at ); 899 } 900 901 } 902 903 break; 904 } 905 906 case XE_UnlockUnlocked: { 907 tl_assert( HG_(is_sane_LockP)( xe->XE.UnlockUnlocked.lock ) ); 908 tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockUnlocked.thr ) ); 909 910 if (xml) { 911 912 emit( " <kind>UnlockUnlocked</kind>\n"); 913 emit( " <xwhat>\n" ); 914 emit( " <text>Thread #%d unlocked a " 915 "not-locked lock at %p</text>\n", 916 (Int)xe->XE.UnlockUnlocked.thr->errmsg_index, 917 (void*)xe->XE.UnlockUnlocked.lock->guestaddr ); 918 emit( " <hthreadid>%d</hthreadid>\n", 919 (Int)xe->XE.UnlockUnlocked.thr->errmsg_index ); 920 emit( " </xwhat>\n" ); 921 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 922 if (xe->XE.UnlockUnlocked.lock->appeared_at) { 923 emit( " <auxwhat>Lock at %p was first observed</auxwhat>\n", 924 (void*)xe->XE.UnlockUnlocked.lock->guestaddr ); 925 VG_(pp_ExeContext)( xe->XE.UnlockUnlocked.lock->appeared_at ); 926 } 927 928 } else { 929 930 emit( "Thread #%d unlocked a not-locked lock at %p\n", 931 (Int)xe->XE.UnlockUnlocked.thr->errmsg_index, 932 (void*)xe->XE.UnlockUnlocked.lock->guestaddr ); 933 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 934 if (xe->XE.UnlockUnlocked.lock->appeared_at) { 935 emit( " Lock at %p was first observed\n", 936 (void*)xe->XE.UnlockUnlocked.lock->guestaddr ); 937 VG_(pp_ExeContext)( xe->XE.UnlockUnlocked.lock->appeared_at ); 938 } 939 940 } 941 942 break; 943 } 944 945 case XE_Race: { 946 Addr err_ga; 947 HChar* what; 948 Int szB; 949 what = xe->XE.Race.isWrite ? "write" : "read"; 950 szB = xe->XE.Race.szB; 951 err_ga = VG_(get_error_address)(err); 952 953 tl_assert( HG_(is_sane_Thread)( xe->XE.Race.thr )); 954 if (xe->XE.Race.h2_ct) 955 tl_assert( HG_(is_sane_Thread)( xe->XE.Race.h2_ct )); 956 957 if (xml) { 958 959 /* ------ XML ------ */ 960 emit( " <kind>Race</kind>\n" ); 961 emit( " <xwhat>\n" ); 962 emit( " <text>Possible data race during %s of size %d " 963 "at %#lx by thread #%d</text>\n", 964 what, szB, err_ga, (Int)xe->XE.Race.thr->errmsg_index ); 965 emit( " <hthreadid>%d</hthreadid>\n", 966 (Int)xe->XE.Race.thr->errmsg_index ); 967 emit( " </xwhat>\n" ); 968 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 969 970 if (xe->XE.Race.h2_ct) { 971 tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra 972 emit( " <xauxwhat>\n"); 973 emit( " <text>This conflicts with a previous %s of size %d " 974 "by thread #%d</text>\n", 975 xe->XE.Race.h2_ct_accIsW ? "write" : "read", 976 xe->XE.Race.h2_ct_accSzB, 977 xe->XE.Race.h2_ct->errmsg_index ); 978 emit( " <hthreadid>%d</hthreadid>\n", 979 xe->XE.Race.h2_ct->errmsg_index); 980 emit(" </xauxwhat>\n"); 981 VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC ); 982 } 983 984 if (xe->XE.Race.h1_ct) { 985 emit( " <xauxwhat>\n"); 986 emit( " <text>This conflicts with a previous access " 987 "by thread #%d, after</text>\n", 988 xe->XE.Race.h1_ct->errmsg_index ); 989 emit( " <hthreadid>%d</hthreadid>\n", 990 xe->XE.Race.h1_ct->errmsg_index ); 991 emit(" </xauxwhat>\n"); 992 if (xe->XE.Race.h1_ct_mbsegstartEC) { 993 VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegstartEC ); 994 } else { 995 emit( " <auxwhat>(the start of the thread)</auxwhat>\n" ); 996 } 997 emit( " <auxwhat>but before</auxwhat>\n" ); 998 if (xe->XE.Race.h1_ct_mbsegendEC) { 999 VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegendEC ); 1000 } else { 1001 emit( " <auxwhat>(the end of the the thread)</auxwhat>\n" ); 1002 } 1003 } 1004 1005 } else { 1006 1007 /* ------ Text ------ */ 1008 emit( "Possible data race during %s of size %d " 1009 "at %#lx by thread #%d\n", 1010 what, szB, err_ga, (Int)xe->XE.Race.thr->errmsg_index ); 1011 VG_(pp_ExeContext)( VG_(get_error_where)(err) ); 1012 1013 if (xe->XE.Race.h2_ct) { 1014 tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra 1015 emit( " This conflicts with a previous %s of size %d " 1016 "by thread #%d\n", 1017 xe->XE.Race.h2_ct_accIsW ? "write" : "read", 1018 xe->XE.Race.h2_ct_accSzB, 1019 xe->XE.Race.h2_ct->errmsg_index ); 1020 VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC ); 1021 } 1022 1023 if (xe->XE.Race.h1_ct) { 1024 emit( " This conflicts with a previous access by thread #%d, " 1025 "after\n", 1026 xe->XE.Race.h1_ct->errmsg_index ); 1027 if (xe->XE.Race.h1_ct_mbsegstartEC) { 1028 VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegstartEC ); 1029 } else { 1030 emit( " (the start of the thread)\n" ); 1031 } 1032 emit( " but before\n" ); 1033 if (xe->XE.Race.h1_ct_mbsegendEC) { 1034 VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegendEC ); 1035 } else { 1036 emit( " (the end of the the thread)\n" ); 1037 } 1038 } 1039 1040 } 1041 1042 /* If we have a description of the address in terms of a heap 1043 block, show it. */ 1044 if (xe->XE.Race.hctxt) { 1045 SizeT delta = err_ga - xe->XE.Race.haddr; 1046 if (xml) { 1047 emit(" <auxwhat>Address %#lx is %ld bytes inside a block " 1048 "of size %ld alloc'd</auxwhat>\n", err_ga, delta, 1049 xe->XE.Race.hszB); 1050 VG_(pp_ExeContext)( xe->XE.Race.hctxt ); 1051 } else { 1052 emit(" Address %#lx is %ld bytes inside a block " 1053 "of size %ld alloc'd\n", err_ga, delta, 1054 xe->XE.Race.hszB); 1055 VG_(pp_ExeContext)( xe->XE.Race.hctxt ); 1056 } 1057 } 1058 1059 /* If we have a better description of the address, show it. 1060 Note that in XML mode, it will already by nicely wrapped up 1061 in tags, either <auxwhat> or <xauxwhat>, so we can just emit 1062 it verbatim. */ 1063 if (xe->XE.Race.descr1) 1064 emit( "%s%s\n", xml ? " " : " ", 1065 (HChar*)VG_(indexXA)( xe->XE.Race.descr1, 0 ) ); 1066 if (xe->XE.Race.descr2) 1067 emit( "%s%s\n", xml ? " " : " ", 1068 (HChar*)VG_(indexXA)( xe->XE.Race.descr2, 0 ) ); 1069 1070 break; /* case XE_Race */ 1071 } /* case XE_Race */ 1072 1073 default: 1074 tl_assert(0); 1075 } /* switch (VG_(get_error_kind)(err)) */ 1076 } 1077 1078 Char* HG_(get_error_name) ( Error* err ) 1079 { 1080 switch (VG_(get_error_kind)(err)) { 1081 case XE_Race: return "Race"; 1082 case XE_UnlockUnlocked: return "UnlockUnlocked"; 1083 case XE_UnlockForeign: return "UnlockForeign"; 1084 case XE_UnlockBogus: return "UnlockBogus"; 1085 case XE_PthAPIerror: return "PthAPIerror"; 1086 case XE_LockOrder: return "LockOrder"; 1087 case XE_Misc: return "Misc"; 1088 default: tl_assert(0); /* fill in missing case */ 1089 } 1090 } 1091 1092 Bool HG_(recognised_suppression) ( Char* name, Supp *su ) 1093 { 1094 # define TRY(_name,_xskind) \ 1095 if (0 == VG_(strcmp)(name, (_name))) { \ 1096 VG_(set_supp_kind)(su, (_xskind)); \ 1097 return True; \ 1098 } 1099 TRY("Race", XS_Race); 1100 TRY("FreeMemLock", XS_FreeMemLock); 1101 TRY("UnlockUnlocked", XS_UnlockUnlocked); 1102 TRY("UnlockForeign", XS_UnlockForeign); 1103 TRY("UnlockBogus", XS_UnlockBogus); 1104 TRY("PthAPIerror", XS_PthAPIerror); 1105 TRY("LockOrder", XS_LockOrder); 1106 TRY("Misc", XS_Misc); 1107 return False; 1108 # undef TRY 1109 } 1110 1111 Bool HG_(read_extra_suppression_info) ( Int fd, Char** bufpp, SizeT* nBufp, 1112 Supp* su ) 1113 { 1114 /* do nothing -- no extra suppression info present. Return True to 1115 indicate nothing bad happened. */ 1116 return True; 1117 } 1118 1119 Bool HG_(error_matches_suppression) ( Error* err, Supp* su ) 1120 { 1121 switch (VG_(get_supp_kind)(su)) { 1122 case XS_Race: return VG_(get_error_kind)(err) == XE_Race; 1123 case XS_UnlockUnlocked: return VG_(get_error_kind)(err) == XE_UnlockUnlocked; 1124 case XS_UnlockForeign: return VG_(get_error_kind)(err) == XE_UnlockForeign; 1125 case XS_UnlockBogus: return VG_(get_error_kind)(err) == XE_UnlockBogus; 1126 case XS_PthAPIerror: return VG_(get_error_kind)(err) == XE_PthAPIerror; 1127 case XS_LockOrder: return VG_(get_error_kind)(err) == XE_LockOrder; 1128 case XS_Misc: return VG_(get_error_kind)(err) == XE_Misc; 1129 //case XS_: return VG_(get_error_kind)(err) == XE_; 1130 default: tl_assert(0); /* fill in missing cases */ 1131 } 1132 } 1133 1134 Bool HG_(get_extra_suppression_info) ( Error* err, 1135 /*OUT*/Char* buf, Int nBuf ) 1136 { 1137 /* Do nothing */ 1138 return False; 1139 } 1140 1141 1142 /*--------------------------------------------------------------------*/ 1143 /*--- end hg_errors.c ---*/ 1144 /*--------------------------------------------------------------------*/ 1145