blob: edc559471df3ea7df24ac1cb33a150e52fa82b31 [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- Management of error messages. vg_errcontext.c ---*/
4/*--------------------------------------------------------------------*/
5
6/*
njnc9539842002-10-02 13:26:35 +00007 This file is part of Valgrind, an extensible x86 protected-mode
8 emulator for monitoring program execution on x86-Unixes.
sewardjde4a1d02002-03-22 01:27:54 +00009
njn0e1b5142003-04-15 14:58:06 +000010 Copyright (C) 2000-2003 Julian Seward
sewardjde4a1d02002-03-22 01:27:54 +000011 jseward@acm.org
sewardjde4a1d02002-03-22 01:27:54 +000012
13 This program is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2 of the
16 License, or (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful, but
19 WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
26 02111-1307, USA.
27
njn25e49d8e72002-09-23 09:36:25 +000028 The GNU General Public License is contained in the file COPYING.
sewardjde4a1d02002-03-22 01:27:54 +000029*/
30
31#include "vg_include.h"
sewardjde4a1d02002-03-22 01:27:54 +000032
33/*------------------------------------------------------------*/
njn25e49d8e72002-09-23 09:36:25 +000034/*--- Globals ---*/
sewardjde4a1d02002-03-22 01:27:54 +000035/*------------------------------------------------------------*/
36
sewardjde4a1d02002-03-22 01:27:54 +000037/* The list of error contexts found, both suppressed and unsuppressed.
38 Initially empty, and grows as errors are detected. */
njn810086f2002-11-14 12:42:47 +000039static Error* vg_errors = NULL;
sewardjde4a1d02002-03-22 01:27:54 +000040
41/* The list of suppression directives, as read from the specified
42 suppressions file. */
njn810086f2002-11-14 12:42:47 +000043static Supp* vg_suppressions = NULL;
sewardjde4a1d02002-03-22 01:27:54 +000044
45/* Running count of unsuppressed errors detected. */
njn47363ab2003-04-21 13:24:40 +000046UInt VG_(n_errs_found) = 0;
sewardjde4a1d02002-03-22 01:27:54 +000047
48/* Running count of suppressed errors detected. */
49static UInt vg_n_errs_suppressed = 0;
50
51/* forwards ... */
njn810086f2002-11-14 12:42:47 +000052static Supp* is_suppressible_error ( Error* err );
sewardjde4a1d02002-03-22 01:27:54 +000053
54
55/*------------------------------------------------------------*/
56/*--- Helper fns ---*/
57/*------------------------------------------------------------*/
58
sewardjde4a1d02002-03-22 01:27:54 +000059/* Compare error contexts, to detect duplicates. Note that if they
60 are otherwise the same, the faulting addrs and associated rwoffsets
61 are allowed to be different. */
njn810086f2002-11-14 12:42:47 +000062static Bool eq_Error ( VgRes res, Error* e1, Error* e2 )
sewardjde4a1d02002-03-22 01:27:54 +000063{
njn810086f2002-11-14 12:42:47 +000064 if (e1->ekind != e2->ekind)
sewardjde4a1d02002-03-22 01:27:54 +000065 return False;
njn25e49d8e72002-09-23 09:36:25 +000066 if (!VG_(eq_ExeContext)(res, e1->where, e2->where))
sewardjde4a1d02002-03-22 01:27:54 +000067 return False;
68
njn810086f2002-11-14 12:42:47 +000069 switch (e1->ekind) {
sewardj4dced352002-06-04 22:54:20 +000070 case PThreadErr:
njn25e49d8e72002-09-23 09:36:25 +000071 vg_assert(VG_(needs).core_errors);
njn810086f2002-11-14 12:42:47 +000072 if (e1->string == e2->string)
sewardj4dced352002-06-04 22:54:20 +000073 return True;
njn810086f2002-11-14 12:42:47 +000074 if (0 == VG_(strcmp)(e1->string, e2->string))
sewardj4dced352002-06-04 22:54:20 +000075 return True;
76 return False;
sewardjde4a1d02002-03-22 01:27:54 +000077 default:
njn25e49d8e72002-09-23 09:36:25 +000078 if (VG_(needs).skin_errors)
njn810086f2002-11-14 12:42:47 +000079 return SK_(eq_SkinError)(res, e1, e2);
njn25e49d8e72002-09-23 09:36:25 +000080 else {
81 VG_(printf)("\nUnhandled error type: %u. VG_(needs).skin_errors\n"
82 "probably needs to be set.\n",
njn810086f2002-11-14 12:42:47 +000083 e1->ekind);
njne427a662002-10-02 11:08:25 +000084 VG_(skin_panic)("unhandled error type");
njn25e49d8e72002-09-23 09:36:25 +000085 }
sewardjde4a1d02002-03-22 01:27:54 +000086 }
87}
88
njn810086f2002-11-14 12:42:47 +000089static void pp_Error ( Error* err, Bool printCount )
sewardjde4a1d02002-03-22 01:27:54 +000090{
sewardjde4a1d02002-03-22 01:27:54 +000091 if (printCount)
njn25e49d8e72002-09-23 09:36:25 +000092 VG_(message)(Vg_UserMsg, "Observed %d times:", err->count );
93 if (err->tid > 1)
94 VG_(message)(Vg_UserMsg, "Thread %d:", err->tid );
95
njn810086f2002-11-14 12:42:47 +000096 switch (err->ekind) {
sewardj4dced352002-06-04 22:54:20 +000097 case PThreadErr:
njn25e49d8e72002-09-23 09:36:25 +000098 vg_assert(VG_(needs).core_errors);
njn810086f2002-11-14 12:42:47 +000099 VG_(message)(Vg_UserMsg, "%s", err->string );
njn25e49d8e72002-09-23 09:36:25 +0000100 VG_(pp_ExeContext)(err->where);
sewardj4dced352002-06-04 22:54:20 +0000101 break;
sewardjde4a1d02002-03-22 01:27:54 +0000102 default:
njn25e49d8e72002-09-23 09:36:25 +0000103 if (VG_(needs).skin_errors)
njn43c799e2003-04-08 00:08:52 +0000104 SK_(pp_SkinError)( err );
njn25e49d8e72002-09-23 09:36:25 +0000105 else {
106 VG_(printf)("\nUnhandled error type: %u. VG_(needs).skin_errors\n"
107 "probably needs to be set?\n",
njn810086f2002-11-14 12:42:47 +0000108 err->ekind);
njne427a662002-10-02 11:08:25 +0000109 VG_(skin_panic)("unhandled error type");
njn25e49d8e72002-09-23 09:36:25 +0000110 }
sewardjde4a1d02002-03-22 01:27:54 +0000111 }
112}
113
sewardjde4a1d02002-03-22 01:27:54 +0000114/* Figure out if we want to attach for GDB for this error, possibly
115 by asking the user. */
njn43c799e2003-04-08 00:08:52 +0000116Bool VG_(is_action_requested) ( Char* action, Bool* clo )
sewardjde4a1d02002-03-22 01:27:54 +0000117{
118 Char ch, ch2;
119 Int res;
120
njn43c799e2003-04-08 00:08:52 +0000121 if (*clo == False)
sewardjde4a1d02002-03-22 01:27:54 +0000122 return False;
123
124 VG_(message)(Vg_UserMsg, "");
125
126 again:
127 VG_(printf)(
128 "==%d== "
njn43c799e2003-04-08 00:08:52 +0000129 "---- %s ? --- [Return/N/n/Y/y/C/c] ---- ",
130 VG_(getpid)(), action
sewardjde4a1d02002-03-22 01:27:54 +0000131 );
132
133 res = VG_(read)(0 /*stdin*/, &ch, 1);
134 if (res != 1) goto ioerror;
135 /* res == 1 */
136 if (ch == '\n') return False;
137 if (ch != 'N' && ch != 'n' && ch != 'Y' && ch != 'y'
138 && ch != 'C' && ch != 'c') goto again;
139
140 res = VG_(read)(0 /*stdin*/, &ch2, 1);
141 if (res != 1) goto ioerror;
142 if (ch2 != '\n') goto again;
143
njn43c799e2003-04-08 00:08:52 +0000144 /* No, don't want to do action. */
sewardjde4a1d02002-03-22 01:27:54 +0000145 if (ch == 'n' || ch == 'N') return False;
njn43c799e2003-04-08 00:08:52 +0000146 /* Yes, want to do action. */
sewardjde4a1d02002-03-22 01:27:54 +0000147 if (ch == 'y' || ch == 'Y') return True;
njn43c799e2003-04-08 00:08:52 +0000148 /* No, don't want to do action, and don't ask again either. */
sewardjde4a1d02002-03-22 01:27:54 +0000149 vg_assert(ch == 'c' || ch == 'C');
150
151 ioerror:
njn43c799e2003-04-08 00:08:52 +0000152 *clo = False;
sewardjde4a1d02002-03-22 01:27:54 +0000153 return False;
154}
155
156
njn25e49d8e72002-09-23 09:36:25 +0000157/* I've gone all object-oriented... initialisation depends on where the
158 error comes from:
159
160 - If from generated code (tst == NULL), the %EIP/%EBP values that we
njn3e884182003-04-15 13:03:23 +0000161 need in order to attach GDB are picked up out of VG_(baseBlock) rather
162 than from the thread table (vg_threads in vg_scheduler.c).
njn25e49d8e72002-09-23 09:36:25 +0000163
164 - If not from generated code but in response to requests passed back to
165 the scheduler (tst != NULL), we pick up %EIP/%EBP values from the
166 stored thread state, not from VG_(baseBlock).
167*/
168static __inline__
njn43c799e2003-04-08 00:08:52 +0000169void construct_error ( Error* err, ThreadState* tst, ErrorKind ekind, Addr a,
njn3e884182003-04-15 13:03:23 +0000170 Char* s, void* extra, ExeContext* where,
171 /*out*/Addr* m_eip, /*out*/Addr* m_esp,
172 /*out*/Addr* m_ebp )
sewardjde4a1d02002-03-22 01:27:54 +0000173{
njn810086f2002-11-14 12:42:47 +0000174 /* Core-only parts */
njn25e49d8e72002-09-23 09:36:25 +0000175 err->next = NULL;
176 err->supp = NULL;
177 err->count = 1;
njn43c799e2003-04-08 00:08:52 +0000178 if (NULL == where)
179 err->where = VG_(get_ExeContext)( tst );
180 else
181 err->where = where;
njn1d6c4bc2002-11-21 13:38:08 +0000182
njn25e49d8e72002-09-23 09:36:25 +0000183 if (NULL == tst) {
184 err->tid = VG_(get_current_tid)();
njn3e884182003-04-15 13:03:23 +0000185 *m_eip = VG_(baseBlock)[VGOFF_(m_eip)];
186 *m_esp = VG_(baseBlock)[VGOFF_(m_esp)];
187 *m_ebp = VG_(baseBlock)[VGOFF_(m_ebp)];
njn25e49d8e72002-09-23 09:36:25 +0000188 } else {
njn25e49d8e72002-09-23 09:36:25 +0000189 err->tid = tst->tid;
njn3e884182003-04-15 13:03:23 +0000190 *m_eip = tst->m_eip;
191 *m_esp = tst->m_esp;
192 *m_ebp = tst->m_ebp;
njn25e49d8e72002-09-23 09:36:25 +0000193 }
194
njn810086f2002-11-14 12:42:47 +0000195 /* Skin-relevant parts */
196 err->ekind = ekind;
197 err->addr = a;
198 err->string = s;
199 err->extra = extra;
njn25e49d8e72002-09-23 09:36:25 +0000200
201 /* sanity... */
202 vg_assert(err->tid >= 0 && err->tid < VG_N_THREADS);
203}
204
njn43c799e2003-04-08 00:08:52 +0000205void VG_(gen_suppression)(Error* err)
206{
sewardj05bcdcb2003-05-18 10:05:38 +0000207 Int i;
njn43c799e2003-04-08 00:08:52 +0000208 UChar buf[M_VG_ERRTXT];
209 ExeContext* ec = VG_(get_error_where)(err);
210 Int stop_at = VG_(clo_backtrace_size);
211 Char* name = SK_(get_error_name)(err);
212
213 if (NULL == name) {
214 VG_(message)(Vg_UserMsg, "(skin does not allow error to be suppressed)");
215 return;
216 }
217
njn633de322003-05-12 20:40:13 +0000218 if (stop_at > 4) stop_at = 4; /* At most four names */
njn43c799e2003-04-08 00:08:52 +0000219 vg_assert(stop_at > 0);
220
221 VG_(printf)("{\n");
222 VG_(printf)(" <insert a suppression name here>\n");
223 VG_(printf)(" %s:%s\n", VG_(details).name, name);
224 SK_(print_extra_suppression_info)(err);
225
226 /* This loop condensed from VG_(mini_stack_dump)() */
227 i = 0;
228 do {
229 Addr eip = ec->eips[i];
230 if (i > 0)
231 eip--; /* point to calling line */
232
233 if ( VG_(get_fnname_nodemangle) (eip, buf, M_VG_ERRTXT) ) {
234 VG_(printf)(" fun:%s\n", buf);
235 } else if ( VG_(get_objname)(eip, buf, M_VG_ERRTXT) ) {
236 VG_(printf)(" obj:%s\n", buf);
237 } else {
238 VG_(printf)(" ???:??? "
239 "# unknown, suppression will not work, sorry)\n");
240 }
241 i++;
242 } while (i < stop_at && ec->eips[i] != 0);
243
244 VG_(printf)("}\n");
245}
246
njnb4aee052003-04-15 14:09:58 +0000247static
njn3e884182003-04-15 13:03:23 +0000248void do_actions_on_error(Error* err, Bool allow_GDB_attach,
249 Addr m_eip, Addr m_esp, Addr m_ebp )
njn43c799e2003-04-08 00:08:52 +0000250{
251 /* Perhaps we want a GDB attach at this point? */
njn3e884182003-04-15 13:03:23 +0000252 if (allow_GDB_attach &&
253 VG_(is_action_requested)( "Attach to GDB", & VG_(clo_GDB_attach) ))
254 {
255 VG_(swizzle_esp_then_start_GDB)( m_eip, m_esp, m_ebp );
njn43c799e2003-04-08 00:08:52 +0000256 }
257 /* Or maybe we want to generate the error's suppression? */
258 if (VG_(is_action_requested)( "Print suppression",
259 & VG_(clo_gen_suppressions) )) {
260 VG_(gen_suppression)(err);
261 }
262}
263
264/* Shared between VG_(maybe_record_error)() and VG_(unique_error)(),
265 just for pretty printing purposes. */
266static Bool is_first_shown_context = True;
267
njn25e49d8e72002-09-23 09:36:25 +0000268/* Top-level entry point to the error management subsystem.
269 All detected errors are notified here; this routine decides if/when the
270 user should see the error. */
271void VG_(maybe_record_error) ( ThreadState* tst,
272 ErrorKind ekind, Addr a, Char* s, void* extra )
273{
njn3e884182003-04-15 13:03:23 +0000274 Addr m_eip, m_esp, m_ebp;
njn810086f2002-11-14 12:42:47 +0000275 Error err;
276 Error* p;
277 Error* p_prev;
njn43c799e2003-04-08 00:08:52 +0000278 UInt extra_size;
njn810086f2002-11-14 12:42:47 +0000279 VgRes exe_res = Vg_MedRes;
njn810086f2002-11-14 12:42:47 +0000280 static Bool stopping_message = False;
281 static Bool slowdown_message = False;
282 static Int vg_n_errs_shown = 0;
sewardjde4a1d02002-03-22 01:27:54 +0000283
sewardjf2537be2002-04-24 21:03:47 +0000284 /* After M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN different errors have
285 been found, or M_VG_COLLECT_NO_ERRORS_AFTER_FOUND total errors
286 have been found, just refuse to collect any more. This stops
287 the burden of the error-management system becoming excessive in
288 extremely buggy programs, although it does make it pretty
289 pointless to continue the Valgrind run after this point. */
sewardj2e432902002-06-13 20:44:00 +0000290 if (VG_(clo_error_limit)
sewardj72f98ff2002-06-13 17:23:38 +0000291 && (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN
njn47363ab2003-04-21 13:24:40 +0000292 || VG_(n_errs_found) >= M_VG_COLLECT_NO_ERRORS_AFTER_FOUND)) {
sewardjde4a1d02002-03-22 01:27:54 +0000293 if (!stopping_message) {
294 VG_(message)(Vg_UserMsg, "");
sewardjf2537be2002-04-24 21:03:47 +0000295
296 if (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN) {
297 VG_(message)(Vg_UserMsg,
298 "More than %d different errors detected. "
299 "I'm not reporting any more.",
300 M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN );
301 } else {
302 VG_(message)(Vg_UserMsg,
303 "More than %d total errors detected. "
304 "I'm not reporting any more.",
305 M_VG_COLLECT_NO_ERRORS_AFTER_FOUND );
306 }
307
sewardjde4a1d02002-03-22 01:27:54 +0000308 VG_(message)(Vg_UserMsg,
sewardjf2537be2002-04-24 21:03:47 +0000309 "Final error counts will be inaccurate. Go fix your program!");
sewardj72f98ff2002-06-13 17:23:38 +0000310 VG_(message)(Vg_UserMsg,
sewardj2e432902002-06-13 20:44:00 +0000311 "Rerun with --error-limit=no to disable this cutoff. Note");
sewardj72f98ff2002-06-13 17:23:38 +0000312 VG_(message)(Vg_UserMsg,
njn25e49d8e72002-09-23 09:36:25 +0000313 "that errors may occur in your program without prior warning from");
sewardj72f98ff2002-06-13 17:23:38 +0000314 VG_(message)(Vg_UserMsg,
315 "Valgrind, because errors are no longer being displayed.");
sewardjde4a1d02002-03-22 01:27:54 +0000316 VG_(message)(Vg_UserMsg, "");
317 stopping_message = True;
318 }
319 return;
320 }
321
322 /* After M_VG_COLLECT_ERRORS_SLOWLY_AFTER different errors have
323 been found, be much more conservative about collecting new
324 ones. */
325 if (vg_n_errs_shown >= M_VG_COLLECT_ERRORS_SLOWLY_AFTER) {
njn25e49d8e72002-09-23 09:36:25 +0000326 exe_res = Vg_LowRes;
sewardjde4a1d02002-03-22 01:27:54 +0000327 if (!slowdown_message) {
328 VG_(message)(Vg_UserMsg, "");
329 VG_(message)(Vg_UserMsg,
330 "More than %d errors detected. Subsequent errors",
331 M_VG_COLLECT_ERRORS_SLOWLY_AFTER);
332 VG_(message)(Vg_UserMsg,
333 "will still be recorded, but in less detail than before.");
334 slowdown_message = True;
335 }
336 }
337
njn25e49d8e72002-09-23 09:36:25 +0000338 /* Build ourselves the error */
njn3e884182003-04-15 13:03:23 +0000339 construct_error ( &err, tst, ekind, a, s, extra, NULL,
340 &m_eip, &m_esp, &m_ebp );
sewardjde4a1d02002-03-22 01:27:54 +0000341
342 /* First, see if we've got an error record matching this one. */
njn25e49d8e72002-09-23 09:36:25 +0000343 p = vg_errors;
sewardjde4a1d02002-03-22 01:27:54 +0000344 p_prev = NULL;
345 while (p != NULL) {
njn810086f2002-11-14 12:42:47 +0000346 if (eq_Error(exe_res, p, &err)) {
sewardjde4a1d02002-03-22 01:27:54 +0000347 /* Found it. */
348 p->count++;
349 if (p->supp != NULL) {
350 /* Deal correctly with suppressed errors. */
351 p->supp->count++;
352 vg_n_errs_suppressed++;
353 } else {
njn47363ab2003-04-21 13:24:40 +0000354 VG_(n_errs_found)++;
sewardjde4a1d02002-03-22 01:27:54 +0000355 }
356
357 /* Move p to the front of the list so that future searches
358 for it are faster. */
359 if (p_prev != NULL) {
360 vg_assert(p_prev->next == p);
361 p_prev->next = p->next;
njn25e49d8e72002-09-23 09:36:25 +0000362 p->next = vg_errors;
363 vg_errors = p;
sewardjde4a1d02002-03-22 01:27:54 +0000364 }
365 return;
366 }
367 p_prev = p;
368 p = p->next;
369 }
370
371 /* Didn't see it. Copy and add. */
372
njn43c799e2003-04-08 00:08:52 +0000373 /* OK, we're really going to collect it. The context is on the stack and
374 will disappear shortly, so we must copy it. First do the main
375 (non-`extra') part.
njn25e49d8e72002-09-23 09:36:25 +0000376
njn43c799e2003-04-08 00:08:52 +0000377 Then SK_(update_extra) can update the `extra' part. This is for when
378 there are more details to fill in which take time to work out but
379 don't affect our earlier decision to include the error -- by
njn25e49d8e72002-09-23 09:36:25 +0000380 postponing those details until now, we avoid the extra work in the
njn810086f2002-11-14 12:42:47 +0000381 case where we ignore the error. Ugly.
njn43c799e2003-04-08 00:08:52 +0000382
383 Then, if there is an `extra' part, copy it too, using the size that
384 SK_(update_extra) returned.
385 */
386
387 /* copy main part */
njn810086f2002-11-14 12:42:47 +0000388 p = VG_(arena_malloc)(VG_AR_ERRORS, sizeof(Error));
njn25e49d8e72002-09-23 09:36:25 +0000389 *p = err;
njn43c799e2003-04-08 00:08:52 +0000390
391 /* update `extra' */
392 extra_size = SK_(update_extra)(p);
393
394 /* copy `extra' if there is one */
395 if (NULL != p->extra) {
396 void* new_extra = VG_(malloc)(extra_size);
397 VG_(memcpy)(new_extra, p->extra, extra_size);
398 p->extra = new_extra;
399 }
400
njn25e49d8e72002-09-23 09:36:25 +0000401 p->next = vg_errors;
402 p->supp = is_suppressible_error(&err);
403 vg_errors = p;
sewardjde4a1d02002-03-22 01:27:54 +0000404 if (p->supp == NULL) {
njn47363ab2003-04-21 13:24:40 +0000405 VG_(n_errs_found)++;
sewardjde4a1d02002-03-22 01:27:54 +0000406 if (!is_first_shown_context)
407 VG_(message)(Vg_UserMsg, "");
njn43c799e2003-04-08 00:08:52 +0000408 pp_Error(p, False);
sewardjde4a1d02002-03-22 01:27:54 +0000409 is_first_shown_context = False;
410 vg_n_errs_shown++;
njn3e884182003-04-15 13:03:23 +0000411 do_actions_on_error(p, /*allow_GDB_attach*/True, m_eip, m_esp, m_ebp );
sewardjde4a1d02002-03-22 01:27:54 +0000412 } else {
413 vg_n_errs_suppressed++;
414 p->supp->count++;
415 }
416}
417
njn43c799e2003-04-08 00:08:52 +0000418/* Second top-level entry point to the error management subsystem, for
419 errors that the skin want to report immediately, eg. because they're
420 guaranteed to only happen once. This avoids all the recording and
421 comparing stuff. But they can be suppressed; returns True if it is
njn47363ab2003-04-21 13:24:40 +0000422 suppressed. Bool `print_error' dictates whether to print the error.
423 Bool `count_error' dictates whether to count the error in VG_(n_errs_found)
424*/
njn43c799e2003-04-08 00:08:52 +0000425Bool VG_(unique_error) ( ThreadState* tst, ErrorKind ekind, Addr a, Char* s,
njn3e884182003-04-15 13:03:23 +0000426 void* extra, ExeContext* where, Bool print_error,
njn47363ab2003-04-21 13:24:40 +0000427 Bool allow_GDB_attach, Bool count_error )
njn43c799e2003-04-08 00:08:52 +0000428{
429 Error err;
njn3e884182003-04-15 13:03:23 +0000430 Addr m_eip, m_esp, m_ebp;
njn43c799e2003-04-08 00:08:52 +0000431
432 /* Build ourselves the error */
njn3e884182003-04-15 13:03:23 +0000433 construct_error ( &err, tst, ekind, a, s, extra, where,
434 &m_eip, &m_esp, &m_ebp );
njn43c799e2003-04-08 00:08:52 +0000435
436 /* Unless it's suppressed, we're going to show it. Don't need to make
437 a copy, because it's only temporary anyway.
438
439 Then update the `extra' part with SK_(update_extra), because that can
440 have an affect on whether it's suppressed. Ignore the size return
441 value of SK_(update_extra), because we're not copying `extra'. */
442 (void)SK_(update_extra)(&err);
443
444 if (NULL == is_suppressible_error(&err)) {
njn47363ab2003-04-21 13:24:40 +0000445 if (count_error)
446 VG_(n_errs_found)++;
njn43c799e2003-04-08 00:08:52 +0000447
448 if (print_error) {
449 if (!is_first_shown_context)
450 VG_(message)(Vg_UserMsg, "");
451 pp_Error(&err, False);
452 is_first_shown_context = False;
453 }
njn3e884182003-04-15 13:03:23 +0000454 do_actions_on_error(&err, allow_GDB_attach, m_eip, m_esp, m_ebp);
njn43c799e2003-04-08 00:08:52 +0000455
456 return False;
457
458 } else {
459 vg_n_errs_suppressed++;
460 return True;
461 }
462}
463
sewardjde4a1d02002-03-22 01:27:54 +0000464
sewardjde4a1d02002-03-22 01:27:54 +0000465/*------------------------------------------------------------*/
466/*--- Exported fns ---*/
467/*------------------------------------------------------------*/
468
njn25e49d8e72002-09-23 09:36:25 +0000469/* These are called not from generated code but from the scheduler */
sewardj8c824512002-04-14 04:16:48 +0000470
njn25e49d8e72002-09-23 09:36:25 +0000471void VG_(record_pthread_error) ( ThreadId tid, Char* msg )
sewardjde4a1d02002-03-22 01:27:54 +0000472{
njn25e49d8e72002-09-23 09:36:25 +0000473 if (! VG_(needs).core_errors) return;
474 VG_(maybe_record_error)( &VG_(threads)[tid], PThreadErr, /*addr*/0, msg,
475 /*extra*/NULL );
sewardjde4a1d02002-03-22 01:27:54 +0000476}
477
sewardj8c824512002-04-14 04:16:48 +0000478/*------------------------------*/
479
sewardjde4a1d02002-03-22 01:27:54 +0000480void VG_(show_all_errors) ( void )
481{
njn810086f2002-11-14 12:42:47 +0000482 Int i, n_min;
483 Int n_err_contexts, n_supp_contexts;
484 Error *p, *p_min;
485 Supp *su;
486 Bool any_supp;
sewardjde4a1d02002-03-22 01:27:54 +0000487
488 if (VG_(clo_verbosity) == 0)
489 return;
490
491 n_err_contexts = 0;
njn25e49d8e72002-09-23 09:36:25 +0000492 for (p = vg_errors; p != NULL; p = p->next) {
sewardjde4a1d02002-03-22 01:27:54 +0000493 if (p->supp == NULL)
494 n_err_contexts++;
495 }
496
497 n_supp_contexts = 0;
498 for (su = vg_suppressions; su != NULL; su = su->next) {
499 if (su->count > 0)
500 n_supp_contexts++;
501 }
sewardjde4a1d02002-03-22 01:27:54 +0000502 VG_(message)(Vg_UserMsg,
503 "ERROR SUMMARY: "
504 "%d errors from %d contexts (suppressed: %d from %d)",
njn47363ab2003-04-21 13:24:40 +0000505 VG_(n_errs_found), n_err_contexts,
sewardjde4a1d02002-03-22 01:27:54 +0000506 vg_n_errs_suppressed, n_supp_contexts );
507
508 if (VG_(clo_verbosity) <= 1)
509 return;
510
511 /* Print the contexts in order of increasing error count. */
512 for (i = 0; i < n_err_contexts; i++) {
513 n_min = (1 << 30) - 1;
514 p_min = NULL;
njn25e49d8e72002-09-23 09:36:25 +0000515 for (p = vg_errors; p != NULL; p = p->next) {
sewardjde4a1d02002-03-22 01:27:54 +0000516 if (p->supp != NULL) continue;
517 if (p->count < n_min) {
518 n_min = p->count;
519 p_min = p;
520 }
521 }
njne427a662002-10-02 11:08:25 +0000522 if (p_min == NULL) VG_(skin_panic)("show_all_errors()");
sewardjde4a1d02002-03-22 01:27:54 +0000523
524 VG_(message)(Vg_UserMsg, "");
525 VG_(message)(Vg_UserMsg, "%d errors in context %d of %d:",
526 p_min->count,
527 i+1, n_err_contexts);
njn810086f2002-11-14 12:42:47 +0000528 pp_Error( p_min, False );
sewardjde4a1d02002-03-22 01:27:54 +0000529
530 if ((i+1 == VG_(clo_dump_error))) {
sewardj1e8cdc92002-04-18 11:37:52 +0000531 VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to below NULLs */,
sewardj22854b92002-11-30 14:00:47 +0000532 p_min->where->eips[0], NULL, NULL, NULL, NULL );
sewardjde4a1d02002-03-22 01:27:54 +0000533 }
534
535 p_min->count = 1 << 30;
536 }
537
538 if (n_supp_contexts > 0)
539 VG_(message)(Vg_DebugMsg, "");
540 any_supp = False;
541 for (su = vg_suppressions; su != NULL; su = su->next) {
542 if (su->count > 0) {
543 any_supp = True;
njn25e49d8e72002-09-23 09:36:25 +0000544 VG_(message)(Vg_DebugMsg, "supp: %4d %s", su->count, su->sname);
sewardjde4a1d02002-03-22 01:27:54 +0000545 }
546 }
547
548 if (n_err_contexts > 0) {
549 if (any_supp)
550 VG_(message)(Vg_UserMsg, "");
551 VG_(message)(Vg_UserMsg,
552 "IN SUMMARY: "
553 "%d errors from %d contexts (suppressed: %d from %d)",
njn47363ab2003-04-21 13:24:40 +0000554 VG_(n_errs_found), n_err_contexts,
sewardjde4a1d02002-03-22 01:27:54 +0000555 vg_n_errs_suppressed,
556 n_supp_contexts );
557 VG_(message)(Vg_UserMsg, "");
558 }
559}
560
561/*------------------------------------------------------------*/
562/*--- Standard suppressions ---*/
563/*------------------------------------------------------------*/
564
565/* Get a non-blank, non-comment line of at most nBuf chars from fd.
566 Skips leading spaces on the line. Return True if EOF was hit instead.
567*/
568
569#define VG_ISSPACE(ch) (((ch)==' ') || ((ch)=='\n') || ((ch)=='\t'))
570
njn4ba5a792002-09-30 10:23:54 +0000571Bool VG_(get_line) ( Int fd, Char* buf, Int nBuf )
sewardjde4a1d02002-03-22 01:27:54 +0000572{
573 Char ch;
574 Int n, i;
575 while (True) {
576 /* First, read until a non-blank char appears. */
577 while (True) {
578 n = VG_(read)(fd, &ch, 1);
579 if (n == 1 && !VG_ISSPACE(ch)) break;
580 if (n == 0) return True;
581 }
582
583 /* Now, read the line into buf. */
584 i = 0;
585 buf[i++] = ch; buf[i] = 0;
586 while (True) {
587 n = VG_(read)(fd, &ch, 1);
588 if (n == 0) return False; /* the next call will return True */
589 if (ch == '\n') break;
590 if (i > 0 && i == nBuf-1) i--;
591 buf[i++] = ch; buf[i] = 0;
592 }
593 while (i > 1 && VG_ISSPACE(buf[i-1])) {
594 i--; buf[i] = 0;
595 };
596
597 /* VG_(printf)("The line is `%s'\n", buf); */
598 /* Ok, we have a line. If a non-comment line, return.
599 If a comment line, start all over again. */
600 if (buf[0] != '#') return False;
601 }
602}
603
604
605/* *p_caller contains the raw name of a caller, supposedly either
606 fun:some_function_name or
607 obj:some_object_name.
608 Set *p_ty accordingly and advance *p_caller over the descriptor
609 (fun: or obj:) part.
610 Returns False if failed.
611*/
njn25e49d8e72002-09-23 09:36:25 +0000612static Bool setLocationTy ( Char** p_caller, SuppLocTy* p_ty )
sewardjde4a1d02002-03-22 01:27:54 +0000613{
614 if (VG_(strncmp)(*p_caller, "fun:", 4) == 0) {
615 (*p_caller) += 4;
616 *p_ty = FunName;
617 return True;
618 }
619 if (VG_(strncmp)(*p_caller, "obj:", 4) == 0) {
620 (*p_caller) += 4;
621 *p_ty = ObjName;
622 return True;
623 }
624 VG_(printf)("location should start with fun: or obj:\n");
625 return False;
626}
627
628
njn11cc9252002-10-07 14:42:59 +0000629/* Look for "skin" in a string like "skin1,skin2,skin3" */
630static __inline__
631Bool skin_name_present(Char *name, Char *names)
632{
633 Bool found;
634 Char *s = NULL; /* Shut gcc up */
635 Int len = VG_(strlen)(name);
636
637 found = (NULL != (s = VG_(strstr)(names, name)) &&
638 (s == names || *(s-1) == ',') &&
639 (*(s+len) == ',' || *(s+len) == '\0')
640 );
641
642 return found;
643}
644
sewardjde4a1d02002-03-22 01:27:54 +0000645/* Read suppressions from the file specified in vg_clo_suppressions
646 and place them in the suppressions list. If there's any difficulty
647 doing this, just give up -- there's no point in trying to recover.
648*/
sewardjde4a1d02002-03-22 01:27:54 +0000649static void load_one_suppressions_file ( Char* filename )
650{
651# define N_BUF 200
njnc40c3a82002-10-02 11:02:27 +0000652 Int fd, i;
653 Bool eof;
654 Char buf[N_BUF+1];
njn11cc9252002-10-07 14:42:59 +0000655 Char* skin_names;
njnc40c3a82002-10-02 11:02:27 +0000656 Char* supp_name;
657
njn25e49d8e72002-09-23 09:36:25 +0000658 fd = VG_(open)( filename, VKI_O_RDONLY, 0 );
sewardjde4a1d02002-03-22 01:27:54 +0000659 if (fd == -1) {
njn25e49d8e72002-09-23 09:36:25 +0000660 VG_(message)(Vg_UserMsg, "FATAL: can't open suppressions file `%s'",
sewardjde4a1d02002-03-22 01:27:54 +0000661 filename );
662 VG_(exit)(1);
663 }
664
665 while (True) {
njn25e49d8e72002-09-23 09:36:25 +0000666 /* Assign and initialise the two suppression halves (core and skin) */
njn810086f2002-11-14 12:42:47 +0000667 Supp* supp;
668 supp = VG_(arena_malloc)(VG_AR_CORE, sizeof(Supp));
sewardjde4a1d02002-03-22 01:27:54 +0000669 supp->count = 0;
njn25e49d8e72002-09-23 09:36:25 +0000670 for (i = 0; i < VG_N_SUPP_CALLERS; i++) supp->caller[i] = NULL;
njn810086f2002-11-14 12:42:47 +0000671 supp->string = supp->extra = NULL;
sewardjde4a1d02002-03-22 01:27:54 +0000672
njn4ba5a792002-09-30 10:23:54 +0000673 eof = VG_(get_line) ( fd, buf, N_BUF );
sewardjde4a1d02002-03-22 01:27:54 +0000674 if (eof) break;
675
njn43c799e2003-04-08 00:08:52 +0000676 if (!VG_STREQ(buf, "{")) goto syntax_error;
sewardjde4a1d02002-03-22 01:27:54 +0000677
njn4ba5a792002-09-30 10:23:54 +0000678 eof = VG_(get_line) ( fd, buf, N_BUF );
njn43c799e2003-04-08 00:08:52 +0000679 if (eof || VG_STREQ(buf, "}")) goto syntax_error;
njn25e49d8e72002-09-23 09:36:25 +0000680 supp->sname = VG_(arena_strdup)(VG_AR_CORE, buf);
sewardjde4a1d02002-03-22 01:27:54 +0000681
njn4ba5a792002-09-30 10:23:54 +0000682 eof = VG_(get_line) ( fd, buf, N_BUF );
njn25e49d8e72002-09-23 09:36:25 +0000683
sewardjde4a1d02002-03-22 01:27:54 +0000684 if (eof) goto syntax_error;
sewardjde4a1d02002-03-22 01:27:54 +0000685
njn11cc9252002-10-07 14:42:59 +0000686 /* Check it has the "skin1,skin2,...:supp" form (look for ':') */
njnc40c3a82002-10-02 11:02:27 +0000687 i = 0;
688 while (True) {
689 if (buf[i] == ':') break;
690 if (buf[i] == '\0') goto syntax_error;
691 i++;
njn25e49d8e72002-09-23 09:36:25 +0000692 }
njnc40c3a82002-10-02 11:02:27 +0000693 buf[i] = '\0'; /* Replace ':', splitting into two strings */
694
njn11cc9252002-10-07 14:42:59 +0000695 skin_names = & buf[0];
696 supp_name = & buf[i+1];
njnc40c3a82002-10-02 11:02:27 +0000697
njn11cc9252002-10-07 14:42:59 +0000698 /* Is it a core suppression? */
699 if (VG_(needs).core_errors && skin_name_present("core", skin_names))
njnc40c3a82002-10-02 11:02:27 +0000700 {
njn43c799e2003-04-08 00:08:52 +0000701 if (VG_STREQ(supp_name, "PThread"))
njn810086f2002-11-14 12:42:47 +0000702 supp->skind = PThreadSupp;
njnc40c3a82002-10-02 11:02:27 +0000703 else
704 goto syntax_error;
705 }
706
njn11cc9252002-10-07 14:42:59 +0000707 /* Is it a skin suppression? */
708 else if (VG_(needs).skin_errors &&
709 skin_name_present(VG_(details).name, skin_names))
njnc40c3a82002-10-02 11:02:27 +0000710 {
njn810086f2002-11-14 12:42:47 +0000711 if (SK_(recognised_suppression)(supp_name, supp))
njnc40c3a82002-10-02 11:02:27 +0000712 {
njn810086f2002-11-14 12:42:47 +0000713 /* Do nothing, function fills in supp->skind */
njnc40c3a82002-10-02 11:02:27 +0000714 } else
715 goto syntax_error;
716 }
717
njn25e49d8e72002-09-23 09:36:25 +0000718 else {
njnc40c3a82002-10-02 11:02:27 +0000719 /* Ignore rest of suppression */
njn25e49d8e72002-09-23 09:36:25 +0000720 while (True) {
njn4ba5a792002-09-30 10:23:54 +0000721 eof = VG_(get_line) ( fd, buf, N_BUF );
njn25e49d8e72002-09-23 09:36:25 +0000722 if (eof) goto syntax_error;
njn43c799e2003-04-08 00:08:52 +0000723 if (VG_STREQ(buf, "}"))
njn25e49d8e72002-09-23 09:36:25 +0000724 break;
725 }
726 continue;
sewardjde4a1d02002-03-22 01:27:54 +0000727 }
728
njn25e49d8e72002-09-23 09:36:25 +0000729 if (VG_(needs).skin_errors &&
njn810086f2002-11-14 12:42:47 +0000730 !SK_(read_extra_suppression_info)(fd, buf, N_BUF, supp))
sewardjde4a1d02002-03-22 01:27:54 +0000731 goto syntax_error;
732
njn25e49d8e72002-09-23 09:36:25 +0000733 /* "i > 0" ensures at least one caller read. */
njn633de322003-05-12 20:40:13 +0000734 for (i = 0; i <= VG_N_SUPP_CALLERS; i++) {
njn4ba5a792002-09-30 10:23:54 +0000735 eof = VG_(get_line) ( fd, buf, N_BUF );
sewardjde4a1d02002-03-22 01:27:54 +0000736 if (eof) goto syntax_error;
njn43c799e2003-04-08 00:08:52 +0000737 if (i > 0 && VG_STREQ(buf, "}"))
njn25e49d8e72002-09-23 09:36:25 +0000738 break;
njn633de322003-05-12 20:40:13 +0000739 if (i == VG_N_SUPP_CALLERS)
740 break;
njn25e49d8e72002-09-23 09:36:25 +0000741 supp->caller[i] = VG_(arena_strdup)(VG_AR_CORE, buf);
742 if (!setLocationTy(&(supp->caller[i]), &(supp->caller_ty[i])))
743 goto syntax_error;
sewardjde4a1d02002-03-22 01:27:54 +0000744 }
745
sewardj57a8f5f2003-07-06 01:40:11 +0000746 /* make sure to grab the '}' if the num callers is >=
747 VG_N_SUPP_CALLERS */
748 if (!VG_STREQ(buf, "}")) {
749 do {
750 eof = VG_(get_line) ( fd, buf, N_BUF );
751 } while (!eof && !VG_STREQ(buf, "}"));
752 }
753
sewardjde4a1d02002-03-22 01:27:54 +0000754 supp->next = vg_suppressions;
755 vg_suppressions = supp;
756 }
sewardjde4a1d02002-03-22 01:27:54 +0000757 VG_(close)(fd);
758 return;
759
760 syntax_error:
761 if (eof) {
762 VG_(message)(Vg_UserMsg,
763 "FATAL: in suppressions file `%s': unexpected EOF",
764 filename );
765 } else {
766 VG_(message)(Vg_UserMsg,
njn11cc9252002-10-07 14:42:59 +0000767 "FATAL: in suppressions file: `%s': syntax error on: %s",
sewardjde4a1d02002-03-22 01:27:54 +0000768 filename, buf );
769 }
770 VG_(close)(fd);
771 VG_(message)(Vg_UserMsg, "exiting now.");
772 VG_(exit)(1);
773
774# undef N_BUF
775}
776
777
778void VG_(load_suppressions) ( void )
779{
780 Int i;
781 vg_suppressions = NULL;
782 for (i = 0; i < VG_(clo_n_suppressions); i++) {
783 if (VG_(clo_verbosity) > 1) {
784 VG_(message)(Vg_UserMsg, "Reading suppressions file: %s",
785 VG_(clo_suppressions)[i] );
786 }
787 load_one_suppressions_file( VG_(clo_suppressions)[i] );
788 }
789}
790
njn25e49d8e72002-09-23 09:36:25 +0000791/* Return the name of an erring fn in a way which is useful
792 for comparing against the contents of a suppressions file.
793 Doesn't demangle the fn name, because we want to refer to
794 mangled names in the suppressions file.
sewardj99aac972002-12-26 01:53:45 +0000795*/
njn43c799e2003-04-08 00:08:52 +0000796static void get_objname_fnname ( Addr a, Char* obj_buf, Int n_obj_buf,
797 Char* fun_buf, Int n_fun_buf )
njn25e49d8e72002-09-23 09:36:25 +0000798{
799 (void)VG_(get_objname) ( a, obj_buf, n_obj_buf );
800 (void)VG_(get_fnname_nodemangle)( a, fun_buf, n_fun_buf );
801}
802
803static __inline__
njn810086f2002-11-14 12:42:47 +0000804Bool supp_matches_error(Supp* su, Error* err)
njn25e49d8e72002-09-23 09:36:25 +0000805{
njn810086f2002-11-14 12:42:47 +0000806 switch (su->skind) {
njn25e49d8e72002-09-23 09:36:25 +0000807 case PThreadSupp:
njn810086f2002-11-14 12:42:47 +0000808 return (err->ekind == PThreadErr);
njn25e49d8e72002-09-23 09:36:25 +0000809 default:
810 if (VG_(needs).skin_errors) {
njn810086f2002-11-14 12:42:47 +0000811 return SK_(error_matches_suppression)(err, su);
njn25e49d8e72002-09-23 09:36:25 +0000812 } else {
813 VG_(printf)(
814 "\nUnhandled suppression type: %u. VG_(needs).skin_errors\n"
815 "probably needs to be set.\n",
njn810086f2002-11-14 12:42:47 +0000816 err->ekind);
njne427a662002-10-02 11:08:25 +0000817 VG_(skin_panic)("unhandled suppression type");
njn25e49d8e72002-09-23 09:36:25 +0000818 }
819 }
820}
821
822static __inline__
njn810086f2002-11-14 12:42:47 +0000823Bool supp_matches_callers(Supp* su, Char caller_obj[][M_VG_ERRTXT],
824 Char caller_fun[][M_VG_ERRTXT])
njn25e49d8e72002-09-23 09:36:25 +0000825{
826 Int i;
827
njn633de322003-05-12 20:40:13 +0000828 for (i = 0; i < VG_N_SUPP_CALLERS && su->caller[i] != NULL; i++) {
njn25e49d8e72002-09-23 09:36:25 +0000829 switch (su->caller_ty[i]) {
njn4ba5a792002-09-30 10:23:54 +0000830 case ObjName: if (VG_(string_match)(su->caller[i],
831 caller_obj[i])) break;
njn25e49d8e72002-09-23 09:36:25 +0000832 return False;
njn4ba5a792002-09-30 10:23:54 +0000833 case FunName: if (VG_(string_match)(su->caller[i],
834 caller_fun[i])) break;
njn25e49d8e72002-09-23 09:36:25 +0000835 return False;
njn43c799e2003-04-08 00:08:52 +0000836 default: VG_(skin_panic)("supp_matches_callers");
njn25e49d8e72002-09-23 09:36:25 +0000837 }
838 }
839
840 /* If we reach here, it's a match */
841 return True;
842}
sewardjde4a1d02002-03-22 01:27:54 +0000843
njn810086f2002-11-14 12:42:47 +0000844/* Does an error context match a suppression? ie is this a suppressible
845 error? If so, return a pointer to the Supp record, otherwise NULL.
njn25e49d8e72002-09-23 09:36:25 +0000846 Tries to minimise the number of symbol searches since they are expensive.
sewardjde4a1d02002-03-22 01:27:54 +0000847*/
njn810086f2002-11-14 12:42:47 +0000848static Supp* is_suppressible_error ( Error* err )
sewardjde4a1d02002-03-22 01:27:54 +0000849{
njn25e49d8e72002-09-23 09:36:25 +0000850 Int i;
sewardjde4a1d02002-03-22 01:27:54 +0000851
njn25e49d8e72002-09-23 09:36:25 +0000852 Char caller_obj[VG_N_SUPP_CALLERS][M_VG_ERRTXT];
853 Char caller_fun[VG_N_SUPP_CALLERS][M_VG_ERRTXT];
sewardjde4a1d02002-03-22 01:27:54 +0000854
njn810086f2002-11-14 12:42:47 +0000855 Supp* su;
sewardjde4a1d02002-03-22 01:27:54 +0000856
njn25e49d8e72002-09-23 09:36:25 +0000857 /* get_objname_fnname() writes the function name and object name if
njn43c799e2003-04-08 00:08:52 +0000858 it finds them in the debug info. So the strings in the suppression
njn25e49d8e72002-09-23 09:36:25 +0000859 file should match these.
sewardjde4a1d02002-03-22 01:27:54 +0000860 */
861
862 /* Initialise these strs so they are always safe to compare, even
njn25e49d8e72002-09-23 09:36:25 +0000863 if get_objname_fnname doesn't write anything to them. */
864 for (i = 0; i < VG_N_SUPP_CALLERS; i++)
865 caller_obj[i][0] = caller_fun[i][0] = 0;
sewardjde4a1d02002-03-22 01:27:54 +0000866
njn25e49d8e72002-09-23 09:36:25 +0000867 for (i = 0; i < VG_N_SUPP_CALLERS && i < VG_(clo_backtrace_size); i++) {
njn43c799e2003-04-08 00:08:52 +0000868 get_objname_fnname ( err->where->eips[i], caller_obj[i], M_VG_ERRTXT,
869 caller_fun[i], M_VG_ERRTXT );
sewardjde4a1d02002-03-22 01:27:54 +0000870 }
871
872 /* See if the error context matches any suppression. */
873 for (su = vg_suppressions; su != NULL; su = su->next) {
njn25e49d8e72002-09-23 09:36:25 +0000874 if (supp_matches_error(su, err) &&
875 supp_matches_callers(su, caller_obj, caller_fun)) {
876 return su;
sewardjde4a1d02002-03-22 01:27:54 +0000877 }
sewardjde4a1d02002-03-22 01:27:54 +0000878 }
njn25e49d8e72002-09-23 09:36:25 +0000879 return NULL; /* no matches */
sewardjde4a1d02002-03-22 01:27:54 +0000880}
881
sewardjde4a1d02002-03-22 01:27:54 +0000882/*--------------------------------------------------------------------*/
883/*--- end vg_errcontext.c ---*/
884/*--------------------------------------------------------------------*/