1 /* -*- mode: C; c-basic-offset: 3; -*- */
2 
3 /*--------------------------------------------------------------------*/
4 /*--- Libc printing.                                 m_libcprint.c ---*/
5 /*--------------------------------------------------------------------*/
6 
7 /*
8    This file is part of Valgrind, a dynamic binary instrumentation
9    framework.
10 
11    Copyright (C) 2000-2017 Julian Seward
12       jseward@acm.org
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 "vgversion.h"
33 #include "pub_core_basics.h"
34 #include "pub_core_vki.h"
35 #include "pub_core_vkiscnums.h"
36 #include "pub_core_debuglog.h"
37 #include "pub_core_gdbserver.h"  // VG_(gdb_printf)
38 #include "pub_core_libcbase.h"
39 #include "pub_core_libcassert.h"
40 #include "pub_core_libcfile.h"   // VG_(write)(), VG_(write_socket)()
41 #include "pub_core_libcprint.h"
42 #include "pub_core_libcproc.h"   // VG_(getpid)(), VG_(read_millisecond_timer()
43 #include "pub_core_mallocfree.h" // VG_(malloc)
44 #include "pub_core_machine.h"    // VG_(machine_get_VexArchInfo)
45 #include "pub_core_options.h"
46 #include "pub_core_clreq.h"      // For RUNNING_ON_VALGRIND
47 #include "pub_core_clientstate.h"
48 #include "pub_core_syscall.h"    // VG_(strerror)
49 #include "pub_core_tooliface.h"  // VG_(details)
50 
51 
52 /*====================================================================*/
53 /*=== Printing the preamble                                        ===*/
54 /*====================================================================*/
55 
56 // Returns a strdup'd copy of |str| in which characters which are not in the
57 // obviously-harmless-ASCII range are replaced with '_'.  Not doing this has
58 // been observed to cause xfce4-terminal to assert.  Caller takes ownership
59 // of the returned string.
sanitise_arg(const HChar * arg)60 static HChar* sanitise_arg (const HChar* arg)
61 {
62    HChar* clone = VG_(strdup)("m_libcprint.sanitise_arg", arg);
63    for (HChar* p = clone; *p; p++) {
64       UInt c = * ((UChar*)p);
65       if (c < 32 || c > 127) c = '_';
66       *p = (HChar)c;
67    }
68    return clone;
69 }
70 
71 // Print the argument, escaping any chars that require it.
umsg_arg(const HChar * unsanitised_arg)72 static void umsg_arg(const HChar *unsanitised_arg)
73 {
74    HChar* arg = sanitise_arg(unsanitised_arg);
75    SizeT len = VG_(strlen)(arg);
76    const HChar *special = " \\<>";
77    for (UInt i = 0; i < len; i++) {
78       if (VG_(strchr)(special, arg[i])) {
79          VG_(umsg)("\\");   // escape with a backslash if necessary
80       }
81       VG_(umsg)("%c", arg[i]);
82    }
83    VG_(free)(arg);
84 }
85 
86 // Send output to the XML-stream and escape any XML meta-characters.
xml_arg(const HChar * unsanitised_arg)87 static void xml_arg(const HChar *unsanitised_arg)
88 {
89    HChar* arg = sanitise_arg(unsanitised_arg);
90    VG_(printf_xml)("%pS", arg);
91    VG_(free)(arg);
92 }
93 
94 // Write the name and value of log file qualifiers to the xml file.
95 // We can safely assume here that the format string is well-formed.
96 // It has been checked earlier in VG_(expand_file_name) when processing
97 // command line options.
print_file_vars(const HChar * format)98 static void print_file_vars(const HChar *format)
99 {
100    UInt i = 0;
101 
102    while (format[i]) {
103       if (format[i] == '%') {
104          // We saw a '%'.  What's next...
105          i++;
106          if ('q' == format[i]) {
107             i++;
108             if ('{' == format[i]) {
109                // Get the env var name, print its contents.
110                UInt begin_qualname = ++i;
111                while (True) {
112                   if ('}' == format[i]) {
113                      UInt qualname_len = i - begin_qualname;
114                      HChar qualname[qualname_len + 1];
115                      VG_(strncpy)(qualname, format + begin_qualname,
116                                   qualname_len);
117                      qualname[qualname_len] = '\0';
118                      HChar *qual = VG_(getenv)(qualname);
119                      i++;
120                      VG_(printf_xml)("<logfilequalifier> <var>%pS</var> "
121                                      "<value>%pS</value> </logfilequalifier>\n",
122                                      qualname, qual);
123                      break;
124                   }
125                   i++;
126                }
127             }
128          }
129       } else {
130          i++;
131       }
132    }
133 }
134 
135 /* Ok, the logging sink is running now.  Print a suitable preamble.
136    If logging to file or a socket, write details of parent PID and
137    command line args, to help people trying to interpret the
138    results of a run which encompasses multiple processes. */
VG_(print_preamble)139 void VG_(print_preamble)(Bool logging_to_fd)
140 {
141    const HChar *xpre  = VG_(clo_xml) ? "  <line>" : "";
142    const HChar *xpost = VG_(clo_xml) ? "</line>" : "";
143    UInt (*umsg_or_xml)( const HChar *, ... )
144       = VG_(clo_xml) ? VG_(printf_xml) : VG_(umsg);
145    void (*umsg_or_xml_arg)( const HChar *) = VG_(clo_xml) ? xml_arg : umsg_arg;
146 
147    vg_assert( VG_(args_for_client) );
148    vg_assert( VG_(args_for_valgrind) );
149    vg_assert( VG_(clo_toolname) );
150 
151    if (VG_(clo_xml)) {
152       VG_(printf_xml)("<?xml version=\"1.0\"?>\n");
153       VG_(printf_xml)("\n");
154       VG_(printf_xml)("<valgrindoutput>\n");
155       VG_(printf_xml)("\n");
156       VG_(printf_xml)("<protocolversion>4</protocolversion>\n");
157       VG_(printf_xml)("<protocoltool>%s</protocoltool>\n", VG_(clo_toolname));
158       VG_(printf_xml)("\n");
159    }
160 
161    if (VG_(clo_xml) || VG_(clo_verbosity) > 0) {
162 
163       if (VG_(clo_xml))
164          VG_(printf_xml)("<preamble>\n");
165 
166       /* Tool details */
167       umsg_or_xml(VG_(clo_xml) ? "%s%pS%pS%pS, %pS%s\n" : "%s%s%s%s, %s%s\n",
168                   xpre,
169                   VG_(details).name,
170                   NULL == VG_(details).version ? "" : "-",
171                   NULL == VG_(details).version ? "" : VG_(details).version,
172                   VG_(details).description,
173                   xpost);
174 
175       if (VG_(strlen)(VG_(clo_toolname)) >= 4 &&
176           VG_STREQN(4, VG_(clo_toolname), "exp-")) {
177          umsg_or_xml("%sNOTE: This is an Experimental-Class Valgrind Tool%s\n",
178                      xpre, xpost);
179       }
180 
181       umsg_or_xml(VG_(clo_xml) ? "%s%pS%s\n" : "%s%s%s\n",
182                   xpre, VG_(details).copyright_author, xpost);
183 
184       /* Core details */
185       umsg_or_xml(
186          "%sUsing Valgrind-%s and LibVEX; rerun with -h for copyright info%s\n",
187          xpre, VG_(clo_verbosity) <= 1 ? VERSION : VERSION "-" VGGIT, xpost);
188 
189       // Print the command line.  At one point we wrapped at 80 chars and
190       // printed a '\' as a line joiner, but that makes it hard to cut and
191       // paste the command line (because of the "==pid==" prefixes), so we now
192       // favour utility and simplicity over aesthetics.
193       umsg_or_xml("%sCommand: ", xpre);
194       umsg_or_xml_arg(VG_(args_the_exename));
195 
196       for (UInt i = 0; i < VG_(sizeXA)( VG_(args_for_client)); i++) {
197          HChar *s = *(HChar **)VG_(indexXA)( VG_(args_for_client), i);
198          umsg_or_xml(" ");
199          umsg_or_xml_arg(s);
200       }
201       umsg_or_xml("%s\n", xpost);
202 
203       if (VG_(clo_xml))
204          VG_(printf_xml)("</preamble>\n");
205    }
206 
207    // Print the parent PID, and other stuff, if necessary.
208    if (!VG_(clo_xml) && VG_(clo_verbosity) > 0 && !logging_to_fd) {
209       VG_(umsg)("Parent PID: %d\n", VG_(getppid)());
210    } else if (VG_(clo_xml)) {
211       VG_(printf_xml)("\n");
212       VG_(printf_xml)("<pid>%d</pid>\n", VG_(getpid)());
213       VG_(printf_xml)("<ppid>%d</ppid>\n", VG_(getppid)());
214       VG_(printf_xml)("<tool>%pS</tool>\n", VG_(clo_toolname));
215       if (VG_(clo_xml_fname_unexpanded) != NULL)
216          print_file_vars(VG_(clo_xml_fname_unexpanded));
217       if (VG_(clo_xml_user_comment)) {
218          /* Note: the user comment itself is XML and is therefore to
219             be passed through verbatim (%s) rather than escaped (%pS). */
220          VG_(printf_xml)("<usercomment>%s</usercomment>\n",
221                          VG_(clo_xml_user_comment));
222       }
223       VG_(printf_xml)("\n");
224       VG_(printf_xml)("<args>\n");
225 
226       VG_(printf_xml)("  <vargv>\n");
227       if (VG_(name_of_launcher))
228          VG_(printf_xml)("    <exe>%pS</exe>\n", VG_(name_of_launcher));
229       else
230          VG_(printf_xml)("    <exe>%pS</exe>\n", "(launcher name unknown)");
231       for (UInt i = 0; i < VG_(sizeXA)( VG_(args_for_valgrind) ); i++) {
232          VG_(printf_xml)(
233             "    <arg>%pS</arg>\n",
234             *(HChar **) VG_(indexXA)( VG_(args_for_valgrind), i));
235       }
236       VG_(printf_xml)("  </vargv>\n");
237 
238       VG_(printf_xml)("  <argv>\n");
239       VG_(printf_xml)("    <exe>%pS</exe>\n", VG_(args_the_exename));
240       for (UInt i = 0; i < VG_(sizeXA)( VG_(args_for_client) ); i++) {
241          VG_(printf_xml)(
242             "    <arg>%pS</arg>\n",
243             *(HChar **) VG_(indexXA)( VG_(args_for_client), i));
244       }
245       VG_(printf_xml)("  </argv>\n");
246 
247       VG_(printf_xml)("</args>\n");
248    }
249 
250    // Last thing in the preamble is a blank line.
251    if (VG_(clo_xml))
252       VG_(printf_xml)("\n");
253    else if (VG_(clo_verbosity) > 0)
254       VG_(umsg)("\n");
255 
256    if (VG_(clo_verbosity) > 1) {
257 # if defined(VGO_linux)
258       SysRes fd;
259 # endif
260       VexArch vex_arch;
261       VexArchInfo vex_archinfo;
262       if (!logging_to_fd)
263          VG_(message)(Vg_DebugMsg, "\n");
264       VG_(message)(Vg_DebugMsg, "Valgrind options:\n");
265       for (UInt i = 0; i < VG_(sizeXA)( VG_(args_for_valgrind) ); i++) {
266          VG_(message)(Vg_DebugMsg,
267                      "   %s\n",
268                      *(HChar **) VG_(indexXA)( VG_(args_for_valgrind), i));
269       }
270 
271 # if defined(VGO_linux)
272       VG_(message)(Vg_DebugMsg, "Contents of /proc/version:\n");
273       fd = VG_(open)("/proc/version", VKI_O_RDONLY, 0);
274       if (sr_isError(fd)) {
275          VG_(message)(Vg_DebugMsg, "  can't open /proc/version\n");
276       } else {
277          const SizeT bufsiz = 255;
278          HChar version_buf[bufsiz+1];
279          VG_(message)(Vg_DebugMsg, "  ");
280          Int n, fdno = sr_Res(fd);
281          do {
282             n = VG_(read)(fdno, version_buf, bufsiz);
283             if (n < 0) {
284                VG_(message)(Vg_DebugMsg, "  error reading /proc/version\n");
285                break;
286             }
287             version_buf[n] = '\0';
288             VG_(message)(Vg_DebugMsg, "%s", version_buf);
289          } while (n == bufsiz);
290          VG_(message)(Vg_DebugMsg, "\n");
291          VG_(close)(fdno);
292       }
293 # elif defined(VGO_darwin)
294       VG_(message)(Vg_DebugMsg, "Output from sysctl({CTL_KERN,KERN_VERSION}):\n");
295       /* Note: preferable to use sysctlbyname("kern.version", kernelVersion, &len, NULL, 0)
296          however that syscall is OS X 10.10+ only. */
297       Int mib[] = {CTL_KERN, KERN_VERSION};
298       SizeT len;
299       VG_(sysctl)(mib, sizeof(mib)/sizeof(Int), NULL, &len, NULL, 0);
300       HChar *kernelVersion = VG_(malloc)("main.pp.1", len);
301       VG_(sysctl)(mib, sizeof(mib)/sizeof(Int), kernelVersion, &len, NULL, 0);
302       VG_(message)(Vg_DebugMsg, "  %s\n", kernelVersion);
303       VG_(free)( kernelVersion );
304 # elif defined(VGO_solaris)
305       /* There is no /proc/version file on Solaris so we try to get some
306          system information using the uname(2) syscall. */
307       struct vki_utsname uts;
308       VG_(message)(Vg_DebugMsg, "System information:\n");
309       SysRes res = VG_(do_syscall1)(__NR_uname, (UWord)&uts);
310       if (sr_isError(res))
311          VG_(message)(Vg_DebugMsg, "  uname() failed\n");
312       else
313          VG_(message)(Vg_DebugMsg, "  %s %s %s %s\n",
314                       uts.sysname, uts.release, uts.version, uts.machine);
315 # endif
316 
317       VG_(machine_get_VexArchInfo)(&vex_arch, &vex_archinfo);
318       VG_(message)(
319          Vg_DebugMsg,
320          "Arch and hwcaps: %s, %s, %s\n",
321          LibVEX_ppVexArch    ( vex_arch ),
322          LibVEX_ppVexEndness ( vex_archinfo.endness ),
323          LibVEX_ppVexHwCaps  ( vex_arch, vex_archinfo.hwcaps )
324       );
325       VG_(message)(Vg_DebugMsg,
326                   "Page sizes: currently %u, max supported %u\n",
327                   (UInt) VKI_PAGE_SIZE, (UInt) VKI_MAX_PAGE_SIZE);
328       VG_(message)(Vg_DebugMsg,
329                    "Valgrind library directory: %s\n", VG_(libdir));
330    }
331 }
332 
333 /* ---------------------------------------------------------------------
334    Writing to file or a socket
335    ------------------------------------------------------------------ */
336 
337 /* The destination sinks for normal and XML output.  These have their
338    initial values here; they are set to final values by
339    m_main.main_process_cmd_line_options().  See comment at the top of
340    that function for the associated logic.
341    After startup, the gdbserver monitor command might temporarily
342    set the fd of log_output_sink to -2 to indicate that output is
343    to be given to gdb rather than output to the startup fd */
344 OutputSink VG_(log_output_sink) = {  2, VgLogTo_Fd, NULL }; /* 2 = stderr */
345 OutputSink VG_(xml_output_sink) = { -1, VgLogTo_Fd, NULL }; /* disabled */
346 
revert_sink_to_stderr(OutputSink * sink)347 static void revert_sink_to_stderr ( OutputSink *sink )
348 {
349    sink->fd = 2; /* stderr */
350    sink->type = VgLogTo_Fd;
351    VG_(free)(sink->fsname_expanded);
352    sink->fsname_expanded = NULL;
353 }
354 
prepare_sink_fd(const HChar * clo_fname_unexpanded,OutputSink * sink,Bool is_xml)355 static Int prepare_sink_fd(const HChar *clo_fname_unexpanded, OutputSink *sink,
356                            Bool is_xml)
357 {
358    vg_assert(clo_fname_unexpanded != NULL);
359    vg_assert(VG_(strlen)(clo_fname_unexpanded) <= 900); /* paranoia */
360 
361    // Nb: we overwrite an existing file of this name without asking
362    // any questions.
363    HChar *logfilename = VG_(expand_file_name)(
364                                          (is_xml) ? "--xml-file" : "--log-file",
365                                          clo_fname_unexpanded);
366    SysRes sres = VG_(open)(logfilename,
367                            VKI_O_CREAT|VKI_O_WRONLY|VKI_O_TRUNC,
368                            VKI_S_IRUSR|VKI_S_IWUSR|VKI_S_IRGRP|VKI_S_IROTH);
369    if (!sr_isError(sres)) {
370       Int fd = sr_Res(sres);
371       sink->fsname_expanded = logfilename;
372       sink->type = VgLogTo_File;
373       return fd;
374    } else {
375       VG_(fmsg)("Cannot create %s file '%s': %s\n",
376                 (is_xml) ? "XML" : "log", logfilename,
377                 VG_(strerror)(sr_Err(sres)));
378       VG_(exit)(1);
379       /*NOTREACHED*/
380    }
381 }
382 
prepare_sink_socket(const HChar * clo_fname_unexpanded,OutputSink * sink,Bool is_xml)383 static Int prepare_sink_socket(const HChar *clo_fname_unexpanded,
384                                OutputSink *sink, Bool is_xml)
385 {
386    vg_assert(clo_fname_unexpanded != NULL);
387    vg_assert(VG_(strlen)(clo_fname_unexpanded) <= 900); /* paranoia */
388 
389    Int fd = VG_(connect_via_socket)(clo_fname_unexpanded);
390    if (fd == -1) {
391       VG_(fmsg)("Invalid %s spec of '%s'\n",
392                 (is_xml) ? "--xml-socket" : "--log-socket",
393                 clo_fname_unexpanded);
394       VG_(exit)(1);
395       /*NOTREACHED*/
396    }
397    if (fd == -2) {
398       VG_(umsg)("Failed to connect to %slogging server '%s'.\n"
399                 "%s will be sent to stderr instead.\n",
400                 (is_xml) ? "XML " : "",
401                 clo_fname_unexpanded,
402                 (is_xml) ? "XML output" : "Logging messages");
403       /* We don't change anything here. */
404       vg_assert(sink->fd == 2);
405       vg_assert(sink->type == VgLogTo_Fd);
406       return 2;
407    } else {
408       vg_assert(fd > 0);
409       sink->type = VgLogTo_Socket;
410       return fd;
411    }
412 }
413 
finalize_sink_fd(OutputSink * sink,Int new_fd,Bool is_xml)414 static void finalize_sink_fd(OutputSink *sink, Int new_fd, Bool is_xml)
415 {
416    // Move new_fd into the safe range, so it doesn't conflict with any app fds.
417    Int safe_fd = VG_(fcntl)(new_fd, VKI_F_DUPFD, VG_(fd_hard_limit));
418    if (safe_fd < 0) {
419       VG_(message)(Vg_UserMsg, "Valgrind: failed to move %s file descriptor "
420                                "into safe range, using stderr\n",
421                                (is_xml) ? "XML" : "log");
422       revert_sink_to_stderr(sink);
423    } else {
424       VG_(fcntl)(safe_fd, VKI_F_SETFD, VKI_FD_CLOEXEC);
425       sink->fd = safe_fd;
426    }
427 }
428 
429 /* Re-opens an output file sink when exanded file name differs from what we
430    have now. Returns 'True' if the sink was reopened  */
reopen_sink_if_needed(const HChar * clo_fname_unexpanded,OutputSink * sink,Bool is_xml)431 static Bool reopen_sink_if_needed(const HChar *clo_fname_unexpanded,
432                                   OutputSink *sink, Bool is_xml)
433 {
434    if (sink->type == VgLogTo_File) {
435       /* Try to expand --log|xml-file again and see if it differs from what
436          we have now. */
437       HChar *logfilename = VG_(expand_file_name)(
438                                          (is_xml) ? "--xml-file" : "--log-file",
439                                          clo_fname_unexpanded);
440       if (VG_(strcmp)(logfilename, sink->fsname_expanded) != 0) {
441          Int fd = prepare_sink_fd(clo_fname_unexpanded, sink, is_xml);
442          finalize_sink_fd(sink, fd, is_xml);
443          return True;
444       }
445       VG_(free)(logfilename);
446    }
447 
448    return False;
449 }
450 
VG_(logging_atfork_child)451 void VG_(logging_atfork_child)(ThreadId tid)
452 {
453    /* If --child-silent-after-fork=yes was specified, set the output file
454       descriptors to 'impossible' values. This is noticed by
455       send_bytes_to_logging_sink(), which duly stops writing any further
456       output. */
457    if (VG_(clo_child_silent_after_fork)) {
458       if (VG_(log_output_sink).type != VgLogTo_Socket) {
459          VG_(log_output_sink).fd = -1;
460          VG_(log_output_sink).type = VgLogTo_Fd;
461       }
462       if (VG_(xml_output_sink).type != VgLogTo_Socket) {
463          VG_(xml_output_sink).fd = -1;
464          VG_(xml_output_sink).type = VgLogTo_Fd;
465       }
466    } else {
467       if (reopen_sink_if_needed(VG_(clo_log_fname_unexpanded),
468                                 &VG_(log_output_sink), False) ||
469           reopen_sink_if_needed(VG_(clo_xml_fname_unexpanded),
470                                 &VG_(xml_output_sink), True)) {
471          VG_(print_preamble)(VG_(log_output_sink).type != VgLogTo_File);
472       }
473    }
474 }
475 
476 /* Initializes normal log and xml sinks (of type fd, file, or socket).
477    Any problem encountered is considered a hard error and causes V. to exit.
478 
479    Comments on how the logging options are handled:
480 
481    User can specify:
482       --log-fd=      for a fd to write to (default setting, fd = 2)
483       --log-file=    for a file name to write to
484       --log-socket=  for a socket to write to
485 
486    As a result of examining these and doing relevant socket/file
487    opening, a final fd is established.  This is stored in
488    VG_(log_output_sink) in m_libcprint.  Also, if --log-file=STR was
489    specified, then it is stored in VG_(clo_log_fname_unexpanded), in m_options.
490    And then STR, after expansion of %p and %q templates within
491    it, is stored in VG_(log_output_sink), just in case anybody wants to know
492    what it is.
493 
494    When printing, VG_(log_output_sink) is consulted to find the
495    fd to send output to.
496 
497    Exactly analogous actions are undertaken for the XML output
498    channel, with the one difference that the default fd is -1, meaning
499    the channel is disabled by default. */
VG_(init_log_xml_sinks)500 void VG_(init_log_xml_sinks)(VgLogTo log_to, VgLogTo xml_to,
501                              Int /*initial*/log_fd, Int /*initial*/xml_fd)
502 {
503    // VG_(clo_log_fd) is used by all the messaging.  It starts as 2 (stderr)
504    // and we cannot change it until we know what we are changing it to is ok.
505 
506    /* Start setting up logging now. After this is done, VG_(log_output_sink)
507       and (if relevant) VG_(xml_output_sink) should be connected to whatever
508       sink has been selected, and we indiscriminately chuck stuff into it
509       without worrying what the nature of it is.
510       Oh the wonder of Unix streams. */
511 
512    vg_assert(VG_(log_output_sink).fd == 2 /* stderr */);
513    vg_assert(VG_(log_output_sink).type == VgLogTo_Fd);
514    vg_assert(VG_(log_output_sink).fsname_expanded == NULL);
515 
516    vg_assert(VG_(xml_output_sink).fd == -1 /* disabled */);
517    vg_assert(VG_(xml_output_sink).type == VgLogTo_Fd);
518    vg_assert(VG_(xml_output_sink).fsname_expanded == NULL);
519 
520    /* --- set up the normal text output channel --- */
521    switch (log_to) {
522       case VgLogTo_Fd:
523          vg_assert(VG_(clo_log_fname_unexpanded) == NULL);
524          break;
525 
526       case VgLogTo_File:
527          log_fd = prepare_sink_fd(VG_(clo_log_fname_unexpanded),
528                                   &VG_(log_output_sink), False);
529          break;
530 
531       case VgLogTo_Socket:
532          log_fd = prepare_sink_socket(VG_(clo_log_fname_unexpanded),
533                                       &VG_(log_output_sink), False);
534          break;
535    }
536 
537    /* --- set up the XML output channel --- */
538    switch (xml_to) {
539       case VgLogTo_Fd:
540          vg_assert(VG_(clo_xml_fname_unexpanded) == NULL);
541          break;
542 
543       case VgLogTo_File:
544          xml_fd = prepare_sink_fd(VG_(clo_xml_fname_unexpanded),
545                                   &VG_(xml_output_sink), True);
546          break;
547 
548       case VgLogTo_Socket:
549          xml_fd = prepare_sink_socket(VG_(clo_xml_fname_unexpanded),
550                                       &VG_(xml_output_sink), True);
551          break;
552    }
553 
554    /* If we've got this far, and XML mode was requested, but no XML
555       output channel appears to have been specified, just stop.  We
556       could continue, and XML output will simply vanish into nowhere,
557       but that is likely to confuse the hell out of users, which is
558       distinctly Ungood. */
559    if (VG_(clo_xml) && xml_fd == -1) {
560       VG_(fmsg_bad_option)(
561           "--xml=yes, but no XML destination specified",
562           "--xml=yes has been specified, but there is no XML output\n"
563           "destination.  You must specify an XML output destination\n"
564           "using --xml-fd, --xml-file or --xml-socket.\n"
565       );
566    }
567 
568    // Finalise the output fds: the log fd ..
569    if (log_fd >= 0) {
570       finalize_sink_fd(&VG_(log_output_sink), log_fd, False);
571    } else {
572       // If they said --log-fd=-1, don't print anything.  Plausible for use in
573       // regression testing suites that use client requests to count errors.
574       VG_(log_output_sink).fd = -1;
575       VG_(log_output_sink).type = VgLogTo_Fd;
576    }
577 
578    // Finalise the output fds: and the XML fd ..
579    if (xml_fd >= 0) {
580       finalize_sink_fd(&VG_(xml_output_sink), xml_fd, True);
581    } else {
582       // If they said --xml-fd=-1, don't print anything.  Plausible for use in
583       // regression testing suites that use client requests to count errors.
584       VG_(xml_output_sink).fd = -1;
585       VG_(xml_output_sink).type = VgLogTo_Fd;
586    }
587 }
588 
589 /* Do the low-level send of a message to the logging sink. */
590 static
send_bytes_to_logging_sink(OutputSink * sink,const HChar * msg,Int nbytes)591 void send_bytes_to_logging_sink ( OutputSink* sink, const HChar* msg, Int nbytes )
592 {
593    if (sink->type == VgLogTo_Socket) {
594       Int rc = VG_(write_socket)( sink->fd, msg, nbytes );
595       if (rc == -1) {
596          // For example, the listener process died.  Switch back to stderr.
597          revert_sink_to_stderr(sink);
598          VG_(write)( sink->fd, msg, nbytes );
599       }
600    } else {
601       /* sink->fd could have been set to -1 in the various
602          sys-wrappers for sys_fork, if --child-silent-after-fork=yes
603          is in effect.  That is a signal that we should not produce
604          any more output. */
605       if (sink->fd >= 0)
606          VG_(write)( sink->fd, msg, nbytes );
607       else if (sink->fd == -2 && nbytes > 0)
608          /* send to gdb the provided data, which must be
609             a null terminated string with len >= 1 */
610          VG_(gdb_printf)("%s", msg);
611    }
612 }
613 
614 
615 /* ---------------------------------------------------------------------
616    printf() and friends
617    ------------------------------------------------------------------ */
618 
619 /* --------- printf --------- */
620 
621 typedef
622    struct {
623       HChar       buf[512];
624       Int         buf_used;
625       OutputSink* sink;
626    }
627    printf_buf_t;
628 
629 // Adds a single char to the buffer.  When the buffer gets sufficiently
630 // full, we write its contents to the logging sink.
add_to__printf_buf(HChar c,void * p)631 static void add_to__printf_buf ( HChar c, void *p )
632 {
633    printf_buf_t *b = (printf_buf_t *)p;
634 
635    if (b->buf_used > sizeof(b->buf) - 2 ) {
636       send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
637       b->buf_used = 0;
638    }
639    b->buf[b->buf_used++] = c;
640    b->buf[b->buf_used]   = 0;
641    vg_assert(b->buf_used < sizeof(b->buf));
642 }
643 
vprintf_to_buf(printf_buf_t * b,const HChar * format,va_list vargs)644 static UInt vprintf_to_buf ( printf_buf_t* b,
645                              const HChar *format, va_list vargs )
646 {
647    UInt ret = 0;
648    if (b->sink->fd >= 0 || b->sink->fd == -2) {
649       ret = VG_(debugLog_vprintf)
650                ( add_to__printf_buf, b, format, vargs );
651    }
652    return ret;
653 }
654 
vprintf_WRK(OutputSink * sink,const HChar * format,va_list vargs)655 static UInt vprintf_WRK ( OutputSink* sink,
656                           const HChar *format, va_list vargs )
657 {
658    printf_buf_t myprintf_buf
659       = { "", 0, sink };
660    UInt ret
661       = vprintf_to_buf(&myprintf_buf, format, vargs);
662    // Write out any chars left in the buffer.
663    if (myprintf_buf.buf_used > 0) {
664       send_bytes_to_logging_sink( myprintf_buf.sink,
665                                   myprintf_buf.buf,
666                                   myprintf_buf.buf_used );
667    }
668    return ret;
669 }
670 
VG_(vprintf)671 UInt VG_(vprintf) ( const HChar *format, va_list vargs )
672 {
673    return vprintf_WRK( &VG_(log_output_sink), format, vargs );
674 }
675 
VG_(printf)676 UInt VG_(printf) ( const HChar *format, ... )
677 {
678    UInt ret;
679    va_list vargs;
680    va_start(vargs, format);
681    ret = VG_(vprintf)(format, vargs);
682    va_end(vargs);
683    return ret;
684 }
685 
VG_(vprintf_xml)686 UInt VG_(vprintf_xml) ( const HChar *format, va_list vargs )
687 {
688    return vprintf_WRK( &VG_(xml_output_sink), format, vargs );
689 }
690 
VG_(printf_xml)691 UInt VG_(printf_xml) ( const HChar *format, ... )
692 {
693    UInt ret;
694    va_list vargs;
695    va_start(vargs, format);
696    ret = VG_(vprintf_xml)(format, vargs);
697    va_end(vargs);
698    return ret;
699 }
700 
emit_WRK(const HChar * format,va_list vargs)701 static UInt emit_WRK ( const HChar* format, va_list vargs )
702 {
703    if (VG_(clo_xml)) {
704       return VG_(vprintf_xml)(format, vargs);
705    } else if (VG_(log_output_sink).fd == -2) {
706       return VG_(vprintf) (format, vargs);
707    } else {
708       return VG_(vmessage)(Vg_UserMsg, format, vargs);
709    }
710 }
VG_(emit)711 UInt VG_(emit) ( const HChar* format, ... )
712 {
713    UInt ret;
714    va_list vargs;
715    va_start(vargs, format);
716    ret = emit_WRK(format, vargs);
717    va_end(vargs);
718    return ret;
719 }
720 
721 /* --------- sprintf --------- */
722 
723 /* If we had an explicit buf structure here, it would contain only one
724    field, indicating where the next char is to go.  So use p directly
725    for that, rather than having it be a pointer to a structure. */
726 
add_to__sprintf_buf(HChar c,void * p)727 static void add_to__sprintf_buf ( HChar c, void *p )
728 {
729    HChar** b = p;
730    *(*b)++ = c;
731 }
732 
VG_(vsprintf)733 UInt VG_(vsprintf) ( HChar* buf, const HChar *format, va_list vargs )
734 {
735    Int ret;
736    HChar* sprintf_ptr = buf;
737 
738    ret = VG_(debugLog_vprintf)
739             ( add_to__sprintf_buf, &sprintf_ptr, format, vargs );
740    add_to__sprintf_buf('\0', &sprintf_ptr);
741 
742    vg_assert(VG_(strlen)(buf) == ret);
743 
744    return ret;
745 }
746 
VG_(sprintf)747 UInt VG_(sprintf) ( HChar* buf, const HChar *format, ... )
748 {
749    UInt ret;
750    va_list vargs;
751    va_start(vargs,format);
752    ret = VG_(vsprintf)(buf, format, vargs);
753    va_end(vargs);
754    return ret;
755 }
756 
757 
758 /* --------- snprintf --------- */
759 
760 /* The return value of VG_(snprintf) and VG_(vsnprintf) differs from
761    what is defined in C99. Let S be the size of the buffer as given in
762    the 2nd argument.
763    Return value R:
764      R < S:  The output string was successfully written to the buffer.
765              It is null-terminated and R == strlen( output string )
766      R == S: The supplied buffer was too small to hold the output string.
767              The first S-1 characters of the output string were written
768              to the buffer followed by the terminating null character.
769 */
770 
771 typedef
772    struct {
773       HChar* buf;
774       Int    buf_size;
775       Int    buf_used;
776    }
777    snprintf_buf_t;
778 
add_to__snprintf_buf(HChar c,void * p)779 static void add_to__snprintf_buf ( HChar c, void* p )
780 {
781    snprintf_buf_t* b = p;
782    if (b->buf_size > 0 && b->buf_used < b->buf_size) {
783       b->buf[b->buf_used++] = c;
784       if (b->buf_used < b->buf_size)
785          b->buf[b->buf_used] = 0;
786       else
787          b->buf[b->buf_size-1] = 0; /* pre: b->buf_size > 0 */
788    }
789 }
790 
VG_(vsnprintf)791 UInt VG_(vsnprintf) ( HChar* buf, Int size, const HChar *format, va_list vargs )
792 {
793    snprintf_buf_t b;
794    b.buf      = buf;
795    b.buf_size = size < 0 ? 0 : size;
796    b.buf_used = 0;
797    if (b.buf_size > 0)
798       b.buf[0] = 0; // ensure to null terminate buf if empty format
799    (void) VG_(debugLog_vprintf)
800              ( add_to__snprintf_buf, &b, format, vargs );
801 
802    return b.buf_used;
803 }
804 
VG_(snprintf)805 UInt VG_(snprintf) ( HChar* buf, Int size, const HChar *format, ... )
806 {
807    UInt ret;
808    va_list vargs;
809    va_start(vargs,format);
810    ret = VG_(vsnprintf)(buf, size, format, vargs);
811    va_end(vargs);
812    return ret;
813 }
814 
815 
816 /* --------- vcbprintf --------- */
817 
VG_(vcbprintf)818 void VG_(vcbprintf)( void(*char_sink)(HChar, void* opaque),
819                      void* opaque,
820                      const HChar* format, va_list vargs )
821 {
822    (void) VG_(debugLog_vprintf)
823              ( char_sink, opaque, format, vargs );
824 }
825 
826 
827 /* --------- fprintf ---------- */
828 
829 /* This is like [v]fprintf, except it writes to a file handle using
830    VG_(write). */
831 
832 #define VGFILE_BUFSIZE  8192
833 
834 struct _VgFile {
835    HChar buf[VGFILE_BUFSIZE];
836    UInt  num_chars;   // number of characters in buf
837    Int   fd;          // file descriptor to write to
838 };
839 
840 
add_to__vgfile(HChar c,void * p)841 static void add_to__vgfile ( HChar c, void *p )
842 {
843    VgFile *fp = p;
844 
845    fp->buf[fp->num_chars++] = c;
846 
847    if (fp->num_chars == VGFILE_BUFSIZE) {
848       VG_(write)(fp->fd, fp->buf, fp->num_chars);
849       fp->num_chars = 0;
850    }
851 }
852 
VG_(fopen)853 VgFile *VG_(fopen)(const HChar *name, Int flags, Int mode)
854 {
855    SysRes res = VG_(open)(name, flags, mode);
856 
857    if (sr_isError(res))
858       return NULL;
859 
860    VgFile *fp = VG_(malloc)("fopen", sizeof(VgFile));
861 
862    fp->fd = sr_Res(res);
863    fp->num_chars = 0;
864 
865    return fp;
866 }
867 
868 
VG_(vfprintf)869 UInt VG_(vfprintf) ( VgFile *fp, const HChar *format, va_list vargs )
870 {
871    return VG_(debugLog_vprintf)(add_to__vgfile, fp, format, vargs);
872 }
873 
VG_(fprintf)874 UInt VG_(fprintf) ( VgFile *fp, const HChar *format, ... )
875 {
876    UInt ret;
877    va_list vargs;
878    va_start(vargs,format);
879    ret = VG_(vfprintf)(fp, format, vargs);
880    va_end(vargs);
881    return ret;
882 }
883 
VG_(fclose)884 void VG_(fclose)( VgFile *fp )
885 {
886    // Flush the buffer.
887    if (fp->num_chars)
888       VG_(write)(fp->fd, fp->buf, fp->num_chars);
889 
890    VG_(close)(fp->fd);
891    VG_(free)(fp);
892 }
893 
894 
895 /* ---------------------------------------------------------------------
896    elapsed_wallclock_time()
897    ------------------------------------------------------------------ */
898 
899 /* Get the elapsed wallclock time since startup into buf, which must
900    16 chars long.  This is unchecked.  It also relies on the
901    millisecond timer having been set to zero by an initial read in
902    m_main during startup. */
903 
VG_(elapsed_wallclock_time)904 void VG_(elapsed_wallclock_time) ( /*OUT*/HChar* buf, SizeT bufsize )
905 {
906    UInt t, ms, s, mins, hours, days;
907 
908    vg_assert(bufsize > 20);
909 
910    t  = VG_(read_millisecond_timer)(); /* milliseconds */
911 
912    ms = t % 1000;
913    t /= 1000; /* now in seconds */
914 
915    s = t % 60;
916    t /= 60; /* now in minutes */
917 
918    mins = t % 60;
919    t /= 60; /* now in hours */
920 
921    hours = t % 24;
922    t /= 24; /* now in days */
923 
924    days = t;
925 
926    VG_(sprintf)(buf, "%02u:%02u:%02u:%02u.%03u ", days, hours, mins, s, ms);
927 }
928 
929 
930 /* ---------------------------------------------------------------------
931    message()
932    ------------------------------------------------------------------ */
933 
934 /* A buffer for accumulating VG_(message) style output.  This is
935    pretty much the same as VG_(printf)'s scheme, with two differences:
936 
937    * The message buffer persists between calls, so that multiple
938      calls to VG_(message) can build up output.
939 
940    * Whenever the first character on a line is emitted, the
941      ==PID== style preamble is stuffed in before it.
942 */
943 typedef
944    struct {
945       HChar buf[512+128];
946       Int   buf_used;
947       Bool  atLeft; /* notionally, is the next char position at the
948                        leftmost column? */
949       /* Current message kind - changes from call to call */
950       VgMsgKind kind;
951       /* destination */
952       OutputSink* sink;
953    }
954    vmessage_buf_t;
955 
956 static vmessage_buf_t vmessage_buf
957    = { "", 0, True, Vg_UserMsg, &VG_(log_output_sink) };
958 
959 
960 // Adds a single char to the buffer.  We aim to have at least 128
961 // bytes free in the buffer, so that it's always possible to emit
962 // the preamble into the buffer if c happens to be the character
963 // following a \n.  When the buffer gets too full, we write its
964 // contents to the logging sink.
add_to__vmessage_buf(HChar c,void * p)965 static void add_to__vmessage_buf ( HChar c, void *p )
966 {
967    HChar tmp[64];
968    vmessage_buf_t* b = (vmessage_buf_t*)p;
969 
970    vg_assert(b->buf_used >= 0 && b->buf_used < sizeof(b->buf)-128);
971 
972    if (UNLIKELY(b->atLeft)) {
973       // insert preamble
974       HChar ch;
975       Int   i, depth;
976 
977       // Print one '>' in front of the messages for each level of
978       // self-hosting being performed.
979       // Do not print such '>' if sim hint "no-inner-prefix" given
980       // (useful to run regression tests in an outer/inner setup
981       // and avoid the diff failing due to these unexpected '>').
982       depth = RUNNING_ON_VALGRIND;
983       if (depth > 0
984           && !SimHintiS(SimHint_no_inner_prefix, VG_(clo_sim_hints))) {
985          if (depth > 10)
986             depth = 10; // ?!?!
987          for (i = 0; i < depth; i++) {
988             b->buf[b->buf_used++] = '>';
989          }
990       }
991 
992       if (Vg_FailMsg == b->kind) {
993          // "valgrind: " prefix.
994          b->buf[b->buf_used++] = 'v';
995          b->buf[b->buf_used++] = 'a';
996          b->buf[b->buf_used++] = 'l';
997          b->buf[b->buf_used++] = 'g';
998          b->buf[b->buf_used++] = 'r';
999          b->buf[b->buf_used++] = 'i';
1000          b->buf[b->buf_used++] = 'n';
1001          b->buf[b->buf_used++] = 'd';
1002          b->buf[b->buf_used++] = ':';
1003          b->buf[b->buf_used++] = ' ';
1004       } else {
1005          switch (b->kind) {
1006             case Vg_UserMsg:       ch = '='; break;
1007             case Vg_DebugMsg:      ch = '-'; break;
1008             case Vg_ClientMsg:     ch = '*'; break;
1009             default:               ch = '?'; break;
1010          }
1011 
1012          b->buf[b->buf_used++] = ch;
1013          b->buf[b->buf_used++] = ch;
1014 
1015          if (VG_(clo_time_stamp)) {
1016             VG_(elapsed_wallclock_time)(tmp, sizeof tmp);
1017             for (i = 0; tmp[i]; i++)
1018                b->buf[b->buf_used++] = tmp[i];
1019          }
1020 
1021          VG_(sprintf)(tmp, "%d", VG_(getpid)());
1022          tmp[sizeof(tmp)-1] = 0;
1023          for (i = 0; tmp[i]; i++)
1024             b->buf[b->buf_used++] = tmp[i];
1025 
1026          b->buf[b->buf_used++] = ch;
1027          b->buf[b->buf_used++] = ch;
1028          b->buf[b->buf_used++] = ' ';
1029       }
1030 
1031       /* We can't possibly have stuffed 96 chars in merely as a result
1032          of making the preamble (can we?) */
1033       vg_assert(b->buf_used < sizeof(b->buf)-32);
1034    }
1035 
1036    b->buf[b->buf_used++] = c;
1037    b->buf[b->buf_used]   = 0;
1038 
1039    if (b->buf_used >= sizeof(b->buf) - 128) {
1040       send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
1041       b->buf_used = 0;
1042    }
1043 
1044    b->atLeft = c == '\n';
1045 }
1046 
1047 
VG_(vmessage)1048 UInt VG_(vmessage) ( VgMsgKind kind, const HChar* format, va_list vargs )
1049 {
1050    UInt ret;
1051 
1052    /* Note (carefully) that the buf persists from call to call, unlike
1053       with the other printf variants in earlier parts of this file. */
1054    vmessage_buf_t* b = &vmessage_buf; /* shorthand for convenience */
1055 
1056    /* We have to set this each call, so that the correct flavour
1057       of preamble is emitted at each \n. */
1058    b->kind = kind;
1059 
1060    ret = VG_(debugLog_vprintf) ( add_to__vmessage_buf,
1061                                  b, format, vargs );
1062 
1063    /* If the message finished exactly with a \n, then flush it at this
1064       point.  If not, assume more bits of the same line will turn up
1065       in later messages, so don't bother to flush it right now. */
1066 
1067    if (b->atLeft && b->buf_used > 0) {
1068       send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
1069       b->buf_used = 0;
1070    }
1071 
1072    return ret;
1073 }
1074 
1075 /* Send a simple single-part message. */
VG_(message)1076 UInt VG_(message) ( VgMsgKind kind, const HChar* format, ... )
1077 {
1078    UInt count;
1079    va_list vargs;
1080    va_start(vargs,format);
1081    count = VG_(vmessage) ( kind, format, vargs );
1082    va_end(vargs);
1083    return count;
1084 }
1085 
revert_to_stderr(void)1086 static void revert_to_stderr ( void )
1087 {
1088    revert_sink_to_stderr(&VG_(log_output_sink));
1089 }
1090 
1091 /* VG_(message) variants with hardwired first argument. */
1092 
VG_(fmsg)1093 UInt VG_(fmsg) ( const HChar* format, ... )
1094 {
1095    UInt count;
1096    va_list vargs;
1097    va_start(vargs,format);
1098    count = VG_(vmessage) ( Vg_FailMsg, format, vargs );
1099    va_end(vargs);
1100    return count;
1101 }
1102 
VG_(fmsg_bad_option)1103 void VG_(fmsg_bad_option) ( const HChar* opt, const HChar* format, ... )
1104 {
1105    va_list vargs;
1106    va_start(vargs,format);
1107    revert_to_stderr();
1108    VG_(message) (Vg_FailMsg, "Bad option: %s\n", opt);
1109    VG_(vmessage)(Vg_FailMsg, format, vargs );
1110    VG_(message) (Vg_FailMsg, "Use --help for more information or consult the user manual.\n");
1111    va_end(vargs);
1112    VG_(exit)(1);
1113 }
1114 
VG_(fmsg_unknown_option)1115 void VG_(fmsg_unknown_option) ( const HChar* opt)
1116 {
1117    revert_to_stderr();
1118    VG_(message) (Vg_FailMsg, "Unknown option: %s\n", opt);
1119    VG_(message) (Vg_FailMsg, "Use --help for more information or consult the user manual.\n");
1120    VG_(exit)(1);
1121 }
1122 
VG_(umsg)1123 UInt VG_(umsg) ( const HChar* format, ... )
1124 {
1125    UInt count;
1126    va_list vargs;
1127    va_start(vargs,format);
1128    count = VG_(vmessage) ( Vg_UserMsg, format, vargs );
1129    va_end(vargs);
1130    return count;
1131 }
1132 
VG_(dmsg)1133 UInt VG_(dmsg) ( const HChar* format, ... )
1134 {
1135    UInt count;
1136    va_list vargs;
1137    va_start(vargs,format);
1138    count = VG_(vmessage) ( Vg_DebugMsg, format, vargs );
1139    va_end(vargs);
1140    return count;
1141 }
1142 
1143 /* Flush any output that has accumulated in vmessage_buf as a
1144    result of previous calls to VG_(message) et al. */
VG_(message_flush)1145 void VG_(message_flush) ( void )
1146 {
1147    vmessage_buf_t* b = &vmessage_buf;
1148    send_bytes_to_logging_sink( b->sink, b->buf, b->buf_used );
1149    b->buf_used = 0;
1150 }
1151 
1152 __attribute__((noreturn))
VG_(err_missing_prog)1153 void VG_(err_missing_prog) ( void  )
1154 {
1155    revert_to_stderr();
1156    VG_(fmsg)("no program specified\n");
1157    VG_(fmsg)("Use --help for more information.\n");
1158    VG_(exit)(1);
1159 }
1160 
1161 __attribute__((noreturn))
VG_(err_config_error)1162 void VG_(err_config_error) ( const HChar* format, ... )
1163 {
1164    va_list vargs;
1165    va_start(vargs,format);
1166    revert_to_stderr();
1167    VG_(message) (Vg_FailMsg, "Startup or configuration error:\n   ");
1168    VG_(vmessage)(Vg_FailMsg, format, vargs );
1169    VG_(message) (Vg_FailMsg, "Unable to start up properly.  Giving up.\n");
1170    va_end(vargs);
1171    VG_(exit)(1);
1172 }
1173 
1174 /* ---------------------------------------------------------------------
1175    VG_(sr_as_string)()
1176    ------------------------------------------------------------------ */
1177 
1178 #if defined(VGO_linux) || defined(VGO_dragonfly)
1179 // FIXME: Does this function need to be adjusted for MIPS's _valEx ?
VG_(sr_as_string)1180 const HChar *VG_(sr_as_string) ( SysRes sr )
1181 {
1182    static HChar buf[7+1+2+16+1+1];   // large enough
1183 
1184    if (sr_isError(sr))
1185       VG_(sprintf)(buf, "Failure(0x%" FMT_REGWORD "x)", (RegWord)sr_Err(sr));
1186    else
1187       VG_(sprintf)(buf, "Success(0x%" FMT_REGWORD "x)", (RegWord)sr_Res(sr));
1188    return buf;
1189 }
1190 
1191 #elif defined(VGO_darwin) || defined(VGO_solaris)
1192 
VG_(sr_as_string)1193 const HChar *VG_(sr_as_string) ( SysRes sr )
1194 {
1195    static HChar buf[7+1+2+16+1+2+16+1+1];   // large enough
1196 
1197    if (sr_isError(sr))
1198       VG_(sprintf)(buf, "Failure(0x%lx)", sr_Err(sr));
1199    else
1200       VG_(sprintf)(buf, "Success(0x%lx:0x%lx)", sr_ResHI(sr), sr_Res(sr));
1201    return buf;
1202 }
1203 
1204 #else
1205 
1206 #error unknown OS
1207 
1208 #endif
1209 
1210 /*--------------------------------------------------------------------*/
1211 /*--- end                                                          ---*/
1212 /*--------------------------------------------------------------------*/
1213