/*--------------------------------------------------------------------*/ /*--- Libc printing. m_libcprint.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2005 Julian Seward jseward@acm.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #include "pub_core_basics.h" #include "pub_core_debuglog.h" #include "pub_core_libcbase.h" #include "pub_core_libcassert.h" #include "pub_core_libcfile.h" // For VG_(write)(), VG_(write_socket)() #include "pub_core_libcprint.h" #include "pub_core_libcproc.h" // For VG_(getpid)() #include "pub_core_options.h" #include "valgrind.h" // For RUNNING_ON_VALGRIND #include #include /* --------------------------------------------------------------------- Writing to file or a socket ------------------------------------------------------------------ */ /* Tell the logging mechanism whether we are logging to a file descriptor or a socket descriptor. */ Bool VG_(logging_to_socket) = False; /* Do the low-level send of a message to the logging sink. */ static void send_bytes_to_logging_sink ( Char* msg, Int nbytes ) { if (!VG_(logging_to_socket)) { VG_(write)( VG_(clo_log_fd), msg, nbytes ); } else { Int rc = VG_(write_socket)( VG_(clo_log_fd), msg, nbytes ); if (rc == -1) { // For example, the listener process died. Switch back to stderr. VG_(logging_to_socket) = False; VG_(clo_log_fd) = 2; VG_(write)( VG_(clo_log_fd), msg, nbytes ); } } } /* --------------------------------------------------------------------- printf() and friends ------------------------------------------------------------------ */ typedef struct { char buf[100]; int n; } printf_buf; // Adds a single char to the buffer. When the buffer gets sufficiently // full, we write its contents to the logging sink. static void add_to_myprintf_buf ( HChar c, void *p ) { printf_buf *myprintf_buf = (printf_buf *)p; if (myprintf_buf->n >= 100-10 /*paranoia*/ ) { send_bytes_to_logging_sink( myprintf_buf->buf, myprintf_buf->n ); myprintf_buf->n = 0; } myprintf_buf->buf[myprintf_buf->n++] = c; myprintf_buf->buf[myprintf_buf->n] = 0; } UInt VG_(vprintf) ( const HChar *format, va_list vargs ) { UInt ret = 0; printf_buf myprintf_buf = {"",0}; if (VG_(clo_log_fd) >= 0) { ret = VG_(debugLog_vprintf) ( add_to_myprintf_buf, &myprintf_buf, format, vargs ); // Write out any chars left in the buffer. if (myprintf_buf.n > 0) { send_bytes_to_logging_sink( myprintf_buf.buf, myprintf_buf.n ); } } return ret; } UInt VG_(printf) ( const HChar *format, ... ) { UInt ret; va_list vargs; va_start(vargs, format); ret = VG_(vprintf)(format, vargs); va_end(vargs); return ret; } /* A general replacement for sprintf(). */ static void add_to_vg_sprintf_buf ( HChar c, void *p ) { char **vg_sprintf_ptr = p; *(*vg_sprintf_ptr)++ = c; } UInt VG_(vsprintf) ( Char* buf, const HChar *format, va_list vargs ) { Int ret; Char *vg_sprintf_ptr = buf; ret = VG_(debugLog_vprintf) ( add_to_vg_sprintf_buf, &vg_sprintf_ptr, format, vargs ); add_to_vg_sprintf_buf('\0', &vg_sprintf_ptr); vg_assert(VG_(strlen)(buf) == ret); return ret; } UInt VG_(sprintf) ( Char* buf, const HChar *format, ... ) { UInt ret; va_list vargs; va_start(vargs,format); ret = VG_(vsprintf)(buf, format, vargs); va_end(vargs); return ret; } /* --------------------------------------------------------------------- percentify() ------------------------------------------------------------------ */ // Percentify n/m with d decimal places. Includes the '%' symbol at the end. void VG_(percentify)(UInt n, UInt m, UInt d, Int n_buf, char buf[]) { Int i, len, space; ULong p1; if (m == 0) { VG_(sprintf)(buf, "--%%"); return; } p1 = (100*n) / m; if (d == 0) { VG_(sprintf)(buf, "%lld%%", p1); } else { ULong p2; UInt ex; Char fmt[32]; switch (d) { case 1: ex = 10; break; case 2: ex = 100; break; case 3: ex = 1000; break; default: VG_(tool_panic)("Currently can only handle 3 decimal places"); } p2 = ((100*n*ex) / m) % ex; // Have to generate the format string in order to be flexible about // the width of the post-decimal-point part. VG_(sprintf)(fmt, "%%lld.%%0%dlld%%%%", d); // fmt is now "%lld.%0lld%%" where is 1,2,3... VG_(sprintf)(buf, fmt, p1, p2); } len = VG_(strlen)(buf); space = n_buf - len; if (space < 0) space = 0; /* Allow for v. small field_width */ i = len; /* Right justify in field */ for ( ; i >= 0; i--) buf[i + space] = buf[i]; for (i = 0; i < space; i++) buf[i] = ' '; } /* --------------------------------------------------------------------- ctime() ------------------------------------------------------------------ */ /* BUF must be at least 25 characters long. This is unchecked. */ void VG_(ctime) ( /*OUT*/HChar* buf ) { struct timeval tv; struct tm tm; buf[0] = 0; if ( gettimeofday( &tv, NULL ) == 0 && localtime_r( &tv.tv_sec, &tm ) == &tm ) { VG_(sprintf)( buf, "%04d-%02d-%02d %02d:%02d:%02d.%03d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec / 1000 ); } } /* --------------------------------------------------------------------- message() ------------------------------------------------------------------ */ UInt VG_(vmessage) ( VgMsgKind kind, const HChar* format, va_list vargs ) { UInt count = 0; Char c; const Char* pfx_s; static const Char pfx[] = ">>>>>>>>>>>>>>>>"; switch (kind) { case Vg_UserMsg: c = '='; break; case Vg_DebugMsg: c = '-'; break; case Vg_DebugExtraMsg: c = '+'; break; case Vg_ClientMsg: c = '*'; break; default: c = '?'; break; } // The pfx trick prints one or more '>' characters in front of the // messages when running Valgrind under Valgrind, one per level of // self-hosting. pfx_s = &pfx[sizeof(pfx)-1-RUNNING_ON_VALGRIND], // Print the message count = 0; if (!VG_(clo_xml)) count += VG_(printf) ("%s%c%c", pfx_s, c,c); if (VG_(clo_time_stamp)) { HChar buf[50]; VG_(ctime)(buf); count += VG_(printf)( "%s ", buf); } if (!VG_(clo_xml)) count += VG_(printf) ("%d%c%c ", VG_(getpid)(), c,c); count += VG_(vprintf)(format, vargs); count += VG_(printf) ("\n"); return count; } /* Send a simple single-part message. */ UInt VG_(message) ( VgMsgKind kind, const HChar* format, ... ) { UInt count; va_list vargs; va_start(vargs,format); count = VG_(vmessage) ( kind, format, vargs ); va_end(vargs); return count; } /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/