blob: d0fac407ec326263c7f412d036f8c32f1826a57a [file] [log] [blame]
sewardjf98e1c02008-10-25 16:22:41 +00001
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
sewardj0f157dd2013-10-18 14:27:36 +000011 Copyright (C) 2007-2013 OpenWorks Ltd
sewardjf98e1c02008-10-25 16:22:41 +000012 info@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"
sewardj24118492009-07-15 14:50:02 +000042#include "pub_tool_options.h" // VG_(clo_xml)
philippef7ec77f2014-11-24 17:46:41 +000043#include "pub_tool_aspacemgr.h"
philippe07c08522014-05-14 20:39:27 +000044#include "pub_tool_addrinfo.h"
sewardjf98e1c02008-10-25 16:22:41 +000045
46#include "hg_basics.h"
philippef5774342014-05-03 11:12:50 +000047#include "hg_addrdescr.h"
sewardjf98e1c02008-10-25 16:22:41 +000048#include "hg_wordset.h"
49#include "hg_lock_n_thread.h"
sewardjc5ea9962008-12-07 01:41:46 +000050#include "libhb.h"
sewardjf98e1c02008-10-25 16:22:41 +000051#include "hg_errors.h" /* self */
52
53
54/*----------------------------------------------------------------*/
sewardj24118492009-07-15 14:50:02 +000055/*--- Error management -- storage ---*/
sewardjf98e1c02008-10-25 16:22:41 +000056/*----------------------------------------------------------------*/
57
58/* maps (by value) strings to a copy of them in ARENA_TOOL */
59
60static WordFM* string_table = NULL;
61
62ULong HG_(stats__string_table_queries) = 0;
63
64ULong HG_(stats__string_table_get_map_size) ( void ) {
65 return string_table ? (ULong)VG_(sizeFM)(string_table) : 0;
66}
67
68static Word string_table_cmp ( UWord s1, UWord s2 ) {
69 return (Word)VG_(strcmp)( (HChar*)s1, (HChar*)s2 );
70}
71
florian19f91bb2012-11-10 22:29:54 +000072static HChar* string_table_strdup ( const HChar* str ) {
sewardjf98e1c02008-10-25 16:22:41 +000073 HChar* copy = NULL;
74 HG_(stats__string_table_queries)++;
75 if (!str)
76 str = "(null)";
77 if (!string_table) {
78 string_table = VG_(newFM)( HG_(zalloc), "hg.sts.1",
79 HG_(free), string_table_cmp );
sewardjf98e1c02008-10-25 16:22:41 +000080 }
81 if (VG_(lookupFM)( string_table,
florian6bf37262012-10-21 03:23:36 +000082 NULL, (UWord*)&copy, (UWord)str )) {
sewardjf98e1c02008-10-25 16:22:41 +000083 tl_assert(copy);
84 if (0) VG_(printf)("string_table_strdup: %p -> %p\n", str, copy );
85 return copy;
86 } else {
87 copy = HG_(strdup)("hg.sts.2", str);
florian19f91bb2012-11-10 22:29:54 +000088 VG_(addToFM)( string_table, (UWord)copy, (UWord)copy );
sewardjf98e1c02008-10-25 16:22:41 +000089 return copy;
90 }
91}
92
93/* maps from Lock .unique fields to LockP*s */
94
95static WordFM* map_LockN_to_P = NULL;
96
97ULong HG_(stats__LockN_to_P_queries) = 0;
98
99ULong HG_(stats__LockN_to_P_get_map_size) ( void ) {
100 return map_LockN_to_P ? (ULong)VG_(sizeFM)(map_LockN_to_P) : 0;
101}
102
103static Word lock_unique_cmp ( UWord lk1W, UWord lk2W )
104{
105 Lock* lk1 = (Lock*)lk1W;
106 Lock* lk2 = (Lock*)lk2W;
107 tl_assert( HG_(is_sane_LockNorP)(lk1) );
108 tl_assert( HG_(is_sane_LockNorP)(lk2) );
109 if (lk1->unique < lk2->unique) return -1;
110 if (lk1->unique > lk2->unique) return 1;
111 return 0;
112}
113
sewardjffce8152011-06-24 10:09:41 +0000114/* Given a normal Lock (LockN), convert it to a persistent Lock
115 (LockP). In some cases the LockN could be invalid (if it's been
116 freed), so we enquire, in hg_main.c's admin_locks list, whether it
117 is in fact valid. If allowed_to_be_invalid is True, then it's OK
118 for the LockN to be invalid, in which case Lock_INVALID is
119 returned. In all other cases, we insist that the LockN is a valid
120 lock, and return its corresponding LockP.
121
122 Why can LockNs sometimes be invalid? Because they are harvested
123 from locksets that are attached to the OldRef info for conflicting
124 threads. By the time we detect a race, the some of the elements of
125 the lockset may have been destroyed by the client, in which case
126 the corresponding Lock structures we maintain will have been freed.
127
128 So we check that each LockN is a member of the admin_locks double
129 linked list of all Lock structures. That stops us prodding around
130 in potentially freed-up Lock structures. However, it's not quite a
131 proper check: if a new Lock has been reallocated at the same
132 address as one which was previously freed, we'll wind up copying
133 the new one as the basis for the LockP, which is completely bogus
134 because it is unrelated to the previous Lock that lived there.
135 Let's hope that doesn't happen too often.
136*/
137static Lock* mk_LockP_from_LockN ( Lock* lkn,
138 Bool allowed_to_be_invalid )
sewardjf98e1c02008-10-25 16:22:41 +0000139{
140 Lock* lkp = NULL;
141 HG_(stats__LockN_to_P_queries)++;
sewardjffce8152011-06-24 10:09:41 +0000142
143 /* First off, let's do some sanity checks. If
144 allowed_to_be_invalid is False, we _must_ be able to find 'lkn'
145 in admin_locks; else we must assert. If it is True, it's OK for
146 it not to be findable, but in that case we must return
147 Lock_INVALID right away. */
148 Lock* lock_list = HG_(get_admin_locks)();
149 while (lock_list) {
150 if (lock_list == lkn)
151 break;
152 lock_list = lock_list->admin_next;
153 }
154 if (lock_list == NULL) {
155 /* We didn't find it. That possibility has to be OK'd by the
156 caller. */
157 tl_assert(allowed_to_be_invalid);
158 return Lock_INVALID;
159 }
160
161 /* So we must be looking at a valid LockN. */
sewardjf98e1c02008-10-25 16:22:41 +0000162 tl_assert( HG_(is_sane_LockN)(lkn) );
sewardjffce8152011-06-24 10:09:41 +0000163
sewardjf98e1c02008-10-25 16:22:41 +0000164 if (!map_LockN_to_P) {
165 map_LockN_to_P = VG_(newFM)( HG_(zalloc), "hg.mLPfLN.1",
166 HG_(free), lock_unique_cmp );
sewardjf98e1c02008-10-25 16:22:41 +0000167 }
florian6bf37262012-10-21 03:23:36 +0000168 if (!VG_(lookupFM)( map_LockN_to_P, NULL, (UWord*)&lkp, (UWord)lkn)) {
sewardjf98e1c02008-10-25 16:22:41 +0000169 lkp = HG_(zalloc)( "hg.mLPfLN.2", sizeof(Lock) );
170 *lkp = *lkn;
sewardj1d7c3322011-02-28 09:22:51 +0000171 lkp->admin_next = NULL;
172 lkp->admin_prev = NULL;
sewardjf98e1c02008-10-25 16:22:41 +0000173 lkp->magic = LockP_MAGIC;
174 /* Forget about the bag of lock holders - don't copy that.
175 Also, acquired_at should be NULL whenever heldBy is, and vice
176 versa. Also forget about the associated libhb synch object. */
177 lkp->heldW = False;
178 lkp->heldBy = NULL;
179 lkp->acquired_at = NULL;
180 lkp->hbso = NULL;
florian6bf37262012-10-21 03:23:36 +0000181 VG_(addToFM)( map_LockN_to_P, (UWord)lkp, (UWord)lkp );
sewardjf98e1c02008-10-25 16:22:41 +0000182 }
183 tl_assert( HG_(is_sane_LockP)(lkp) );
184 return lkp;
185}
186
florianf631b022015-03-13 13:50:08 +0000187static Int sort_by_guestaddr(const void* n1, const void* n2)
188{
189 const Lock* l1 = *(const Lock *const *)n1;
190 const Lock* l2 = *(const Lock *const *)n2;
191
192 Addr a1 = l1 == Lock_INVALID ? 0 : l1->guestaddr;
193 Addr a2 = l2 == Lock_INVALID ? 0 : l2->guestaddr;
194 if (a1 < a2) return -1;
195 if (a1 > a2) return 1;
196 return 0;
197}
198
sewardjffce8152011-06-24 10:09:41 +0000199/* Expand a WordSet of LockN*'s into a NULL-terminated vector of
200 LockP*'s. Any LockN's that can't be converted into a LockP
201 (because they have been freed, see comment on mk_LockP_from_LockN)
202 are converted instead into the value Lock_INVALID. Hence the
203 returned vector is a sequence: zero or more (valid LockP* or
204 LockN_INVALID), terminated by a NULL. */
205static
206Lock** enumerate_WordSet_into_LockP_vector( WordSetU* univ_lsets,
207 WordSetID lockset,
208 Bool allowed_to_be_invalid )
209{
210 tl_assert(univ_lsets);
211 tl_assert( HG_(plausibleWS)(univ_lsets, lockset) );
212 UWord nLocks = HG_(cardinalityWS)(univ_lsets, lockset);
213 Lock** lockPs = HG_(zalloc)( "hg.eWSiLPa",
214 (nLocks+1) * sizeof(Lock*) );
sewardjffce8152011-06-24 10:09:41 +0000215 tl_assert(lockPs[nLocks] == NULL); /* pre-NULL terminated */
216 UWord* lockNs = NULL;
217 UWord nLockNs = 0;
218 if (nLocks > 0) {
219 /* HG_(getPayloadWS) doesn't assign non-NULL to &lockNs if the
220 lockset is empty; hence the guarding "if". Sigh. */
221 HG_(getPayloadWS)( &lockNs, &nLockNs, univ_lsets, lockset );
222 tl_assert(lockNs);
223 }
224 UWord i;
225 /* Convert to LockPs. */
226 for (i = 0; i < nLockNs; i++) {
227 lockPs[i] = mk_LockP_from_LockN( (Lock*)lockNs[i],
228 allowed_to_be_invalid );
229 }
florianf631b022015-03-13 13:50:08 +0000230 /* Sort the locks by increasing Lock::guestaddr to avoid jitters
231 in the output. */
232 VG_(ssort)(lockPs, nLockNs, sizeof lockPs[0], sort_by_guestaddr);
233
sewardjffce8152011-06-24 10:09:41 +0000234 return lockPs;
235}
236
237/* Get the number of useful elements in a vector created by
238 enumerate_WordSet_into_LockP_vector. Returns both the total number
239 of elements (not including the terminating NULL) and the number of
240 non-Lock_INVALID elements. */
241static void count_LockP_vector ( /*OUT*/UWord* nLocks,
242 /*OUT*/UWord* nLocksValid,
243 Lock** vec )
244{
245 tl_assert(vec);
246 *nLocks = *nLocksValid = 0;
247 UWord n = 0;
248 while (vec[n]) {
249 (*nLocks)++;
250 if (vec[n] != Lock_INVALID)
251 (*nLocksValid)++;
252 n++;
253 }
254}
255
256/* Find out whether 'lk' is in 'vec'. */
257static Bool elem_LockP_vector ( Lock** vec, Lock* lk )
258{
259 tl_assert(vec);
260 tl_assert(lk);
261 UWord n = 0;
262 while (vec[n]) {
263 if (vec[n] == lk)
264 return True;
265 n++;
266 }
267 return False;
268}
269
270
sewardjf98e1c02008-10-25 16:22:41 +0000271/* Errors:
272
273 race: program counter
274 read or write
275 data size
276 previous state
277 current state
278
279 FIXME: how does state printing interact with lockset gc?
280 Are the locksets in prev/curr state always valid?
281 Ditto question for the threadsets
282 ThreadSets - probably are always valid if Threads
283 are never thrown away.
284 LockSets - could at least print the lockset elements that
285 correspond to actual locks at the time of printing. Hmm.
286*/
287
288/* Error kinds */
289typedef
290 enum {
291 XE_Race=1101, // race
sewardjf98e1c02008-10-25 16:22:41 +0000292 XE_UnlockUnlocked, // unlocking a not-locked lock
293 XE_UnlockForeign, // unlocking a lock held by some other thread
294 XE_UnlockBogus, // unlocking an address not known to be a lock
295 XE_PthAPIerror, // error from the POSIX pthreads API
296 XE_LockOrder, // lock order error
297 XE_Misc // misc other error (w/ string to describe it)
298 }
299 XErrorTag;
300
301/* Extra contexts for kinds */
302typedef
303 struct {
304 XErrorTag tag;
305 union {
306 struct {
sewardj23f12002009-07-24 08:45:08 +0000307 Addr data_addr;
308 Int szB;
philippe07c08522014-05-14 20:39:27 +0000309 AddrInfo data_addrinfo;
sewardj23f12002009-07-24 08:45:08 +0000310 Bool isWrite;
311 Thread* thr;
sewardjffce8152011-06-24 10:09:41 +0000312 Lock** locksHeldW;
sewardj095d61e2010-03-11 13:43:18 +0000313 /* h1_* and h2_* provide some description of a previously
314 observed access with which we are conflicting. */
sewardj23f12002009-07-24 08:45:08 +0000315 Thread* h1_ct; /* non-NULL means h1 info present */
316 ExeContext* h1_ct_mbsegstartEC;
317 ExeContext* h1_ct_mbsegendEC;
318 Thread* h2_ct; /* non-NULL means h2 info present */
319 ExeContext* h2_ct_accEC;
320 Int h2_ct_accSzB;
321 Bool h2_ct_accIsW;
sewardjffce8152011-06-24 10:09:41 +0000322 Lock** h2_ct_locksHeldW;
sewardjf98e1c02008-10-25 16:22:41 +0000323 } Race;
324 struct {
sewardjf98e1c02008-10-25 16:22:41 +0000325 Thread* thr; /* doing the unlocking */
326 Lock* lock; /* lock (that is already unlocked) */
327 } UnlockUnlocked;
328 struct {
329 Thread* thr; /* doing the unlocking */
330 Thread* owner; /* thread that actually holds the lock */
331 Lock* lock; /* lock (that is held by 'owner') */
332 } UnlockForeign;
333 struct {
334 Thread* thr; /* doing the unlocking */
335 Addr lock_ga; /* purported address of the lock */
336 } UnlockBogus;
337 struct {
338 Thread* thr;
339 HChar* fnname; /* persistent, in tool-arena */
340 Word err; /* pth error code */
341 HChar* errstr; /* persistent, in tool-arena */
342 } PthAPIerror;
343 struct {
344 Thread* thr;
sewardjffce8152011-06-24 10:09:41 +0000345 /* The first 4 fields describe the previously observed
346 (should-be) ordering. */
philippe46daf0d2014-07-29 20:08:15 +0000347 Lock* shouldbe_earlier_lk;
348 Lock* shouldbe_later_lk;
sewardjffce8152011-06-24 10:09:41 +0000349 ExeContext* shouldbe_earlier_ec;
350 ExeContext* shouldbe_later_ec;
351 /* In principle we need to record two more stacks, from
352 this thread, when acquiring the locks in the "wrong"
353 order. In fact the wallclock-later acquisition by this
354 thread is recorded in the main stack for this error.
355 So we only need a stack for the earlier acquisition by
356 this thread. */
357 ExeContext* actual_earlier_ec;
sewardjf98e1c02008-10-25 16:22:41 +0000358 } LockOrder;
359 struct {
sewardj8fef6252010-07-29 05:28:02 +0000360 Thread* thr;
361 HChar* errstr; /* persistent, in tool-arena */
362 HChar* auxstr; /* optional, persistent, in tool-arena */
363 ExeContext* auxctx; /* optional */
sewardjf98e1c02008-10-25 16:22:41 +0000364 } Misc;
365 } XE;
366 }
367 XError;
368
369static void init_XError ( XError* xe ) {
370 VG_(memset)(xe, 0, sizeof(*xe) );
371 xe->tag = XE_Race-1; /* bogus */
372}
373
374
375/* Extensions of suppressions */
376typedef
377 enum {
378 XS_Race=1201, /* race */
379 XS_FreeMemLock,
380 XS_UnlockUnlocked,
381 XS_UnlockForeign,
382 XS_UnlockBogus,
383 XS_PthAPIerror,
384 XS_LockOrder,
385 XS_Misc
386 }
387 XSuppTag;
388
389
390/* Updates the copy with address info if necessary. */
florian8e3fbb52014-10-20 19:02:38 +0000391UInt HG_(update_extra) ( const Error* err )
sewardjf98e1c02008-10-25 16:22:41 +0000392{
393 XError* xe = (XError*)VG_(get_error_extra)(err);
394 tl_assert(xe);
395 //if (extra != NULL && Undescribed == extra->addrinfo.akind) {
396 // describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) );
397 //}
398
399 if (xe->tag == XE_Race) {
sewardj24118492009-07-15 14:50:02 +0000400
sewardjffce8152011-06-24 10:09:41 +0000401 /* Note the set of locks that the thread is (w-)holding.
402 Convert the WordSetID of LockN*'s into a NULL-terminated
403 vector of LockP*'s. We don't expect to encounter any invalid
404 LockNs in this conversion. */
405 tl_assert(xe->XE.Race.thr);
406 xe->XE.Race.locksHeldW
407 = enumerate_WordSet_into_LockP_vector(
408 HG_(get_univ_lsets)(),
409 xe->XE.Race.thr->locksetW,
410 False/*!allowed_to_be_invalid*/
411 );
412
sewardjf98e1c02008-10-25 16:22:41 +0000413 /* See if we can come up with a source level description of the
414 raced-upon address. This is potentially expensive, which is
415 why it's only done at the update_extra point, not when the
416 error is initially created. */
sewardjc5ea9962008-12-07 01:41:46 +0000417 static Int xxx = 0;
418 xxx++;
419 if (0)
420 VG_(printf)("HG_(update_extra): "
421 "%d conflicting-event queries\n", xxx);
sewardj095d61e2010-03-11 13:43:18 +0000422
philippe07c08522014-05-14 20:39:27 +0000423 HG_(describe_addr) (xe->XE.Race.data_addr, &xe->XE.Race.data_addrinfo);
sewardj24118492009-07-15 14:50:02 +0000424
425 /* And poke around in the conflicting-event map, to see if we
426 can rustle up a plausible-looking conflicting memory access
427 to show. */
sewardj23f12002009-07-24 08:45:08 +0000428 if (HG_(clo_history_level) >= 2) {
sewardjffce8152011-06-24 10:09:41 +0000429 Thr* thrp = NULL;
430 ExeContext* wherep = NULL;
431 Addr acc_addr = xe->XE.Race.data_addr;
432 Int acc_szB = xe->XE.Race.szB;
433 Thr* acc_thr = xe->XE.Race.thr->hbthr;
434 Bool acc_isW = xe->XE.Race.isWrite;
435 SizeT conf_szB = 0;
436 Bool conf_isW = False;
437 WordSetID conf_locksHeldW = 0;
sewardj23f12002009-07-24 08:45:08 +0000438 tl_assert(!xe->XE.Race.h2_ct_accEC);
439 tl_assert(!xe->XE.Race.h2_ct);
440 if (libhb_event_map_lookup(
sewardjffce8152011-06-24 10:09:41 +0000441 &wherep, &thrp, &conf_szB, &conf_isW, &conf_locksHeldW,
sewardj23f12002009-07-24 08:45:08 +0000442 acc_thr, acc_addr, acc_szB, acc_isW )) {
443 Thread* threadp;
444 tl_assert(wherep);
445 tl_assert(thrp);
sewardj60626642011-03-10 15:14:37 +0000446 threadp = libhb_get_Thr_hgthread( thrp );
sewardj23f12002009-07-24 08:45:08 +0000447 tl_assert(threadp);
448 xe->XE.Race.h2_ct_accEC = wherep;
449 xe->XE.Race.h2_ct = threadp;
450 xe->XE.Race.h2_ct_accSzB = (Int)conf_szB;
451 xe->XE.Race.h2_ct_accIsW = conf_isW;
sewardjffce8152011-06-24 10:09:41 +0000452 xe->XE.Race.h2_ct_locksHeldW
453 = enumerate_WordSet_into_LockP_vector(
454 HG_(get_univ_lsets)(),
455 conf_locksHeldW,
456 True/*allowed_to_be_invalid*/
457 );
sewardjc5ea9962008-12-07 01:41:46 +0000458 }
459 }
sewardj23f12002009-07-24 08:45:08 +0000460
461 // both NULL or both non-NULL
462 tl_assert( (!!xe->XE.Race.h2_ct) == (!!xe->XE.Race.h2_ct_accEC) );
sewardjf98e1c02008-10-25 16:22:41 +0000463 }
464
465 return sizeof(XError);
466}
467
468void HG_(record_error_Race) ( Thread* thr,
sewardja781be62008-12-08 00:12:28 +0000469 Addr data_addr, Int szB, Bool isWrite,
sewardj23f12002009-07-24 08:45:08 +0000470 Thread* h1_ct,
471 ExeContext* h1_ct_segstart,
472 ExeContext* h1_ct_mbsegendEC )
sewardjf98e1c02008-10-25 16:22:41 +0000473{
474 XError xe;
475 tl_assert( HG_(is_sane_Thread)(thr) );
476
477# if defined(VGO_linux)
478 /* Skip any races on locations apparently in GOTPLT sections. This
479 is said to be caused by ld.so poking PLT table entries (or
480 whatever) when it writes the resolved address of a dynamically
481 linked routine, into the table (or whatever) when it is called
482 for the first time. */
483 {
floriane08950b2014-11-13 21:41:28 +0000484 VgSectKind sect = VG_(DebugInfo_sect_kind)( NULL, data_addr );
sewardjf98e1c02008-10-25 16:22:41 +0000485 if (0) VG_(printf)("XXXXXXXXX RACE on %#lx %s\n",
486 data_addr, VG_(pp_SectKind)(sect));
sewardj52104132008-12-23 00:10:26 +0000487 /* SectPLT is required on ???-linux */
sewardjf98e1c02008-10-25 16:22:41 +0000488 if (sect == Vg_SectGOTPLT) return;
sewardj52104132008-12-23 00:10:26 +0000489 /* SectPLT is required on ppc32/64-linux */
490 if (sect == Vg_SectPLT) return;
mjw4fa71082014-09-01 15:29:55 +0000491 /* SectGOT is required on arm-linux */
492 if (sect == Vg_SectGOT) return;
sewardjf98e1c02008-10-25 16:22:41 +0000493 }
494# endif
495
496 init_XError(&xe);
497 xe.tag = XE_Race;
498 xe.XE.Race.data_addr = data_addr;
499 xe.XE.Race.szB = szB;
500 xe.XE.Race.isWrite = isWrite;
sewardjf98e1c02008-10-25 16:22:41 +0000501 xe.XE.Race.thr = thr;
sewardjf98e1c02008-10-25 16:22:41 +0000502 tl_assert(isWrite == False || isWrite == True);
sewardja781be62008-12-08 00:12:28 +0000503 tl_assert(szB == 8 || szB == 4 || szB == 2 || szB == 1);
sewardj24118492009-07-15 14:50:02 +0000504 /* Skip on the detailed description of the raced-on address at this
505 point; it's expensive. Leave it for the update_extra function
506 if we ever make it that far. */
philippe07c08522014-05-14 20:39:27 +0000507 xe.XE.Race.data_addrinfo.tag = Addr_Undescribed;
sewardjf98e1c02008-10-25 16:22:41 +0000508 // FIXME: tid vs thr
sewardjc5ea9962008-12-07 01:41:46 +0000509 // Skip on any of the conflicting-access info at this point.
510 // It's expensive to obtain, and this error is more likely than
511 // not to be discarded. We'll fill these fields in in
512 // HG_(update_extra) just above, assuming the error ever makes
513 // it that far (unlikely).
sewardj23f12002009-07-24 08:45:08 +0000514 xe.XE.Race.h2_ct_accSzB = 0;
515 xe.XE.Race.h2_ct_accIsW = False;
516 xe.XE.Race.h2_ct_accEC = NULL;
517 xe.XE.Race.h2_ct = NULL;
sewardjf98e1c02008-10-25 16:22:41 +0000518 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) );
519 tl_assert( thr->coretid != VG_INVALID_THREADID );
sewardj23f12002009-07-24 08:45:08 +0000520
521 xe.XE.Race.h1_ct = h1_ct;
522 xe.XE.Race.h1_ct_mbsegstartEC = h1_ct_segstart;
523 xe.XE.Race.h1_ct_mbsegendEC = h1_ct_mbsegendEC;
524
sewardjf98e1c02008-10-25 16:22:41 +0000525 VG_(maybe_record_error)( thr->coretid,
526 XE_Race, data_addr, NULL, &xe );
527}
528
sewardjf98e1c02008-10-25 16:22:41 +0000529void HG_(record_error_UnlockUnlocked) ( Thread* thr, Lock* lk )
530{
531 XError xe;
532 tl_assert( HG_(is_sane_Thread)(thr) );
533 tl_assert( HG_(is_sane_LockN)(lk) );
534 init_XError(&xe);
535 xe.tag = XE_UnlockUnlocked;
sewardjffce8152011-06-24 10:09:41 +0000536 xe.XE.UnlockUnlocked.thr
537 = thr;
538 xe.XE.UnlockUnlocked.lock
539 = mk_LockP_from_LockN(lk, False/*!allowed_to_be_invalid*/);
sewardjf98e1c02008-10-25 16:22:41 +0000540 // FIXME: tid vs thr
541 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) );
542 tl_assert( thr->coretid != VG_INVALID_THREADID );
543 VG_(maybe_record_error)( thr->coretid,
544 XE_UnlockUnlocked, 0, NULL, &xe );
545}
546
547void HG_(record_error_UnlockForeign) ( Thread* thr,
548 Thread* owner, Lock* lk )
549{
550 XError xe;
551 tl_assert( HG_(is_sane_Thread)(thr) );
552 tl_assert( HG_(is_sane_Thread)(owner) );
553 tl_assert( HG_(is_sane_LockN)(lk) );
554 init_XError(&xe);
555 xe.tag = XE_UnlockForeign;
556 xe.XE.UnlockForeign.thr = thr;
557 xe.XE.UnlockForeign.owner = owner;
sewardjffce8152011-06-24 10:09:41 +0000558 xe.XE.UnlockForeign.lock
559 = mk_LockP_from_LockN(lk, False/*!allowed_to_be_invalid*/);
sewardjf98e1c02008-10-25 16:22:41 +0000560 // FIXME: tid vs thr
561 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) );
562 tl_assert( thr->coretid != VG_INVALID_THREADID );
563 VG_(maybe_record_error)( thr->coretid,
564 XE_UnlockForeign, 0, NULL, &xe );
565}
566
567void HG_(record_error_UnlockBogus) ( Thread* thr, Addr lock_ga )
568{
569 XError xe;
570 tl_assert( HG_(is_sane_Thread)(thr) );
571 init_XError(&xe);
572 xe.tag = XE_UnlockBogus;
573 xe.XE.UnlockBogus.thr = thr;
574 xe.XE.UnlockBogus.lock_ga = lock_ga;
575 // FIXME: tid vs thr
576 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) );
577 tl_assert( thr->coretid != VG_INVALID_THREADID );
578 VG_(maybe_record_error)( thr->coretid,
579 XE_UnlockBogus, 0, NULL, &xe );
580}
581
582void HG_(record_error_LockOrder)(
sewardjffce8152011-06-24 10:09:41 +0000583 Thread* thr,
philippe46daf0d2014-07-29 20:08:15 +0000584 Lock* shouldbe_earlier_lk,
585 Lock* shouldbe_later_lk,
sewardjffce8152011-06-24 10:09:41 +0000586 ExeContext* shouldbe_earlier_ec,
587 ExeContext* shouldbe_later_ec,
588 ExeContext* actual_earlier_ec
sewardjf98e1c02008-10-25 16:22:41 +0000589 )
590{
591 XError xe;
592 tl_assert( HG_(is_sane_Thread)(thr) );
sewardjc1fb9d22011-02-28 09:03:44 +0000593 tl_assert(HG_(clo_track_lockorders));
sewardjf98e1c02008-10-25 16:22:41 +0000594 init_XError(&xe);
595 xe.tag = XE_LockOrder;
596 xe.XE.LockOrder.thr = thr;
philippe46daf0d2014-07-29 20:08:15 +0000597 xe.XE.LockOrder.shouldbe_earlier_lk
598 = mk_LockP_from_LockN(shouldbe_earlier_lk,
599 False/*!allowed_to_be_invalid*/);
sewardjffce8152011-06-24 10:09:41 +0000600 xe.XE.LockOrder.shouldbe_earlier_ec = shouldbe_earlier_ec;
philippe46daf0d2014-07-29 20:08:15 +0000601 xe.XE.LockOrder.shouldbe_later_lk
602 = mk_LockP_from_LockN(shouldbe_later_lk,
603 False/*!allowed_to_be_invalid*/);
sewardjffce8152011-06-24 10:09:41 +0000604 xe.XE.LockOrder.shouldbe_later_ec = shouldbe_later_ec;
605 xe.XE.LockOrder.actual_earlier_ec = actual_earlier_ec;
sewardjf98e1c02008-10-25 16:22:41 +0000606 // FIXME: tid vs thr
607 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) );
608 tl_assert( thr->coretid != VG_INVALID_THREADID );
609 VG_(maybe_record_error)( thr->coretid,
610 XE_LockOrder, 0, NULL, &xe );
611}
612
florian6bd9dc12012-11-23 16:17:43 +0000613void HG_(record_error_PthAPIerror) ( Thread* thr, const HChar* fnname,
614 Word err, const HChar* errstr )
sewardjf98e1c02008-10-25 16:22:41 +0000615{
616 XError xe;
617 tl_assert( HG_(is_sane_Thread)(thr) );
618 tl_assert(fnname);
619 tl_assert(errstr);
620 init_XError(&xe);
621 xe.tag = XE_PthAPIerror;
622 xe.XE.PthAPIerror.thr = thr;
623 xe.XE.PthAPIerror.fnname = string_table_strdup(fnname);
624 xe.XE.PthAPIerror.err = err;
625 xe.XE.PthAPIerror.errstr = string_table_strdup(errstr);
626 // FIXME: tid vs thr
627 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) );
628 tl_assert( thr->coretid != VG_INVALID_THREADID );
629 VG_(maybe_record_error)( thr->coretid,
630 XE_PthAPIerror, 0, NULL, &xe );
631}
632
florian6bd9dc12012-11-23 16:17:43 +0000633void HG_(record_error_Misc_w_aux) ( Thread* thr, const HChar* errstr,
634 const HChar* auxstr, ExeContext* auxctx )
sewardjf98e1c02008-10-25 16:22:41 +0000635{
636 XError xe;
637 tl_assert( HG_(is_sane_Thread)(thr) );
638 tl_assert(errstr);
639 init_XError(&xe);
640 xe.tag = XE_Misc;
641 xe.XE.Misc.thr = thr;
642 xe.XE.Misc.errstr = string_table_strdup(errstr);
sewardj8fef6252010-07-29 05:28:02 +0000643 xe.XE.Misc.auxstr = auxstr ? string_table_strdup(auxstr) : NULL;
644 xe.XE.Misc.auxctx = auxctx;
sewardjf98e1c02008-10-25 16:22:41 +0000645 // FIXME: tid vs thr
646 tl_assert( HG_(is_sane_ThreadId)(thr->coretid) );
647 tl_assert( thr->coretid != VG_INVALID_THREADID );
648 VG_(maybe_record_error)( thr->coretid,
649 XE_Misc, 0, NULL, &xe );
650}
651
florian6bd9dc12012-11-23 16:17:43 +0000652void HG_(record_error_Misc) ( Thread* thr, const HChar* errstr )
sewardj8fef6252010-07-29 05:28:02 +0000653{
654 HG_(record_error_Misc_w_aux)(thr, errstr, NULL, NULL);
655}
656
florian8e3fbb52014-10-20 19:02:38 +0000657Bool HG_(eq_Error) ( VgRes not_used, const Error* e1, const Error* e2 )
sewardjf98e1c02008-10-25 16:22:41 +0000658{
659 XError *xe1, *xe2;
660
661 tl_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
662
663 xe1 = (XError*)VG_(get_error_extra)(e1);
664 xe2 = (XError*)VG_(get_error_extra)(e2);
665 tl_assert(xe1);
666 tl_assert(xe2);
667
668 switch (VG_(get_error_kind)(e1)) {
669 case XE_Race:
670 return xe1->XE.Race.szB == xe2->XE.Race.szB
671 && xe1->XE.Race.isWrite == xe2->XE.Race.isWrite
672 && (HG_(clo_cmp_race_err_addrs)
673 ? xe1->XE.Race.data_addr == xe2->XE.Race.data_addr
674 : True);
sewardjf98e1c02008-10-25 16:22:41 +0000675 case XE_UnlockUnlocked:
676 return xe1->XE.UnlockUnlocked.thr == xe2->XE.UnlockUnlocked.thr
677 && xe1->XE.UnlockUnlocked.lock == xe2->XE.UnlockUnlocked.lock;
678 case XE_UnlockForeign:
679 return xe1->XE.UnlockForeign.thr == xe2->XE.UnlockForeign.thr
680 && xe1->XE.UnlockForeign.owner == xe2->XE.UnlockForeign.owner
681 && xe1->XE.UnlockForeign.lock == xe2->XE.UnlockForeign.lock;
682 case XE_UnlockBogus:
683 return xe1->XE.UnlockBogus.thr == xe2->XE.UnlockBogus.thr
684 && xe1->XE.UnlockBogus.lock_ga == xe2->XE.UnlockBogus.lock_ga;
685 case XE_PthAPIerror:
686 return xe1->XE.PthAPIerror.thr == xe2->XE.PthAPIerror.thr
687 && 0==VG_(strcmp)(xe1->XE.PthAPIerror.fnname,
688 xe2->XE.PthAPIerror.fnname)
689 && xe1->XE.PthAPIerror.err == xe2->XE.PthAPIerror.err;
690 case XE_LockOrder:
691 return xe1->XE.LockOrder.thr == xe2->XE.LockOrder.thr;
692 case XE_Misc:
693 return xe1->XE.Misc.thr == xe2->XE.Misc.thr
694 && 0==VG_(strcmp)(xe1->XE.Misc.errstr, xe2->XE.Misc.errstr);
695 default:
696 tl_assert(0);
697 }
698
699 /*NOTREACHED*/
700 tl_assert(0);
701}
702
703
sewardj24118492009-07-15 14:50:02 +0000704/*----------------------------------------------------------------*/
705/*--- Error management -- printing ---*/
706/*----------------------------------------------------------------*/
707
708/* Do a printf-style operation on either the XML or normal output
709 channel, depending on the setting of VG_(clo_xml).
710*/
florian6bf37262012-10-21 03:23:36 +0000711static void emit_WRK ( const HChar* format, va_list vargs )
sewardj24118492009-07-15 14:50:02 +0000712{
713 if (VG_(clo_xml)) {
714 VG_(vprintf_xml)(format, vargs);
715 } else {
716 VG_(vmessage)(Vg_UserMsg, format, vargs);
717 }
718}
florian6bf37262012-10-21 03:23:36 +0000719static void emit ( const HChar* format, ... ) PRINTF_CHECK(1, 2);
720static void emit ( const HChar* format, ... )
sewardj24118492009-07-15 14:50:02 +0000721{
722 va_list vargs;
723 va_start(vargs, format);
724 emit_WRK(format, vargs);
725 va_end(vargs);
726}
sewardj24118492009-07-15 14:50:02 +0000727
728
sewardjf98e1c02008-10-25 16:22:41 +0000729/* Announce (that is, print the point-of-creation) of 'thr'. Only do
730 this once, as we only want to see these announcements once per
sewardj24118492009-07-15 14:50:02 +0000731 thread. Returned Bool indicates whether or not an announcement was
732 made.
733*/
734static Bool announce_one_thread ( Thread* thr )
sewardjf98e1c02008-10-25 16:22:41 +0000735{
736 tl_assert(HG_(is_sane_Thread)(thr));
737 tl_assert(thr->errmsg_index >= 1);
sewardj24118492009-07-15 14:50:02 +0000738 if (thr->announced)
739 return False;
740
741 if (VG_(clo_xml)) {
742
743 VG_(printf_xml)("<announcethread>\n");
sewardj11b5c6d2009-09-03 10:29:57 +0000744 VG_(printf_xml)(" <hthreadid>%d</hthreadid>\n", thr->errmsg_index);
sewardjf98e1c02008-10-25 16:22:41 +0000745 if (thr->errmsg_index == 1) {
746 tl_assert(thr->created_at == NULL);
sewardj24118492009-07-15 14:50:02 +0000747 VG_(printf_xml)(" <isrootthread></isrootthread>\n");
sewardjf98e1c02008-10-25 16:22:41 +0000748 } else {
749 tl_assert(thr->created_at != NULL);
sewardj24118492009-07-15 14:50:02 +0000750 VG_(pp_ExeContext)( thr->created_at );
751 }
752 VG_(printf_xml)("</announcethread>\n\n");
753
754 } else {
755
sewardjffce8152011-06-24 10:09:41 +0000756 VG_(umsg)("---Thread-Announcement----------"
757 "--------------------------------" "\n");
758 VG_(umsg)("\n");
759
sewardj24118492009-07-15 14:50:02 +0000760 if (thr->errmsg_index == 1) {
761 tl_assert(thr->created_at == NULL);
762 VG_(message)(Vg_UserMsg,
763 "Thread #%d is the program's root thread\n",
764 thr->errmsg_index);
765 } else {
766 tl_assert(thr->created_at != NULL);
767 VG_(message)(Vg_UserMsg, "Thread #%d was created\n",
sewardjf98e1c02008-10-25 16:22:41 +0000768 thr->errmsg_index);
769 VG_(pp_ExeContext)( thr->created_at );
770 }
sewardj24118492009-07-15 14:50:02 +0000771 VG_(message)(Vg_UserMsg, "\n");
772
773 }
774
775 thr->announced = True;
776 return True;
777}
778
sewardjffce8152011-06-24 10:09:41 +0000779/* Announce 'lk'. */
780static void announce_LockP ( Lock* lk )
781{
782 tl_assert(lk);
783 if (lk == Lock_INVALID)
784 return; /* Can't be announced -- we know nothing about it. */
785 tl_assert(lk->magic == LockP_MAGIC);
sewardjffce8152011-06-24 10:09:41 +0000786
787 if (VG_(clo_xml)) {
philippe46daf0d2014-07-29 20:08:15 +0000788 if (lk->appeared_at) {
789 emit( " <auxwhat>Lock at %p was first observed</auxwhat>\n",
790 (void*)lk );
791 VG_(pp_ExeContext)( lk->appeared_at );
792 }
793
sewardjffce8152011-06-24 10:09:41 +0000794 } else {
philippe80612012014-07-24 21:00:24 +0000795 if (lk->appeared_at) {
philippe46daf0d2014-07-29 20:08:15 +0000796 VG_(umsg)( " Lock at %p was first observed\n",
philippe80612012014-07-24 21:00:24 +0000797 (void*)lk->guestaddr );
798 VG_(pp_ExeContext)( lk->appeared_at );
799 } else {
philippe46daf0d2014-07-29 20:08:15 +0000800 VG_(umsg)( " Lock at %p : no stacktrace for first observation\n",
philippe80612012014-07-24 21:00:24 +0000801 (void*)lk->guestaddr );
802 }
803 HG_(get_and_pp_addrdescr) (lk->guestaddr);
sewardjffce8152011-06-24 10:09:41 +0000804 VG_(umsg)("\n");
805 }
806}
807
808/* Announce (that is, print point-of-first-observation) for the
809 locks in 'lockvec' and, if non-NULL, 'lockvec2'. */
810static void announce_combined_LockP_vecs ( Lock** lockvec,
811 Lock** lockvec2 )
812{
813 UWord i;
814 tl_assert(lockvec);
815 for (i = 0; lockvec[i]; i++) {
816 announce_LockP(lockvec[i]);
817 }
818 if (lockvec2) {
819 for (i = 0; lockvec2[i]; i++) {
820 Lock* lk = lockvec2[i];
821 if (!elem_LockP_vector(lockvec, lk))
822 announce_LockP(lk);
823 }
824 }
825}
826
827
florian6bd9dc12012-11-23 16:17:43 +0000828static void show_LockP_summary_textmode ( Lock** locks, const HChar* pre )
sewardjffce8152011-06-24 10:09:41 +0000829{
830 tl_assert(locks);
831 UWord i;
832 UWord nLocks = 0, nLocksValid = 0;
833 count_LockP_vector(&nLocks, &nLocksValid, locks);
834 tl_assert(nLocksValid <= nLocks);
835
836 if (nLocks == 0) {
837 VG_(umsg)( "%sLocks held: none", pre );
838 } else {
839 VG_(umsg)( "%sLocks held: %lu, at address%s ",
840 pre, nLocks, nLocksValid == 1 ? "" : "es" );
841 }
842
843 if (nLocks > 0) {
844 for (i = 0; i < nLocks; i++) {
845 if (locks[i] == Lock_INVALID)
846 continue;
847 VG_(umsg)( "%p", (void*)locks[i]->guestaddr);
848 if (locks[i+1] != NULL)
849 VG_(umsg)(" ");
850 }
851 if (nLocksValid < nLocks)
852 VG_(umsg)(" (and %lu that can't be shown)", nLocks - nLocksValid);
853 }
854 VG_(umsg)("\n");
855}
856
857
sewardj24118492009-07-15 14:50:02 +0000858/* This is the "this error is due to be printed shortly; so have a
859 look at it any print any preamble you want" function. We use it to
860 announce any previously un-announced threads in the upcoming error
861 message.
862*/
florian8e3fbb52014-10-20 19:02:38 +0000863void HG_(before_pp_Error) ( const Error* err )
sewardj24118492009-07-15 14:50:02 +0000864{
865 XError* xe;
866 tl_assert(err);
867 xe = (XError*)VG_(get_error_extra)(err);
868 tl_assert(xe);
869
870 switch (VG_(get_error_kind)(err)) {
871 case XE_Misc:
872 announce_one_thread( xe->XE.Misc.thr );
873 break;
874 case XE_LockOrder:
875 announce_one_thread( xe->XE.LockOrder.thr );
876 break;
877 case XE_PthAPIerror:
878 announce_one_thread( xe->XE.PthAPIerror.thr );
879 break;
880 case XE_UnlockBogus:
881 announce_one_thread( xe->XE.UnlockBogus.thr );
882 break;
883 case XE_UnlockForeign:
884 announce_one_thread( xe->XE.UnlockForeign.thr );
885 announce_one_thread( xe->XE.UnlockForeign.owner );
886 break;
887 case XE_UnlockUnlocked:
888 announce_one_thread( xe->XE.UnlockUnlocked.thr );
889 break;
890 case XE_Race:
891 announce_one_thread( xe->XE.Race.thr );
sewardj23f12002009-07-24 08:45:08 +0000892 if (xe->XE.Race.h2_ct)
893 announce_one_thread( xe->XE.Race.h2_ct );
894 if (xe->XE.Race.h1_ct)
895 announce_one_thread( xe->XE.Race.h1_ct );
philippe0c9ac8d2014-07-18 00:03:58 +0000896 if (xe->XE.Race.data_addrinfo.Addr.Block.alloc_tinfo.tnr) {
897 Thread* thr = get_admin_threads();
898 while (thr) {
899 if (thr->errmsg_index
900 == xe->XE.Race.data_addrinfo.Addr.Block.alloc_tinfo.tnr) {
901 announce_one_thread (thr);
902 break;
903 }
904 thr = thr->admin;
905 }
906 }
sewardj24118492009-07-15 14:50:02 +0000907 break;
908 default:
909 tl_assert(0);
sewardjf98e1c02008-10-25 16:22:41 +0000910 }
911}
912
florian8e3fbb52014-10-20 19:02:38 +0000913void HG_(pp_Error) ( const Error* err )
sewardjf98e1c02008-10-25 16:22:41 +0000914{
sewardj24118492009-07-15 14:50:02 +0000915 const Bool xml = VG_(clo_xml); /* a shorthand, that's all */
916
sewardjffce8152011-06-24 10:09:41 +0000917 if (!xml) {
918 VG_(umsg)("--------------------------------"
919 "--------------------------------" "\n");
920 VG_(umsg)("\n");
921 }
922
sewardjf98e1c02008-10-25 16:22:41 +0000923 XError *xe = (XError*)VG_(get_error_extra)(err);
sewardj24118492009-07-15 14:50:02 +0000924 tl_assert(xe);
sewardjf98e1c02008-10-25 16:22:41 +0000925
bart2fa18d92011-10-04 16:28:42 +0000926 if (xml)
927 emit( " <kind>%s</kind>\n", HG_(get_error_name)(err));
928
sewardjf98e1c02008-10-25 16:22:41 +0000929 switch (VG_(get_error_kind)(err)) {
930
931 case XE_Misc: {
sewardjf98e1c02008-10-25 16:22:41 +0000932 tl_assert( HG_(is_sane_Thread)( xe->XE.Misc.thr ) );
sewardj24118492009-07-15 14:50:02 +0000933
934 if (xml) {
935
sewardj24118492009-07-15 14:50:02 +0000936 emit( " <xwhat>\n" );
937 emit( " <text>Thread #%d: %s</text>\n",
938 (Int)xe->XE.Misc.thr->errmsg_index,
939 xe->XE.Misc.errstr );
940 emit( " <hthreadid>%d</hthreadid>\n",
941 (Int)xe->XE.Misc.thr->errmsg_index );
942 emit( " </xwhat>\n" );
943 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
sewardj8fef6252010-07-29 05:28:02 +0000944 if (xe->XE.Misc.auxstr) {
945 emit(" <auxwhat>%s</auxwhat>\n", xe->XE.Misc.auxstr);
946 if (xe->XE.Misc.auxctx)
947 VG_(pp_ExeContext)( xe->XE.Misc.auxctx );
948 }
sewardj24118492009-07-15 14:50:02 +0000949
950 } else {
951
952 emit( "Thread #%d: %s\n",
953 (Int)xe->XE.Misc.thr->errmsg_index,
954 xe->XE.Misc.errstr );
955 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
sewardj8fef6252010-07-29 05:28:02 +0000956 if (xe->XE.Misc.auxstr) {
957 emit(" %s\n", xe->XE.Misc.auxstr);
958 if (xe->XE.Misc.auxctx)
959 VG_(pp_ExeContext)( xe->XE.Misc.auxctx );
960 }
sewardj24118492009-07-15 14:50:02 +0000961
962 }
sewardjf98e1c02008-10-25 16:22:41 +0000963 break;
964 }
965
966 case XE_LockOrder: {
sewardjf98e1c02008-10-25 16:22:41 +0000967 tl_assert( HG_(is_sane_Thread)( xe->XE.LockOrder.thr ) );
sewardj24118492009-07-15 14:50:02 +0000968
969 if (xml) {
970
sewardj24118492009-07-15 14:50:02 +0000971 emit( " <xwhat>\n" );
972 emit( " <text>Thread #%d: lock order \"%p before %p\" "
973 "violated</text>\n",
974 (Int)xe->XE.LockOrder.thr->errmsg_index,
philippe46daf0d2014-07-29 20:08:15 +0000975 (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr,
976 (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr );
sewardj24118492009-07-15 14:50:02 +0000977 emit( " <hthreadid>%d</hthreadid>\n",
978 (Int)xe->XE.LockOrder.thr->errmsg_index );
979 emit( " </xwhat>\n" );
980 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
sewardjffce8152011-06-24 10:09:41 +0000981 if (xe->XE.LockOrder.shouldbe_earlier_ec
982 && xe->XE.LockOrder.shouldbe_later_ec) {
sewardj24118492009-07-15 14:50:02 +0000983 emit( " <auxwhat>Required order was established by "
984 "acquisition of lock at %p</auxwhat>\n",
philippe46daf0d2014-07-29 20:08:15 +0000985 (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr );
sewardjffce8152011-06-24 10:09:41 +0000986 VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_earlier_ec );
sewardj24118492009-07-15 14:50:02 +0000987 emit( " <auxwhat>followed by a later acquisition "
988 "of lock at %p</auxwhat>\n",
philippe46daf0d2014-07-29 20:08:15 +0000989 (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr );
sewardjffce8152011-06-24 10:09:41 +0000990 VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_later_ec );
sewardj24118492009-07-15 14:50:02 +0000991 }
philippe46daf0d2014-07-29 20:08:15 +0000992 announce_LockP ( xe->XE.LockOrder.shouldbe_earlier_lk );
993 announce_LockP ( xe->XE.LockOrder.shouldbe_later_lk );
sewardj24118492009-07-15 14:50:02 +0000994
995 } else {
996
997 emit( "Thread #%d: lock order \"%p before %p\" violated\n",
998 (Int)xe->XE.LockOrder.thr->errmsg_index,
philippe46daf0d2014-07-29 20:08:15 +0000999 (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr,
1000 (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr );
sewardjffce8152011-06-24 10:09:41 +00001001 emit( "\n" );
1002 emit( "Observed (incorrect) order is: "
1003 "acquisition of lock at %p\n",
philippe46daf0d2014-07-29 20:08:15 +00001004 (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr);
sewardjffce8152011-06-24 10:09:41 +00001005 if (xe->XE.LockOrder.actual_earlier_ec) {
1006 VG_(pp_ExeContext)(xe->XE.LockOrder.actual_earlier_ec);
1007 } else {
1008 emit(" (stack unavailable)\n");
1009 }
1010 emit( "\n" );
1011 emit(" followed by a later acquisition of lock at %p\n",
philippe46daf0d2014-07-29 20:08:15 +00001012 (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr);
sewardj24118492009-07-15 14:50:02 +00001013 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
sewardjffce8152011-06-24 10:09:41 +00001014 if (xe->XE.LockOrder.shouldbe_earlier_ec
1015 && xe->XE.LockOrder.shouldbe_later_ec) {
1016 emit("\n");
1017 emit( "Required order was established by "
sewardj24118492009-07-15 14:50:02 +00001018 "acquisition of lock at %p\n",
philippe46daf0d2014-07-29 20:08:15 +00001019 (void*)xe->XE.LockOrder.shouldbe_earlier_lk->guestaddr );
sewardjffce8152011-06-24 10:09:41 +00001020 VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_earlier_ec );
1021 emit( "\n" );
1022 emit( " followed by a later acquisition of lock at %p\n",
philippe46daf0d2014-07-29 20:08:15 +00001023 (void*)xe->XE.LockOrder.shouldbe_later_lk->guestaddr );
sewardjffce8152011-06-24 10:09:41 +00001024 VG_(pp_ExeContext)( xe->XE.LockOrder.shouldbe_later_ec );
sewardj24118492009-07-15 14:50:02 +00001025 }
philippe46daf0d2014-07-29 20:08:15 +00001026 emit("\n");
1027 announce_LockP ( xe->XE.LockOrder.shouldbe_earlier_lk );
1028 announce_LockP ( xe->XE.LockOrder.shouldbe_later_lk );
sewardj24118492009-07-15 14:50:02 +00001029
sewardjf98e1c02008-10-25 16:22:41 +00001030 }
sewardj24118492009-07-15 14:50:02 +00001031
sewardjf98e1c02008-10-25 16:22:41 +00001032 break;
1033 }
1034
1035 case XE_PthAPIerror: {
sewardjf98e1c02008-10-25 16:22:41 +00001036 tl_assert( HG_(is_sane_Thread)( xe->XE.PthAPIerror.thr ) );
sewardj24118492009-07-15 14:50:02 +00001037
1038 if (xml) {
1039
sewardj24118492009-07-15 14:50:02 +00001040 emit( " <xwhat>\n" );
bartb3af9cf2011-10-06 19:08:37 +00001041 emit(
1042 " <text>Thread #%d's call to %pS failed</text>\n",
sewardj24118492009-07-15 14:50:02 +00001043 (Int)xe->XE.PthAPIerror.thr->errmsg_index,
1044 xe->XE.PthAPIerror.fnname );
1045 emit( " <hthreadid>%d</hthreadid>\n",
1046 (Int)xe->XE.PthAPIerror.thr->errmsg_index );
1047 emit( " </xwhat>\n" );
1048 emit( " <what>with error code %ld (%s)</what>\n",
1049 xe->XE.PthAPIerror.err, xe->XE.PthAPIerror.errstr );
1050 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
1051
1052 } else {
1053
bartb3af9cf2011-10-06 19:08:37 +00001054 emit( "Thread #%d's call to %pS failed\n",
sewardj24118492009-07-15 14:50:02 +00001055 (Int)xe->XE.PthAPIerror.thr->errmsg_index,
1056 xe->XE.PthAPIerror.fnname );
1057 emit( " with error code %ld (%s)\n",
1058 xe->XE.PthAPIerror.err, xe->XE.PthAPIerror.errstr );
1059 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
1060
1061 }
1062
sewardjf98e1c02008-10-25 16:22:41 +00001063 break;
1064 }
1065
1066 case XE_UnlockBogus: {
sewardjf98e1c02008-10-25 16:22:41 +00001067 tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockBogus.thr ) );
sewardj24118492009-07-15 14:50:02 +00001068
1069 if (xml) {
1070
sewardj24118492009-07-15 14:50:02 +00001071 emit( " <xwhat>\n" );
1072 emit( " <text>Thread #%d unlocked an invalid "
1073 "lock at %p</text>\n",
1074 (Int)xe->XE.UnlockBogus.thr->errmsg_index,
1075 (void*)xe->XE.UnlockBogus.lock_ga );
1076 emit( " <hthreadid>%d</hthreadid>\n",
1077 (Int)xe->XE.UnlockBogus.thr->errmsg_index );
1078 emit( " </xwhat>\n" );
1079 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
1080
1081 } else {
1082
1083 emit( "Thread #%d unlocked an invalid lock at %p\n",
1084 (Int)xe->XE.UnlockBogus.thr->errmsg_index,
1085 (void*)xe->XE.UnlockBogus.lock_ga );
1086 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
1087
1088 }
1089
sewardjf98e1c02008-10-25 16:22:41 +00001090 break;
1091 }
1092
1093 case XE_UnlockForeign: {
sewardjf98e1c02008-10-25 16:22:41 +00001094 tl_assert( HG_(is_sane_LockP)( xe->XE.UnlockForeign.lock ) );
1095 tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockForeign.owner ) );
1096 tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockForeign.thr ) );
sewardj24118492009-07-15 14:50:02 +00001097
1098 if (xml) {
1099
sewardj24118492009-07-15 14:50:02 +00001100 emit( " <xwhat>\n" );
1101 emit( " <text>Thread #%d unlocked lock at %p "
1102 "currently held by thread #%d</text>\n",
1103 (Int)xe->XE.UnlockForeign.thr->errmsg_index,
1104 (void*)xe->XE.UnlockForeign.lock->guestaddr,
1105 (Int)xe->XE.UnlockForeign.owner->errmsg_index );
1106 emit( " <hthreadid>%d</hthreadid>\n",
1107 (Int)xe->XE.UnlockForeign.thr->errmsg_index );
1108 emit( " <hthreadid>%d</hthreadid>\n",
1109 (Int)xe->XE.UnlockForeign.owner->errmsg_index );
1110 emit( " </xwhat>\n" );
1111 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
philippe46daf0d2014-07-29 20:08:15 +00001112 announce_LockP ( xe->XE.UnlockForeign.lock );
sewardj24118492009-07-15 14:50:02 +00001113
1114 } else {
1115
1116 emit( "Thread #%d unlocked lock at %p "
1117 "currently held by thread #%d\n",
1118 (Int)xe->XE.UnlockForeign.thr->errmsg_index,
1119 (void*)xe->XE.UnlockForeign.lock->guestaddr,
1120 (Int)xe->XE.UnlockForeign.owner->errmsg_index );
1121 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
philippe46daf0d2014-07-29 20:08:15 +00001122 announce_LockP ( xe->XE.UnlockForeign.lock );
sewardj24118492009-07-15 14:50:02 +00001123
sewardjf98e1c02008-10-25 16:22:41 +00001124 }
sewardj24118492009-07-15 14:50:02 +00001125
sewardjf98e1c02008-10-25 16:22:41 +00001126 break;
1127 }
1128
1129 case XE_UnlockUnlocked: {
sewardjf98e1c02008-10-25 16:22:41 +00001130 tl_assert( HG_(is_sane_LockP)( xe->XE.UnlockUnlocked.lock ) );
1131 tl_assert( HG_(is_sane_Thread)( xe->XE.UnlockUnlocked.thr ) );
sewardjf98e1c02008-10-25 16:22:41 +00001132
sewardj24118492009-07-15 14:50:02 +00001133 if (xml) {
1134
sewardj24118492009-07-15 14:50:02 +00001135 emit( " <xwhat>\n" );
1136 emit( " <text>Thread #%d unlocked a "
1137 "not-locked lock at %p</text>\n",
1138 (Int)xe->XE.UnlockUnlocked.thr->errmsg_index,
1139 (void*)xe->XE.UnlockUnlocked.lock->guestaddr );
1140 emit( " <hthreadid>%d</hthreadid>\n",
1141 (Int)xe->XE.UnlockUnlocked.thr->errmsg_index );
1142 emit( " </xwhat>\n" );
1143 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
philippe46daf0d2014-07-29 20:08:15 +00001144 announce_LockP ( xe->XE.UnlockUnlocked.lock);
sewardj24118492009-07-15 14:50:02 +00001145
1146 } else {
1147
1148 emit( "Thread #%d unlocked a not-locked lock at %p\n",
1149 (Int)xe->XE.UnlockUnlocked.thr->errmsg_index,
1150 (void*)xe->XE.UnlockUnlocked.lock->guestaddr );
1151 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
philippe46daf0d2014-07-29 20:08:15 +00001152 announce_LockP ( xe->XE.UnlockUnlocked.lock);
sewardj24118492009-07-15 14:50:02 +00001153
sewardjf98e1c02008-10-25 16:22:41 +00001154 }
sewardj24118492009-07-15 14:50:02 +00001155
sewardjf98e1c02008-10-25 16:22:41 +00001156 break;
1157 }
1158
1159 case XE_Race: {
1160 Addr err_ga;
florian6bf37262012-10-21 03:23:36 +00001161 const HChar* what;
sewardjf98e1c02008-10-25 16:22:41 +00001162 Int szB;
1163 what = xe->XE.Race.isWrite ? "write" : "read";
1164 szB = xe->XE.Race.szB;
1165 err_ga = VG_(get_error_address)(err);
1166
sewardj24118492009-07-15 14:50:02 +00001167 tl_assert( HG_(is_sane_Thread)( xe->XE.Race.thr ));
sewardj23f12002009-07-24 08:45:08 +00001168 if (xe->XE.Race.h2_ct)
1169 tl_assert( HG_(is_sane_Thread)( xe->XE.Race.h2_ct ));
sewardj24118492009-07-15 14:50:02 +00001170
1171 if (xml) {
1172
1173 /* ------ XML ------ */
sewardj24118492009-07-15 14:50:02 +00001174 emit( " <xwhat>\n" );
1175 emit( " <text>Possible data race during %s of size %d "
sewardjffce8152011-06-24 10:09:41 +00001176 "at %p by thread #%d</text>\n",
1177 what, szB, (void*)err_ga, (Int)xe->XE.Race.thr->errmsg_index );
sewardj24118492009-07-15 14:50:02 +00001178 emit( " <hthreadid>%d</hthreadid>\n",
1179 (Int)xe->XE.Race.thr->errmsg_index );
1180 emit( " </xwhat>\n" );
1181 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
1182
sewardj23f12002009-07-24 08:45:08 +00001183 if (xe->XE.Race.h2_ct) {
1184 tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra
1185 emit( " <xauxwhat>\n");
1186 emit( " <text>This conflicts with a previous %s of size %d "
1187 "by thread #%d</text>\n",
1188 xe->XE.Race.h2_ct_accIsW ? "write" : "read",
1189 xe->XE.Race.h2_ct_accSzB,
1190 xe->XE.Race.h2_ct->errmsg_index );
1191 emit( " <hthreadid>%d</hthreadid>\n",
1192 xe->XE.Race.h2_ct->errmsg_index);
1193 emit(" </xauxwhat>\n");
1194 VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC );
1195 }
1196
1197 if (xe->XE.Race.h1_ct) {
1198 emit( " <xauxwhat>\n");
1199 emit( " <text>This conflicts with a previous access "
1200 "by thread #%d, after</text>\n",
1201 xe->XE.Race.h1_ct->errmsg_index );
1202 emit( " <hthreadid>%d</hthreadid>\n",
1203 xe->XE.Race.h1_ct->errmsg_index );
1204 emit(" </xauxwhat>\n");
1205 if (xe->XE.Race.h1_ct_mbsegstartEC) {
1206 VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegstartEC );
sewardj24118492009-07-15 14:50:02 +00001207 } else {
sewardj23f12002009-07-24 08:45:08 +00001208 emit( " <auxwhat>(the start of the thread)</auxwhat>\n" );
sewardj24118492009-07-15 14:50:02 +00001209 }
sewardj23f12002009-07-24 08:45:08 +00001210 emit( " <auxwhat>but before</auxwhat>\n" );
1211 if (xe->XE.Race.h1_ct_mbsegendEC) {
1212 VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegendEC );
1213 } else {
1214 emit( " <auxwhat>(the end of the the thread)</auxwhat>\n" );
1215 }
sewardjf98e1c02008-10-25 16:22:41 +00001216 }
sewardj24118492009-07-15 14:50:02 +00001217
1218 } else {
1219
1220 /* ------ Text ------ */
sewardjffce8152011-06-24 10:09:41 +00001221 announce_combined_LockP_vecs( xe->XE.Race.locksHeldW,
1222 xe->XE.Race.h2_ct_locksHeldW );
1223
sewardj24118492009-07-15 14:50:02 +00001224 emit( "Possible data race during %s of size %d "
sewardjffce8152011-06-24 10:09:41 +00001225 "at %p by thread #%d\n",
1226 what, szB, (void*)err_ga, (Int)xe->XE.Race.thr->errmsg_index );
1227
1228 tl_assert(xe->XE.Race.locksHeldW);
1229 show_LockP_summary_textmode( xe->XE.Race.locksHeldW, "" );
sewardj24118492009-07-15 14:50:02 +00001230 VG_(pp_ExeContext)( VG_(get_error_where)(err) );
sewardj23f12002009-07-24 08:45:08 +00001231
1232 if (xe->XE.Race.h2_ct) {
1233 tl_assert(xe->XE.Race.h2_ct_accEC); // assured by update_extra
sewardjffce8152011-06-24 10:09:41 +00001234 tl_assert(xe->XE.Race.h2_ct_locksHeldW);
1235 emit( "\n" );
1236 emit( "This conflicts with a previous %s of size %d "
sewardj23f12002009-07-24 08:45:08 +00001237 "by thread #%d\n",
1238 xe->XE.Race.h2_ct_accIsW ? "write" : "read",
1239 xe->XE.Race.h2_ct_accSzB,
1240 xe->XE.Race.h2_ct->errmsg_index );
sewardjffce8152011-06-24 10:09:41 +00001241 show_LockP_summary_textmode( xe->XE.Race.h2_ct_locksHeldW, "" );
sewardj23f12002009-07-24 08:45:08 +00001242 VG_(pp_ExeContext)( xe->XE.Race.h2_ct_accEC );
1243 }
1244
1245 if (xe->XE.Race.h1_ct) {
1246 emit( " This conflicts with a previous access by thread #%d, "
1247 "after\n",
1248 xe->XE.Race.h1_ct->errmsg_index );
1249 if (xe->XE.Race.h1_ct_mbsegstartEC) {
1250 VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegstartEC );
sewardj24118492009-07-15 14:50:02 +00001251 } else {
sewardj23f12002009-07-24 08:45:08 +00001252 emit( " (the start of the thread)\n" );
sewardj24118492009-07-15 14:50:02 +00001253 }
sewardj23f12002009-07-24 08:45:08 +00001254 emit( " but before\n" );
1255 if (xe->XE.Race.h1_ct_mbsegendEC) {
1256 VG_(pp_ExeContext)( xe->XE.Race.h1_ct_mbsegendEC );
1257 } else {
1258 emit( " (the end of the the thread)\n" );
1259 }
sewardj24118492009-07-15 14:50:02 +00001260 }
1261
sewardjf98e1c02008-10-25 16:22:41 +00001262 }
philippe07c08522014-05-14 20:39:27 +00001263 VG_(pp_addrinfo) (err_ga, &xe->XE.Race.data_addrinfo);
sewardjf98e1c02008-10-25 16:22:41 +00001264 break; /* case XE_Race */
1265 } /* case XE_Race */
1266
1267 default:
1268 tl_assert(0);
1269 } /* switch (VG_(get_error_kind)(err)) */
1270}
1271
florian8e3fbb52014-10-20 19:02:38 +00001272const HChar* HG_(get_error_name) ( const Error* err )
sewardjf98e1c02008-10-25 16:22:41 +00001273{
1274 switch (VG_(get_error_kind)(err)) {
1275 case XE_Race: return "Race";
sewardjf98e1c02008-10-25 16:22:41 +00001276 case XE_UnlockUnlocked: return "UnlockUnlocked";
1277 case XE_UnlockForeign: return "UnlockForeign";
1278 case XE_UnlockBogus: return "UnlockBogus";
1279 case XE_PthAPIerror: return "PthAPIerror";
1280 case XE_LockOrder: return "LockOrder";
1281 case XE_Misc: return "Misc";
1282 default: tl_assert(0); /* fill in missing case */
1283 }
1284}
1285
florian19f91bb2012-11-10 22:29:54 +00001286Bool HG_(recognised_suppression) ( const HChar* name, Supp *su )
sewardjf98e1c02008-10-25 16:22:41 +00001287{
1288# define TRY(_name,_xskind) \
1289 if (0 == VG_(strcmp)(name, (_name))) { \
1290 VG_(set_supp_kind)(su, (_xskind)); \
1291 return True; \
1292 }
1293 TRY("Race", XS_Race);
1294 TRY("FreeMemLock", XS_FreeMemLock);
1295 TRY("UnlockUnlocked", XS_UnlockUnlocked);
1296 TRY("UnlockForeign", XS_UnlockForeign);
1297 TRY("UnlockBogus", XS_UnlockBogus);
1298 TRY("PthAPIerror", XS_PthAPIerror);
1299 TRY("LockOrder", XS_LockOrder);
1300 TRY("Misc", XS_Misc);
1301 return False;
1302# undef TRY
1303}
1304
florian19f91bb2012-11-10 22:29:54 +00001305Bool HG_(read_extra_suppression_info) ( Int fd, HChar** bufpp, SizeT* nBufp,
philippe362441d2013-07-22 22:00:13 +00001306 Int* lineno, Supp* su )
sewardjf98e1c02008-10-25 16:22:41 +00001307{
1308 /* do nothing -- no extra suppression info present. Return True to
1309 indicate nothing bad happened. */
1310 return True;
1311}
1312
florian8e3fbb52014-10-20 19:02:38 +00001313Bool HG_(error_matches_suppression) ( const Error* err, const Supp* su )
sewardjf98e1c02008-10-25 16:22:41 +00001314{
1315 switch (VG_(get_supp_kind)(su)) {
1316 case XS_Race: return VG_(get_error_kind)(err) == XE_Race;
sewardjf98e1c02008-10-25 16:22:41 +00001317 case XS_UnlockUnlocked: return VG_(get_error_kind)(err) == XE_UnlockUnlocked;
1318 case XS_UnlockForeign: return VG_(get_error_kind)(err) == XE_UnlockForeign;
1319 case XS_UnlockBogus: return VG_(get_error_kind)(err) == XE_UnlockBogus;
1320 case XS_PthAPIerror: return VG_(get_error_kind)(err) == XE_PthAPIerror;
1321 case XS_LockOrder: return VG_(get_error_kind)(err) == XE_LockOrder;
1322 case XS_Misc: return VG_(get_error_kind)(err) == XE_Misc;
1323 //case XS_: return VG_(get_error_kind)(err) == XE_;
1324 default: tl_assert(0); /* fill in missing cases */
1325 }
1326}
1327
florian8e3fbb52014-10-20 19:02:38 +00001328SizeT HG_(get_extra_suppression_info) ( const Error* err,
floriandbb35842012-10-27 18:39:11 +00001329 /*OUT*/HChar* buf, Int nBuf )
sewardjf98e1c02008-10-25 16:22:41 +00001330{
florian3e81b8b2014-10-07 14:28:52 +00001331 tl_assert(nBuf >= 1);
sewardjf98e1c02008-10-25 16:22:41 +00001332 /* Do nothing */
florian3e81b8b2014-10-07 14:28:52 +00001333 buf[0] = '\0';
1334 return 0;
sewardjf98e1c02008-10-25 16:22:41 +00001335}
1336
florian8e3fbb52014-10-20 19:02:38 +00001337SizeT HG_(print_extra_suppression_use) ( const Supp* su,
philippe4e32d672013-10-17 22:10:41 +00001338 /*OUT*/HChar* buf, Int nBuf )
1339{
florian3e81b8b2014-10-07 14:28:52 +00001340 tl_assert(nBuf >= 1);
philippe4e32d672013-10-17 22:10:41 +00001341 /* Do nothing */
florian3e81b8b2014-10-07 14:28:52 +00001342 buf[0] = '\0';
1343 return 0;
philippe4e32d672013-10-17 22:10:41 +00001344}
1345
florian8e3fbb52014-10-20 19:02:38 +00001346void HG_(update_extra_suppression_use) ( const Error* err, const Supp* su )
philippe4e32d672013-10-17 22:10:41 +00001347{
1348 /* Do nothing */
1349 return;
1350}
1351
sewardjf98e1c02008-10-25 16:22:41 +00001352
1353/*--------------------------------------------------------------------*/
1354/*--- end hg_errors.c ---*/
1355/*--------------------------------------------------------------------*/