📄 mc-tech-docs.html
字号:
All <code class="function">malloc</code>, <code class="function">free</code>, etc calls that the client program makes are eventually routed to a call to <code class="function">vg_trap_here</code>, and Valgrind does its own special thing with these calls. In effect this provides a trapdoor, by which Valgrind can intercept certain calls on the simulated CPU, run the call as it sees fit itself (on the real CPU), and return the result to the simulated CPU, quite transparently to the client program.</p></li></ul></div><p>Valgrind intercepts the client's<code class="function">malloc</code>,<code class="function">free</code>, etc, calls, so that it canstore additional information. Each block<code class="function">malloc</code>'d by the client givesrise to a shadow block in which Valgrind stores the call stack atthe time of the <code class="function">malloc</code> call.When the client calls <code class="function">free</code>,Valgrind tries to find the shadow block corresponding to theaddress passed to <code class="function">free</code>, andemits an error message if none can be found. If it is found, theblock is placed on the freed blocks queue<code class="computeroutput">vg_freed_list</code>, it is marked asinaccessible, and its shadow block now records the call stack atthe time of the <code class="function">free</code> call.Keeping <code class="computeroutput">free</code>'d blocks in thisqueue allows Valgrind to spot all (presumably invalid) accessesto them. However, once the volume of blocks in the free queueexceeds <code class="function">VG_(clo_freelist_vol)</code>,blocks are finally removed from the queue.</p><p>Keeping track of <code class="literal">A</code> and<code class="literal">V</code> bits (note: if you don't know what theseare, you haven't read the user guide carefully enough) for memoryis done in <code class="filename">vg_memory.c</code>. This implements asparse array structure which covers the entire 4G address spacein a way which is reasonably fast and reasonably space efficient.The 4G address space is divided up into 64K sections, eachcovering 64Kb of address space. Given a 32-bit address, the top16 bits are used to select one of the 65536 entries in<code class="function">VG_(primary_map)</code>. The resulting"secondary" (<code class="computeroutput">SecMap</code>) holds A andV bits for the 64k of address space chunk corresponding to thelower 16 bits of the address.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="mc-tech-docs.design"></a>1.1.3.燚esign decisions</h3></div></div></div><p>Some design decisions were motivated by the need to makeValgrind debuggable. Imagine you are writing a CPU simulator.It works fairly well. However, you run some large program, likeNetscape, and after tens of millions of instructions, it crashes.How can you figure out where in your simulator the bug is?</p><p>Valgrind's answer is: cheat. Valgrind is designed so thatit is possible to switch back to running the client program onthe real CPU at any point. Using the<code class="option">--stop-after= </code> flag, you can askValgrind to run just some number of basic blocks, and then runthe rest of the way on the real CPU. If you are searching for abug in the simulated CPU, you can use this to do a binary search,which quickly leads you to the specific basic block which iscausing the problem.</p><p>This is all very handy. It does constrain the design incertain unimportant ways. Firstly, the layout of memory, whenviewed from the client's point of view, must be identicalregardless of whether it is running on the real or simulated CPU.This means that Valgrind can't do pointer swizzling -- well, nogreat loss -- and it can't run on the same stack as the client --again, no great loss. Valgrind operates on its own stack,<code class="function">VG_(stack)</code>, which it switches toat startup, temporarily switching back to the client's stack whendoing system calls for the client.</p><p>Valgrind also receives signals on its own stack,<code class="computeroutput">VG_(sigstack)</code>, but for differentgruesome reasons discussed below.</p><p>This nice cleanswitch-back-to-the-real-CPU-whenever-you-like story is muddied bysignals. Problem is that signals arrive at arbitrary times andtend to slightly perturb the basic block count, with the resultthat you can get close to the basic block causing a problem butcan't home in on it exactly. My kludgey hack is to define<code class="computeroutput">SIGNAL_SIMULATION</code> to 1 towardsthe bottom of <code class="filename">vg_syscall_mem.c</code>, so thatsignal handlers are run on the real CPU and don't change the BBcounts.</p><p>A second hole in the switch-back-to-real-CPU story is thatValgrind's way of delivering signals to the client is differentfrom that of the kernel. Specifically, the layout of the signaldelivery frame, and the mechanism used to detect a sighandlerreturning, are different. So you can't expect to make thetransition inside a sighandler and still have things working, butin practice that's not much of a restriction.</p><p>Valgrind's implementation of<code class="function">malloc</code>,<code class="function">free</code>, etc, (in<code class="filename">vg_clientmalloc.c</code>, not the low-level stuffin <code class="filename">vg_malloc2.c</code>) is somewhat complicated bythe need to handle switching back at arbitrary points. It doeswork tho.</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="mc-tech-docs.correctness"></a>1.1.4.燙orrectness</h3></div></div></div><p>There's only one of me, and I have a Real Life (tm) as wellas hacking Valgrind [allegedly :-]. That means I don't have timeto waste chasing endless bugs in Valgrind. My emphasis istherefore on doing everything as simply as possible, withcorrectness, stability and robustness being the number onepriority, more important than performance or functionality. As aresult:</p><div class="itemizedlist"><ul type="disc"><li><p>The code is absolutely loaded with assertions, and these are <span><strong class="command">permanently enabled.</strong></span> I have no plan to remove or disable them later. Over the past couple of months, as valgrind has become more widely used, they have shown their worth, pulling up various bugs which would otherwise have appeared as hard-to-find segmentation faults.</p><p>I am of the view that it's acceptable to spend 5% of the total running time of your valgrindified program doing assertion checks and other internal sanity checks.</p></li><li><p>Aside from the assertions, valgrind contains various sets of internal sanity checks, which get run at varying frequencies during normal operation. <code class="function">VG_(do_sanity_checks)</code> runs every 1000 basic blocks, which means 500 to 2000 times/second for typical machines at present. It checks that Valgrind hasn't overrun its private stack, and does some simple checks on the memory permissions maps. Once every 25 calls it does some more extensive checks on those maps. Etc, etc.</p><p>The following components also have sanity check code, which can be enabled to aid debugging:</p><div class="itemizedlist"><ul type="circle"><li><p>The low-level memory-manager (<code class="computeroutput">VG_(mallocSanityCheckArena)</code>). This does a complete check of all blocks and chains in an arena, which is very slow. Is not engaged by default.</p></li><li><p>The symbol table reader(s): various checks to ensure uniqueness of mappings; see <code class="function">VG_(read_symbols)</code> for a start. Is permanently engaged.</p></li><li><p>The A and V bit tracking stuff in <code class="filename">vg_memory.c</code>. This can be compiled with cpp symbol <code class="computeroutput">VG_DEBUG_MEMORY</code> defined, which removes all the fast, optimised cases, and uses simple-but-slow fallbacks instead. Not engaged by default.</p></li><li><p>Ditto <code class="computeroutput">VG_DEBUG_LEAKCHECK</code>.</p></li><li><p>The JITter parses x86 basic blocks into sequences of UCode instructions. It then sanity checks each one with <code class="function">VG_(saneUInstr)</code> and sanity checks the sequence as a whole with <code class="function">VG_(saneUCodeBlock)</code>. This stuff is engaged by default, and has caught some way-obscure bugs in the simulated CPU machinery in its time.</p></li><li><p>The system call wrapper does <code class="function">VG_(first_and_last_secondaries_look_plausible)</code> after every syscall; this is known to pick up bugs in the syscall wrappers. Engaged by default.</p></li><li><p>The main dispatch loop, in <code class="function">VG_(dispatch)</code>, checks that translations do not set <code class="computeroutput">%ebp</code> to any value different from <code class="computeroutput">VG_EBP_DISPATCH_CHECKED</code> or <code class="computeroutput">& VG_(baseBlock)</code>. In effect this test is free, and is permanently engaged.</p></li><li><p>There are a couple of ifdefed-out consistency checks I inserted whilst debugging the new register allocater, <code class="computeroutput">vg_do_register_allocation</code>.</p></li></ul></div></li><li><p>I try to avoid techniques, algorithms, mechanisms, etc, for which I can supply neither a convincing argument that they are correct, nor sanity-check code which might pick up bugs in my implementation. I don't always succeed in this, but I try. Basically the idea is: avoid techniques which are, in practice, unverifiable, in some sense. When doing anything, always have in mind: "how can I verify that this is correct?"</p></li></ul></div><p>Some more specific things are:</p><div class="itemizedlist"><ul type="disc"><li><p>Valgrind runs in the same namespace as the client, at least from <code class="filename">ld.so</code>'s point of view, and it therefore absolutely had better not export any symbol with a name which could clash with that of the client or any of its libraries. Therefore, all globally visible symbols exported from <code class="filename">valgrind.so</code> are defined using the <code class="computeroutput">VG_</code> CPP macro. As you'll see from <code class="filename">vg_constants.h</code>, this appends some arbitrary prefix to the symbol, in order that it be, we hope, globally unique. Currently the prefix is <code class="computeroutput">vgPlain_</code>. For convenience there are also <code class="computeroutput">VGM_</code>, <code class="computeroutput">VGP_</code> and <code class="computeroutput">VGOFF_</code>. All locally defined symbols are declared <code class="computeroutput">static</code> and do not appear in the final shared object.</p><p>To check this, I periodically do <code class="computeroutput">nm valgrind.so | grep " T "</code>, which shows you all the globally exported text symbols. They should all have an approved prefix, except for those like <code class="function">malloc</code>, <code class="function">free</code>, etc, which we deliberately want to shadow and take precedence over the same names exported from <code class="filename">glibc.so</code>, so that valgrind can intercept those calls easily. Similarly, <code class="computeroutput">nm valgrind.so | grep " D "</code> allows you to find any rogue data-segment symbol names.</p></li><li><p>Valgrind tries, and almost succeeds, in being completely independent of all other shared objects, in particular of <code class="filename">glibc.so</code>. For example, we have our own low-level memory manager in <code class="filename">vg_malloc2.c</code>, which is a fairly standard
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -