/memcheck/tests/linux/sys-execveat
/memcheck/tests/linux/check_execveat
/memcheck/tests/linux/enomem
+/memcheck/tests/linux/debuginfod-check
+/memcheck/tests/linux/debuginfod-check.vgtest
+/memcheck/tests/linux/.debuginfod/
# /memcheck/tests/mips32/
/memcheck/tests/mips32/*.stderr.diff
Valgrind. The vector-packed-decimal facility is currently not
exploited by the standard toolchain and libraries.
+* Valgrind now supports debuginfod, an HTTP server for distributing
+ ELF/DWARF debugging information. When a debuginfo file cannot be
+ found locally, Valgrind is able to query debuginfod servers for the
+ file using its build-id. See the user manual for more information
+ about debuginfod support.
+
* ==================== TOOL CHANGES ====================
* DHAT:
432672 vg_regtest: test-specific environment variables not reset between tests
432809 VEX should support REX.W + POPF
432861 PPC modsw and modsd give incorrect results for 1 mod 12
+432215 Add debuginfod functionality
n-i-bz helgrind: If hg_cli__realloc fails, return NULL.
429352 PPC ISA 3.1 support is missing, part 7
memcheck/tests/amd64/Makefile
memcheck/tests/x86/Makefile
memcheck/tests/linux/Makefile
+ memcheck/tests/linux/debuginfod-check.vgtest
memcheck/tests/darwin/Makefile
memcheck/tests/solaris/Makefile
memcheck/tests/amd64-linux/Makefile
pub_core_mallocfree.h \
pub_core_options.h \
pub_core_oset.h \
+ pub_core_pathscan.h \
pub_core_poolalloc.h \
pub_core_rangemap.h \
pub_core_redir.h \
m_gdbserver/target.h \
m_gdbserver/valgrind_low.h \
m_gdbserver/gdb/signals.h \
- m_initimg/priv_initimg_pathscan.h \
m_scheduler/priv_sema.h \
m_scheduler/priv_sched-lock.h \
m_scheduler/priv_sched-lock-impl.h \
m_mallocfree.c \
m_options.c \
m_oset.c \
+ m_pathscan.c \
m_poolalloc.c \
m_rangemap.c \
m_redir.c \
m_initimg/initimg-linux.c \
m_initimg/initimg-darwin.c \
m_initimg/initimg-solaris.c \
- m_initimg/initimg-pathscan.c \
m_mach/mach_basics.c \
m_mach/mach_msg.c \
m_mach/mach_traps-x86-darwin.S \
#include "pub_core_libcbase.h"
#include "pub_core_libcprint.h"
#include "pub_core_libcassert.h"
+#include "pub_core_libcfile.h"
+#include "pub_core_libcproc.h"
#include "pub_core_machine.h" /* VG_ELF_CLASS */
#include "pub_core_options.h"
#include "pub_core_oset.h"
+#include "pub_core_pathscan.h" /* find_executable */
#include "pub_core_syscall.h"
#include "pub_core_tooliface.h" /* VG_(needs) */
#include "pub_core_xarray.h"
return dimg;
}
+#if defined(VGO_linux)
+/* Return the path of the debuginfod-find executable. */
+static
+const HChar* debuginfod_find_path( void )
+{
+ static const HChar* path = (const HChar*) -1;
+
+ if (path == (const HChar*) -1) {
+ if (VG_(getenv)("DEBUGINFOD_URLS") == NULL
+ || VG_(strcmp)("", VG_(getenv("DEBUGINFOD_URLS"))) == 0)
+ path = NULL;
+ else
+ path = VG_(find_executable)("debuginfod-find");
+ }
+
+ return path;
+}
+
+/* Try to find a separate debug file with |buildid| via debuginfod. If found,
+ return its DiImage. Searches for a local debuginfod-find executable and
+ runs it in a child process in order to download the debug file. */
+static
+DiImage* find_debug_file_debuginfod( const HChar* objpath,
+ HChar** debugpath,
+ const HChar* buildid,
+ const UInt crc, Bool rel_ok )
+{
+# define BUF_SIZE 4096
+ Int out_fds[2], err_fds[2]; /* pipe fds */
+ DiImage* dimg = NULL; /* the img we found */
+ HChar buf[BUF_SIZE]; /* executable output buffer */
+ const HChar* path; /* executable path */
+ SizeT len; /* number of bytes read int buf */
+ Int ret; /* result of read call */
+
+ if (buildid == NULL)
+ return NULL;
+
+ if ((path = debuginfod_find_path()) == NULL)
+ return NULL;
+
+ if (VG_(pipe)(out_fds) != 0
+ || VG_(pipe)(err_fds) != 0)
+ return NULL;
+
+ if (VG_(clo_verbosity) > 1)
+ VG_(umsg)("Downloading debug info for %s...\n", objpath);
+
+ /* Run debuginfod-find to query servers for the debuginfo. */
+ Int pid = VG_(fork)();
+ if (pid == 0) {
+ const HChar *argv[4] = { path, "debuginfo", buildid, (HChar*)0 };
+
+ /* Redirect stdout and stderr */
+ SysRes res = VG_(dup2)(out_fds[1], 1);
+ if (sr_Res(res) < 0)
+ VG_(exit)(1);
+
+ res = VG_(dup2)(err_fds[1], 2);
+ if (sr_Res(res) < 0)
+ VG_(exit)(1);
+
+ /* Disable extra stderr output since it does not play well with umesg */
+ VG_(env_unsetenv)(VG_(client_envp), "DEBUGINFOD_VERBOSE", NULL);
+ VG_(env_unsetenv)(VG_(client_envp), "DEBUGINFOD_PROGRESS", NULL);
+
+ VG_(close)(out_fds[0]);
+ VG_(close)(err_fds[0]);
+ VG_(execv)(argv[0], argv);
+
+ /* If we are still alive here, execv failed. */
+ VG_(exit)(1);
+ }
+
+ VG_(close)(out_fds[1]);
+ VG_(close)(err_fds[1]);
+
+ if (pid < 0) {
+ if (VG_(clo_verbosity) > 1)
+ VG_(umsg)("Server Error\n");
+ goto out;
+ }
+ VG_(waitpid)(pid, NULL, 0);
+
+ /* Set dimg if download was successful. */
+ len = 0;
+ ret = -1;
+ while (len >= 0 && len < BUF_SIZE) {
+ ret = VG_(read)(out_fds[0], buf + len, BUF_SIZE - len);
+ if (ret <= 0)
+ break;
+ len += ret;
+ }
+ if (ret >= 0 && len > 0
+ && buf[0] == '/' && buf[len-1] == '\n') {
+
+ /* Remove newline from filename before trying to open debug file */
+ buf[len-1] = '\0';
+ dimg = open_debug_file(buf, buildid, crc, rel_ok, NULL);
+ if (dimg != NULL) {
+ /* Success */
+ if (*debugpath)
+ ML_(dinfo_free)(*debugpath);
+
+ *debugpath = ML_(dinfo_strdup)("di.fdfd.1", buf);
+ if (VG_(clo_verbosity) > 1)
+ VG_(umsg)("Successfully downloaded debug file for %s\n",
+ objpath);
+ goto out;
+ }
+ }
+
+ /* Download failed so try to print error message. */
+ HChar* nl;
+ if (VG_(read)(err_fds[0], buf, BUF_SIZE) > 0
+ && (nl = VG_(strchr)(buf, '\n'))) {
+ *nl = '\0';
+ if (VG_(clo_verbosity) > 1)
+ VG_(umsg)("%s\n", buf);
+ } else
+ if (VG_(clo_verbosity) > 1)
+ VG_(umsg)("Server Error\n");
+
+out:
+ VG_(close)(out_fds[0]);
+ VG_(close)(err_fds[0]);
+ return dimg;
+}
+#endif
/* Try to find a separate debug file for a given object file. If
found, return its DiImage, which should be freed by the caller. If
ML_(dinfo_free)(objdir);
}
+# if defined(VGO_linux)
+ if (dimg == NULL)
+ dimg = find_debug_file_debuginfod(objpath, &debugpath, buildid, crc, rel_ok);
+# endif
+
if (dimg != NULL) {
vg_assert(debugpath);
TRACE_SYMTAB("\n");
#include "pub_core_options.h"
#include "pub_core_tooliface.h" /* VG_TRACK */
#include "pub_core_threadstate.h" /* ThreadArchState */
-#include "priv_initimg_pathscan.h"
+#include "pub_core_pathscan.h" /* find_executable */
#include "pub_core_initimg.h" /* self */
SysRes res;
vg_assert( VG_(args_the_exename) != NULL);
- exe_name = ML_(find_executable)( VG_(args_the_exename) );
+ exe_name = VG_(find_executable)( VG_(args_the_exename) );
if (!exe_name) {
VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename));
#include "pub_core_syscall.h"
#include "pub_core_tooliface.h" /* VG_TRACK */
#include "pub_core_threadstate.h" /* ThreadArchState */
-#include "priv_initimg_pathscan.h"
+#include "pub_core_pathscan.h" /* find_executable */
#include "pub_core_initimg.h" /* self */
/* --- !!! --- EXTERNAL HEADERS start --- !!! --- */
SysRes res;
vg_assert( VG_(args_the_exename) != NULL);
- exe_name = ML_(find_executable)( VG_(args_the_exename) );
+ exe_name = VG_(find_executable)( VG_(args_the_exename) );
if (!exe_name) {
VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename));
#include "pub_core_syswrap.h"
#include "pub_core_tooliface.h" /* VG_TRACK */
#include "pub_core_threadstate.h" /* ThreadArchState */
-#include "priv_initimg_pathscan.h"
+#include "pub_core_pathscan.h" /* find_executable */
#include "pub_core_initimg.h" /* self */
SysRes res;
vg_assert(VG_(args_the_exename));
- exe_name = ML_(find_executable)(VG_(args_the_exename));
+ exe_name = VG_(find_executable)(VG_(args_the_exename));
if (!exe_name) {
VG_(printf)("valgrind: %s: command not found\n", VG_(args_the_exename));
/*--------------------------------------------------------------------*/
-/*--- Startup: search PATH for an executable initimg-pathscan.c ---*/
+/*--- search PATH for an executable m_pathscan.c ---*/
/*--------------------------------------------------------------------*/
/*
#include "pub_core_libcproc.h"
#include "pub_core_libcprint.h"
#include "pub_core_mallocfree.h"
-#include "pub_core_initimg.h" /* self */
-
-#include "priv_initimg_pathscan.h"
+#include "pub_core_pathscan.h" /* self */
/*====================================================================*/
}
// Returns NULL if it wasn't found.
-const HChar* ML_(find_executable) ( const HChar* exec )
+const HChar* VG_(find_executable) ( const HChar* exec )
{
vg_assert(NULL != exec);
/*--------------------------------------------------------------------*/
-/*--- Startup: search PATH for an executable ---*/
-/*--- priv_initimg-pathscan.h ---*/
+/*--- Search PATH for an executable ---*/
+/*--- m_pathscan.h ---*/
/*--------------------------------------------------------------------*/
/*
The GNU General Public License is contained in the file COPYING.
*/
-#ifndef __PRIV_INITIMG_PATHSCAN_H
-#define __PRIV_INITIMG_PATHSCAN_H
+#ifndef __PUB_CORE_PATHSCAN_H
+#define __PUB_CORE_PATHSCAN_H
#include "pub_core_basics.h" // HChar
-extern const HChar* ML_(find_executable) ( const HChar* exec );
+extern const HChar* VG_(find_executable) ( const HChar* exec );
-#endif // ndef __PRIV_INITIMG_PATHSCAN_H
+#endif // ndef __PUB_CORE_PATHSCAN_H
</sect1>
+<sect1 id="manual-core.debuginfod"
+ xreflabel="Debuginfod">
+<title>Debuginfod</title>
+
+<para id="debuginfod.para.1">Valgrind supports the downloading of debuginfo
+files via debuginfod, an HTTP server for distributing ELF/DWARF debugging
+information. When a debuginfo file cannot be found locally, Valgrind is able
+to query debuginfod servers for the file using its build-id.</para>
+
+<para id="debuginfod.para.2">In order to use this feature
+<computeroutput>debuginfod-find</computeroutput> must be installed and
+<computeroutput>$DEBUGINFOD_URLS</computeroutput> must contain URLs of
+debuginfod servers. Valgrind does not support
+<computeroutput>debuginfod-find</computeroutput> verbose output that is
+normally enabled with <computeroutput>$DEBUGINFOD_PROGRESS</computeroutput>
+and <computeroutput>$DEBUGINFOD_VERBOSE</computeroutput>. These environment
+variables will be ignored.</para>
+
+<para id="debuginfod.para.3">For more information regarding debuginfod, see
+https://sourceware.org/elfutils/Debuginfod.html</para>
+
+</sect1>
+
+
<sect1 id="manual-core.options"
xreflabel="Core Command-line Options">
<title>Core Command-line Options</title>
you want to use it, you will have to recompile it by hand using
the command shown at the top
of <computeroutput>auxprogs/valgrind-di-server.c</computeroutput>.</para>
+
+ <para>Valgrind can also download debuginfo via debuginfod. See the
+ DEBUGINFOD section for more information.</para>
+
</listitem>
</varlistentry>
</refsect1>
+<refsect1 id="debuginfod">
+<title>Debuginfod</title>
+
+<xi:include href="manual-core.xml"
+ xpointer="debuginfod.para.1"
+ xmlns:xi="http://www.w3.org/2001/XInclude" />
+<xi:include href="manual-core.xml"
+ xpointer="debuginfod.para.2"
+ xmlns:xi="http://www.w3.org/2001/XInclude" />
+<xi:include href="manual-core.xml"
+ xpointer="debuginfod.para.3"
+ xmlns:xi="http://www.w3.org/2001/XInclude" />
+</refsect1>
<refsect1 id="see_also">
<title>See Also</title>
EXTRA_DIST = \
brk.stderr.exp brk.vgtest \
capget.vgtest capget.stderr.exp capget.stderr.exp2 capget.stderr.exp3 \
+ debuginfod-check.stderr.exp debuginfod-check.vgtest.in \
dlclose_leak-no-keep.stderr.exp dlclose_leak-no-keep.stdout.exp \
dlclose_leak-no-keep.vgtest \
dlclose_leak.stderr.exp dlclose_leak.stdout.exp \
brk \
capget \
check_preadv2_pwritev2 \
+ debuginfod-check \
dlclose_leak dlclose_leak_so.so \
ioctl-tiocsig \
getregset \
--- /dev/null
+#include <stdlib.h>
+
+int main() {
+ char *p = malloc(1);
+ p[-1] = 0;
+ return 0;
+}
--- /dev/null
+#! /usr/bin/perl
+use warnings;
+use strict;
+use Cwd;
+use IPC::Open3;
+
+our $dir = Cwd::realpath("./.debuginfod");
+our $pid = 0;
+
+# Kill the server and remove temporary files.
+sub cleanup_and_exit($) {
+ my $exit_code = $_[0];
+ mysystem("rm -rf $dir");
+ if ($pid != 0) {
+ kill "INT", $pid;
+ }
+ exit $exit_code;
+}
+
+# Propagate Ctrl-C and exit if command results in an error.
+sub mysystem($)
+{
+ my $exit_code = system($_[0]);
+ ($exit_code == 2) and cleanup_and_exit(1); # Exit if SIGINT
+ if ($exit_code != 0) {
+ #warn "Error while executing: $_[0]";
+ cleanup_and_exit(1);
+ }
+ return $exit_code;
+}
+
+# Check that debuginfod and debuginfod-find can be found
+mysystem("debuginfod --help > /dev/null");
+mysystem("debuginfod-find --help > /dev/null");
+
+$SIG{'INT'} = sub { cleanup_and_exit(1) };
+
+my $testname = "debuginfod-check";
+my $tmp = "$dir/debuginfod_test.tmp";
+my $dbg = "$dir/dbg";
+mysystem("rm -rf $dir");
+mysystem("mkdir -p $dbg");
+
+# Compile the test executable, strip its debuginfo and store it so
+# that valgrind cannot find it without debuginfod.
+mysystem("gcc -O0 -g -o $testname $testname.c");
+mysystem("objcopy --only-keep-debug $testname $testname.debug");
+mysystem("objcopy --strip-unneeded $testname");
+mysystem("objcopy --add-gnu-debuglink=$testname.debug $testname");
+mysystem("mv $testname.debug $dbg");
+mysystem("readelf -n $testname > $tmp 2>&1");
+
+my $buildid = "";
+open(TMP, '<', $tmp);
+while (my $out = <TMP>) {
+ if ($out =~ /Build ID: ([0-9a-f]*)/) {
+ $buildid = $1;
+ }
+}
+
+if ($buildid eq "") {
+ warn "can't find $testname build-id";
+ cleanup_and_exit(1);
+}
+
+my $found_port = 0;
+my $port = 7999;
+
+# Find an unused port
+while ($found_port == 0 and $port < 65536) {
+ $port++;
+ $pid = open3(undef, "TMP", undef,
+ "debuginfod", "-d", "$dir/db", '-F', "$dbg", "--port=$port");
+ for (my $i = 0; $i < 5 and $found_port == 0; $i++) {
+ while (my $got = <TMP>) {
+ if ($got =~ /Failed to bind/) {
+ last;
+ } elsif ($got =~ /started http server/) {
+ $found_port = 1;
+ last;
+ }
+ }
+ }
+}
+
+if ($port == 65536) {
+ warn "No available ports";
+ cleanup_and_exit(1);
+}
+
+my $server_ready = 0;
+
+# Confirm that the server is ready to be queried
+for (my $i = 0; $i < 10 and $server_ready == 0; $i++) {
+ sleep 1;
+ my $got = `curl -s http://localhost:$port/metrics`;
+ if ($got =~ /ready 1/
+ and $got =~ /thread_work_total\{role=\"traverse\"\} 1/
+ and $got =~ /thread_work_pending\{role=\"scan\"\} 0/
+ and $got =~ /thread_busy\{role=\"scan\"\} 0/) {
+ $server_ready = 1;
+ }
+}
+
+if ($server_ready == 0) {
+ warn "Can't start debuginfod server";
+ cleanup_and_exit(1);
+}
+
+# Query the server and store the debuginfo in the client cache for valgrind to find.
+my $myres = mysystem("DEBUGINFOD_CACHE_PATH=$dir DEBUGINFOD_URLS=http://localhost:$port debuginfod-find debuginfo $buildid > /dev/null 2>&1");
+if ($myres != 0) {
+ cleanup_and_exit(1);
+}
+kill "INT", $pid;
+exit 0;
--- /dev/null
+Invalid write of size 1
+ at 0x........: main (debuginfod-check.c:5)
+ Address 0x........ is 1 bytes before a block of size 1 alloc'd
+ at 0x........: malloc (vg_replace_malloc.c:...)
+ by 0x........: main (debuginfod-check.c:4)
+
--- /dev/null
+prereq: ./debuginfod-check.pl
+prog: debuginfod-check
+vgopts: -q
+env: DEBUGINFOD_URLS=localhost DEBUGINFOD_CACHE_PATH=@abs_top_builddir@/memcheck/tests/linux/.debuginfod
# nuke VALGRIND_OPTS
$ENV{"VALGRIND_OPTS"} = "";
+# nuke DEBUGINFOD_URLS
+$ENV{"DEBUGINFOD_URLS"} = "";
if ($ENV{"EXTRA_REGTEST_OPTS"}) {
print "\n";