<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>A System Programming Blog</title>
    <description>A System Programming Blog is where I write about user space system programming topics that I find interesting.</description>
    <link>https://system.joekain.com/</link>
    <atom:link href="https://system.joekain.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Wed, 11 Mar 2026 16:34:24 +0000</pubDate>
    <lastBuildDate>Wed, 11 Mar 2026 16:34:24 +0000</lastBuildDate>
    <generator>Jekyll v4.4.1</generator>
    
      <item>
        <title>Demangling the Details: Symbol Resolution in Rusty Trap</title>
        <description>&lt;div class=&quot;debugger-callout&quot; style=&quot;
	    border: 1px solid #eee;
	    padding: 1em;
	    border-radius: 6px;
	    background-color: #f9f9f9;
	    display: flex;
	    align-items: center;
	    gap: 1em;
	    margin: 2em 0;
&quot;&gt;
  &lt;img src=&quot;/rusty_trap.png&quot; alt=&quot;Rusty trap icon&quot; style=&quot;width: 40px; height: 40px; flex-shrink: 0;&quot; /&gt;
  &lt;div&gt;
    &lt;strong&gt;This post is part of my &lt;em&gt;Writing a Debugger&lt;/em&gt; series.&lt;/strong&gt;&lt;br /&gt;
    &lt;a href=&quot;/debugger/&quot;&gt;View all posts and upcoming topics →&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;p&gt;A few posts ago, we &lt;a href=&quot;/2025/08/17/ownership-driven-debugger-design.html&quot;&gt;used ownership in Rust to drive the design of our API&lt;/a&gt; when setting multiple breakpoints in our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; debugger.  Then we did some vibe coded further refactoring of the API.  The API we have for inferiors and breakpoints now is pretty clean and we can move onto the next feature.  This has been a long time coming, and I’m proud to announce we are finally going to implement support for looking up symbols from the symbol table when setting breakpoints.&lt;/p&gt;

&lt;h2 id=&quot;exploring-elf-files-and-symbol-tables&quot;&gt;Exploring ELF Files and Symbol Tables&lt;/h2&gt;

&lt;p&gt;Symbol lookup, or symbol resolution, means that we will no longer have to specify raw addresses for breakpoints.  Instead, our users (and tests) can set breakpoints in “twelve::main” or “loop::foo”.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; will do the work of figuring out what the address is for the breakpoint and then once the address is known the breakpoint can be set and handled the same way we have been doing this entire time.&lt;/p&gt;

&lt;p&gt;For reference, I’m starting at commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;c8fc68e&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, it turns out there are a lot of different symbol tables available.  There are dynamic tables used for dynamic linking, static tables, and debug information.  For this post we will work with the norm symbol table.  Here’s an example of what I mean taken from one of our inferiors.  Our inferior (all programs) gets compiled and linked into an object file or some type.  The format used on Linux is usually ELF or some variant of ELF.  There’s a handy tool called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readelf&lt;/code&gt; that can show us the content of an ELF object file.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readelf -S&lt;/code&gt; shows the different sections of the ELF file.  Below is the output after running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;readelf -S&lt;/code&gt; on our inferior named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;twelve&lt;/code&gt; and it gives a taste of what we are looking at.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;readelf -S twelve
There are 41 section headers, starting at offset 0x37f610:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000350  00000350
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.pr[...] NOTE             0000000000000370  00000370
       0000000000000020  0000000000000000   A       0     0     8
  [ 3] .note.gnu.bu[...] NOTE             0000000000000390  00000390
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             00000000000003b4  000003b4
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003d8  000003d8
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           0000000000000400  00000400
       0000000000000660  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           0000000000000a60  00000a60
       0000000000000412  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           0000000000000e72  00000e72
       0000000000000088  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          0000000000000f00  00000f00
       0000000000000110  0000000000000000   A       7     3     8
  [10] .rela.dyn         RELA             0000000000001010  00001010
       0000000000004740  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             0000000000005750  00005750
       0000000000000030  0000000000000018  AI       6    28     8
  [12] .init             PROGBITS         0000000000006000  00006000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000006020  00006020
       0000000000000030  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000006050  00006050
       0000000000000008  0000000000000008  AX       0     0     8
  [15] .text             PROGBITS         0000000000006060  00006060
       000000000003ceeb  0000000000000000  AX       0     0     16
  [16] .fini             PROGBITS         0000000000042f4c  00042f4c
       000000000000000d  0000000000000000  AX       0     0     4
  [17] .rodata           PROGBITS         0000000000043000  00043000
       0000000000005550  0000000000000000   A       0     0     16
  [18] .debug_gdb_s[...] PROGBITS         0000000000048550  00048550
       0000000000000022  0000000000000001 AMS       0     0     1
  [19] .eh_frame_hdr     PROGBITS         0000000000048574  00048574
       0000000000000fdc  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000049550  00049550
       0000000000005700  0000000000000000   A       0     0     8
  [21] .gcc_except_table PROGBITS         000000000004ec50  0004ec50
       0000000000000fac  0000000000000000   A       0     0     4
  [22] .tdata            PROGBITS         0000000000050fe8  0004ffe8
       0000000000000020  0000000000000000 WAT       0     0     8
  [23] .tbss             NOBITS           0000000000051008  00050008
       0000000000000040  0000000000000000 WAT       0     0     8
  [24] .init_array       INIT_ARRAY       0000000000051008  00050008
       0000000000000010  0000000000000008  WA       0     0     8
  [25] .fini_array       FINI_ARRAY       0000000000051018  00050018
       0000000000000008  0000000000000008  WA       0     0     8
  [26] .data.rel.ro      PROGBITS         0000000000051020  00050020
       00000000000025a8  0000000000000000  WA       0     0     8
  [27] .dynamic          DYNAMIC          00000000000535c8  000525c8
       0000000000000210  0000000000000010  WA       7     0     8
  [28] .got              PROGBITS         00000000000537d8  000527d8
       0000000000000828  0000000000000008  WA       0     0     8
  [29] .data             PROGBITS         0000000000054000  00053000
       0000000000000978  0000000000000000  WA       0     0     8
  [30] .bss              NOBITS           0000000000054978  00053978
       00000000000000e8  0000000000000000  WA       0     0     8
  [31] .comment          PROGBITS         0000000000000000  00053978
       0000000000000057  0000000000000001  MS       0     0     1
  [32] .debug_aranges    PROGBITS         0000000000000000  000539cf
       00000000000074c0  0000000000000000           0     0     1
  [33] .debug_info       PROGBITS         0000000000000000  0005ae8f
       00000000000fae6f  0000000000000000           0     0     1
  [34] .debug_abbrev     PROGBITS         0000000000000000  00155cfe
       00000000000010f2  0000000000000000           0     0     1
  [35] .debug_line       PROGBITS         0000000000000000  00156df0
       000000000006830e  0000000000000000           0     0     1
  [36] .debug_str        PROGBITS         0000000000000000  001bf0fe
       0000000000147907  0000000000000001  MS       0     0     1
  [37] .debug_ranges     PROGBITS         0000000000000000  00306a05
       00000000000672c0  0000000000000000           0     0     1
  [38] .symtab           SYMTAB           0000000000000000  0036dcc8
       0000000000004ae8  0000000000000018          39   461     8
  [39] .strtab           STRTAB           0000000000000000  003727b0
       000000000000ccbb  0000000000000000           0     0     1
  [40] .shstrtab         STRTAB           0000000000000000  0037f46b
       000000000000019e  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), l (large), p (processor specific)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are 40 sections here, the sections are things like .symtab which is the symbol table we will be looking at, .strtab which holds string that can be referenced in other sections (reused strings only get stored once), .data which holds an initialized image of all the global variables.  .bss which represents uninitialized (or zeroed) global data but usually doesn’t occupy any space in the object file.  The size for .bss given as 0xe8 bytes is likely the size of the section header.  There are also .dynsym and .dynstr which are similar to .symtab and .strtab but are used for dynamic linking.&lt;/p&gt;

&lt;p&gt;We can take a look at the symbol table using the -s option, though it prints with mangled names.  We can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--demangle=rust&lt;/code&gt; option to get nice readable symbols.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ readelf -s --demangle=rust twelve | head

Symbol table &apos;.dynsym&apos; contains 68 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [...]@GLIBC_2.2.5 (2)
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [...]@GLIBC_2.2.5 (2)
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free@GLIBC_2.2.5 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _[...]@GLIBC_2.34 (3)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND abort@GLIBC_2.2.5 (2)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _Unw[...]@GCC_3.3 (4)&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are a lot of symbols and it seems that readelf has opted to give us the dynamic symbol table (.dynsym) this may be because our executable is linked PIE and uses, effectively, dynamic loading or at least relocation by default.&lt;/p&gt;

&lt;p&gt;We can search for “main” to find:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;$ readelf -s --demangle=rust twelve | grep main
   381: 0000000000007820    12 FUNC    LOCAL  DEFAULT   15 twelve::main[...]
   692: 0000000000007830    32 FUNC    GLOBAL DEFAULT   15 main&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now this is all fine and good but we don’t really want to exec readelf and grep from within &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt;.  Instead, there is a crate for rust called &lt;a href=&quot;https://docs.rs/object/latest/object/read/index.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object&lt;/code&gt;&lt;/a&gt; that can read the object files for us.&lt;/p&gt;

&lt;h2 id=&quot;using-the-object-crate-for-symbol-resolution&quot;&gt;Using the object Crate for Symbol Resolution&lt;/h2&gt;

&lt;p&gt;Taking a look at the object create I see that the read module provides a unified &lt;a href=&quot;https://docs.rs/object/latest/object/read/index.html&quot;&gt;read api&lt;/a&gt; with a nice example which lists the sections.  It specifically has &lt;a href=&quot;https://docs.rs/object/latest/object/read/struct.SymbolTable.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SymbolTable&lt;/code&gt;&lt;/a&gt; with a symbols iterator - this might be what we want.  Looking deeper, it has &lt;a href=&quot;https://docs.rs/object/latest/object/read/trait.Object.html#method.symbol_by_name&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;symbol_by_name&lt;/code&gt;&lt;/a&gt;.  This is perfect. And of course there is &lt;a href=&quot;https://docs.rs/object/latest/object/read/struct.Symbol.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Symbol&lt;/code&gt;&lt;/a&gt; with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;name&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;address&lt;/code&gt;  Let’s put this into action.&lt;/p&gt;

&lt;h3 id=&quot;tests-for-symbol-resolution&quot;&gt;Tests for Symbol Resolution&lt;/h3&gt;

&lt;p&gt;Instead of writing a new test, instead we’ll update the existing tests to set breakpoints by address.  To pass the tests:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Add the object crate to our build.&lt;/li&gt;
  &lt;li&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_exec&lt;/code&gt; following the example from the unified read API and load the inferior file as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Store the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object&lt;/code&gt; in the inferior.&lt;/li&gt;
  &lt;li&gt;When setting breakpoints&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I guess this leaves us with the question of supporting setting breakpoints by address?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If do this it should be a different interface that takes a proper &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InferiorPointer&lt;/code&gt;.
    &lt;ul&gt;
      &lt;li&gt;Oh it actually takes a u64 which is basically &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InferiorPointer&lt;/code&gt;.  I guess I couldn’t play so fast and loose with the types in Rust, in C it takes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;char *&lt;/code&gt; which I could no interpret as a string.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Tests for this would be very fragile, as we’ve seen already.  I suppose the test could use object to look up the address and then pass in that address to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; but that feels a little bit like overkill.&lt;/li&gt;
  &lt;li&gt;If this is really for educational purposes then I think we don’t need this feature as we have learned this already.  But if you want to be able to set breakpoints on raw addresses you should know how to implement it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are the updated tests:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   tests/lib.rs
@@ -17,7 +17,7 @@&lt;/span&gt; fn it_can_set_breakpoints() {
 
     let inferior = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;[]).unwrap();
     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let (inferior, bp) = rusty_trap::trap_inferior_set_breakpoint(inferior, 0x000055555555b821);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let (inferior, bp) = rusty_trap::trap_inferior_set_breakpoint(inferior, &quot;twelve::main&quot;);
&lt;/span&gt;     let (_, _) = rusty_trap::trap_inferior_continue(inferior, |passed_inferior, passed_bp| {
         assert_eq!(passed_inferior.pid, expected_pid);
         assert_eq!(passed_bp, bp);
&lt;span class=&quot;p&quot;&gt;@@ -33,7 +33,7 @@&lt;/span&gt; fn it_can_handle_a_breakpoint_more_than_once() {
 
     let inferior = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/loop&quot;), &amp;amp;[]).unwrap();
     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let (inferior, bp) = rusty_trap::trap_inferior_set_breakpoint(inferior, ADDRESS_OF_FOO);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let (inferior, bp) = rusty_trap::trap_inferior_set_breakpoint(inferior, &quot;loop::foo&quot;);
&lt;/span&gt;     rusty_trap::trap_inferior_continue(inferior, |passed_inferior, passed_bp| {
         assert_eq!(passed_inferior.pid, expected_pid);
         assert_eq!(passed_bp, bp);
&lt;span class=&quot;p&quot;&gt;@@ -50,8 +50,8 @@&lt;/span&gt; fn it_can_handle_more_than_one_breakpoint() {
 
     let inferior = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/loop&quot;), &amp;amp;[]).unwrap();
     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let (inferior, bp_main) = rusty_trap::trap_inferior_set_breakpoint(inferior, ADDRESS_OF_MAIN);
-    let (inferior, bp_foo) = rusty_trap::trap_inferior_set_breakpoint(inferior, ADDRESS_OF_FOO);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let (inferior, bp_main) = rusty_trap::trap_inferior_set_breakpoint(inferior, &quot;loop::main&quot;);
+    let (inferior, bp_foo) = rusty_trap::trap_inferior_set_breakpoint(inferior, &quot;loop::foo&quot;);
&lt;/span&gt;     let (_, _) = rusty_trap::trap_inferior_continue(inferior, |passed_inferior, passed_bp| {
         assert_eq!(passed_inferior.pid, expected_pid);
         if passed_bp == bp_main {&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;When we run the tests the fail, as expected:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;   Compiling rusty_trap v0.1.0 (/home/jkain/projects/rusty_trap)
error[E0308]: mismatched types
  --&amp;gt; tests/lib.rs:20:77
   |
20 |     let (inferior, bp) = rusty_trap::trap_inferior_set_breakpoint(inferior, &quot;twelve::main&quot;);
   |                          ----------------------------------------           ^^^^^^^^^^^^^^ expected `u64`, found `&amp;amp;str`
   |                          |
   |                          arguments to this function are incorrect
   |
note: function defined here
  --&amp;gt; /home/jkain/projects/rusty_trap/src/breakpoint/mod.rs:71:8
   |
71 | pub fn trap_inferior_set_breakpoint(
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
  --&amp;gt; tests/lib.rs:36:77
   |
36 |     let (inferior, bp) = rusty_trap::trap_inferior_set_breakpoint(inferior, &quot;loop::foo&quot;);
   |                          ----------------------------------------           ^^^^^^^^^^^ expected `u64`, found `&amp;amp;str`
   |                          |
   |                          arguments to this function are incorrect
   |
note: function defined here
  --&amp;gt; /home/jkain/projects/rusty_trap/src/breakpoint/mod.rs:71:8
   |
71 | pub fn trap_inferior_set_breakpoint(
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
  --&amp;gt; tests/lib.rs:53:82
   |
53 |     let (inferior, bp_main) = rusty_trap::trap_inferior_set_breakpoint(inferior, &quot;loop::main&quot;);
   |                               ----------------------------------------           ^^^^^^^^^^^^ expected `u64`, found `&amp;amp;str`
   |                               |
   |                               arguments to this function are incorrect
   |
note: function defined here
  --&amp;gt; /home/jkain/projects/rusty_trap/src/breakpoint/mod.rs:71:8
   |
71 | pub fn trap_inferior_set_breakpoint(
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
  --&amp;gt; tests/lib.rs:54:81
   |
54 |     let (inferior, bp_foo) = rusty_trap::trap_inferior_set_breakpoint(inferior, &quot;loop::foo&quot;);
   |                              ----------------------------------------           ^^^^^^^^^^^ expected `u64`, found `&amp;amp;str`
   |                              |
   |                              arguments to this function are incorrect
   |
note: function defined here
  --&amp;gt; /home/jkain/projects/rusty_trap/src/breakpoint/mod.rs:71:8
   |
71 | pub fn trap_inferior_set_breakpoint(
   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `rusty_trap` (test &quot;lib&quot;) due to 4 previous errors&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;passing-symbol-names-through-rusty_trap&quot;&gt;Passing symbol names through rusty_trap&lt;/h3&gt;

&lt;p&gt;So, it’s not surprising we need to update the API to take a string with the symbols name.  But as we do this let’s rename the current &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_set_breakpoint&lt;/code&gt; because ultimately we will end up setting a breakpoint at an address, that’s just how this works, so we want to keep this functionality.  I’ll call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;set_breakpoint_at_address&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -68,7 +68,7 @@&lt;/span&gt; where
     };
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_set_breakpoint(
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn set_breakpoint_at_address(
&lt;/span&gt;     mut inferior: TrapInferior,
     location: u64,
 ) -&amp;gt; (TrapInferior, TrapBreakpoint) {
&lt;span class=&quot;p&quot;&gt;@@ -91,3 +91,11 @@&lt;/span&gt; pub fn trap_inferior_set_breakpoint(
 
     (inferior, InferiorPointer(location))
 }
&lt;span class=&quot;gi&quot;&gt;+
+pub fn trap_inferior_set_breakpoint(
+    mut inferior: TrapInferior,
+    location: &amp;amp;str,
+) -&amp;gt; (TrapInferior, TrapBreakpoint) {
+    let address: u64 = 0x55555555b9f4;
+    return set_breakpoint_at_address(inferior, address);
+}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So I have a placeholder address in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_set_breakpoint&lt;/code&gt;.  And of course that causes the test to fail.  So, now we need to follow our plan and use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object&lt;/code&gt; crate.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;Add the object crate to our build.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   Cargo.toml
@@ -6,6 +6,7 @@&lt;/span&gt; authors = [&quot;Joseph Kain &amp;lt;joekain@gmail.com&amp;gt;&quot;]
 [dependencies]
 libc = &quot;0.2.174&quot;
 nix = {version = &quot;0.30.1&quot;, features = [&quot;process&quot;, &quot;ptrace&quot;, &quot;signal&quot;]}
&lt;span class=&quot;gi&quot;&gt;+object = &quot;0.37&quot;
&lt;/span&gt; 
 [lib]
 name = &quot;rusty_trap&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Our next steps are:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_exec&lt;/code&gt; following the example from the unified read API and load the inferior file as an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Store the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Object&lt;/code&gt; in the inferior.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;This should be, easy, right?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/inferior/mod.rs
@@ -4,6 +4,9 @@&lt;/span&gt; use libc::pid_t;
 use std::collections::HashMap;
 use std::fmt;
 use std::ops::{Add, Sub};
&lt;span class=&quot;gi&quot;&gt;+use object;
+use std::fs;
+use std::path::Path;
&lt;/span&gt; 
 #[derive(Copy, Clone)]
 pub enum InferiorState {
&lt;span class=&quot;p&quot;&gt;@@ -17,14 +20,17 @@&lt;/span&gt; pub struct Inferior {
     pub pid: pid_t,
     pub state: InferiorState,
     pub breakpoints: HashMap&amp;lt;InferiorPointer, Breakpoint&amp;gt;,
&lt;span class=&quot;gi&quot;&gt;+    obj: object::File,
&lt;/span&gt; }
 
 impl Inferior {
&lt;span class=&quot;gd&quot;&gt;-    pub fn new(pid: pid_t) -&amp;gt; Inferior {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    pub fn new(pid: pid_t, path: &amp;amp;Path) -&amp;gt; Inferior {
+	let data = fs::read(path).unwrap();
&lt;/span&gt;         Inferior {
             pid,
             state: InferiorState::Stopped,
             breakpoints: HashMap::new(),
&lt;span class=&quot;gi&quot;&gt;+	    obj: object::File::parse(&amp;amp;*data).unwrap(),
&lt;/span&gt;         }
     }
 }
&lt;span class=&quot;p&quot;&gt;modified   src/lib.rs
@@ -1,5 +1,6 @@&lt;/span&gt;
 extern crate libc;
 extern crate nix;
&lt;span class=&quot;gi&quot;&gt;+extern crate object;
&lt;/span&gt; 
 use libc::pid_t;
 use nix::sys::wait::*;
&lt;span class=&quot;p&quot;&gt;@@ -45,10 +46,10 @@&lt;/span&gt; fn exec_inferior(filename: &amp;amp;Path, _args: &amp;amp;[&amp;amp;str]) {
     unreachable!();
 }
 
&lt;span class=&quot;gd&quot;&gt;-fn attach_inferior(raw_pid: pid_t) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn attach_inferior(raw_pid: pid_t, filename: &amp;amp;Path) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
&lt;/span&gt;     let nix_pid = Pid::from_raw(raw_pid);
     match waitpid(nix_pid, None) {
&lt;span class=&quot;gd&quot;&gt;-        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(Inferior::new(pid.into())),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(Inferior::new(pid.into(), filename)),
&lt;/span&gt;         Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in attach_inferior&quot;),
         Err(e) =&amp;gt; Err(e),
     }
&lt;span class=&quot;p&quot;&gt;@@ -61,7 +62,7 @@&lt;/span&gt; pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;Inferior, Er
                 exec_inferior(filename, args);
                 unreachable!();
             }
&lt;span class=&quot;gd&quot;&gt;-            Ok(ForkResult::Parent { child: pid }) =&amp;gt; return attach_inferior(pid.into()),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            Ok(ForkResult::Parent { child: pid }) =&amp;gt; return attach_inferior(pid.into(), filename),
&lt;/span&gt;             Err(Error::EAGAIN) =&amp;gt; continue,
             Err(e) =&amp;gt; return Err(e),
         }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Lifetimes?  What is this about &lt;a href=&quot;https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html&quot;&gt;lifetimes&lt;/a&gt;?&lt;/p&gt;

&lt;h2 id=&quot;lifetimes-and-self-referential-structs&quot;&gt;Lifetimes and Self-Referential Structs&lt;/h2&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0106]: missing lifetime specifier
  --&amp;gt; src/inferior/mod.rs:23:18
   |
23 |     obj: object::File,
   |                  ^^^^ expected named lifetime parameter
   |
help: consider introducing a named lifetime parameter
   |
19 ~ pub struct Inferior&amp;lt;&apos;a&amp;gt; {
20 |     pub pid: pid_t,
21 |     pub state: InferiorState,
22 |     pub breakpoints: HashMap&amp;lt;InferiorPointer, Breakpoint&amp;gt;,
23 ~     obj: object::File&amp;lt;&apos;a&amp;gt;,&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, I don’t really understand lifetimes that well.  Well, I understand what lifetimes are conceptually: when object A references object B then B must live longer that A, otherwise when B goes out of scope object A would be left with an invalid or dangling reference.  And in our particular case I know that I want this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File&lt;/code&gt; to live as long as the inferior.  I just don’t have a good understanding of the Rust syntax.  That said, I can follow the compiler’s suggestion.&lt;/p&gt;

&lt;p&gt;But, I think I can understand that the compiler is saying what I just said I wanted.  The lifetime of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; and of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File&lt;/code&gt; are the same and we express that as lifetime &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Fixing this just unlocks follow on errors:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0726]: implicit elided lifetime not allowed here
  --&amp;gt; src/inferior/mod.rs:26:6
   |
26 | impl Inferior {
   |      ^^^^^^^^ expected lifetime parameter
   |
help: indicate the anonymous lifetime
   |
26 | impl Inferior&amp;lt;&apos;_&amp;gt; {
   |              ++++

error[E0106]: missing lifetime specifier
  --&amp;gt; src/inferior/mod.rs:38:25
   |
38 | pub type TrapInferior = Inferior;
   |                         ^^^^^^^^ expected named lifetime parameter
   |
help: consider introducing a named lifetime parameter
   |
38 | pub type TrapInferior&amp;lt;&apos;a&amp;gt; = Inferior&amp;lt;&apos;a&amp;gt;;
   |                      ++++           ++++

error[E0106]: missing lifetime specifier
  --&amp;gt; src/breakpoint/mod.rs:34:84
   |
34 | fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;Inferior) -&amp;gt; Option&amp;lt;&amp;amp;Breakpoint&amp;gt; {
   |                                                               ---------            ^ expected named lifetime parameter
   |
   = help: this function&apos;s return type contains a borrowed value, but the signature does not say which one of `inf`&apos;s 2 lifetimes it is borrowed from
help: consider introducing a named lifetime parameter
   |
34 | fn find_breakpoint_matching_inferior_instruction_pointer&amp;lt;&apos;a&amp;gt;(inf: &amp;amp;&apos;a Inferior&amp;lt;&apos;a&amp;gt;) -&amp;gt; Option&amp;lt;&amp;amp;&apos;a Breakpoint&amp;gt; {
   |                                                         ++++       ++         ++++             ++

error[E0106]: missing lifetime specifier
  --&amp;gt; src/lib.rs:58:69
   |
58 | pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
   |                                     -----        -------            ^^^^^^^^ expected named lifetime parameter
   |
   = help: this function&apos;s return type contains a borrowed value, but the signature does not say whether it is borrowed from `filename` or one of `args`&apos;s 2 lifetimes
help: consider introducing a named lifetime parameter
   |
58 | pub fn trap_inferior_exec&amp;lt;&apos;a&amp;gt;(filename: &amp;amp;&apos;a Path, args: &amp;amp;&apos;a [&amp;amp;&apos;a str]) -&amp;gt; Result&amp;lt;Inferior&amp;lt;&apos;a&amp;gt;, Error&amp;gt; {
   |                          ++++            ++              ++   ++                         ++++

error[E0277]: the trait bound `object::File&amp;lt;&apos;_, &amp;amp;[u8]&amp;gt;: Clone` is not satisfied
  --&amp;gt; src/inferior/mod.rs:23:5
   |
18 | #[derive(Clone)]
   |          ----- in this derive macro expansion
...
23 |     obj: object::File&amp;lt;&apos;a&amp;gt;,
   |     ^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `object::File&amp;lt;&apos;_, &amp;amp;[u8]&amp;gt;`

error[E0515]: cannot return value referencing local variable `data`
  --&amp;gt; src/inferior/mod.rs:29:9
   |
29 | /         Inferior {
30 | |             pid,
31 | |             state: InferiorState::Stopped,
32 | |             breakpoints: HashMap::new(),
33 | |             obj: object::File::parse(&amp;amp;*data).unwrap(),
   | |                                        ---- `data` is borrowed here
34 | |         }
   | |_________^ returns a value referencing data owned by the current function&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;A couple of these stand out as something more than just propagating the lifetime annotation.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0515]: cannot return value referencing local variable `data`
  --&amp;gt; src/inferior/mod.rs:29:9
   |
29 | /         Inferior {
30 | |             pid,
31 | |             state: InferiorState::Stopped,
32 | |             breakpoints: HashMap::new(),
33 | |             obj: object::File::parse(&amp;amp;*data).unwrap(),
   | |                                        ---- `data` is borrowed here
34 | |         }
   | |_________^ returns a value referencing data owned by the current function&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, so the data is read outside of setting up the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; struct and then discarded when &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior::new&lt;/code&gt; returns.  I guess it is borrowed into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File::parse&lt;/code&gt;?  Need to fix that.  And the second error that stands out is:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0106]: missing lifetime specifier
  --&amp;gt; src/inferior/mod.rs:38:25
   |
38 | pub type TrapInferior = Inferior;
   |                         ^^^^^^^^ expected named lifetime parameter&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I guess on one hand this is just propagation of the lifetime parameter, but on the other this is a leftover wart from some prior refactoring.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; used to be just a handle that we returned to the user and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; was the real struct.  Now there is no handle and we return the real struct to the user.  And this was a hack to avoid renaming everything.  I want to clean this up.  Ideally, I would clean this up before making the changes the required addition of lifetimes so that I could compile successfully to verify the rename is correct.  I guess I’ll back up and do that first.  The way I do this is to:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;save the commit so I can cherry-pick it later: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f3f9a19&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;reset back to origin/inferior-ownership-api (starting point for this post)&lt;/li&gt;
  &lt;li&gt;Verify that build is good&lt;/li&gt;
  &lt;li&gt;Rename all occurrences&lt;/li&gt;
  &lt;li&gt;Verify that the build is still good&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, this was just a straightforward search and replace.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -31,13 +31,13 @@&lt;/span&gt; fn set(inferior: &amp;amp;TrapInferior, bp: &amp;amp;Breakpoint) {
     poke_text(inferior.pid, bp.aligned_address, modified);
 }
 
&lt;span class=&quot;gd&quot;&gt;-fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;Inferior) -&amp;gt; Option&amp;lt;&amp;amp;Breakpoint&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;TrapInferior) -&amp;gt; Option&amp;lt;&amp;amp;Breakpoint&amp;gt; {
&lt;/span&gt;     let InferiorPointer(ip) = get_instruction_pointer(inf.pid);
     let ip = InferiorPointer(ip - 1);
     return inf.breakpoints.get(&amp;amp;ip);
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn handle&amp;lt;F&amp;gt;(inferior: &amp;amp;mut Inferior, callback: &amp;amp;mut F) -&amp;gt; InferiorState
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn handle&amp;lt;F&amp;gt;(inferior: &amp;amp;mut TrapInferior, callback: &amp;amp;mut F) -&amp;gt; InferiorState
&lt;/span&gt; where
     F: FnMut(&amp;amp;TrapInferior, TrapBreakpoint),
 {
&lt;span class=&quot;p&quot;&gt;modified   src/inferior/mod.rs
@@ -13,15 +13,15 @@&lt;/span&gt; pub enum InferiorState {
 }
 
 #[derive(Clone)]
&lt;span class=&quot;gd&quot;&gt;-pub struct Inferior {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub struct TrapInferior {
&lt;/span&gt;     pub pid: pid_t,
     pub state: InferiorState,
     pub breakpoints: HashMap&amp;lt;InferiorPointer, Breakpoint&amp;gt;,
 }
 
&lt;span class=&quot;gd&quot;&gt;-impl Inferior {
-    pub fn new(pid: pid_t) -&amp;gt; Inferior {
-        Inferior {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+impl TrapInferior {
+    pub fn new(pid: pid_t) -&amp;gt; TrapInferior {
+        TrapInferior {
&lt;/span&gt;             pid,
             state: InferiorState::Stopped,
             breakpoints: HashMap::new(),
&lt;span class=&quot;p&quot;&gt;@@ -29,8 +29,6 @@&lt;/span&gt; impl Inferior {
     }
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub type TrapInferior = Inferior;
-
&lt;/span&gt; #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Hash)]
 pub struct InferiorPointer(pub u64);
 impl InferiorPointer {
&lt;span class=&quot;p&quot;&gt;modified   src/lib.rs
@@ -45,16 +45,16 @@&lt;/span&gt; fn exec_inferior(filename: &amp;amp;Path, _args: &amp;amp;[&amp;amp;str]) {
     unreachable!();
 }
 
&lt;span class=&quot;gd&quot;&gt;-fn attach_inferior(raw_pid: pid_t) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn attach_inferior(raw_pid: pid_t) -&amp;gt; Result&amp;lt;TrapInferior, Error&amp;gt; {
&lt;/span&gt;     let nix_pid = Pid::from_raw(raw_pid);
     match waitpid(nix_pid, None) {
&lt;span class=&quot;gd&quot;&gt;-        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(Inferior::new(pid.into())),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(TrapInferior::new(pid.into())),
&lt;/span&gt;         Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in attach_inferior&quot;),
         Err(e) =&amp;gt; Err(e),
     }
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;TrapInferior, Error&amp;gt; {
&lt;/span&gt;     loop {
         match unsafe { fork() } {
             Ok(ForkResult::Child) =&amp;gt; {&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The build looks good and the tests pass.&lt;/p&gt;

&lt;p&gt;Now, I did this in a side branch and I think I can just rebase my current changes on that branch.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot; data-lang=&quot;shell&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git checkout object-files
Switched to branch &lt;span class=&quot;s1&quot;&gt;&apos;object-files&apos;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;git rebase one-trap-inferior-type
Auto-merging src/inferior/mod.rs
CONFLICT &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: Merge conflict &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;src/inferior/mod.rs
Auto-merging src/lib.rs
CONFLICT &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;content&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;: Merge conflict &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;src/lib.rs
error: could not apply f3f9a19... lifetimes?
hint: Resolve all conflicts manually, mark them as resolved with
hint: &lt;span class=&quot;s2&quot;&gt;&quot;git add/rm &amp;lt;conflicted_files&amp;gt;&quot;&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;then &lt;/span&gt;run &lt;span class=&quot;s2&quot;&gt;&quot;git rebase --continue&quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
hint: You can instead skip this commit: run &lt;span class=&quot;s2&quot;&gt;&quot;git rebase --skip&quot;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
hint: To abort and get back to the state before &lt;span class=&quot;s2&quot;&gt;&quot;git rebase&quot;&lt;/span&gt;, run &lt;span class=&quot;s2&quot;&gt;&quot;git rebase --abort&quot;&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, so let’s fix the merge conflicts starting with inferior/mod.rs:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[derive(Clone)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HEAD&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=======&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f3f9a19&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lifetimes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, so we want the bottom version which we updated with the Path and we need to change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then the conflict in src/lib.rs:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HEAD&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;attach_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nix_pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nix_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SIGTRAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())),&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;=======&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;attach_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nix_pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;raw_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nix_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SIGTRAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;f3f9a19&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lifetimes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop in attach_inferior&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Again, I want the bottom version that has the filename handling and I’ll have to do the rename.&lt;/p&gt;

&lt;p&gt;Ok, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;git rebase --continue&lt;/code&gt; and we are done.  Note this does not build right now which is to be expected.  We are now back to working on getting the lifetimes right.  And it’s pretty much the list of errors we had before minus needing a lifetime on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; type alias as we have just removed the alias altogether.&lt;/p&gt;

&lt;p&gt;So I want to try to address the one that seems most important first.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0515]: cannot return value referencing local variable `data`
  --&amp;gt; src/inferior/mod.rs:29:9
   |
29 | /         TrapInferior {
30 | |             pid,
31 | |             state: InferiorState::Stopped,
32 | |             breakpoints: HashMap::new(),
33 | |             obj: object::File::parse(&amp;amp;*data).unwrap(),
   | |                                        ---- `data` is borrowed here
34 | |         }
   | |_________^ returns a value referencing data owned by the current function&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I ended up spending a lot of of time working on this after writing the above part of the blog.  And this is more of a rabbit hole that I expected.  At first I wasn’t quite sure what was going on.  But it turns out that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File::parse&lt;/code&gt; just builds up references to parts of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; and uses that to provide the sections.  It’s zero-copy.  Cool, that’s efficient.  But it means I have to keep the data around for the lifetime of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My first inclination was to just keep the data in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt;like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/inferior/mod.rs
@@ -21,16 +21,19 @@&lt;/span&gt; pub struct TrapInferior&amp;lt;&apos;a&amp;gt; {
     pub state: InferiorState,
     pub breakpoints: HashMap&amp;lt;InferiorPointer, Breakpoint&amp;gt;,
     obj: object::File&amp;lt;&apos;a&amp;gt;,
&lt;span class=&quot;gi&quot;&gt;+    data: Vec&amp;lt;u8&amp;gt;,
&lt;/span&gt; }
 
 impl TrapInferior {
     pub fn new(pid: pid_t, path: &amp;amp;Path) -&amp;gt; TrapInferior {
 	let data = fs::read(path).unwrap();
&lt;span class=&quot;gi&quot;&gt;+	let obj = object::File::parse(&amp;amp;*data).unwrap();
&lt;/span&gt;         TrapInferior {
             pid,
             state: InferiorState::Stopped,
             breakpoints: HashMap::new(),
&lt;span class=&quot;gd&quot;&gt;-            obj: object::File::parse(&amp;amp;*data).unwrap(),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            obj,
+	    data,
&lt;/span&gt;         }
     }
 }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, there are a bunch of problems with this.  The first, is that I still have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; and now also &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj&lt;/code&gt; on the stack in this function so I can’t return them.&lt;/p&gt;

&lt;p&gt;Now, it’s hard to try to get them into the struct together but even if I could it wouldn’t work.  I didn’t know this but Rust does not support self-referential structs.  That is, one field in a struct can’t contain a reference to another field.  I found this both by asking GitHub Copilot and from this &lt;a href=&quot;https://stackoverflow.com/a/25415689&quot;&gt;Stack Overflow answer&lt;/a&gt;.  To summarize, the reason Rust does not support self-referential structs is because they can lead to unsafe memory accesses.  The recommendation for how to solve this is to have the user call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fs::read&lt;/code&gt; and pass in the data.&lt;/p&gt;

&lt;p&gt;Google Gemini proposed an alternative of using the &lt;a href=&quot;https://docs.rs/ouroboros/latest/ouroboros/&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ouroboros&lt;/code&gt; crate&lt;/a&gt; which allows creation of self referential structs.  However, Gemini recommended that having the user call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fs::read&lt;/code&gt; is idiomatic Rust.&lt;/p&gt;

&lt;p&gt;Coming from languages other than Rust this seemed strange to me.  I would favor building an API that takes on the responsibility of reading the file so as to:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Unburden the user from having to do it.&lt;/li&gt;
  &lt;li&gt;Hide the implementation detail of where the data comes from and what format it is in.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reading a file isn’t really a detail that needs to be hidden, I suppose.  But the fact that it matches the input to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File::parse&lt;/code&gt; is.  What if I switched to a different crate one day?  Now, it’s also just a vector of bytes so maybe any object parser would want that.  But maybe it wouldn’t.  In my opinion this couples my API to the implementation a little too tightly.&lt;/p&gt;

&lt;p&gt;Gemini points out that the flip side of burdening the user with responsibility is empowering them with control over the lifetime of these objects.  Which does sound like a good trade.  Gemini provided a number of examples of popular crates that have APIs like this (serde, gimli).&lt;/p&gt;

&lt;p&gt;Gemini goes to say&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This pattern is a cornerstone of a philosophy in the Rust community called “Parse, Don’t Validate.” Instead of repeatedly passing around raw data (like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;[u8]&lt;/code&gt;) and validating it in every function, you parse it &lt;em&gt;once&lt;/em&gt; into a structured type (like your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&amp;lt;&apos;data&amp;gt;&lt;/code&gt;) that &lt;em&gt;guarantees&lt;/em&gt; validity through the type system. The lifetime parameter is the mechanism that makes this zero-copy parsing possible and safe.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That makes sense and I buy into “parse, don’t validate”.  But, one thing about this bothered me: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_exec&lt;/code&gt; is where we are currently passed the binary filename.  And it will need to be updated to take the binary data as well.  So it will look something like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapSession&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This looks great.  Well except for one thing, if I’m going to validate up front and then encode all the validated inputs in the type system then do I need to validate that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filename&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; actually correspond to each other?  That is, how do I validate that the user actually called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fs::read&lt;/code&gt; on the right file?  Or should I validate this?&lt;/p&gt;

&lt;h2 id=&quot;encapsulating-validated-file-names-and-file-data&quot;&gt;Encapsulating Validated File Names and File Data&lt;/h2&gt;

&lt;p&gt;If I really wanted to validate it then that would mean calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fs::read&lt;/code&gt; inside of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_exec&lt;/code&gt; and comparing that the data I read matches the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;data&lt;/code&gt; the user passed in.  This seems inefficient at best and silly at worse.  This is a (proposed) code smell that this is not a good API choice.&lt;/p&gt;

&lt;p&gt;When I asked Gemini it agreed that this was overkill (it’s very agreeable) and then ran off to tell me all about how to implement it.&lt;/p&gt;

&lt;p&gt;So instead of blindly accepting Gemini’s validation code, I sat back and thought a little bit.  Parse, don’t validate–right?  What I would really like is for there to be a type, a struct, that encapsulates both the filename and the data.  And that type should indicate that the two are in correspondence.  I see no reason why such a thing can’t exist.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; should just provide this type and a API to create it.  The type can be created from just the filename and the API can do the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fs::read&lt;/code&gt;.  Then it can return the validated type to the user and the user can in turn pass it (lend) into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_exec&lt;/code&gt;.  The user can control the lifetime of this thing.&lt;/p&gt;

&lt;p&gt;Ok, so let’s build that.  I guess call it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapData&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ideally, I should go back to an earlier commit where tests pass and add this but I’m tired from arguing with Gemini.  Let’s just do it.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;TrapData&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	    &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, let’s see if I can implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; using this.&lt;/p&gt;

&lt;p&gt;Ok, this much of it seems to compile:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[derive(Clone)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapData&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;parse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_data&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Well, actually it doesn’t because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File&lt;/code&gt; doesn’t implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Clone&lt;/code&gt; so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; can’t derive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Clone&lt;/code&gt; but I’ll come back to that later.&lt;/p&gt;

&lt;p&gt;I think the lifetimes here are correct.  For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt;: the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File&lt;/code&gt; lifetime has to be at least as long as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; lifetime as a whole.&lt;/p&gt;

&lt;p&gt;And for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior::new&lt;/code&gt; we require that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapData&lt;/code&gt; live at least as long as the returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; because the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; will reference the data.&lt;/p&gt;

&lt;p&gt;Ok, now we can push this interface change up the call chain.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/lib.rs
@@ -46,23 +46,23 @@&lt;/span&gt; fn exec_inferior(filename: &amp;amp;Path, _args: &amp;amp;[&amp;amp;str]) {
     unreachable!();
 }
 
&lt;span class=&quot;gd&quot;&gt;-fn attach_inferior(raw_pid: pid_t, filename: &amp;amp;Path) -&amp;gt; Result&amp;lt;TrapInferior, Error&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn attach_inferior&amp;lt;&apos;a&amp;gt;(raw_pid: pid_t, data: &amp;amp;&apos;a TrapData) -&amp;gt; Result&amp;lt;TrapInferior&amp;lt;&apos;a&amp;gt;, Error&amp;gt; {
&lt;/span&gt;     let nix_pid = Pid::from_raw(raw_pid);
     match waitpid(nix_pid, None) {
&lt;span class=&quot;gd&quot;&gt;-        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(TrapInferior::new(pid.into(), filename)),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(TrapInferior::new(pid.into(), data)),
&lt;/span&gt;         Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in attach_inferior&quot;),
         Err(e) =&amp;gt; Err(e),
     }
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;TrapInferior, Error&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_exec&amp;lt;&apos;a&amp;gt;(data: &amp;amp;&apos;a TrapData, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;TrapInferior&amp;lt;&apos;a&amp;gt;, Error&amp;gt; {
&lt;/span&gt;     loop {
         match unsafe { fork() } {
             Ok(ForkResult::Child) =&amp;gt; {
&lt;span class=&quot;gd&quot;&gt;-                exec_inferior(filename, args);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+                exec_inferior(data.filename, args);
&lt;/span&gt;                 unreachable!();
             }
&lt;span class=&quot;gd&quot;&gt;-            Ok(ForkResult::Parent { child: pid }) =&amp;gt; return attach_inferior(pid.into(), filename),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            Ok(ForkResult::Parent { child: pid }) =&amp;gt; return attach_inferior(pid.into(), data),
&lt;/span&gt;             Err(Error::EAGAIN) =&amp;gt; continue,
             Err(e) =&amp;gt; return Err(e),
         }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, this was pretty straightfoward and seems ok so far.  There are some other compile errors to fix and who knows if the next compile pass will find some new problems here after that.&lt;/p&gt;

&lt;p&gt;Let’s clean up the leftover errors:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0106]: missing lifetime specifier
  --&amp;gt; src/breakpoint/mod.rs:34:88
   |
34 | fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;TrapInferior) -&amp;gt; Option&amp;lt;&amp;amp;Breakpoint&amp;gt; {
   |                                                               -------------            ^ expected named lifetime parameter
   |
   = help: this function&apos;s return type contains a borrowed value, but the signature does not say which one of `inf`&apos;s 2 lifetimes it is borrowed from
help: consider introducing a named lifetime parameter
   |
34 | fn find_breakpoint_matching_inferior_instruction_pointer&amp;lt;&apos;a&amp;gt;(inf: &amp;amp;&apos;a TrapInferior&amp;lt;&apos;a&amp;gt;) -&amp;gt; Option&amp;lt;&amp;amp;&apos;a Breakpoint&amp;gt; {
   |                                                         ++++       ++             ++++             ++&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, we need some lifetimes here because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; just needs one and the returned &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Breakpoint&lt;/code&gt; reference is a borrow from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inf.breakpoints&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -31,7 +31,7 @@&lt;/span&gt; fn set(inferior: &amp;amp;TrapInferior, bp: &amp;amp;Breakpoint) {
     poke_text(inferior.pid, bp.aligned_address, modified);
 }
 
&lt;span class=&quot;gd&quot;&gt;-fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;TrapInferior) -&amp;gt; Option&amp;lt;&amp;amp;Breakpoint&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn find_breakpoint_matching_inferior_instruction_pointer&amp;lt;&apos;a&amp;gt;(inf: &amp;amp;&apos;a TrapInferior) -&amp;gt; Option&amp;lt;&amp;amp;&apos;a Breakpoint&amp;gt; {
&lt;/span&gt;     let InferiorPointer(ip) = get_instruction_pointer(inf.pid);
     let ip = InferiorPointer(ip - 1);
     return inf.breakpoints.get(&amp;amp;ip);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0106]: missing lifetime specifier
  --&amp;gt; src/breakpoint/mod.rs:98:7
   |
96 |     mut inferior: TrapInferior,
   |                   ------------
97 |     location: &amp;amp;str,
   |               ----
98 | ) -&amp;gt; (TrapInferior, TrapBreakpoint) {
   |       ^^^^^^^^^^^^ expected named lifetime parameter
   |
   = help: this function&apos;s return type contains a borrowed value, but the signature does not say whether it is borrowed from `inferior` or `location`
help: consider introducing a named lifetime parameter
   |
95 ~ pub fn trap_inferior_set_breakpoint&amp;lt;&apos;a&amp;gt;(
96 ~     mut inferior: TrapInferior&amp;lt;&apos;a&amp;gt;,
97 ~     location: &amp;amp;&apos;a str,
98 ~ ) -&amp;gt; (TrapInferior&amp;lt;&apos;a&amp;gt;, TrapBreakpoint) {
   |&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is the nice API where we borrow in and then give back.  So it’s borrowed from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inferior&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_set_breakpoint(
-    mut inferior: TrapInferior,
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_set_breakpoint&amp;lt;&apos;a&amp;gt;(
+    mut inferior: TrapInferior&amp;lt;&apos;a&amp;gt;,
&lt;/span&gt;     location: &amp;amp;str,
&lt;span class=&quot;gd&quot;&gt;-) -&amp;gt; (TrapInferior, TrapBreakpoint) {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+) -&amp;gt; (TrapInferior&amp;lt;&apos;a&amp;gt;, TrapBreakpoint) {
&lt;/span&gt;     let address: u64 = 0x55555555b9f4;
     return set_breakpoint_at_address(inferior, address);
 }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, now we are down to this error about clone:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0277]: the trait bound `object::File&amp;lt;&apos;_, &amp;amp;[u8]&amp;gt;: Clone` is not satisfied
  --&amp;gt; src/inferior/mod.rs:38:5
   |
33 | #[derive(Clone)]
   |          ----- in this derive macro expansion
...
38 |     obj: object::File&amp;lt;&apos;a&amp;gt;,
   |     ^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `object::File&amp;lt;&apos;_, &amp;amp;[u8]&amp;gt;`

Some errors have detailed explanations: E0106, E0277.
For more information about an error, try `rustc --explain E0106`.
error: could not compile `rusty_trap` (lib) due to 3 previous errors
warning: build failed, waiting for other jobs to finish...
error: could not compile `rusty_trap` (lib test) due to 3 previous errors&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, so I guess to start with let me ask: why do I need clone?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;I think it’s because I have something like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So that I’m passing these &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt; objects around without references and they would shallow copy.  As a reminder this is what’s in there:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[derive(Clone)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Before I added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;obj&lt;/code&gt; we could easily clone this because it’s just some ints and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HashMap&lt;/code&gt; that can be cloned.  If I got rid of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Clone&lt;/code&gt; I would have to copy and copying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HashMap&lt;/code&gt; seems like a bad idea.&lt;/p&gt;

&lt;p&gt;So, what if instead I get rid of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Clone&lt;/code&gt; and pass by reference?  I guess let’s try it.&lt;/p&gt;

&lt;p&gt;FWIW just removing clone actually builds (though the tests need some modifications).  But, it’s not efficient so let’s keep fixing (honestly maybe that’s not the best choice in real life - get your tests to pass before optimizing).&lt;/p&gt;

&lt;p&gt;Interesting, because I’m allowed to pass a reference through a non-reference I can’t use the compiler to find all the places I need to update.  But, I think this does it:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -68,10 +68,10 @@&lt;/span&gt; where
     };
 }
 
&lt;span class=&quot;gd&quot;&gt;-fn set_breakpoint_at_address(
-    mut inferior: TrapInferior,
-    location: u64,
-) -&amp;gt; (TrapInferior, TrapBreakpoint) {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn set_breakpoint_at_address&amp;lt;&apos;a&amp;gt;(
+    mut inferior: &amp;amp;&apos;a mut TrapInferior&amp;lt;&apos;a&amp;gt;,
+    location: u64
+) -&amp;gt; (&amp;amp;&apos;a TrapInferior&amp;lt;&apos;a&amp;gt;, TrapBreakpoint) {
&lt;/span&gt;     let aligned_address = location &amp;amp; !0x7u64;
     let target_address = InferiorPointer(location);
     inferior.breakpoints.insert(
&lt;span class=&quot;p&quot;&gt;@@ -93,9 +93,9 @@&lt;/span&gt; fn set_breakpoint_at_address(
 }
 
 pub fn trap_inferior_set_breakpoint&amp;lt;&apos;a&amp;gt;(
&lt;span class=&quot;gd&quot;&gt;-    mut inferior: TrapInferior&amp;lt;&apos;a&amp;gt;,
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    mut inferior: &amp;amp;&apos;a mut TrapInferior&amp;lt;&apos;a&amp;gt;,
&lt;/span&gt;     location: &amp;amp;str,
&lt;span class=&quot;gd&quot;&gt;-) -&amp;gt; (TrapInferior&amp;lt;&apos;a&amp;gt;, TrapBreakpoint) {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+) -&amp;gt; (&amp;amp;&apos;a TrapInferior&amp;lt;&apos;a&amp;gt;, TrapBreakpoint) {
&lt;/span&gt;     let address: u64 = 0x55555555b9f4;
     return set_breakpoint_at_address(inferior, address);
 }
&lt;span class=&quot;p&quot;&gt;modified   src/inferior/mod.rs
@@ -30,7 +30,6 @@&lt;/span&gt; pub enum InferiorState {
     SingleStepping,
 }
 
&lt;span class=&quot;gd&quot;&gt;-#[derive(Clone)]
&lt;/span&gt; pub struct TrapInferior&amp;lt;&apos;a&amp;gt; {
     pub pid: pid_t,
     pub state: InferiorState,
&lt;span class=&quot;p&quot;&gt;modified   src/lib.rs
@@ -69,7 +69,7 @@&lt;/span&gt; pub fn trap_inferior_exec&amp;lt;&apos;a&amp;gt;(data: &amp;amp;&apos;a TrapData, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;TrapI
     }
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_continue&amp;lt;F&amp;gt;(mut inferior: TrapInferior, mut callback: F) -&amp;gt; (TrapInferior, i32)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_continue&amp;lt;&apos;a, F&amp;gt;(mut inferior: &amp;amp;&apos;a mut TrapInferior, mut callback: F) -&amp;gt; (&amp;amp;&apos;a TrapInferior&amp;lt;&apos;a&amp;gt;, i32)
&lt;/span&gt; where
     F: FnMut(&amp;amp;TrapInferior, TrapBreakpoint),
 {&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we just need to fix the tests.  I have a bunch of errors like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0308]: mismatched types
  --&amp;gt; tests/lib.rs:9:51
   |
9  | ... = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;...
   |       ------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&amp;amp;TrapData&amp;lt;&apos;_&amp;gt;`, found `&amp;amp;Path`
   |       |
   |       arguments to this function are incorrect
   |
   = note: expected reference `&amp;amp;rusty_trap::inferior::TrapData&amp;lt;&apos;_&amp;gt;`
              found reference `&amp;amp;Path`
note: function defined here
  --&amp;gt; /home/jkain/projects/rusty_trap/src/lib.rs:58:8
   |
58 | pub fn trap_inferior_exec&amp;lt;&apos;a&amp;gt;(data: &amp;amp;&apos;a TrapData, args: &amp;amp;[&amp;amp;str]) -&amp;gt; ...
   |        ^^^^^^^^^^^^^^^^^^&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There is one of these per test.  We need to fix them all by creating a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapData&lt;/code&gt; and passing that instead of the filename.&lt;/p&gt;

&lt;p&gt;Ok, that’s done.  Now tests compile but fail because they are passing in breakpoint names and we are setting some bogus breakpoint address.  E.g. we’ve finally got our types, lifetimes, and references right and can get back to work on adding symbol table support to our debugger.  Let’s use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File&lt;/code&gt; to look up the addresses for the symbol names:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;&apos;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.obj&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.symbols&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Checking symbol {name} at {symbol_address}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;symbol_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set_breakpoint_at_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;implementing-symbol-resolution&quot;&gt;Implementing Symbol Resolution&lt;/h2&gt;

&lt;p&gt;But this never finds the symbol because the symbols look like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Checking symbol _ZN4loop3foo17hcca06054783c5707E at 0x7990
Checking symbol _ZN4loop4main17h32a7c34928272921E at 0x79a0&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This is the name mangling that I mentioned before.  If you are not familiar with these, they are essentially &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop::foo&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop::main&lt;/code&gt; with additional information encoded such as namespaces, type signatures, and a hash.  This ensures uniqueness (think function overloading).  It’s also worth noting that the address is the small address and doesn’t include the offset where the binary gets loaded in memory (e.g. the 0x55555… we keep seeing).  We’ll need to come back to that.  Demangling is the process of converting back to the human readable names and is useful so we can match the names form the symbol table against the function names where we want to set breakpoints.&lt;/p&gt;

&lt;p&gt;First, we can use the &lt;a href=&quot;https://docs.rs/rustc-demangle/latest/rustc_demangle/&quot;&gt;rustc_demangle&lt;/a&gt; crate to decode symbol names.  I’m just following the example and ignorign the hash.  Ok, this much seems to be working:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Found symbol loop::foo at 0x79c0&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now we need to work on the address.  There are two things really:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;We need to find the offset to add to get the real address.&lt;/li&gt;
  &lt;li&gt;It looks like there is something else a little off in that I thought the lower 3 nibbles should match (the page offest) but they don’t.  We used to have:&lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS_OF_FOO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0x55555555b9e0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let’s start by just taking the offset as a constant and adding it on.  That way we can have passing tests and then we can figure out where to get the offset from properly.  I’m assuming that both (all) inferiors would have the same offset.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;    &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BASE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0x555555554000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the tests pass.  Where did I get this address, well I computed&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;0x55555555b9e0 - 0x79c0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Though this actually works out to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x555555554020&lt;/code&gt; but that address wouldn’t make any sense.  It should aligned to 4KB (4096 or 0x1000 bytes).  So I rounded it down.  Now, why doesn’t the subtraction give this number to being with?  I’m not sure, lihely the inferior has changed since the last time I checked.  Let’s check again by loading the inferior in gdb:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;Breakpoint 1, 0x000055555555ba20 in main ()
(gdb) b loop::main
Breakpoint 2 at 0x55555555b9d4: file tests/inferiors/loop.rs, line 6.
(gdb) b loop::foo
Breakpoint 3 at 0x55555555b9c0: file tests/inferiors/loop.rs, line 3.&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So, it seems they have moved and in fact by 0x20 bytes.  This is rather timely as we’ve just implemented a feature (symbol lookup) that our tests immune to these changes.&lt;/p&gt;

&lt;p&gt;And now when I do the subtraction it works out properly:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;(gdb) p /x 0x55555555b9c0 - 0x79c0
$1 = 0x555555554000&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The tests now pass:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-none&quot; data-lang=&quot;none&quot;&gt;running 4 tests
test it_can_exec ... ok
test it_can_handle_a_breakpoint_more_than_once ... ok
test it_can_set_breakpoints ... ok
test it_can_handle_more_than_one_breakpoint ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;finding-the-base-address&quot;&gt;Finding the Base Address&lt;/h2&gt;

&lt;p&gt;Now that we know that having the correct base address will make our tests pass we need to add support to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; to look it up rather than hardcode it.  I poked around at a few different fields in the object file trying to figure out where to get the load address.  But ultimately, I ended up asking Bing Copilot:&lt;/p&gt;

&lt;p&gt;Me:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;When a PIE binary is loaded into memory on Linux to be executed what is the load address?  Usually there is address space randomization but I have disabled that using the personality flag before exexcing my process because I am building a debugger.  I can find symbol addresses in the ELF object but they need to be offset by the load address.  Where should I find this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Copilot:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;When you compile with -pie, the kernel treats your executable as an ET_DYN object and maps its PT_LOAD segments at a runtime-chosen “load base.” With ASLR disabled (ADDR_NO_RANDOMIZE), the kernel uses a fixed, architecture-specific base (on x86_64 it’s traditionally 0x400000).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Why the load base matters
Symbol values in the ELF file (st_value) are relative to the link-time base (often 0 for ET_DYN). To turn a symbol’s file offset into the actual virtual address, you add the runtime load base—aka the load bias or l_addr.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ok, this is basically matches what I understood.  Note that there is a lot of jargon here.  Let me try to explain:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;PIE binary&lt;/strong&gt;: Position Independent Executable - meaning that the binary can be loaded into any address in memory.  Older styles of object files did not do this for the main executable but did for dynamic libraries.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ET_DYN&lt;/strong&gt;: I think Copilot is saying that the binary gets treated the same way that a dynamic library or other dynamic object would be treated.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;PT_LOAD Segment&lt;/strong&gt;: A segment is a range of an ELF object that gets loaded into memory.  It can contain sections.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PT_LOAD&lt;/code&gt; includes the main binary code sections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyway, I vaguely remember the address 0x400000  being the Linux default load address but that’s clearly not what we are seeing here.  Copilot goes on to give some practical advice:&lt;/p&gt;

&lt;p&gt;Three ways to discover the load base&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;/proc/[pid]/maps&lt;/strong&gt;: Open the target’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/proc/[pid]/maps&lt;/code&gt; and locate the entry whose pathname matches your binary. The start address of that VMA is your load base.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Auxiliary vector (auxv)&lt;/strong&gt;: Read &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/proc/[pid]/auxv&lt;/code&gt; and find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AT_PHDR&lt;/code&gt; entry (pointer to the in-memory program headers). Compute:
    &lt;ul&gt;
      &lt;li&gt;load_base = &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AT_PHDR&lt;/code&gt; − &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ELF_HDR.e_phoff&lt;/code&gt;**&lt;/li&gt;
      &lt;li&gt;where&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; e_phoff&lt;/code&gt; comes straight from your ELF header.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Dynamic loader APIs&lt;/strong&gt;: Use dl_iterate_phdr. In its callback, when dlpi_name[0] == ‘\0’, the field dlpi_addr is the main executable’s base. Alternatively, dladdr(&amp;amp;main, &amp;amp;info) returns info.dli_fbase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copilot goes on to give an example of how to implement the first option in C:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;kt&quot;&gt;uint64_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_load_base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exe_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;uint64_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;snprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;/proc/%d/maps&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;FILE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fopen&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;r&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fgets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strstr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exe_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;sscanf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%lx-&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fclose&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This sounds pretty good.  And we may need the maps file again later if we want to find any other dynamic libraries that are loaded (if we wanted to set breakpoints in them).  Let’s implement a function like this in Rust and then we will call it after we &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptrace&lt;/code&gt; the inferior and it stops for the first time.  That’s our first opportunity to poke around.&lt;/p&gt;

&lt;p&gt;I ended up with this function:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Helper function to look up the base address by inspected /proc/pid/maps&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_base_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proc_filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;format!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/proc/{pid}/maps&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;file&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;File&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proc_filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unable to open file&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;canonicalize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;BufReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.lines&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.contains&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expected&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;addr_str&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;&apos;-&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Found base address 0x{addr_str}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
	    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_str_radix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addr_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This should be an error, there should be error handling.&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Could not find base address for {expected}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;assert!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which in fact finds the address and prints:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Found base address 0x555555554000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now we just use this instead of our hard coded version and find that the tests still pass.&lt;/p&gt;

&lt;p&gt;Wow, that was a lot of work, but we’ve done it.  We’ve implemented symbol look for our inferior and can use it to set breakpoints!  Here’s the final set of changes:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -100,14 +100,12 @@&lt;/span&gt; pub fn trap_inferior_set_breakpoint&amp;lt;&apos;a&amp;gt;(
 ) -&amp;gt; (&amp;amp;&apos;a mut TrapInferior&amp;lt;&apos;a&amp;gt;, TrapBreakpoint) {
     let mut address: u64 = 0;
 
&lt;span class=&quot;gd&quot;&gt;-    const BASE: u64 = 0x555555554000;
-
&lt;/span&gt;     for symbol in inferior.obj.symbols() {
 	let name = format!(&quot;{:#}&quot;, demangle(symbol.name().unwrap()));
 	let symbol_address = symbol.address();
 	if name == location {
 	    println!(&quot;Found symbol {name} at 0x{symbol_address:x}&quot;);
&lt;span class=&quot;gd&quot;&gt;-	    address = symbol_address + BASE;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+	    address = symbol_address + inferior.base_address;
&lt;/span&gt; 	    break;
 	}
     }
&lt;span class=&quot;p&quot;&gt;modified   src/inferior/mod.rs
@@ -7,6 +7,7 @@&lt;/span&gt; use std::ops::{Add, Sub};
 use object;
 use std::fs;
 use std::path::Path;
&lt;span class=&quot;gi&quot;&gt;+use std::io::{BufRead, BufReader};
&lt;/span&gt; 
 
 pub struct TrapData&amp;lt;&apos;a&amp;gt; {
&lt;span class=&quot;p&quot;&gt;@@ -35,6 +36,7 @@&lt;/span&gt; pub struct TrapInferior&amp;lt;&apos;a&amp;gt; {
     pub state: InferiorState,
     pub breakpoints: HashMap&amp;lt;InferiorPointer, Breakpoint&amp;gt;,
     pub obj: object::File&amp;lt;&apos;a&amp;gt;,
&lt;span class=&quot;gi&quot;&gt;+    pub base_address: u64,
&lt;/span&gt; }
 
 impl &amp;lt;&apos;a&amp;gt; TrapInferior&amp;lt;&apos;a&amp;gt; {
&lt;span class=&quot;p&quot;&gt;@@ -44,10 +46,33 @@&lt;/span&gt; impl &amp;lt;&apos;a&amp;gt; TrapInferior&amp;lt;&apos;a&amp;gt; {
             state: InferiorState::Stopped,
             breakpoints: HashMap::new(),
             obj: object::File::parse(&amp;amp;*trap_data.data).unwrap(),
&lt;span class=&quot;gi&quot;&gt;+	    base_address: get_base_address(pid, trap_data.filename),
&lt;/span&gt;         }
     }
 }
 
&lt;span class=&quot;gi&quot;&gt;+// Helper function to look up the base address by inspected /proc/pid/maps
+fn get_base_address(pid: pid_t, filename: &amp;amp;Path) -&amp;gt; u64 {
+    let proc_filename = format!(&quot;/proc/{pid}/maps&quot;);
+    let file = fs::File::open(proc_filename).expect(&quot;Unable to open file&quot;);
+    let expected = fs::canonicalize(filename).unwrap();
+    let expected = expected.to_str().unwrap();
+    let reader = BufReader::new(file);
+    for line in reader.lines() {
+	let line = line.unwrap();
+	if line.contains(expected) {
+	    let addr_str = line.split(&apos;-&apos;).next().unwrap();
+	    println!(&quot;Found base address 0x{addr_str}&quot;);
+	    return u64::from_str_radix(addr_str, 16).unwrap();
+	}
+    }
+    // This should be an error, there should be error handling.
+    println!(&quot;Could not find base address for {expected}&quot;);
+    assert!(false);
+    0
+}
+
+
&lt;/span&gt; #[derive(Copy, Clone, PartialEq, PartialOrd, Debug, Eq, Hash)]
 pub struct InferiorPointer(pub u64);
 impl InferiorPointer {&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I’ve pushed a &lt;a href=&quot;https://github.com/joekain/rusty_trap/pull/7&quot;&gt;pull request for Symbol Lookup in breakpoints&lt;/a&gt;.&lt;/p&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;lessons-learned&quot;&gt;Lessons Learned&lt;/h2&gt;

&lt;p&gt;So, we set out implement symbol resolution and along the way learned about lifetimes, self referential structs, and applied systems thinking to our API design (again).  In the end we made our debugger much more powerful in and less fragile.&lt;/p&gt;

&lt;p&gt;In C, I would have just stuffed everything into one struct, given only a cursory thought to lifetime, and focused on the symbol resolution and breakpoint setting.  Rust forces us to think deeply about systems-level problems and that’s a good thing.  In C I might think about the lifetimes of the various objects and I might conclude that things look good inside of the debugger but it would be hard to convey those requirements back to the user.  Rust makes this explicit and the trade off is that I have to think a little more to make sure it’s going to work both for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_tray&lt;/code&gt; and for the user of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt;.  I have to solve that up front and then I can encode it the type system.  This way I solve it on behalf of all the user as well.  In C each user must independently think through this proble..  In some way this is like code-usability except here it’s lifetime reusablity.  The solution to the lifetime issues for rusty_trap are solved once and reused.  The extra thinking required is a good investment.&lt;/p&gt;

&lt;p&gt;The self referential structs are also a interesting topic.  In C, or maybe C++, I would actually prefer to keep the file data and my uses of the file data together.  They are used together so store them together.  Their lifetimes are coupled so keep them together.  In fact, for my use their lifetimes are basically the same - I only want the file data so I can get an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;object::File&lt;/code&gt; to work with.  I suppose though if I wanted to use the file data for something else then coupling tightly together would be a bad idea.  I am happy though that we were able to build a nice way to insure that the file name and file data were in correspondence and encode that in the type system.&lt;/p&gt;

&lt;p&gt;I like the fact that Rust is pushing my understanding of API design by forcing me to think harder about lifetimes, abstractions, and how information should flow through my systems.&lt;/p&gt;

&lt;p&gt;In our next post we’ll see how Rust can inform our design when we work on supporting multithreaded inferiors in our debugger.&lt;/p&gt;

&lt;div class=&quot;debugger-callout&quot; style=&quot;
	    border: 1px solid #eee;
	    padding: 1em;
	    border-radius: 6px;
	    background-color: #f9f9f9;
	    display: flex;
	    align-items: center;
	    gap: 1em;
	    margin: 2em 0;
&quot;&gt;
  &lt;img src=&quot;/rusty_trap.png&quot; alt=&quot;Rusty trap icon&quot; style=&quot;width: 40px; height: 40px; flex-shrink: 0;&quot; /&gt;
  &lt;div&gt;
    &lt;strong&gt;This post is part of my &lt;em&gt;Writing a Debugger&lt;/em&gt; series.&lt;/strong&gt;&lt;br /&gt;
    &lt;a href=&quot;/debugger/&quot;&gt;View all posts and upcoming topics →&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

</description>
        <pubDate>Sun, 31 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/08/31/demangling-the-details-symbol-resolution.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/08/31/demangling-the-details-symbol-resolution.html</guid>
        
        
      </item>
    
      <item>
        <title>Vibe Coding an API Refactor</title>
        <description>&lt;p&gt;In my &lt;a href=&quot;/2025/08/17/ownership-driven-debugger-design.html&quot;&gt;last post I used ownership to drive the API design for multiple breakpoints&lt;/a&gt;, I ended up with an API for the inferiors that I wasn’t entirely happy with.  I wrote&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Now, I say it’s clean meaning it compiled without any warnings.  But I don’t really like the API, it doesn’t feel clean to me.  For you rustaceans that have a better knowledge of idiomatic Rust than I do, what’s the right way to create APIs like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_exec&lt;/code&gt;?  I’m dissatisfied with the way it is now because it forces the user to have a mutable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; just so that it can pass it back into other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; functions.  The user doesn’t really want to know what’s in there or modify anything. They would depend on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; to do that for them.  So I’d like to return at the very least an immutable object.  I guess it’s mutable because they expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; to mutate it.  Let me know in the comments what you think about this or if you have an suggestions for a cleaner API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I had GitHub Copilot with the Claude-3.7 Sonnet Thought model proofread the post it answered this question and suggested:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;An opaque handle type that internally manages mutability&lt;/li&gt;
  &lt;li&gt;A builder pattern for configuration&lt;/li&gt;
  &lt;li&gt;A more functional approach where operations return new states&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I dug in a little deeper and asked&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A functional approach is a nice idea but does this imply copying the data in and out of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; library?  Or do the API calls take ownership and the return the same object back to the caller?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and I think this version of a “functional approach” is a beautiful one, and it aligns well with Rust’s idioms:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exit_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Given that this was Claude’s suggestion I’m going to let it write the code and see how it handles the implementation.&lt;/p&gt;

&lt;p&gt;For reference I’m starting at commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e972885&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;me-initial-ask&quot;&gt;Me: Initial Ask&lt;/h2&gt;

&lt;p&gt;I would like to implement the API you suggested&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exit_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hmm, does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt; need to return the inferior.  By the time it has returned the inferior has exited.&lt;/p&gt;

&lt;h2 id=&quot;claude-on-returning-the-inferior-from-trap_inferior_continue&quot;&gt;Claude: On Returning the Inferior from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;You raise an excellent question about whether &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt; should return the inferior when it has already exited. This depends on your design goals:&lt;/p&gt;

&lt;h3 id=&quot;reasons-to-not-return-the-inferior&quot;&gt;Reasons to Not Return the Inferior&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;The process has terminated, so the primary purpose of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; is complete&lt;/li&gt;
  &lt;li&gt;It signals more clearly to the user that the debugging session is finished&lt;/li&gt;
  &lt;li&gt;It’s more memory efficient as Rust will drop the value&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Implementation that consumes inferior&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;exit_code&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Usage&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exit_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// inferior consumed&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;reasons-to-return-the-inferior&quot;&gt;Reasons to Return the Inferior&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;em&gt;Consistency&lt;/em&gt;: All API functions follow the same ownership pattern&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Post-mortem analysis&lt;/em&gt;: The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; might contain valuable data even after exit&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Future extension&lt;/em&gt;: You might add features that work with completed inferiors&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;Multiple inferiors&lt;/em&gt;: In a multi-process debugger, one inferior might exit while others continue&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// If you return the inferior:&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;continue_execution&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;where&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Implementation&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exit_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    
    &lt;span class=&quot;c1&quot;&gt;// Then you can add methods that work on exited inferiors&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get_memory_dump&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MemoryDump&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Exited&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// Get memory dump from saved state&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;recommended-approach&quot;&gt;Recommended Approach&lt;/h3&gt;

&lt;p&gt;I’d suggest adding an explicit state for exited inferiors and returning the inferior for consistency. This gives you the most flexibility:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// Add to your InferiorState enum&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorState&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;SingleStepping&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;Exited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Store the exit code&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then users can check the state to determine what operations are valid:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.continue_execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Process exited with code {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.continue_execution&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Continue if not exited&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This approach maintains consistency while making the lifecycle explicit.&lt;/p&gt;

&lt;h2 id=&quot;an-aside-my-thoughts-returning-the-inferior-from-trap_inferior_continue&quot;&gt;An aside: My Thoughts returning the inferior from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;I think by the time &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt; returns the inferior no longer exists so it’s not possible to dump memory or inspect anything.  So I’m not going to implement this.&lt;/p&gt;

&lt;p&gt;Let’s just move forward with the inferior ownership API.&lt;/p&gt;

&lt;h2 id=&quot;me-lets-move-on&quot;&gt;Me: Let’s move on&lt;/h2&gt;

&lt;p&gt;Ok, let’s leave the return value as is for now.  Can you implement the changes to pass the inferior and ownership into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; and then return it back to the user via the return value?&lt;/p&gt;

&lt;h2 id=&quot;claude-implementing-ownership-based-debugger-api&quot;&gt;Claude: Implementing Ownership-Based Debugger API&lt;/h2&gt;

&lt;p&gt;Here’s the implementation of the ownership-passing API pattern we discussed. I’ll modify the key functions to take ownership of the inferior and return it back to the user.&lt;/p&gt;

&lt;h3 id=&quot;updated-trap_inferior_set_breakpoint-function&quot;&gt;Updated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_set_breakpoint&lt;/code&gt; Function&lt;/h3&gt;

&lt;p&gt;First, let’s modify the breakpoint setting function to take ownership and return a tuple:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0x7u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.breakpoints&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.insert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;original_breakpoint_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;peek_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.breakpoints&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;updated-trap_inferior_continue-function&quot;&gt;Updated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt; Function&lt;/h3&gt;

&lt;p&gt;Next, the continue function follows the same pattern:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;ptrace_util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;cont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SIGTRAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nn&quot;&gt;breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop on signal {} in trap_inferior_continue.  State: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;n&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;
                &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop in trap_inferior_continue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unhandled error in trap_inferior_continue&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;updating-the-breakpointhandle-function&quot;&gt;Updating the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint::handle&lt;/code&gt; Function&lt;/h3&gt;

&lt;p&gt;We need to modify this function to borrow the inferior instead of taking ownership:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorState&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Could not find breakpoint&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unhandled error in breakpoint::handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.target_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;step_over&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SIGTRAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;cont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop on signal {} in breakpoint::handle.  State: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop in breakpoint::handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unhandled error in breakpoint::handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;updated-test-example&quot;&gt;Updated Test Example&lt;/h3&gt;

&lt;p&gt;Here’s how the tests would change to use the new API:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[test]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;it_can_set_breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoint_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;./target/debug/twelve&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected_pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0x000055555555b821&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;breakpoint_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;breakpoint_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h3 id=&quot;multiple-breakpoints-test&quot;&gt;Multiple Breakpoints Test&lt;/h3&gt;

&lt;p&gt;Here’s the updated multiple breakpoints test:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[test]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;it_can_handle_more_than_one_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;./target/debug/loop&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected_pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS_OF_MAIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS_OF_FOO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;expected_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Unexpected breakpoint {} encountered.  Expected {} or {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This API pattern provides several benefits:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Makes ownership transfers explicit&lt;/li&gt;
  &lt;li&gt;Eliminates mutable references to shared state&lt;/li&gt;
  &lt;li&gt;Creates a more fluent and natural API&lt;/li&gt;
  &lt;li&gt;Enables method chaining in the future (with method implementations)&lt;/li&gt;
  &lt;li&gt;Makes it harder to use the API incorrectly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main downside is that we need to reassign the inferior at each step, but this clearly communicates the ownership transfer semantics, which is a good thing in Rust.&lt;/p&gt;

&lt;h2 id=&quot;aside-review&quot;&gt;Aside: Review&lt;/h2&gt;

&lt;p&gt;That looks pretty good.  I think I had mentioned to Claude earlier (before this post) that I don’t think trap_inferor_set_breakpoint should return the breakpoint anymore.  This was in the example I gave Claude up front.  The reason this is unnecessary is that the user now just uses the address as the handle of the breakpoint.  But maybe that would be weird actually, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferor_set_breakpoint&lt;/code&gt; shouldn’t only return and inferior as that would be confusing.  I’d think it just returns a breakpoint as that’s what it did before.&lt;/p&gt;

&lt;p&gt;There were two other tests that Claude left for me as an exercises.  I could ask it to fix them but it doesn’t seem worth the time.  But that’s one thing I find is that often the AI leaves me a lot of the work.  I thought the point was for it to do work for me.  It often seems more like it’s some Senior engineer helping me out and leaving me to do the grunt work…&lt;/p&gt;

&lt;p&gt;I plugged everything in and ran the tests and they pass:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;    Running tests/lib.rs (/home/jkain/projects/rusty_trap/target/debug/deps/lib-a0fc739a67a23e62)

running 4 tests
test it_can_exec ... ok
test it_can_set_breakpoints ... ok
test it_can_handle_a_breakpoint_more_than_once ... ok
test it_can_handle_more_than_one_breakpoint ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And that’s pretty nice.  Claude gave us a good suggestion and was able to implement it for us.  Here’s it’s changes as a diff for reference:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -68,7 +68,7 @@&lt;/span&gt; where
     };
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_set_breakpoint(inferior: &amp;amp;mut TrapInferior, location: u64) -&amp;gt; TrapBreakpoint {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_set_breakpoint(mut inferior: TrapInferior, location: u64) -&amp;gt; (TrapInferior, TrapBreakpoint) {
&lt;/span&gt;     let aligned_address = location &amp;amp; !0x7u64;
     let target_address = InferiorPointer(location);
     inferior.breakpoints.insert(
&lt;span class=&quot;p&quot;&gt;@@ -81,7 +81,7 @@&lt;/span&gt; pub fn trap_inferior_set_breakpoint(inferior: &amp;amp;mut TrapInferior, location: u64)
         },
     );
 
&lt;span class=&quot;gd&quot;&gt;-    set(inferior, inferior.breakpoints.get(&amp;amp;target_address).unwrap());
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    set(&amp;amp;inferior, inferior.breakpoints.get(&amp;amp;target_address).unwrap());
&lt;/span&gt; 
&lt;span class=&quot;gd&quot;&gt;-    InferiorPointer(location)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    (inferior, InferiorPointer(location))
&lt;/span&gt; }
&lt;span class=&quot;p&quot;&gt;modified   src/lib.rs
@@ -68,7 +68,7 @@&lt;/span&gt; pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;Inferior, Er
     }
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_continue&amp;lt;F&amp;gt;(inferior: &amp;amp;mut TrapInferior, callback: &amp;amp;mut F) -&amp;gt; i32
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_continue&amp;lt;F&amp;gt;(mut inferior: TrapInferior, mut callback: F) -&amp;gt; (TrapInferior, i32)
&lt;/span&gt; where
     F: FnMut(&amp;amp;TrapInferior, TrapBreakpoint),
 {
&lt;span class=&quot;p&quot;&gt;@@ -76,9 +76,9 @@&lt;/span&gt; where
     ptrace_util::cont(inferior.pid);
     loop {
         inferior.state = match waitpid(Pid::from_raw(inferior.pid), None) {
&lt;span class=&quot;gd&quot;&gt;-            Ok(WaitStatus::Exited(_pid, code)) =&amp;gt; return code,
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            Ok(WaitStatus::Exited(_pid, code)) =&amp;gt; return (inferior, code),
&lt;/span&gt;             Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt; {
&lt;span class=&quot;gd&quot;&gt;-                breakpoint::handle(inferior, callback)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+                breakpoint::handle(&amp;amp;mut inferior, &amp;amp;mut callback)
&lt;/span&gt;             }
             Ok(WaitStatus::Stopped(_pid, signal)) =&amp;gt; {
                 panic!(
&lt;span class=&quot;p&quot;&gt;modified   tests/lib.rs
@@ -6,23 +6,19 @@&lt;/span&gt; const ADDRESS_OF_FOO: u64 = 0x55555555b9e0;
 
 #[test]
 fn it_can_exec() {
&lt;span class=&quot;gd&quot;&gt;-    let mut inferior =
-        rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;[]).unwrap();
-    assert_eq!(
-        12,
-        rusty_trap::trap_inferior_continue(&amp;amp;mut inferior, &amp;amp;mut |_, _| {})
-    );
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let inferior = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;[]).unwrap();
+    let (_inferior, exit_code) = rusty_trap::trap_inferior_continue(inferior, |_, _| {});
+    assert_eq!(12, exit_code);
&lt;/span&gt; }
 
 #[test]
 fn it_can_set_breakpoints() {
     let mut breakpoint_count: i32 = 0;
 
&lt;span class=&quot;gd&quot;&gt;-    let mut inferior =
-        rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;[]).unwrap();
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let inferior = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;[]).unwrap();
&lt;/span&gt;     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let bp = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;mut inferior, 0x000055555555b821);
-    rusty_trap::trap_inferior_continue(&amp;amp;mut inferior, &amp;amp;mut |passed_inferior, passed_bp| {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let (inferior, bp) = rusty_trap::trap_inferior_set_breakpoint(inferior, 0x000055555555b821);
+    let (_, _) = rusty_trap::trap_inferior_continue(inferior, |passed_inferior, passed_bp| {
&lt;/span&gt;         assert_eq!(passed_inferior.pid, expected_pid);
         assert_eq!(passed_bp, bp);
         breakpoint_count += 1;
&lt;span class=&quot;p&quot;&gt;@@ -35,11 +31,10 @@&lt;/span&gt; fn it_can_set_breakpoints() {
 fn it_can_handle_a_breakpoint_more_than_once() {
     let mut breakpoint_count: i32 = 0;
 
&lt;span class=&quot;gd&quot;&gt;-    let mut inferior =
-        rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/loop&quot;), &amp;amp;[]).unwrap();
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let inferior = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/loop&quot;), &amp;amp;[]).unwrap();
&lt;/span&gt;     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let bp = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;mut inferior, ADDRESS_OF_FOO);
-    rusty_trap::trap_inferior_continue(&amp;amp;mut inferior, &amp;amp;mut |passed_inferior, passed_bp| {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let (inferior, bp) = rusty_trap::trap_inferior_set_breakpoint(inferior, ADDRESS_OF_FOO);
+    rusty_trap::trap_inferior_continue(inferior, |passed_inferior, passed_bp| {
&lt;/span&gt;         assert_eq!(passed_inferior.pid, expected_pid);
         assert_eq!(passed_bp, bp);
         breakpoint_count += 1;
&lt;span class=&quot;p&quot;&gt;@@ -53,12 +48,11 @@&lt;/span&gt; fn it_can_handle_more_than_one_breakpoint() {
     let mut bp_main_count: i32 = 0;
     let mut bp_foo_count: i32 = 0;
 
&lt;span class=&quot;gd&quot;&gt;-    let mut inferior =
-        rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/loop&quot;), &amp;amp;[]).unwrap();
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let inferior = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/loop&quot;), &amp;amp;[]).unwrap();
&lt;/span&gt;     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let bp_main = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;mut inferior, ADDRESS_OF_MAIN);
-    let bp_foo = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;mut inferior, ADDRESS_OF_FOO);
-    rusty_trap::trap_inferior_continue(&amp;amp;mut inferior, &amp;amp;mut |passed_inferior, passed_bp| {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let (inferior, bp_main) = rusty_trap::trap_inferior_set_breakpoint(inferior, ADDRESS_OF_MAIN);
+    let (inferior, bp_foo) = rusty_trap::trap_inferior_set_breakpoint(inferior, ADDRESS_OF_FOO);
+    let (_, _) = rusty_trap::trap_inferior_continue(inferior, |passed_inferior, passed_bp| {
&lt;/span&gt;         assert_eq!(passed_inferior.pid, expected_pid);
         if passed_bp == bp_main {
             bp_main_count += 1;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;takeaways&quot;&gt;Takeaways&lt;/h2&gt;

&lt;p&gt;I guess I’m taking two things away from this experience:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;I should remember this Rust idiom of taking ownership of a passed argument and returning it give the object back to the caller.  That’s a clean way of managing the ownership.&lt;/li&gt;
  &lt;li&gt;I can just vibe code this whole thing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I joke about vibe coding the entire project but I have been using AI tools in my job a lot lately and I find they are pretty powerful.  They may need to be pointed in the right direction and their work should be reviewed but they often surprise me both in terms of the code they write and also in terms of the insight they can provide.  I plan to write more about AI tools in the future.&lt;/p&gt;

&lt;p&gt;Just to close this out I’ve pushed out a 
&lt;a href=&quot;https://github.com/joekain/rusty_trap/pull/6&quot;&gt;PR&lt;/a&gt;:&lt;/p&gt;

&lt;!-- _includes/image.html --&gt;
&lt;!-- from http://codingtips.kanishkkunal.in/image-caption-jekyll/ --&gt;
&lt;div class=&quot;image-wrapper&quot;&gt;
    
        &lt;img src=&quot;/images/vibe-coded-api.png&quot; alt=&quot;&quot; /&gt;
    
    
&lt;/div&gt;

&lt;div class=&quot;debugger-callout&quot; style=&quot;
	    border: 1px solid #eee;
	    padding: 1em;
	    border-radius: 6px;
	    background-color: #f9f9f9;
	    display: flex;
	    align-items: center;
	    gap: 1em;
	    margin: 2em 0;
&quot;&gt;
  &lt;img src=&quot;/rusty_trap.png&quot; alt=&quot;Rusty trap icon&quot; style=&quot;width: 40px; height: 40px; flex-shrink: 0;&quot; /&gt;
  &lt;div&gt;
    &lt;strong&gt;This post is part of my &lt;em&gt;Writing a Debugger&lt;/em&gt; series.&lt;/strong&gt;&lt;br /&gt;
    &lt;a href=&quot;/debugger/&quot;&gt;View all posts and upcoming topics →&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

</description>
        <pubDate>Mon, 25 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/08/25/vibe-coded-api-refactor.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/08/25/vibe-coded-api-refactor.html</guid>
        
        
      </item>
    
      <item>
        <title>Multiple Breakpoints in Rust: Ownership-Driven Debugger Design</title>
        <description>&lt;p&gt;Welcome back to my &lt;a href=&quot;/debugger&quot;&gt;series on writing a debugger in rust&lt;/a&gt;.  In our previous post we updated our &lt;a href=&quot;/2025/07/28/multiple-breakpoints-redux.html&quot;&gt;custom breakpoint handling to support multiple breakpoints&lt;/a&gt; in C.  And in this post we will do the same thing in Rust.&lt;/p&gt;

&lt;p&gt;For reference I’m starting at commit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a9447ec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But this is Rust and not C and we will follow Rust’s best practices which will guide us toward an implementation that eliminates global state and models ownership properly and makes the relationship between inferiors and their breakpoints explicit in the type system.  What started off as a C-style port became an ownership-driven rearchitecture.&lt;/p&gt;

&lt;p&gt;I started this journey with a new test:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[test]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;it_can_handle_more_than_one_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;./target/debug/loop&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS_OF_MAIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS_OF_FOO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This test is similar to our other tests except that those tests set either a breakpoint in main or in foo and this test does both.  Then in the lambda / callback it checks which breakpoint was reached and increments the corresponding counter.  When it’s all done it checks for expected counts.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; is called once and triggers it’s breakpoint once.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; is called in a loop so it triggers 5 times.&lt;/p&gt;

&lt;p&gt;So, what do I need to do to make it pass?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;We need to allocate breakpoints instead of using one global.
    &lt;ul&gt;
      &lt;li&gt;Put off dynamic allocation until I work through deleting as well.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;We need that fancy version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_handle&lt;/code&gt; that we restructured in the C version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint::handle&lt;/code&gt; I wanted to mimic what is in the C version and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert(state == INFERIOR_RUNNING);&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;error[E0369]: binary operation `==` cannot be applied to type `inferior::InferiorState`
  --&amp;gt; src/breakpoint/mod.rs:52:23
   |
52 |     assert!(inf.state == InferiorState::Running);
   |             --------- ^^ ---------------------- inferior::InferiorState
   |             |
   |             inferior::InferiorState
   |
note: an implementation of `PartialEq` might be missing for `inferior::InferiorState`
  --&amp;gt; src/inferior/mod.rs:6:1
   |
6  | pub enum InferiorState {
   | ^^^^^^^^^^^^^^^^^^^^^^ must implement `PartialEq`
help: consider annotating `inferior::InferiorState` with `#[derive(PartialEq)]`
  --&amp;gt; src/inferior/mod.rs:6:1
   |
6  + #[derive(PartialEq)]
7  | pub enum InferiorState {
   |
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So instead I wrote:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;    &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unhandled error in breakpoint::handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Which works, and I guess I realized this would work because the old version matched on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inf.state&lt;/code&gt; to check for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Running&lt;/code&gt; vs &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleStepping&lt;/code&gt;.  But, is this really what I have to do?  And what does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;match&lt;/code&gt; do that’s different than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;==&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Anyway, here’s the completed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint::handle&lt;/code&gt;&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorState&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;F&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Could not find breakpoint&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unhandled error in breakpoint::handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;step_over&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;waitpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SIGTRAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nf&quot;&gt;cont&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Exited&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_code&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Running&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;WaitStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop on signal {} in breakpoint::handle.  State: {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;signal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.state&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop in breakpoint::handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unhandled error in breakpoint::handle&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Recall, the goal here is to both handle the initial breakpoint and single step in one call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint::handle&lt;/code&gt;.  This seems to work for the new test.  And it seems to work for each test individually but it doesn’t work when I run all the tests:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;running 4 tests
test it_can_exec ... ok
test it_can_handle_a_breakpoint_more_than_once ... ok
test it_can_handle_more_than_one_breakpoint ... FAILED
test it_can_set_breakpoints ... FAILED

failures:

---- it_can_handle_more_than_one_breakpoint stdout ----

thread &apos;it_can_handle_more_than_one_breakpoint&apos; panicked at tests/lib.rs:64:5:
assertion `left == right` failed
  left: 0
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- it_can_set_breakpoints stdout ----

thread &apos;it_can_set_breakpoints&apos; panicked at tests/lib.rs:28:5:
assertion `left == right` failed
  left: 0
 right: 1


failures:
	it_can_handle_more_than_one_breakpoint
	it_can_set_breakpoints

test result: FAILED. 2 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This feels again like the tests are being run concurrently but I’m still running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo test -- --test-threads 1&lt;/code&gt;.  Can I confirm test are running sequentially by putting a mutex in the tests?&lt;/p&gt;

&lt;p&gt;I mutexed the tests based on this &lt;a href=&quot;https://users.rust-lang.org/t/passing-test-threads-1-to-cargo-test-by-default/87225/4&quot;&gt;post&lt;/a&gt;.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   tests/lib.rs
@@ -1,65 +1,89 @@&lt;/span&gt;
 extern crate rusty_trap;
 use std::path::Path;
&lt;span class=&quot;gi&quot;&gt;+use std::sync::Mutex;
&lt;/span&gt; 
 const ADDRESS_OF_MAIN: u64 = 0x55555555b9f4;
 const ADDRESS_OF_FOO: u64 = 0x55555555b9e0;
 
&lt;span class=&quot;gi&quot;&gt;+static LIBRARY_USER: Mutex&amp;lt;Executor&amp;gt; = Mutex::new(Executor);
+
+#[derive(Clone, Copy)]
+struct Executor;
+
+impl Executor {
+    fn run_test(self, f: impl FnOnce()) {
+        f();
+    }
+}
+
&lt;/span&gt; #[test]
 fn it_can_exec() {
&lt;span class=&quot;gd&quot;&gt;-    let inferior = rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;[]).unwrap();
-    assert_eq!(
-        12,
-        rusty_trap::trap_inferior_continue(inferior, &amp;amp;mut |_, _| {})
-    );
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    LIBRARY_USER.lock().unwrap().run_test(|| {
+        let inferior =
+            rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;[]).unwrap();
+        assert_eq!(
+            12,
+            rusty_trap::trap_inferior_continue(inferior, &amp;amp;mut |_, _| {})
+        );
+    });
&lt;/span&gt; }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I’ve only shown the first test, the rest are wrapped in this executor the same way.  The tests still fail which I guess is expected.  But the mutexing also fails?  Here are the test failures:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---- it_can_handle_more_than_one_breakpoint stdout ----

thread &apos;it_can_handle_more_than_one_breakpoint&apos; panicked at tests/lib.rs:87:9:
assertion `left == right` failed
  left: 0
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- it_can_set_breakpoints stdout ----

thread &apos;it_can_set_breakpoints&apos; panicked at tests/lib.rs:33:25:
called `Result::unwrap()` on an `Err` value: PoisonError { .. }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Oh, ok this makes sense the &lt;a href=&quot;https://doc.rust-lang.org/std/sync/struct.Mutex.html&quot;&gt;rust docs for Mutex&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The mutexes in this module implement a strategy called “poisoning” where a mutex is considered poisoned whenever a thread panics while holding the mutex. Once a mutex is poisoned, all other threads are unable to access the data by default as it is likely tainted (some invariant is not being upheld).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So when the first test failed it panicked and poisoned the mutex.  Then the next test that tries to acquire the mutex finds it poisoned and panics as well.  I think this does confirm the tests are running synchronously.&lt;/p&gt;

&lt;p&gt;So then what’s wrong?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Is there some state being carried, with the rusty_trap library, across tests?
    &lt;ul&gt;
      &lt;li&gt;Of course there is &lt;strong&gt;some&lt;/strong&gt; state, we have a global vector of breakpoints after all.  But it should work well enough to run 3 tests back to back, right?&lt;/li&gt;
      &lt;li&gt;But I don’t want to rule it out.  I mean I think it should work but I also the whole system would work and it doesn’t so something’s wrong in my understanding.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;heroquote&gt;
When things don&apos;t work as we expect it means we misunderstand something about the system. When this happens question everything, trust nothing you think you know.
&lt;/heroquote&gt;

&lt;p&gt;That said, I hope the tests are correct.  I removed the mutexing since it doesn’t make a difference - the tests are simpler without it.  And, I’d rather see the test fail than the mutex fail.  Next, I want to use these tests to learn more about the problem.  The errors look like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;running 4 tests
test it_can_exec ... ok
test it_can_handle_a_breakpoint_more_than_once ... ok
test it_can_handle_more_than_one_breakpoint ... FAILED
test it_can_set_breakpoints ... ok

failures:

---- it_can_handle_more_than_one_breakpoint stdout ----

thread &apos;it_can_handle_more_than_one_breakpoint&apos; panicked at tests/lib.rs:64:5:
assertion `left == right` failed
  left: 0
 right: 5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
	it_can_handle_more_than_one_breakpoint

test result: FAILED. 3 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This happens the same way every time which is expected.  And again, if I were to comment out the other tests then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;it_can_handle_more_than_one_breakpoint&lt;/code&gt; would pass.&lt;/p&gt;

&lt;p&gt;What the failure says is that the breakpoint in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; was hit once as expected but the breakpoint in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt; was never hit.  One thing that the test doesn’t capture is if we stopped at the wrong address.  The test should check for this, if this did happen that should also count as a test failure and it should be called out differently.&lt;/p&gt;

&lt;p&gt;Here’s a test that does that:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[test]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;it_can_handle_more_than_one_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;./target/debug/loop&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS_OF_MAIN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ADDRESS_OF_FOO&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;// How do I write this as an assertion?  Or should I?&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;&quot;Unexpected breakpoint {} encountered.  Expected {} or {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;passed_bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp_main_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp_foo_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And when I run this test I get this (along with the failure):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Unexpected breakpoint 0 encountered.  Expected 1 or 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Recall that the breakpoints passed out to the user are just indices into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GLOBAL_BREAKPOINTS&lt;/code&gt;.  So, something is getting crossed where one of the breakpoints from the previous test is being hit.&lt;/p&gt;

&lt;p&gt;Another issue is that we have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global_inferior&lt;/code&gt;  but the 4 different tests call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;exec_inferior&lt;/code&gt; 4 different times.  So this is likely one problem though I would think that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global_inferior&lt;/code&gt; would be overwritten and it would be fine (I’m mean, it’s an awful code smell but I don’t think it would lead to this bug)&lt;/p&gt;

&lt;p&gt;Another thought is that somewhere I forgot to update breakpoint handling to pick the right breakpoint and so we find the wrong one.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Oh, as I write that down I realize that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/code&gt; is wrong.  Remember, it looks like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ptrace_util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_instruction_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_i64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GLOBAL_BREAKPOINTS&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.target_address&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_i64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;None&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So, it does this linear search through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GLOBAL_BREAKPOINTS&lt;/code&gt; until it finds a matching &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt;.  Well, most of the breakpoints in here have the same target address because we are running unit tests that all break in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop:foo&lt;/code&gt; which has the same address every time.&lt;/p&gt;

&lt;p&gt;This is a mess and just shows one of the main problems with global variables that Rust is trying to solve.  And I worked around all of Rust’s efforts by putting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; everywhere and paid the price.&lt;/p&gt;

&lt;p&gt;So let’s do this right and get rid of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GLOBAL_BREAKPOINTS&lt;/code&gt;.  Earlier I said that I would&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Put off dynamic allocation until I work through deleting as well.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So let’s think about deleting. I had been thinking to mimic the C API in which there is a 
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_remove_breakpoint&lt;/code&gt; function that the user can call to delete their breakpoints.  But this is a very C style API.  Between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_set_breakpoint&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_remove_breakpoint&lt;/code&gt; it basically says: “Hi user, here’s your breakpoint it’s your problem now.  Free it, lose it, whatever.  I don’t care”.  That’s not a Rusty attitude.&lt;/p&gt;

&lt;p&gt;In Rust I guess the right idea here is to give the user a breakpoint that they own and that can clean itself up when it goes out of scope.  Also it should not be a reference to anything global or unsafe.  There is a second piece to this which is that we can’t delete the breakpoint while the inferior is running.  So the C version sets the breakpoint in this deferred removal state and cleans it up later.  I don’t know if I want to do this in Rust the same way.  One thing I don’t like about setting this deferred stat is that it is the only mutation the breakpoint goes through.  The breakpoints could be immutable  if we do this a different way.  This also feels like reference counting: the user has one reference and a running inferior with the breakpoint installed has another reference.  Rust has reference counting support so I’ll keep that in mind.&lt;/p&gt;

&lt;p&gt;Anyway, I don’t really want to get into the feature of deleting breakpoints right now because we currently have a failing test and should fix that first.  Deleting breakpoints will require new tests.  So let’s at least start with a basic dynamically allocated breakpoint and worry about proper deletion afterwards.  I think none of the tests we have at the moment will trigger a problem with deleting a breakpoint while the inferior is running.&lt;/p&gt;

&lt;p&gt;So I think I want to Box up a new breakpoint?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;First we need to get rid of Breakpoint and TrapBreakpoint or TrapBreakpoint is Box&lt;Breakpoint&gt;?&lt;/Breakpoint&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think something like this would work:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;original_breakpoint_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapBreakpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0x7u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;original_breakpoint_word&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;peek_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user_bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;user_bp&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Let me fix all the other uses.  Hmm, how do I implement &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/code&gt;?  It is called when all we know is that the inferior stopped somewhere and want to find the corresponding breakpoint.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;We do know the inferior so maybe the inferior should hold on to breakpoints.
    &lt;ul&gt;
      &lt;li&gt;But how do we remove the reference from the inferior?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, just trying to push through incremental changes is not working well. Let’s take a step back and do some systems thinking about ownership and users.&lt;/p&gt;

&lt;p&gt;Breakpoints:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Who uses the them / who needs to access the it?
    &lt;ul&gt;
      &lt;li&gt;User needs it to determine when its hit.
        &lt;ul&gt;
          &lt;li&gt;On the other hand this comparing breakpoints in the callback is kind of gross.  The user is passed the breakpoint in the callback.
            &lt;ul&gt;
              &lt;li&gt;Is comparing against the object something a client needs to do or just an artifact of the testing?  This is also inherited from the C version that uses handles.&lt;/li&gt;
              &lt;li&gt;Worst case the callback could compare against the addresses.&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;The breakpoint module needs access to the breakpoint to handle it properly.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Who should own it?  Or who should control it’s lifetime?
    &lt;ul&gt;
      &lt;li&gt;The user should decide if it is set or deactivated for flow control.
        &lt;ul&gt;
          &lt;li&gt;In order to deactivate (and also to deallocate) the user would need a way to refer to a breakpoint.
            &lt;ul&gt;
              &lt;li&gt;When creating the breakpoint with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_set_breakpoint&lt;/code&gt; the user pases the address to refer to the breakpoint.&lt;/li&gt;
            &lt;/ul&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Should the user decide if it is deallocated?  Should it ever be deallocated?
        &lt;ul&gt;
          &lt;li&gt;If we have to search for a breakpoint then never deallocating them means a larger search space.  Then again if we don’t need an index as a handle we can use a different datastructure, like a map.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, that was a good exploration of the requirements.  And note I used a &lt;a href=&quot;/2025/07/16/question-and-answer-thinking.html&quot;&gt;question and answer investigation&lt;/a&gt; to work through it.  I think the takeaway is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The user doesn’t really need to own the breakpoint.  The user certainly doesn’t need to look inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Breakpoint&lt;/code&gt; struct.&lt;/li&gt;
  &lt;li&gt;The user does need a way to refer to breakpoints and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt; might be a good one.&lt;/li&gt;
  &lt;li&gt;The user should control if a breakpoint is active or not but doesn’t necessarily need to control the lifetime.&lt;/li&gt;
  &lt;li&gt;It isn’t clear that deleting breakpoints is really useful for lifetime of the inferior.&lt;/li&gt;
  &lt;li&gt;The breakpoint module does need access to the breakpoint and the internals of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Breakpoint&lt;/code&gt; structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rust’s ownership model offers surprising clarity when leaned into fully.&lt;/p&gt;

&lt;p&gt;One thing I could do now is to create tests for all these behaviors and then enforce them.  However, we still have a failing test and need to get past that first.&lt;/p&gt;

&lt;p&gt;A design that these requirements seems to suggest is:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Store the breakpoints in a data structure attached to the inferior.&lt;/li&gt;
  &lt;li&gt;The data structure should be a map that maps &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Breakpoint&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The user can just refer to breakpoints by target address.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint::handle&lt;/code&gt; can pass the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt; to the callback and the tests can compare against the addresses rather than comparing breakpoints or breakpoint handles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, so how to do we get from where we are with the failing test to this new design?  And using something like behavior driven development would be nice.&lt;/p&gt;

&lt;h2 id=&quot;approaching-the-problem-with-a-clear-mind&quot;&gt;Approaching the Problem With a Clear Mind&lt;/h2&gt;

&lt;p&gt;I took my dogs for a walk and thought about this problem.  This is a good way to clear my head and remove distractions, at least some kinds of distractions.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The key is getting past this failing test.  I’ve only called that out like 3 times now.&lt;/li&gt;
  &lt;li&gt;I should do something to get the test to pass by brute force just so we can make progress and then clean up later.&lt;/li&gt;
  &lt;li&gt;This will give us passing tests we can use to validate our work step by step.&lt;/li&gt;
  &lt;li&gt;This will also let us write new tests for additional features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get the test passing by brute force I first thought of trying run each test as a separate run of the test program.  This would acheive the goals but then I realized there is another thing we can try.  I can try to reset &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GLOBAL_BREAKPOINTS&lt;/code&gt; between tests.  Whenever a new inferior is created I could reset the breakpoints.  From a code standpoint this will be messy, inferior has to reach into breakpoint to reset, but for the moment that’s fine.  And we should have this fixed as we implement our new design.  So let’s get started breaking through this failing test.&lt;/p&gt;

&lt;p&gt;As I started to work on this I realized there is a simpler solution.  If we return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt; as the breakpoint handle then it should also make the tests pass.  We will still end up creating multiple breakpoints with the same target address but from User’s perspective it won’t matter.  It only sees the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt;.  This is a mess internally but it’s enough to break through this log jam and get us working.  It’s also a step toward our new design in that we do want to use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt; as the breakpoint identifier.&lt;/p&gt;

&lt;p&gt;Ok, that does it.  Here are the changes to the breakpoint module which just return the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapBreakpoint&lt;/code&gt; type:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -14,7 +14,7 @@&lt;/span&gt; struct Breakpoint {
     original_breakpoint_word: i64,
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub type TrapBreakpoint = usize;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub type TrapBreakpoint = InferiorPointer;
&lt;/span&gt; 
 static mut GLOBAL_BREAKPOINTS: Vec&amp;lt;Breakpoint&amp;gt; = Vec::&amp;lt;Breakpoint&amp;gt;::new();
 
&lt;span class=&quot;p&quot;&gt;@@ -33,9 +33,9 @@&lt;/span&gt; fn set(inferior: TrapInferior, bp: Breakpoint) {
 
 fn find_breakpoint_matching_inferior_instruction_pointer(inf: Inferior) -&amp;gt; Option&amp;lt;(TrapBreakpoint, Breakpoint)&amp;gt; {
     let ip = ptrace_util::get_instruction_pointer(inf.pid).as_i64();
&lt;span class=&quot;gd&quot;&gt;-    unsafe { for (idx, bp) in GLOBAL_BREAKPOINTS.iter().enumerate() {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    unsafe { for bp in &amp;amp;GLOBAL_BREAKPOINTS {
&lt;/span&gt; 	if bp.target_address.as_i64() == ip - 1 {
&lt;span class=&quot;gd&quot;&gt;-	    return Some((idx, *bp))
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+	    return Some((bp.target_address, *bp))
&lt;/span&gt; 	}
     } };
     None
&lt;span class=&quot;p&quot;&gt;@@ -76,7 +76,7 @@&lt;/span&gt; where
 
 pub fn trap_inferior_set_breakpoint(inferior: TrapInferior, location: u64) -&amp;gt; TrapBreakpoint {
     let aligned_address = location &amp;amp; !0x7u64;
&lt;span class=&quot;gd&quot;&gt;-    let user_bp = unsafe {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let index = unsafe {
&lt;/span&gt; 	GLOBAL_BREAKPOINTS.push(Breakpoint {
             shift: (location - aligned_address) * 8,
             aligned_address: InferiorPointer(aligned_address),
&lt;span class=&quot;p&quot;&gt;@@ -86,7 +86,7 @@&lt;/span&gt; pub fn trap_inferior_set_breakpoint(inferior: TrapInferior, location: u64) -&amp;gt; Tr
 	GLOBAL_BREAKPOINTS.len() - 1
     };
 
&lt;span class=&quot;gd&quot;&gt;-    set(inferior, unsafe {GLOBAL_BREAKPOINTS[user_bp]});
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    set(inferior, unsafe {GLOBAL_BREAKPOINTS[index]});
&lt;/span&gt; 
&lt;span class=&quot;gd&quot;&gt;-    user_bp
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    InferiorPointer(location)
&lt;/span&gt; }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And I needed a few changes to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InferiorPointer&lt;/code&gt; a little more usable.  I would actually like it if the tests could pass these in but I’ll need a little more work to support that.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/inferior/mod.rs
@@ -1,6 +1,7 @@&lt;/span&gt;
 use libc::c_void;
 use libc::pid_t;
 use std::ops::{Add, Sub};
&lt;span class=&quot;gi&quot;&gt;+use std::fmt;
&lt;/span&gt; 
 #[derive(Copy, Clone)]
 pub enum InferiorState {
&lt;span class=&quot;p&quot;&gt;@@ -17,7 +18,7 @@&lt;/span&gt; pub struct Inferior {
 
 pub type TrapInferior = pid_t;
 
&lt;span class=&quot;gd&quot;&gt;-#[derive(Copy, Clone)]
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
&lt;/span&gt; pub struct InferiorPointer(pub u64);
 impl InferiorPointer {
     pub fn as_voidptr(&amp;amp;self) -&amp;gt; *mut c_void {
&lt;span class=&quot;p&quot;&gt;@@ -52,3 +53,9 @@&lt;/span&gt; impl Sub&amp;lt;i64&amp;gt; for InferiorPointer {
         }
     }
 }
&lt;span class=&quot;gi&quot;&gt;+impl fmt::Display for InferiorPointer {
+    fn fmt(&amp;amp;self, f: &amp;amp;mut fmt::Formatter) -&amp;gt; fmt::Result {
+        let &amp;amp;InferiorPointer(u) = self;
+	write!(f, &quot;{}&quot;, u)
+    }
+}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I’ve implemented traits for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PartialEq&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PartialOrd&lt;/code&gt; so I can compare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InferiorPointer&lt;/code&gt; structs and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Display&lt;/code&gt; to print them out.&lt;/p&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;With this the tests pass.  There are warnings that we will want to clean up, but they pass.  Let’s keep score on the warnings so we can see if we make progress on them along the way (we should as many are related to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GLOBAL_BREAKPOINTS&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;warning: `rusty_trap` (lib) generated 7 warnings
warning: `rusty_trap` (lib test) generated 7 warnings (7 duplicates)

...

running 4 tests
test it_can_exec ... ok
test it_can_handle_a_breakpoint_more_than_once ... ok
test it_can_handle_more_than_one_breakpoint ... ok
test it_can_set_breakpoints ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From here we can start implementing the new design, recall that we want to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Store the breakpoints in a data structure attached to the inferior.&lt;/li&gt;
  &lt;li&gt;The data structure should be a map that maps target_address to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Breakpoint&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;The user can just refer to breakpoints by target address.&lt;/li&gt;
  &lt;li&gt;breakpoint::handle can pass the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt; to the callback and the tests can compare against the addresses rather than comparing breakpoints or breakpoint handles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And we’ve already completed “The user can just refer to breakpoints by target address” and “breakpoint::handle can pass the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt; to the callback”.  Though there are some simplifications we can make.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/code&gt; doesn’t need to return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;(TrapBreakpoint, Breakpoint)&amp;gt;&lt;/code&gt; since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapBreakpoint&lt;/code&gt; is now &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Breakpoint::target_address&lt;/code&gt;.  I’ll make that change now.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -31,11 +31,11 @@&lt;/span&gt; fn set(inferior: TrapInferior, bp: Breakpoint) {
     poke_text(inferior, bp.aligned_address, modified);
 }
 
&lt;span class=&quot;gd&quot;&gt;-fn find_breakpoint_matching_inferior_instruction_pointer(inf: Inferior) -&amp;gt; Option&amp;lt;(TrapBreakpoint, Breakpoint)&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn find_breakpoint_matching_inferior_instruction_pointer(inf: Inferior) -&amp;gt; Option&amp;lt;Breakpoint&amp;gt; {
&lt;/span&gt;     let ip = ptrace_util::get_instruction_pointer(inf.pid).as_i64();
     unsafe { for bp in &amp;amp;GLOBAL_BREAKPOINTS {
 	if bp.target_address.as_i64() == ip - 1 {
&lt;span class=&quot;gd&quot;&gt;-	    return Some((bp.target_address, *bp))
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+	    return Some(*bp)
&lt;/span&gt; 	}
     } };
     None
&lt;span class=&quot;p&quot;&gt;@@ -46,7 +46,7 @@&lt;/span&gt; where
     F: FnMut(TrapInferior, TrapBreakpoint),
 {
     let inferior = inf.pid;
&lt;span class=&quot;gd&quot;&gt;-    let (user_breakpoint, bp) =
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let bp =
&lt;/span&gt;         find_breakpoint_matching_inferior_instruction_pointer(inf)
         .expect(&quot;Could not find breakpoint&quot;);
 
&lt;span class=&quot;p&quot;&gt;@@ -54,7 +54,7 @@&lt;/span&gt; where
         InferiorState::Running =&amp;gt; (),
         _ =&amp;gt; panic!(&quot;Unhandled error in breakpoint::handle&quot;),
     }
&lt;span class=&quot;gd&quot;&gt;-    callback(inferior, user_breakpoint);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    callback(inferior, bp.target_address);
&lt;/span&gt;     step_over(inferior, bp);
     return match waitpid(Pid::from_raw(inf.pid), None) {
         Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt; {&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, and that still passes and still has 7 warnings but these two
design items are completely done.&lt;/p&gt;

&lt;p&gt;Next, let’s add a map to the inferior.  I started simply with this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[derive(Copy,&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;Clone)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// &amp;lt;== Added this&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;but this doesn’t build because I’ve dervied the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Copy&lt;/code&gt; trait:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0204]: the trait `Copy` cannot be implemented for this type
  --&amp;gt; src/inferior/mod.rs:16:10
   |
16 | #[derive(Copy, Clone)]
   |          ^^^^
...
20 |     pub breakpoints: HashMap&amp;lt;InferiorPointer, Breakpoint&amp;gt;
   |     ----------------------------------------------------- this field does not implement `Copy`&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This makes sense, we don’t want to copy the map.  So I should remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Copy&lt;/code&gt;, what about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Clone&lt;/code&gt;?&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Differs from Copy in that Copy is implicit and an inexpensive bit-wise copy, while Clone is always explicit and may or may not be expensive. &lt;a href=&quot;https://doc.rust-lang.org/std/clone/trait.Clone.html&quot;&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right, this is the deep copy.  I guess there is no harm in deriving this.  I’ll remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Copy&lt;/code&gt; and see what the fallout is.  And it begins here:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0382]: use of moved value: `inf`
  --&amp;gt; src/breakpoint/mod.rs:69:25
   |
44 | pub fn handle&amp;lt;F&amp;gt;(inf: Inferior, callback: &amp;amp;mut F) -&amp;gt; InferiorState
   |                  --- move occurs because `inf` has type `inferior::Inferior`, which does not implement the `Copy` trait
...
50 |         find_breakpoint_matching_inferior_instruction_pointer(inf)
   |                                                               --- value moved here
...
69 |                 signal, inf.state as i32
   |                         ^^^^^^^^^ value used here after move
   |
note: consider changing this parameter type in function `find_breakpoint_matching_inferior_instruction_pointer` to borrow instead if owning the value isn&apos;t necessary
   |
34 | fn find_breakpoint_matching_inferior_instruction_pointer(inf: Inferior) -&amp;gt; Option&amp;lt;B...
   |    -----------------------------------------------------      ^^^^^^^^ this parameter takes ownership of the value
   |    |
   |    in this function
help: consider cloning the value if the performance cost is acceptable
   |
50 |         find_breakpoint_matching_inferior_instruction_pointer(inf.clone())
   |                                                                  ++++++++&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/code&gt; can either borrow or I can pass a clone.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/code&gt; doesn’t need to own the inferior it just needs to use it to look up the instruction pointer.  Oh, it will use the new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoints&lt;/code&gt; hashmap as well.  So, it should borrow, immutably.  Ok, that’s easy enough:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;n&quot;&gt;modified&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mod&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.rs&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;31&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;@@&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TrapInferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;nf&quot;&gt;poke_text&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.aligned_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;modified&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Breakpoint&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ptrace_util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;get_instruction_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_i64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GLOBAL_BREAKPOINTS&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;.target_address&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_i64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And then fix the call site as well.&lt;/p&gt;

&lt;p&gt;Then we have another similar error:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0382]: assign to part of moved value: `inf`
  --&amp;gt; src/lib.rs:88:9
   |
85 |       let mut inf = unsafe { global_inferior };
   |           ------- move occurs because `inf` has type `inferior::Inferior`, which does not implement the `Copy` trait
86 |       ptrace_util::cont(inf.pid);
87 |       loop {
   |       ---- inside of this loop
88 | /         inf.state = match waitpid(Pid::from_raw(inf.pid), None) {
89 | |             Ok(WaitStatus::Exited(_pid, code)) =&amp;gt; return code,
90 | |             Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt; breakpoint::handle(inf, c...
   | |                                                                                  --- value moved here
91 | |             Ok(WaitStatus::Stopped(_pid, signal)) =&amp;gt; {
...  |
98 | |             Err(_) =&amp;gt; panic!(&quot;Unhandled error in trap_inferior_continue&quot;),
99 | |         };
   | |_________^ value partially assigned here after move
   |
note: consider changing this parameter type in function `handle` to borrow instead if owning the value isn&apos;t necessary
  --&amp;gt; src/breakpoint/mod.rs:44:23
   |
44 | pub fn handle&amp;lt;F&amp;gt;(inf: Inferior, callback: &amp;amp;mut F) -&amp;gt; InferiorState
   |        ------         ^^^^^^^^ this parameter takes ownership of the value
   |        |
   |        in this function
help: consider cloning the value if the performance cost is acceptable
   |
90 |             Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt; breakpoint::handle(inf.clone(), callback),
   |                                                                                     ++++++++&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ah, ok this is dealing with the global_inferior.  One option is to just compute the state and then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; assign into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global_inferior&lt;/code&gt;.  But I think we will be getting rid of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global_inferior&lt;/code&gt; soon because there are a number of other errors and warnings related to it.  The main set of issues being related to initializing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global_inferior&lt;/code&gt; in the first place.  I’ve added this line to try to initialize &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoints&lt;/code&gt; but it fails:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;@@ -26,6 +27,7 @@&lt;/span&gt; use breakpoint::TrapBreakpoint;
 static mut global_inferior: Inferior = Inferior {
     pid: 0,
     state: InferiorState::Stopped,
&lt;span class=&quot;gi&quot;&gt;+    breakpoints: HashMap::new(),
&lt;/span&gt; };&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0015]: cannot call non-const associated function `HashMap::&amp;lt;inferior::InferiorPointer, Breakpoint&amp;gt;::new` in statics
  --&amp;gt; src/lib.rs:30:18
   |
30 |     breakpoints: HashMap::new(),
   |                  ^^^^^^^^^^^^^^
   |
   = note: calls in statics are limited to constant functions, tuple structs and tuple variants
   = note: consider wrapping this expression in `std::sync::LazyLock::new(|| ...)`&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And also here, I haven’t initialized breakpoints but don’t expect it to go well:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0063]: missing field `breakpoints` in initializer of `inferior::Inferior`
  --&amp;gt; src/lib.rs:58:69
   |
58 |         Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(Inferior {
   |                                                                     ^^^^^^^^ missing `breakpoints`&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Well, maybe this one would be easier to initialize since it isn’t static global.  But, I think the right option here is to dynamically allocate the inferior and return it (and its ownership) to the user.  Then borrow it from them during these operations.  Hmm, is that right?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;If the user passes it into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt; then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; is borrowing it from them.&lt;/li&gt;
  &lt;li&gt;When &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; calls the callback it lends it back to the user.&lt;/li&gt;
  &lt;li&gt;So the user won’t be able to reference their original copy.&lt;/li&gt;
  &lt;li&gt;The tests won’t be able to write: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert_eq!(passed_inferior, inferior);&lt;/code&gt;
    &lt;ul&gt;
      &lt;li&gt;Instead it could compare the process ids.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Would a real app want to compare?
    &lt;ul&gt;
      &lt;li&gt;I guess it would as that’s the only way for it to know which breakpoint is which.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note, all of this depends on the app needing more than one inferior which might be rare but is an intended feature of the debugger.  This gets into what the purpose of a debugger like this is for.  I’ll come back to that at the end of the post.&lt;/p&gt;

&lt;p&gt;But let’s move forward with dynamically allocating the inferior.  First, a function, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;new&lt;/code&gt;, to intialize the Inferior.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[derive(Copy,&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;Clone,&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;PartialEq,&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;PartialOrd,&lt;/span&gt; &lt;span class=&quot;nd&quot;&gt;Debug)]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;InferiorPointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InferiorPointer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;pid_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;n&quot;&gt;Inferior&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;InferiorState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Stopped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then the rest:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -18,17 +18,17 @@&lt;/span&gt; pub type TrapBreakpoint = InferiorPointer;
 
 static mut GLOBAL_BREAKPOINTS: Vec&amp;lt;Breakpoint&amp;gt; = Vec::&amp;lt;Breakpoint&amp;gt;::new();
 
&lt;span class=&quot;gd&quot;&gt;-fn step_over(inferior: TrapInferior, bp: Breakpoint) {
-    poke_text(inferior, bp.aligned_address, bp.original_breakpoint_word);
-    set_instruction_pointer(inferior, bp.target_address);
-    single_step(inferior);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn step_over(inferior: &amp;amp;TrapInferior, bp: Breakpoint) {
+    poke_text(inferior.pid, bp.aligned_address, bp.original_breakpoint_word);
+    set_instruction_pointer(inferior.pid, bp.target_address);
+    single_step(inferior.pid);
&lt;/span&gt; }
 
&lt;span class=&quot;gd&quot;&gt;-fn set(inferior: TrapInferior, bp: Breakpoint) {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn set(inferior: &amp;amp;TrapInferior, bp: Breakpoint) {
&lt;/span&gt;     let mut modified = bp.original_breakpoint_word;
     modified &amp;amp;= !(0xFFi64 &amp;lt;&amp;lt; bp.shift);
     modified |= 0xCCi64 &amp;lt;&amp;lt; bp.shift;
&lt;span class=&quot;gd&quot;&gt;-    poke_text(inferior, bp.aligned_address, modified);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    poke_text(inferior.pid, bp.aligned_address, modified);
&lt;/span&gt; }
 
 fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;Inferior) -&amp;gt; Option&amp;lt;Breakpoint&amp;gt; {
&lt;span class=&quot;p&quot;&gt;@@ -41,32 +41,31 @@&lt;/span&gt; fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;Inferior) -&amp;gt; Opti
     None
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn handle&amp;lt;F&amp;gt;(inf: Inferior, callback: &amp;amp;mut F) -&amp;gt; InferiorState
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn handle&amp;lt;F&amp;gt;(inferior: &amp;amp;mut Inferior, callback: &amp;amp;mut F) -&amp;gt; InferiorState
&lt;/span&gt; where
&lt;span class=&quot;gd&quot;&gt;-    F: FnMut(TrapInferior, TrapBreakpoint),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    F: FnMut(&amp;amp;TrapInferior, TrapBreakpoint),
&lt;/span&gt; {
&lt;span class=&quot;gd&quot;&gt;-    let inferior = inf.pid;
&lt;/span&gt;     let bp =
&lt;span class=&quot;gd&quot;&gt;-        find_breakpoint_matching_inferior_instruction_pointer(&amp;amp;inf)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        find_breakpoint_matching_inferior_instruction_pointer(inferior)
&lt;/span&gt;         .expect(&quot;Could not find breakpoint&quot;);
 
&lt;span class=&quot;gd&quot;&gt;-    match inf.state {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    match inferior.state {
&lt;/span&gt;         InferiorState::Running =&amp;gt; (),
         _ =&amp;gt; panic!(&quot;Unhandled error in breakpoint::handle&quot;),
     }
     callback(inferior, bp.target_address);
     step_over(inferior, bp);
&lt;span class=&quot;gd&quot;&gt;-    return match waitpid(Pid::from_raw(inf.pid), None) {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    return match waitpid(Pid::from_raw(inferior.pid), None) {
&lt;/span&gt;         Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt; {
             set(inferior, bp);
&lt;span class=&quot;gd&quot;&gt;-            cont(inferior);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            cont(inferior.pid);
&lt;/span&gt;             InferiorState::Running
         }
         Ok(WaitStatus::Exited(_pid, _code)) =&amp;gt; InferiorState::Running,
         Ok(WaitStatus::Stopped(_pid, signal)) =&amp;gt; {
             panic!(
                 &quot;Unexpected stop on signal {} in breakpoint::handle.  State: {}&quot;,
&lt;span class=&quot;gd&quot;&gt;-                signal, inf.state as i32
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+                signal, inferior.state as i32
&lt;/span&gt;             )
         }
         Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in breakpoint::handle&quot;),
&lt;span class=&quot;p&quot;&gt;@@ -74,14 +73,14 @@&lt;/span&gt; where
     };
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_set_breakpoint(inferior: TrapInferior, location: u64) -&amp;gt; TrapBreakpoint {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_set_breakpoint(inferior: &amp;amp;TrapInferior, location: u64) -&amp;gt; TrapBreakpoint {
&lt;/span&gt;     let aligned_address = location &amp;amp; !0x7u64;
     let index = unsafe {
 	GLOBAL_BREAKPOINTS.push(Breakpoint {
             shift: (location - aligned_address) * 8,
             aligned_address: InferiorPointer(aligned_address),
             target_address: InferiorPointer(location),
&lt;span class=&quot;gd&quot;&gt;-            original_breakpoint_word: peek_text(inferior, InferiorPointer(aligned_address)),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            original_breakpoint_word: peek_text(inferior.pid, InferiorPointer(aligned_address)),
&lt;/span&gt; 	});
 	GLOBAL_BREAKPOINTS.len() - 1
     };
&lt;span class=&quot;p&quot;&gt;modified   src/inferior/mod.rs
@@ -17,10 +17,20 @@&lt;/span&gt; pub enum InferiorState {
 pub struct Inferior {
     pub pid: pid_t,
     pub state: InferiorState,
&lt;span class=&quot;gd&quot;&gt;-    pub breakpoints: HashMap&amp;lt;InferiorPointer, Breakpoint&amp;gt;  // Added this
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    pub breakpoints: HashMap&amp;lt;InferiorPointer, Breakpoint&amp;gt;
&lt;/span&gt; }
 
&lt;span class=&quot;gd&quot;&gt;-pub type TrapInferior = pid_t;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+impl Inferior {
+    pub fn new(pid: pid_t) -&amp;gt; Inferior {
+	Inferior {
+	    pid,
+	    state: InferiorState::Stopped,
+	    breakpoints: HashMap::new(),
+	}
+    }
+}
+
+pub type TrapInferior = Inferior;
&lt;/span&gt; 
 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
 pub struct InferiorPointer(pub u64);
&lt;span class=&quot;p&quot;&gt;modified   src/lib.rs
@@ -10,7 +10,6 @@&lt;/span&gt; use nix::{
     unistd::{execve, fork, ForkResult},
     Error,
 };
&lt;span class=&quot;gd&quot;&gt;-use std::collections::HashMap;
&lt;/span&gt; use std::ffi::{CStr, CString};
 use std::path::Path;
 
&lt;span class=&quot;p&quot;&gt;@@ -24,12 +23,6 @@&lt;/span&gt; mod breakpoint;
 pub use self::breakpoint::trap_inferior_set_breakpoint;
 use breakpoint::TrapBreakpoint;
 
&lt;span class=&quot;gd&quot;&gt;-static mut global_inferior: Inferior = Inferior {
-    pid: 0,
-    state: InferiorState::Stopped,
-    breakpoints: HashMap::new(),
-};
-
&lt;/span&gt; fn disable_address_space_layout_randomization() {
     unsafe {
         let old = libc::personality(0xffffffff);
&lt;span class=&quot;p&quot;&gt;@@ -55,49 +48,46 @@&lt;/span&gt; fn exec_inferior(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) {
 fn attach_inferior(raw_pid: pid_t) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
     let nix_pid = Pid::from_raw(raw_pid);
     match waitpid(nix_pid, None) {
&lt;span class=&quot;gd&quot;&gt;-        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(Inferior {
-            pid: pid.into(),
-            state: InferiorState::Running,
-        }),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt; Ok(Inferior::new(pid.into())),
&lt;/span&gt;         Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in attach_inferior&quot;),
         Err(e) =&amp;gt; Err(e),
     }
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;TrapInferior, Error&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
&lt;/span&gt;     loop {
         match unsafe { fork() } {
&lt;span class=&quot;gd&quot;&gt;-            Ok(ForkResult::Child) =&amp;gt; exec_inferior(filename, args),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            Ok(ForkResult::Child) =&amp;gt; {
+		exec_inferior(filename, args);
+		unreachable!();
+	    }
&lt;/span&gt;             Ok(ForkResult::Parent { child: pid }) =&amp;gt; {
&lt;span class=&quot;gd&quot;&gt;-                unsafe { global_inferior = attach_inferior(pid.into()).ok().unwrap() };
-                return Ok(pid.into());
-            }
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+		return attach_inferior(pid.into())
+	    }
&lt;/span&gt;             Err(Error::EAGAIN) =&amp;gt; continue,
             Err(e) =&amp;gt; return Err(e),
         }
     }
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_continue&amp;lt;F&amp;gt;(inferior: TrapInferior, callback: &amp;amp;mut F) -&amp;gt; i32
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_continue&amp;lt;F&amp;gt;(inferior: &amp;amp;mut TrapInferior, callback: &amp;amp;mut F) -&amp;gt; i32
&lt;/span&gt; where
&lt;span class=&quot;gd&quot;&gt;-    F: FnMut(TrapInferior, TrapBreakpoint),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    F: FnMut(&amp;amp;TrapInferior, TrapBreakpoint),
&lt;/span&gt; {
&lt;span class=&quot;gd&quot;&gt;-    let mut inf = unsafe { global_inferior };
-    ptrace_util::cont(inf.pid);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    ptrace_util::cont(inferior.pid);
&lt;/span&gt;     loop {
&lt;span class=&quot;gd&quot;&gt;-        inf.state = match waitpid(Pid::from_raw(inf.pid), None) {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        inferior.state = match waitpid(Pid::from_raw(inferior.pid), None) {
&lt;/span&gt;             Ok(WaitStatus::Exited(_pid, code)) =&amp;gt; return code,
&lt;span class=&quot;gd&quot;&gt;-            Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt; breakpoint::handle(inf, callback),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt;
+		breakpoint::handle(inferior, callback),
&lt;/span&gt;             Ok(WaitStatus::Stopped(_pid, signal)) =&amp;gt; {
                 panic!(
                     &quot;Unexpected stop on signal {} in trap_inferior_continue.  State: {}&quot;,
&lt;span class=&quot;gd&quot;&gt;-                    signal, inf.state as i32
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+                    signal, inferior.state as i32
&lt;/span&gt;                 )
             }
             Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in trap_inferior_continue&quot;),
             Err(_) =&amp;gt; panic!(&quot;Unhandled error in trap_inferior_continue&quot;),
         };
&lt;span class=&quot;gd&quot;&gt;-
-        unsafe { global_inferior = inf };
&lt;/span&gt;     }
 }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And then we have the problem I expected that the tests can’t reference the inferior because they lent it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt;:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;error[E0502]: cannot borrow `inferior` as immutable because it is also borrowed as mutable
  --&amp;gt; tests/lib.rs:24:60
   |
24 |     rusty_trap::trap_inferior_continue(&amp;amp;mut inferior, &amp;amp;mut |passed_inferior, passed_bp| {
   |     ---------------------------------- -------------       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
   |     |                                  |
   |     |                                  mutable borrow occurs here
   |     mutable borrow later used by call
25 |         assert_eq!(passed_inferior.pid, inferior.pid);
   |                                         ------------ second borrow occurs due to use of `inferior` in closure&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, good well at least I’m understanding Rust well enough to have expected this.  For now, the test will need to copy out the pid before lending the inferior back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And with that, the tests pass.  And, we are down to only 4 warnings!&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;warning: unused variable: `args`
  --&amp;gt; src/lib.rs:33:35
   |
33 | fn exec_inferior(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) {
   |                                   ^^^^ help: if this is intentional, prefix it with an underscore: `_args`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: creating a shared reference to mutable static
  --&amp;gt; src/breakpoint/mod.rs:36:24
   |
36 |     unsafe { for bp in &amp;amp;GLOBAL_BREAKPOINTS {
   |                        ^^^^^^^^^^^^^^^^^^^ shared reference to mutable static
   |
   = note: for more information, see &amp;lt;https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html&amp;gt;
   = note: shared references to mutable statics are dangerous; it&apos;s undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives
   = note: `#[warn(static_mut_refs)]` on by default
help: use `&amp;amp;raw const` instead to create a raw pointer
   |
36 |     unsafe { for bp in &amp;amp;raw const GLOBAL_BREAKPOINTS {
   |                         +++++++++

warning: creating a mutable reference to mutable static
  --&amp;gt; src/breakpoint/mod.rs:79:2
   |
79 | /     GLOBAL_BREAKPOINTS.push(Breakpoint {
80 | |             shift: (location - aligned_address) * 8,
81 | |             aligned_address: InferiorPointer(aligned_address),
82 | |             target_address: InferiorPointer(location),
83 | |             original_breakpoint_word: peek_text(inferior.pid, InferiorPointer(aligned_address)),
84 | |     });
   | |______^ mutable reference to mutable static
   |
   = note: for more information, see &amp;lt;https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html&amp;gt;
   = note: mutable references to mutable statics are dangerous; it&apos;s undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives

warning: creating a shared reference to mutable static
  --&amp;gt; src/breakpoint/mod.rs:85:2
   |
85 |     GLOBAL_BREAKPOINTS.len() - 1
   |     ^^^^^^^^^^^^^^^^^^^^^^^^ shared reference to mutable static
   |
   = note: for more information, see &amp;lt;https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html&amp;gt;
   = note: shared references to mutable statics are dangerous; it&apos;s undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives

warning: `rusty_trap` (lib) generated 4 warnings
warning: `rusty_trap` (lib test) generated 4 warnings (4 duplicates)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.36s
     Running unittests src/lib.rs (/home/jkain/projects/rusty_trap/target/debug/deps/rusty_trap-dd302d51f38b1a8c)

running 4 tests
test it_can_exec ... ok
test it_can_handle_a_breakpoint_more_than_once ... ok
test it_can_handle_more_than_one_breakpoint ... ok
test it_can_set_breakpoints ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, so these warnings all about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GLOBAL_BREAKPOINTS&lt;/code&gt;.  We have added the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoints&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;HashMap&lt;/code&gt; to the inferior but aren’t using it yet.  Let’s do that now.&lt;/p&gt;

&lt;p&gt;The main change is to update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_breakpoint_matching_inferior_instruction_pointer&lt;/code&gt; like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;gd&quot;&gt;-fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;Inferior) -&amp;gt; Option&amp;lt;Breakpoint&amp;gt; {
-    let ip = ptrace_util::get_instruction_pointer(inf.pid).as_i64();
-    unsafe { for bp in &amp;amp;GLOBAL_BREAKPOINTS {
-	if bp.target_address.as_i64() == ip - 1 {
-	    return Some(*bp)
-	}
-    } };
-    None
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn find_breakpoint_matching_inferior_instruction_pointer(inf: &amp;amp;Inferior) -&amp;gt; Option&amp;lt;&amp;amp;Breakpoint&amp;gt; {
+    let InferiorPointer(ip) = get_instruction_pointer(inf.pid);
+    let ip = InferiorPointer(ip - 1);
+    return inf.breakpoints.get(&amp;amp;ip);
&lt;/span&gt; }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then just some minor changes to change &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Breakpoint&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;Breakpoint&lt;/code&gt;, etc.  And finally, we remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GLOBAL_BREAKPOINTS&lt;/code&gt; from everything else:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -67,19 +65,17 @@&lt;/span&gt; where
     };
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_set_breakpoint(inferior: &amp;amp;TrapInferior, location: u64) -&amp;gt; TrapBreakpoint {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_set_breakpoint(inferior: &amp;amp;mut TrapInferior, location: u64) -&amp;gt; TrapBreakpoint {
&lt;/span&gt;     let aligned_address = location &amp;amp; !0x7u64;
&lt;span class=&quot;gd&quot;&gt;-    let index = unsafe {
-	GLOBAL_BREAKPOINTS.push(Breakpoint {
-            shift: (location - aligned_address) * 8,
-            aligned_address: InferiorPointer(aligned_address),
-            target_address: InferiorPointer(location),
-            original_breakpoint_word: peek_text(inferior.pid, InferiorPointer(aligned_address)),
-	});
-	GLOBAL_BREAKPOINTS.len() - 1
-    };
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let target_address = InferiorPointer(location);
+    inferior.breakpoints.insert(target_address, Breakpoint {
+        shift: (location - aligned_address) * 8,
+        aligned_address: InferiorPointer(aligned_address),
+        target_address,
+        original_breakpoint_word: peek_text(inferior.pid, InferiorPointer(aligned_address)),
+    });
&lt;/span&gt; 
&lt;span class=&quot;gd&quot;&gt;-    set(inferior, unsafe {GLOBAL_BREAKPOINTS[index]});
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    set(inferior, inferior.breakpoints.get(&amp;amp;target_address).unwrap());
&lt;/span&gt; 
     InferiorPointer(location)
 }
&lt;span class=&quot;p&quot;&gt;modified   tests/lib.rs
@@ -21,7 +21,7 @@&lt;/span&gt; fn it_can_set_breakpoints() {
     let mut inferior =
         rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/twelve&quot;), &amp;amp;[]).unwrap();
     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let bp = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;inferior, 0x000055555555b821);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let bp = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;mut inferior, 0x000055555555b821);
&lt;/span&gt;     rusty_trap::trap_inferior_continue(&amp;amp;mut inferior, &amp;amp;mut |passed_inferior, passed_bp| {
         assert_eq!(passed_inferior.pid, expected_pid);
         assert_eq!(passed_bp, bp);
&lt;span class=&quot;p&quot;&gt;@@ -38,7 +38,7 @@&lt;/span&gt; fn it_can_handle_a_breakpoint_more_than_once() {
     let mut inferior =
         rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/loop&quot;), &amp;amp;[]).unwrap();
     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let bp = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;inferior, ADDRESS_OF_FOO);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let bp = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;mut inferior, ADDRESS_OF_FOO);
&lt;/span&gt;     rusty_trap::trap_inferior_continue(&amp;amp;mut inferior, &amp;amp;mut |passed_inferior, passed_bp| {
         assert_eq!(passed_inferior.pid, expected_pid);
         assert_eq!(passed_bp, bp);
&lt;span class=&quot;p&quot;&gt;@@ -56,8 +56,8 @@&lt;/span&gt; fn it_can_handle_more_than_one_breakpoint() {
     let mut inferior =
         rusty_trap::trap_inferior_exec(Path::new(&quot;./target/debug/loop&quot;), &amp;amp;[]).unwrap();
     let expected_pid = inferior.pid;
&lt;span class=&quot;gd&quot;&gt;-    let bp_main = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;inferior, ADDRESS_OF_MAIN);
-    let bp_foo = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;inferior, ADDRESS_OF_FOO);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let bp_main = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;mut inferior, ADDRESS_OF_MAIN);
+    let bp_foo = rusty_trap::trap_inferior_set_breakpoint(&amp;amp;mut inferior, ADDRESS_OF_FOO);
&lt;/span&gt;     rusty_trap::trap_inferior_continue(&amp;amp;mut inferior, &amp;amp;mut |passed_inferior, passed_bp| {
         assert_eq!(passed_inferior.pid, expected_pid);
         if passed_bp == bp_main {&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And the tests pass and there is only one warning left:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot; data-lang=&quot;text&quot;&gt;warning: unused variable: `args`
  --&amp;gt; src/lib.rs:33:35
   |
33 | fn exec_inferior(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) {
   |                                   ^^^^ help: if this is intentional, prefix it with an underscore: `_args`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: `rusty_trap` (lib) generated 1 warning&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, I guess this was a placeholder I left in case the user wants to pass arguments to the inferior.  In general this should be supported but I’m not going to build it and it’s not really that interesting for this blog.  So, I’m just going to take the compiler’s advice and rename the argument &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_args_&lt;/code&gt;.  If you have an interest in this please go ahead and implement it.&lt;/p&gt;

&lt;p&gt;And now, it’s clean.  And not only that, the tests pass without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--test-threads 1&lt;/code&gt; option.  Nice.&lt;/p&gt;

&lt;h2 id=&quot;aside-on-api-design&quot;&gt;Aside on API design?&lt;/h2&gt;

&lt;p&gt;Now, I say it’s clean meaning it compiled without any warnings.  But I don’t really like the API, it doesn’t feel clean to me.  For you rustaceans that have a better knowledge of idiomatic Rust than I do, what’s the right way to create APIs like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_exec&lt;/code&gt;?  I’m dissatisfied with the way it is now because it forces the user to have a mutable &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; just so that it can pass it back into other &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; functions.  The user doesn’t really want to know what’s in there or modify anything. They would depend on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; to do that for them.  So I’d like to return at the very least an immutable object.  I guess it’s mutable because they expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; to mutate it.  Let me know in the comments what you think about this or if you have an suggestions for a cleaner API.&lt;/p&gt;

&lt;p&gt;But for now I’ve pushed and merged a &lt;a href=&quot;https://github.com/joekain/rusty_trap/pull/5&quot;&gt;PR&lt;/a&gt;:&lt;/p&gt;

&lt;!-- _includes/image.html --&gt;
&lt;!-- from http://codingtips.kanishkkunal.in/image-caption-jekyll/ --&gt;
&lt;div class=&quot;image-wrapper&quot;&gt;
    
        &lt;img src=&quot;/images/multiple-breakpoints.png&quot; alt=&quot;&quot; /&gt;
    
    
&lt;/div&gt;

&lt;h2 id=&quot;conclusion-from-global-state-to-ownership&quot;&gt;Conclusion: From Global State to Ownership&lt;/h2&gt;

&lt;p&gt;In this post, we’ve seen how implementing multiple breakpoints in Rust pushed us to reconsider our design approach fundamentally. What started as a seemingly straightforward port from C evolved into an exploration of Rust’s ownership principles and safer design patterns.&lt;/p&gt;

&lt;p&gt;Throughout this journey, I applied the &lt;a href=&quot;/2025/07/16/question-and-answer-thinking.html&quot;&gt;question and answer investigation&lt;/a&gt; approach to systematically break down problems:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Breakpoints:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Who uses them / who needs to access them?&lt;/li&gt;
    &lt;li&gt;Who should own them? Or who should control their lifetime?&lt;/li&gt;
    &lt;li&gt;Should the user decide if it is deallocated? Should it ever be deallocated?&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This structured questioning helped clarify requirements and guide us toward better solutions. When I hit roadblocks, I asked specific questions about ownership relationships and data flow, letting the answers drive implementation decisions.&lt;/p&gt;

&lt;p&gt;Our journey took us from:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A design with problematic global state and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; code&lt;/li&gt;
  &lt;li&gt;Through a failing test that exposed the limitations of that approach&lt;/li&gt;
  &lt;li&gt;To a cleaner design with explicit ownership of breakpoints tied to their inferior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key lessons we learned include:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Global state is particularly problematic in Rust, which actively encourages you to make relationships explicit through the type system&lt;/li&gt;
  &lt;li&gt;Rust’s ownership model guides you toward designs where data relationships are clear and statically verified.&lt;/li&gt;
  &lt;li&gt;Even temporary “brute force” solutions can help you make incremental progress toward a better design&lt;/li&gt;
  &lt;li&gt;Asking the right questions about ownership and responsibility drives better architecture decisions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While our current API still has room for improvement, we’ve eliminated most of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe&lt;/code&gt; code and made the relationship between inferiors and their breakpoints much more explicit. The tests now pass reliably without special handling for concurrency issues - a testament to the benefits of thoughful ownership.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s Next?&lt;/h2&gt;

&lt;p&gt;Looking ahead, there are several opportunities to improve our debugger:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Symbol tables: We’ll need to parse the inferiors symbol table to make function names to addresses rather than the fragile hard coding of addresses that we have now.&lt;/li&gt;
  &lt;li&gt;Dynamic library support: Using the debugger hook for loading and debugging dynamic libraries.&lt;/li&gt;
  &lt;li&gt;Multiple threads: Adding support for debugging multi-threaded applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Symbol tables will likely be next major topic, as they’re essential for providing a usable debugging experience. The Rust ecosystem’s strong support for parsing binary formats should make this an interesting and productive exploration.&lt;/p&gt;

&lt;p&gt;Through this series, we’re not just learning how to build a debugger – we’re discovering how Rust’s design principles can guide us toward more robust software architecture decisions. The ownership-driven design we’ve developed here will continue to evolve as we add more complex debugging capabilities.&lt;/p&gt;

&lt;div class=&quot;debugger-callout&quot; style=&quot;
	    border: 1px solid #eee;
	    padding: 1em;
	    border-radius: 6px;
	    background-color: #f9f9f9;
	    display: flex;
	    align-items: center;
	    gap: 1em;
	    margin: 2em 0;
&quot;&gt;
  &lt;img src=&quot;/rusty_trap.png&quot; alt=&quot;Rusty trap icon&quot; style=&quot;width: 40px; height: 40px; flex-shrink: 0;&quot; /&gt;
  &lt;div&gt;
    &lt;strong&gt;This post is part of my &lt;em&gt;Writing a Debugger&lt;/em&gt; series.&lt;/strong&gt;&lt;br /&gt;
    &lt;a href=&quot;/debugger/&quot;&gt;View all posts and upcoming topics →&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

</description>
        <pubDate>Sun, 17 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/08/17/ownership-driven-debugger-design.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/08/17/ownership-driven-debugger-design.html</guid>
        
        
      </item>
    
      <item>
        <title>How I read new code</title>
        <description>&lt;h2 id=&quot;ring-theory&quot;&gt;Ring Theory&lt;/h2&gt;

&lt;p&gt;When I was in college I studied computer science but I also took
a lot of math classes including abstract math.  My intro to abstract algebra went well and I enjoyed the class.  I decided to take the next course which was on ring theory, if I remember correctly.  The beginning of this class started off as sort of a shock, yes it got more abstract and harder to understand but our professor also had an interesting approach. He assigned reading, we were to go back and read the text book and then do some exercises.  And he didn’t really want to teach us the material via lecture, he wanted to have a discussion about the material in class.  Ok, this seemed more like what I would expect in a philosophy or literature class but in math?  I was really conditioned to learning math from lectures.&lt;/p&gt;

&lt;p&gt;My friend Rob was in the class as well and we would study together.  He and I tried to read the text.  Our professor had told us to read until the point we lose understanding and then come to office hours and ask him for help.  &lt;!--excerpt_start--&gt;The problem was that we didn’t really have a single point where we would lose understanding.  It was more that we had a gradual decay of understanding over time. Until at some point you realize you haven’t understood a thing for several paragraphs.&lt;!--excerpt_end--&gt;&lt;/p&gt;

&lt;p&gt;As our frustration with our lack of progress grew Rob and I decided to try something different.  We flipped to the end of the chapter and started reading the exercises we had been assigned.  We thought we should try to get through them anyway we could.  This ended up working extremely well.  I can’t remember any of the exercises we worked on that day but here’s one I found online in &lt;a href=&quot;https://users.ox.ac.uk/~kebl6646/ringtheory.pdf&quot;&gt;Ring Theory&lt;/a&gt; that can serve as an example:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;6.1
1. Suppose that R satisfies all ring axioms for a ring with identity, except that for all
 xy in R, x+y=y+x. Show that this axiom is implied by the rest.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So we read the question and then asked ourselves, “what does this even mean?”&lt;/p&gt;

&lt;p&gt;And we went back through the text to read and to find answers to the questions:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;What is a ring?&lt;/li&gt;
  &lt;li&gt;What are the axioms for a ring with identity?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then we at least understood the question.  From there we could work with the other axioms to try to prove that for all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xy in R, x+y=y+x&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;reading-code&quot;&gt;Reading Code&lt;/h2&gt;

&lt;p&gt;Ok, so what does this story about my ring theory class have to do with reading code?  Well, everything.  I think the take away from that story is that:&lt;/p&gt;

&lt;heroquote&gt;
The key to our understanding was the context created by the question in the exercise.
&lt;/heroquote&gt;

&lt;p&gt;And this is maybe one of the most impactful things I’ve ever learned in my life.&lt;/p&gt;

&lt;p&gt;So, when I read code that’s new to me I like to have a question to provide context.  Without a question I just read function after function and my understanding of the system as a whole just starts to degrade over time.&lt;/p&gt;

&lt;p&gt;When I’m starting out with a new system I get a small bug or task to work on and then read the code in the context of that bug or task.  This helps me focus on a few details at a time and how they relate to the bug I’m working on, and in turn to each other.  A small bug to fix or task to work on is something that a company might give you as a new hire, but I always treat as a way frame learning the system rather than as just an assignment.&lt;/p&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;top-down--bottom-up&quot;&gt;Top down / Bottom up&lt;/h2&gt;

&lt;p&gt;Reading functions is what I would call building a bottom up understanding of a system.  Functions are low-level details within the system and conceivably if you read and understood all of them you could build a complete understanding of the system.  However, what I find hard to understand when just reading functions is how they fit together.  Sure, you can get an idea of how a few of them fit together if you read them in the right order.  But, calls between functions can be far apart in the code base making it harder to build this understanding.  Creating a context around a bug builds, what feels to me like, a container that ties a few functions together and makes it easier to think about the relationships.  Pushing to the extreme is what I think of at top down thinking.  That is, puting the relationships first - what are the overarching activities or data flows that are in a system?  These activities and data flows are implemented using the functions in the system but the functions themselves don’t always reveal what the over arching flows are.&lt;/p&gt;

&lt;p&gt;I once started a new job at a company that was building a distributed system.  I was hired to do performance work and so I started looking at one of the nodes, let’s call it node A, in the system. Others had told me to take a look at node A because they thought it had high latency and would be a good place to start.  I dug into node A with the context of: where does this node do most of its work?  I did some profiling and made a few minor improvements. Great, I got something accomplished and I learned a little more about node A.&lt;/p&gt;

&lt;p&gt;From there I looked at another node and realized a couple of things:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The individual nodes were relatively easy to read.&lt;/li&gt;
  &lt;li&gt;There was a common structure, a base class, that all the nodes were built on that gave them a consistent structure and life cycle while providing pub/sub capability.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Understanding this structure helped me to fit other nodes into a mental model so that I could dig into them and understand them more quickly.&lt;/p&gt;

&lt;p&gt;I realized that to really look at the latency of the system as a whole I needed to better understand the system as a whole.  Looking at nodes randomly or based on rumor was only going to get me so far.&lt;/p&gt;

&lt;p&gt;This is what I consider top down vs. bottom up thinking.  Looking at individual nodes in the distributed system is bottom up.  It’s great for understanding the details of a system.  And in fact, for something like performance, a lot of improvements will come from making changes in the details.  That’s where work gets done, in the details.&lt;/p&gt;

&lt;p&gt;That said, a top down approach is also very useful and can lead to even more significant improvements in performance.  Before I describe how, let me first expand on what I mean by top down.  I think of the top down understanding of a system like this as understanding how the parts fit together and how information or work flows through the system.  In the case of this distributed system, each node performs a different kind of task and they collaborate to solve a larger problem.  How they are put together is critical.&lt;/p&gt;

&lt;p&gt;The classical performance strategy for a system like this is to
understand the critical path through the graph of nodes.  To do this
you need to understand the system, first of all where are the inputs?
Where does data come into the system from the external world?  This is
the “start” of your critical path.  Where do the outputs come from
(and how do they get delivered to the external world)?  This the end
of the critical path.  Then the critical path through the nodes in between can be identified and work can be done to improve it.&lt;/p&gt;

&lt;p&gt;Another example from the system I worked on was that there was a lot of time spent “between” the nodes or in nodes deciding when to act.  Some nodes took input from several other nodes and might have to decide to wait or timeout when data isn’t available from a specific input.  Acting sooner reduces latency but risks missing newer inputs.  We have to ask, which inputs are critical?  Are there certain inputs that should be acted on immediately (e.g. for safety reasons).  Are there certain inputs that can’t be dropped (also for safety reasons).  If so, not waiting for other inputs can be the right solution here and can save time.  But to decide this we need to understand the system holistically, to know what’s important and what’s critical to safety.&lt;/p&gt;

&lt;p&gt;Earlier, I promised to explain how top down thinking can be used to make significant improvements in performance.  Well, in my mind bottom up thinking is great at identifying where time is spend and then allowing you to make those activities faster thereby improving performance.  But, working top down is the best way to identify work that doesn’t have to be done at all.  By understanding the connections between the nodes we can identify redundant work or unimportant work and eliminate or deprioritize it so the rest of the system can focus on what’s important.&lt;/p&gt;

&lt;p&gt;While both top down and bottom up understandings are valuable I tend to have a preference for top down understanding.  Bottom up understanding is more concerned with details, like individual functions.  I find that:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;The functions aren’t that important on their own.  It’s how they work together that really shows what’s going on.&lt;/li&gt;
  &lt;li&gt;In a system, there are a lot of more details than there are architectures or overarching structures so keeping the top down view in mind is a lot easier.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem I find with focusing on the details is that I find that they generally change more rapidly than the overall system architecture.  So I tend to understand functions on demand – a bug or task takes me to a function or set of functions and I read it, understand it, work with, and move on.  And, I often found when working in high paced companies that the next time I came back to the function it had changed, or been removed, or was used in a completely new way.  So getting intimately knowledgeable about that function didn’t pay off.  But understanding larger aspects of the system helped so that when I had a bug or task to work on I knew which part of the system to look at.  The big blocks of functionality don’t change that frequently so I could come back to them with a broad understanding and come up to speed on the details quickly.&lt;/p&gt;

&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;

&lt;p&gt;Don’t get me wrong, the details are important too.&lt;/p&gt;

&lt;heroquote&gt;
The details are where the work happens. And the top down architecture or structure is where the decisions about what work to do are made.
&lt;/heroquote&gt;

&lt;p&gt;I guess what I’m saying is, when diving into a new system: don’t sweat the small stuff.&lt;/p&gt;
</description>
        <pubDate>Mon, 04 Aug 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/08/04/reading-code.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/08/04/reading-code.html</guid>
        
        
      </item>
    
      <item>
        <title>Handling multiple breakpoints in Trap Redux</title>
        <description>&lt;p&gt;In a &lt;a href=&quot;/2025/07/08/trap-back-up-and-running.html&quot;&gt;previous post &lt;/a&gt; I worked on getting Trap building on a new system and found that the unit tests were not passing.  It looked like I had found a real bug:&lt;/p&gt;

&lt;p&gt;I found:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The first call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; must be when the breakpoint in main is reached.  And the second call is 3 bytes later.  I think this is trying to resolve the stop in execution that happens when we are in state &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INFERIOR_SINGLE_STEPPING&lt;/code&gt; to step over the breakpoint.  And in this case the instruction pointer will have moved past the byte where we inserted the breakpoint – past the entire original instruction.  So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; should fail as this address isn’t one of our breakpoints.  That is, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; assumes we executed a one byte instruction and subtracts 1 from the instruction pointer to find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;This is not correct and wasn’t correct when I originally wrote it.  I don’t know how this worked before.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, to make progress I’m going to go back to the commit I started with at the beginning of &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;Handling multiple breakpoints in Trap&lt;/a&gt; to retrace my steps without introducing this bug.  So I’m starting at commit &lt;a href=&quot;https://github.com/joekain/trap/commit/fb7b3c6c71dd17940a5289d82edfeb82b73c7ba2&quot;&gt;fb7b3c6&lt;/a&gt;
.
First, I need to cherry-pick my changes to fix the build and disable address space randomization.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/inferior_load.c
@@ -5,6 +5,8 @@&lt;/span&gt;
 #include &amp;lt;sys/wait.h&amp;gt;
 #include &amp;lt;assert.h&amp;gt;
 #include &amp;lt;errno.h&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+#include &amp;lt;sys/personality.h&amp;gt;
+#include &amp;lt;signal.h&amp;gt;
&lt;/span&gt; #include &amp;lt;stdio.h&amp;gt;
 #include &amp;lt;stdlib.h&amp;gt;
 
&lt;span class=&quot;p&quot;&gt;@@ -12,6 +14,10 @@&lt;/span&gt; static inferior_t g_inferior;
 
 static void setup_inferior(const char *path, char *const argv[])
 {
&lt;span class=&quot;gi&quot;&gt;+  unsigned long old = personality(0xFFFFFFFF);
+  if (personality(old | ADDR_NO_RANDOMIZE) &amp;lt; 0) {
+    perror(&quot;Failed to set personality:&quot;);
+  }
&lt;/span&gt;   ptrace_util_traceme();
   execv(path, argv);
 }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Alright, it builds and the tests fail because I need to update the breakpoint addresses again.  I’ll follow the style of the nice clean up that I used in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; that sets some constants for the two addresses I use (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;)&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   test/test_breakpoint.c
@@ -2,6 +2,8 @@&lt;/span&gt;
 #include &amp;lt;trap.h&amp;gt;
 #include &amp;lt;unistd.h&amp;gt;
 
&lt;span class=&quot;gi&quot;&gt;+const char *ADDRESS_OF_MAIN = (char*)0x0000555555555151;
+
&lt;/span&gt; int g_breakpoint_count = 0;
 trap_inferior_t g_inferior = 0;
 trap_breakpoint_t g_bp = 0;
&lt;span class=&quot;p&quot;&gt;@@ -20,7 +22,7 @@&lt;/span&gt; int main()
 
   trap_breakpoint_set_callback(breakpoint_callback);
   g_inferior = trap_inferior_exec(&quot;./inferiors/hello&quot;, argv);
&lt;span class=&quot;gd&quot;&gt;-  g_bp = trap_inferior_set_breakpoint(g_inferior, (char *)0x000000000040079d);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  g_bp = trap_inferior_set_breakpoint(g_inferior, ADDRESS_OF_MAIN);
&lt;/span&gt;   trap_inferior_continue(g_inferior);
 
   assert(g_breakpoint_count == 1);
&lt;span class=&quot;p&quot;&gt;modified   test/test_multi_breakpoint.c
@@ -2,6 +2,8 @@&lt;/span&gt;
 #include &amp;lt;trap.h&amp;gt;
 #include &amp;lt;unistd.h&amp;gt;
 
&lt;span class=&quot;gi&quot;&gt;+const char *ADDRESS_OF_FOO = (char*)0x0000555555555131;
+
&lt;/span&gt; int g_breakpoint_count = 0;
 trap_inferior_t g_inferior = 0;
 trap_breakpoint_t g_bp = 0;
&lt;span class=&quot;p&quot;&gt;@@ -20,7 +22,7 @@&lt;/span&gt; int main()
 
   trap_breakpoint_set_callback(breakpoint_callback);
   g_inferior = trap_inferior_exec(&quot;./inferiors/loop&quot;, argv);
&lt;span class=&quot;gd&quot;&gt;-  g_bp = trap_inferior_set_breakpoint(g_inferior, (char *)0x000000000040076d);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  g_bp = trap_inferior_set_breakpoint(g_inferior, ADDRESS_OF_FOO);
&lt;/span&gt;   trap_inferior_continue(g_inferior);
 
   assert(g_breakpoint_count == 5);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;There are two interesting things to note here.&lt;/p&gt;

&lt;p&gt;The first is minor: I created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt; globals to store the address out of habit and now there is a warming that passing the constant to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_set_breakpoint&lt;/code&gt; discards &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt;.  I should fix the signature. This can be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As for the second, why do I already have a test_multi_breakpoint?!?!  I thought the point of &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;Handling multiple breakpoints in Trap&lt;/a&gt; was to build that functionality?  And strangely the test passes!  Ok, let me review &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;Handling multiple breakpoints in Trap&lt;/a&gt; again - the tagline does say I also worked on handling more than one breakpoint not just breakpoints triggered multiple times.&lt;/p&gt;

&lt;p&gt;I asked Claude 3.7 Sonnet Thought to summarize the post for me.  I like doing this because it can make sure I don’t miss anything.  The one complaint I have is that no matter how much brevity I ask for it always gives me too much.  Anyway, here’s the summary of the main steps:&lt;/p&gt;

&lt;h3 id=&quot;ai-generated-summary&quot;&gt;AI Generated Summary&lt;/h3&gt;
&lt;h4 id=&quot;initial-goals&quot;&gt;Initial Goals&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Address warnings from previous state machine implementation&lt;/li&gt;
  &lt;li&gt;Write a test for multiple breakpoints&lt;/li&gt;
  &lt;li&gt;Make the test pass&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;implementing-multiple-breakpoints-support&quot;&gt;Implementing Multiple Breakpoints Support&lt;/h4&gt;
&lt;ol&gt;
  &lt;li&gt;Created a new test (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test_multiple_breakpoints.c&lt;/code&gt;) that sets two breakpoints&lt;/li&gt;
  &lt;li&gt;Identified the core issue: reliance on a single global &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g_breakpoint&lt;/code&gt; variable&lt;/li&gt;
  &lt;li&gt;Replaced global variable with an array of breakpoints (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g_breakpoints[MAX_BREAKPOINTS]&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_allocate()&lt;/code&gt; to get breakpoints from the array&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;resolving-the-right-breakpoint&quot;&gt;Resolving the Right Breakpoint&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptrace_util_get_instruction_pointer()&lt;/code&gt; to determine current position&lt;/li&gt;
  &lt;li&gt;Created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve()&lt;/code&gt; to find which breakpoint was hit&lt;/li&gt;
  &lt;li&gt;Implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;find_breakpoint_with_target_address()&lt;/code&gt; for lookup&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;handling-duplicate-breakpoints&quot;&gt;Handling Duplicate Breakpoints&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Created a test that sets two breakpoints at the same address&lt;/li&gt;
  &lt;li&gt;Considered API design options (abort, return same handle, create distinct handles)&lt;/li&gt;
  &lt;li&gt;Decided on option to return the same handle for duplicate breakpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;removing-breakpoints&quot;&gt;Removing Breakpoints&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Added functionality to remove breakpoints (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_remove_breakpoint&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Created tests for removing breakpoints (both before execution and during callbacks)&lt;/li&gt;
  &lt;li&gt;Discovered issues when removing breakpoints from within callbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;key-bug-duplicate-breakpoint-reading&quot;&gt;Key Bug: Duplicate Breakpoint Reading&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Identified that when setting a second breakpoint at the same address, the code was reading the modified memory (with INT 3 already inserted) rather than using the original instruction&lt;/li&gt;
  &lt;li&gt;Fixed by checking if a breakpoint already exists at an address and copying its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;original_breakpoint_word&lt;/code&gt; rather than reading memory&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;deferred-removal-implementation&quot;&gt;Deferred Removal Implementation&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Added state tracking to breakpoints (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BREAKPOINT_ACTIVE&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BREAKPOINT_DEFERED_REMOVE&lt;/code&gt;)&lt;/li&gt;
  &lt;li&gt;Implemented deferred removal for breakpoints deleted during callbacks&lt;/li&gt;
  &lt;li&gt;Created &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;do_deferred_removals()&lt;/code&gt; to process pending removals&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;refactoring-and-cleanup&quot;&gt;Refactoring and Cleanup&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Balanced structure between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;start_breakpoint()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finish_breakpoint()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Identified duplication in code that iterates over breakpoint collections&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;final-issues&quot;&gt;Final Issues&lt;/h4&gt;
&lt;ul&gt;
  &lt;li&gt;Noted need for more robust breakpoint container&lt;/li&gt;
  &lt;li&gt;Identified remaining warnings indicating incomplete support for multiple inferiors&lt;/li&gt;
&lt;/ul&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;my-summary-of-the-summary&quot;&gt;My Summary of the Summary&lt;/h2&gt;

&lt;p&gt;So &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;Handling multiple breakpoints in Trap&lt;/a&gt;  was really about setting multiple breakpoints and not at all about handling a single breakpoint multiple times.  The latter works now.  Handling more than one breakpoint introduced the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; which is used when the inferior stops to look up which breakpoint was hit using the instruction pointer as a key.  Then we went on to work through the details of handling the breakpoints, fixing bugs, and cleaning up.&lt;/p&gt;

&lt;p&gt;Most of what I see in the summary sounds pretty reasonable with the exception of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt;.  As we saw &lt;a href=&quot;/2025/07/08/trap-back-up-and-running.html&quot;&gt;before&lt;/a&gt; it doesn’t work because each breakpoint trigger causes the inferior to stop twice:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;When the breakpoint is executed - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; works in this case.&lt;/li&gt;
  &lt;li&gt;When single stepping over the original instruction - &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; does not work in this case.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;retracing-our-steps&quot;&gt;Retracing Our Steps&lt;/h2&gt;

&lt;p&gt;Given that most of what was done in &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;Handling multiple breakpoints in Trap&lt;/a&gt; seems like the right things to do let’s retrace our steps using the summary above as a map and the old post for more detail.  When we get to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; we can take what we know now and come up with better test coverage and drive a more robust solution.&lt;/p&gt;

&lt;p&gt;So, let’s start with&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Created a new test (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;test_multiple_breakpoints.c&lt;/code&gt;) that sets two breakpoints&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s actually just take the original version of the test.  For trap implementation code I’m not sure I want to just take the old versions.  But for the tests I think I do.  By using the same test we can drive toward the same feature set and API while building a more robust solution.  Here’s the test:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;assert.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;trap.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_foo_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_main_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_bp_main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_bp_foo&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;breakpoint_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_bp_main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g_main_count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_bp_foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g_foo_count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;trap_breakpoint_set_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;breakpoint_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;g_inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;./inferiors/loop&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;g_bp_main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x0000000000400778&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;g_bp_foo&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x000000000040076d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_main_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_foo_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I will update the addresses though and extract them into constants.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   test/test_multiple_breakpoints.c
@@ -2,6 +2,9 @@&lt;/span&gt;
 #include &amp;lt;trap.h&amp;gt;
 #include &amp;lt;unistd.h&amp;gt;
 
&lt;span class=&quot;gi&quot;&gt;+const char *ADDRESS_OF_MAIN = (char*)0x0000555555555140;
+const char *ADDRESS_OF_FOO  = (char*)0x0000555555555131;
+
&lt;/span&gt; int g_foo_count = 0;
 int g_main_count = 0;
 
&lt;span class=&quot;p&quot;&gt;@@ -26,8 +29,8 @@&lt;/span&gt; int main()
 
   trap_breakpoint_set_callback(breakpoint_callback);
   g_inferior = trap_inferior_exec(&quot;./inferiors/loop&quot;, argv);
&lt;span class=&quot;gd&quot;&gt;-  g_bp_main = trap_inferior_set_breakpoint(g_inferior, (char *)0x0000000000400778);
-  g_bp_foo  = trap_inferior_set_breakpoint(g_inferior, (char *)0x000000000040076d);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  g_bp_main = trap_inferior_set_breakpoint(g_inferior, ADDRESS_OF_MAIN);
+  g_bp_foo  = trap_inferior_set_breakpoint(g_inferior, ADDRESS_OF_FOO);
&lt;/span&gt;   trap_inferior_continue(g_inferior);
 
   assert(g_main_count == 1);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And as expected this test fails because we use single global breakpoint and can’t track both the breakpoints the test uses.  Let’s work on fixing that.  Last time we:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;ol&gt;
    &lt;li&gt;Replaced global variable with an array of breakpoints (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g_breakpoints[MAX_BREAKPOINTS]&lt;/code&gt;)&lt;/li&gt;
    &lt;li&gt;Implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_allocate()&lt;/code&gt; to get breakpoints from the array&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;While I don’t think a global array of breakpoints is the ideal form let’s go in this direction for now.  But, eventually I think we’ll need to dynamically allocate breakpoints.&lt;/p&gt;

&lt;h2 id=&quot;implementing-multiple-breakpoints-with-working-resolution&quot;&gt;Implementing Multiple Breakpoints with Working Resolution&lt;/h2&gt;
&lt;p&gt;Now we can start working on a proper implementation by writing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_allocate&lt;/code&gt;.  Oh, now that I open up &lt;a href=&quot;https://github.com/joekain/trap/blob/82ade0369aa8a1b71c8ab785d36440a4206b0ea4/src/breakpoint.c&quot;&gt;breakpoint.c&lt;/a&gt; and start looking for a place to add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_allocate&lt;/code&gt; I find:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;breakpoint_resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; has been here for a while.  This old version ‘works’ because there is only one breakpoint so we can simple resolve to that single breakpoint.  It doesn’t matter what state the inferior is in as there is only one breakpoint to consider.  This function will need to do more to support multiple breakpoints.&lt;/p&gt;

&lt;p&gt;Ok, at least adding an array of breakpoints and a simple allocator is straightforward:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint.c
@@ -12,7 +12,8 @@&lt;/span&gt; struct breakpoint_t {
 };
 typedef struct breakpoint_t breakpoint_t;
 
&lt;span class=&quot;gd&quot;&gt;-breakpoint_t g_breakpoint;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+#define MAX_BREAKPOINTS 100
+breakpoint_t g_breakpoints[MAX_BREAKPOINTS];
&lt;/span&gt; 
 static trap_breakpoint_callback_t g_callback;
 
&lt;span class=&quot;p&quot;&gt;@@ -34,18 +35,28 @@&lt;/span&gt; static void breakpoint_set(trap_inferior_t inferior, breakpoint_t *bp)
   ptrace_util_poke_text(pid, bp-&amp;gt;aligned_address, modified_word);
 }
 
&lt;span class=&quot;gi&quot;&gt;+breakpoint_t *breakpoint_alloc() {
+  // linear search for unused breakpoint
+  for (breakpoint_t *bp = g_breakpoints; bp &amp;lt; g_breakpoints[MAX_BREAKPOINTS]; bp++) {
+    if (bp-&amp;gt;target_address == 0) {
+      // This is free, take it.
+      return bp;
+    }
+  }
+  assert(!&quot;Failed to find a free breakpoint!&quot;);
+}
+
&lt;/span&gt; trap_breakpoint_t trap_inferior_set_breakpoint(trap_inferior_t inferior,
                                                char *location)
 {
   const uintptr_t target_address = (uintptr_t)location;
   uintptr_t aligned_address = target_address &amp;amp; ~(0x7UL);
&lt;span class=&quot;gd&quot;&gt;-  breakpoint_t *bp = &amp;amp;g_breakpoint;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  breakpoint_t *bp = breakpoint_alloc();
&lt;/span&gt; 
   bp-&amp;gt;original_breakpoint_word = ptrace_util_peek_text(inferior,
 						       aligned_address);
   bp-&amp;gt;target_address = target_address;
   bp-&amp;gt;aligned_address = aligned_address;
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;   breakpoint_set(inferior, bp);
 
   return bp;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Next, we need to rethink &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt;, it’s used like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;breakpoint_resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// ...&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;step_over_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_regs_struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;pid_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoint_resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;// &amp;lt;===== Here ====&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;breakpoint_remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;breakpoint_trigger_callback&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ptrace_util_get_regs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ptrace_util_set_instruction_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ptrace_util_single_step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;finish_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;breakpoint_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoint_resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;          &lt;span class=&quot;c1&quot;&gt;// &amp;lt;====== Here ====&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;breakpoint_set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ptrace_util_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior_state_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;breakpoint_handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior_state_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INFERIOR_RUNNING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;step_over_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INFERIOR_SINGLE_STEPPING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INFERIOR_SINGLE_STEPPING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;finish_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INFERIOR_RUNNING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;nl&quot;&gt;default:&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step_over_breakpoint&lt;/code&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; is fine.  That is, the inferior was running normally  and stopped at a normal breakpoint and want to step over it.  Inferior’s instruction pointer points to the end of the breakpoint instruction (0xCC) so one byte after the breakpoint target address.&lt;/p&gt;

&lt;p&gt;But, in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finish_breakpoint&lt;/code&gt; we have just finished single stepping and the inferior’s instruction pointer points to the start of the next instruction.&lt;/p&gt;

&lt;p&gt;I’m going to remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; call from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_finish&lt;/code&gt;.  But, what do I replace it with?  You might think that we could subtract something other than 1 from the instruction pointer to find the corresponding target_address.  There are two things that make this impossible:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;x86 has variable sized instructions.  We could figure out the size of the instruction if we were at the start of the instruction by decoding it.  But, we are past that instruction so we have no chance of decoding it.&lt;/li&gt;
  &lt;li&gt;The instruction that was just executed could have been some sort of branching instruction (e.g jump or call) so that by the end the instruction pointer has branched far away from the start of the original instruction.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, basically the inferior’s current instruction pointer really tells us nothing about the address of the instruction that we just finished stepping over.  I think this means that we have to save the breakpoint across the single step.  There are two ways we can do this:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Stash it in a global.  This is easy but I’m worried that as we develop new features in trap that this will be unsafe.  I can’t quite imagine the scenarios that will lead to problems but it doesn’t feel right.&lt;/li&gt;
  &lt;li&gt;Restructure the breakpoint handling.  I wonder if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_handle&lt;/code&gt; shouldn’t return to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt; instead it could call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;waitpid&lt;/code&gt; itself.  Then, it can just keep the breakpoint it found initially.&lt;/li&gt;
&lt;/ol&gt;

&lt;heroquote&gt;
This isn&apos;t a matter of making breakpoint_resolve smarter; it&apos;s a structural problem requiring us to maintain state across operations.
&lt;/heroquote&gt;

&lt;p&gt;So I’m proposing a structural change that would look something like this:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior_state_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;breakpoint_handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior_state_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// This is the critical fix - maintaining breakpoint state&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// across the single-step operation&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoint_resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INFERIOR_RUNNING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;step_over_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;waitpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WIFSTOPPED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WSTOPSIG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SIGTRAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;finish_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WIFEXITED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop in trap_inferior_continue: 0x%x&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, so to explain again: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_continue&lt;/code&gt; will wake up when the breakpoint is triggered and call this new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_handle&lt;/code&gt; which will:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Look up (or resolve) the breakpoint.&lt;/li&gt;
  &lt;li&gt;Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step_over_breakpoint&lt;/code&gt; to remove the breakpoint and call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PTRACE_SINGLESTEP&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;waitpid&lt;/code&gt; to wait for the inferior to finish single stepping.&lt;/li&gt;
  &lt;li&gt;Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finish_breakpoint&lt;/code&gt; to restore the original breakpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this way &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_handle&lt;/code&gt; doesn’t return back to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferor_continue&lt;/code&gt; between single stepping and finishing.  This means it can maintain state across the single step by simply keeping it on the stack.  I think this is our solution.&lt;/p&gt;

&lt;p&gt;Now we just have to fix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_finish&lt;/code&gt; to take the breakpoint, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bp&lt;/code&gt;, as an argument (I’ll leave that as an exercise for the reader) and implement a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; to look the address up out of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;g_breakpoints&lt;/code&gt; array.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;breakpoint_resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;pid_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user_regs_struct&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;uintptr_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;ptrace_util_get_regs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;regs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// Back up to the start of the breakpoint&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// linear search for target_address&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;breakpoint_t&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;g_breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;g_breakpoints&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MAX_BREAKPOINTS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;target_address&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to find existing breakpoint at address&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;With a few more minor changes the tests pass!&lt;/p&gt;

&lt;p&gt;The changes are to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_handle&lt;/code&gt; to:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Take over the responsibility of calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;resolve_breakpoint&lt;/code&gt;. Pass
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bp&lt;/code&gt; to both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;step_over_breakpoint&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;finish_breakpoint&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Clean up some types&lt;/li&gt;
  &lt;li&gt;Make sure to return a value reflecting the current state in all successful branches.&lt;/li&gt;
&lt;/ol&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior_state_t&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;breakpoint_handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;trap_inferior_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior_state_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;pid_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// This is the critical fix - maintaining breakpoint state&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// across the single-step operation&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;trap_breakpoint_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;breakpoint_resolve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INFERIOR_RUNNING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;step_over_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;waitpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WIFSTOPPED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WSTOPSIG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SIGTRAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;finish_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INFERIOR_RUNNING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WIFEXITED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// This is a lie, but it should cause trap_inferior_continue to wait again.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;INFERIOR_RUNNING&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stderr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Unexpected stop in trap_inferior_continue: 0x%x&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;

&lt;p&gt;We have fixed our bug by making a structural change to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_handle&lt;/code&gt; that merges what was two calls into a single call that can maintain state about the breakpoint throughout the entire handling process.  The old test that we originally wrote now passes.&lt;/p&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;While I have put together a &lt;a href=&quot;https://github.com/joekain/trap/pull/11&quot;&gt;PR&lt;/a&gt; for this change we really should consider two options now.  We can continue retracing our steps through  &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;Handling multiple breakpoints in Trap&lt;/a&gt; or we can take what we learned and fix the bug directly in the master branch.&lt;/p&gt;

&lt;p&gt;Assuming the rest of the changes in the original &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;Handling multiple breakpoints in Trap&lt;/a&gt; were good then I think porting the fix forward to the master branch makes the most sense.  That’s not to say that the idea to retrace our steps was a bad one.  There a number of advantages in resetting back to an old commit to debug.  For me, I was able to look at the change fresh to better see what was going wrong.  And from a test first perspective, resetting insures that we start from a passing test and can add one failing test at a time.&lt;/p&gt;

&lt;p&gt;And while resetting has these advantages we should also keep in mind that once we leverage these advantages to solve the problem we shouldn’t let ourselves become overly attached to the new branch.  If taking our new understanding and porting our fix forward yields quicker results then we should definitely do that.  Though we must insure that we validate the fix properly in the master branch.&lt;/p&gt;

&lt;p&gt;And I think that’s the case here because there are a lot of steps left in the roadmap we have based on the summary of the old post.  We have done things like:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Removing Breakpoints&lt;/li&gt;
  &lt;li&gt;Deferred Removal&lt;/li&gt;
  &lt;li&gt;Dealing with setting a duplicate breakpoint.&lt;/li&gt;
  &lt;li&gt;Clean up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By porting forward we can keep all of these features and their tests.  I’ll take care of &lt;a href=&quot;https://github.com/joekain/trap/commit/fc193b857fce1a8e95176992e134dd3048923064&quot; title=&quot;Somehow I screwed up creating a PR?&quot;&gt;porting the fix forward behind the scenes&lt;/a&gt; and end this post.  This should leave trap in working state on my new development system.  And as this post ends we should take away the following lessons:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Some bugs require a structural change to resolve especially when managing state throughout a longer process.&lt;/li&gt;
  &lt;li&gt;The technique of resetting and retracing our steps is a powerful one that can help us better focus on the problem in front on us.  And once that problem is solved we should reevaluate the need to continue retracing vs porting the fix forward.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;debugger-callout&quot; style=&quot;
	    border: 1px solid #eee;
	    padding: 1em;
	    border-radius: 6px;
	    background-color: #f9f9f9;
	    display: flex;
	    align-items: center;
	    gap: 1em;
	    margin: 2em 0;
&quot;&gt;
  &lt;img src=&quot;/rusty_trap.png&quot; alt=&quot;Rusty trap icon&quot; style=&quot;width: 40px; height: 40px; flex-shrink: 0;&quot; /&gt;
  &lt;div&gt;
    &lt;strong&gt;This post is part of my &lt;em&gt;Writing a Debugger&lt;/em&gt; series.&lt;/strong&gt;&lt;br /&gt;
    &lt;a href=&quot;/debugger/&quot;&gt;View all posts and upcoming topics →&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

</description>
        <pubDate>Mon, 28 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/07/28/multiple-breakpoints-redux.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/07/28/multiple-breakpoints-redux.html</guid>
        
        
      </item>
    
      <item>
        <title>The Official Relaunch of &quot;A System Programming Blog&quot;</title>
        <description>&lt;p&gt;Wow—exactly 10 years ago, I launched this blog. And while it only lived for a few short months, the original intent still resonates.&lt;/p&gt;

&lt;p&gt;Today, I’m relaunching it with the same goals at heart:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;To explore the Rust programming language&lt;/li&gt;
  &lt;li&gt;To demonstrate how to build low-level systems projects in C&lt;/li&gt;
  &lt;li&gt;To investigate emerging tools and technologies&lt;/li&gt;
  &lt;li&gt;To write long-form technical series&lt;/li&gt;
  &lt;li&gt;And to occasionally dive into smaller, focused topics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve already written two new posts in which I get back up to speed on the debugger project and get the two projects building again.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/07/08/trap-back-up-and-running.html&quot;&gt;Trap - Back up and Running&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/07/09/cleaning-up-a-rusty-trap.html&quot;&gt;Cleaning a rusty_trap&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;/2025/07/16/question-and-answer-thinking.html&quot;&gt;Question and Answer Thinking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here’s what’s coming up:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;I’ll finish work on the &lt;a href=&quot;/debugger/&quot;&gt;how to write a debugger&lt;/a&gt; series, likely with a more streamlined scope.&lt;/li&gt;
  &lt;li&gt;I’m launching a new series on Systems Thinking—these posts will appear between deeper technical dives.&lt;/li&gt;
  &lt;li&gt;I’ll also begin a new series on building a dynamic loader, using it to understand how executable loading works—and maybe even build a full binary interpreter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you’re new here or remember the original launch, thanks for being part of this reboot. This time, I’m going the distance.&lt;/p&gt;
</description>
        <pubDate>Wed, 16 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/07/16/relaunch.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/07/16/relaunch.html</guid>
        
        
      </item>
    
      <item>
        <title>Question and Answer Thinking</title>
        <description>&lt;p&gt;In this post I want to describe one form of Systems Thinking that I apply in my work and honestly in many aspects of my life.  This is what I call Question and Answer thinking.&lt;/p&gt;

&lt;p&gt;Whenever I start investigating an issue, solving a problem, or thinking about a new system to build I start by asking myself a question.  I keep a notes document and I write this question down.  Then, I take that question as a heading and start trying to answer it.&lt;/p&gt;

&lt;p&gt;The first question is usually very broad, extremely broad. If I’m looking a bug in an OpenGL driver implementation in which the wrong image is rendered I simply ask: why did it draw this?  If I’m looking at a high latency issue in a component of an AV system I might ask how does this component spend its time?&lt;/p&gt;

&lt;p&gt;Are these questions helpful?  These questions are obvious: of course we don’t know why the wrong image is drawn or how the time is spent.  Or maybe we do know why in which case the question is even less helpful, right?&lt;/p&gt;

&lt;p&gt;I find these questions and writing down the questions to be helpful in setting the context of the investigation, or design, or whatever work I’m trying to do.  Writing down the questions sets an implicit goal of trying to write down an answer.  When we arrive at an answer, writing it down is very helpful in that it serves to&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Document something true, and apparently not obvious, about our system.&lt;/li&gt;
  &lt;li&gt;Helps to maintain context of what we know about the system&lt;/li&gt;
  &lt;li&gt;Directs further investigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, so we have a broad, obvious question written down which sets a goal to find an answer and document truth.  Great, now what?  Well, I usually start by asking a smaller question?  Let’s continue with the example of looking at performance in an AV system.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Q: How does this component spend its time?
    &lt;ul&gt;
      &lt;li&gt;Q: What tools do I have for investigating how a component spends its time?
        &lt;ul&gt;
          &lt;li&gt;A: I have a tracing tool to extract and visualize timing data.&lt;/li&gt;
          &lt;li&gt;I’ve run the tracing tool and generated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mycomponent.trace.html&lt;/code&gt;.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;Q: What does the trace say?
        &lt;ul&gt;
          &lt;li&gt;A: The trace shows a lot of time spent in function foo?&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, this is an example refining questions and getting answers.  I have questions and answers, and also maybe some other notes I took along the way explaining what I did or things that were unexpected.  Now, I like to do this in a system that allows for folding.  So I can fold closed questions.  I’ve done this in org-mode and in Google docs.  I also like to do a little curating of the answered questions by reordering the question and the answer.  That is, when I have an answer I make the answer the headline and put the question under.  If I do that I might end up with:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A: The component spends a lot of time in function foo.
    &lt;ul&gt;
      &lt;li&gt;Q: How does this component spend its time?&lt;/li&gt;
      &lt;li&gt;A: I have a tracing tool to extract and visualize timing data.
        &lt;ul&gt;
          &lt;li&gt;Q: What tools do I have for investigating how a component spends its time?&lt;/li&gt;
          &lt;li&gt;I’ve run the tracing tool and generated &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mycomponent.trace.html&lt;/code&gt;.&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
      &lt;li&gt;A: The trace shows a lot of time spent in function foo?
        &lt;ul&gt;
          &lt;li&gt;Q: What does the trace say?&lt;/li&gt;
        &lt;/ul&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, depending on the tools you are using this curating can be a little tedious.  I’ll admit that I found it tedious just now while writing the example.  That said, in a real investigation you might spend quite a bit of time, say, reading and interpreting the trace so maybe this tedium is broken up by real work and overhead is low.&lt;/p&gt;

&lt;p&gt;The advantage of curating the answers to the top is that when folded it reads like this:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A: The component spends a lot of time in function foo.
    &lt;ul&gt;
      &lt;li&gt;A: I have a tracing tool to extract and visualize timing data.&lt;/li&gt;
      &lt;li&gt;A: The trace shows a lot of time spent in function foo?&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On one level, this is as I said before in that the answers document true and not so obvious facts about our system.  But when we use this system of refinement, hide the questions, and just look at the answers we have not just a set of true statements but a true statement that answered our big broad question and a series of justifications of that fact.  Further, we can open up any level to find notes on how we arrived at these conclusions.&lt;/p&gt;

&lt;p&gt;This is very powerful.&lt;/p&gt;

&lt;p&gt;Imagine you were working on a hard problem that’s going to take a while, and you get pulled off of it to work on some high priority bug.  When you come back to the hard problem you can review a series of true statements about the problem that summarize what you know so far organized as a line of reasoning that you believe is directed toward the solution to your problem.&lt;/p&gt;

&lt;p&gt;One thing to note, just because I’ve answered the initial broad obvious question doesn’t mean I’m done.  In our performance example we concluded “The component spends a lot of time in function foo.” but so what?  This doesn’t fix the problem or even really tell us how to fix the problem.  But, it might help us figure out how to fix the problem if we ask more questions given our new knowledge.  On one hand maybe we should have asked an even broader question first, on the other it doesn’t really matter.  I have no objection to adding more broad questions at the top level.  I can continue with any or all of:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;How much time should we spend in the function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;?&lt;/li&gt;
  &lt;li&gt;Are we hitting a particularly bad case in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;foo&lt;/code&gt;?&lt;/li&gt;
  &lt;li&gt;How does the component spend its time in a well performing case?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Answering these new questions will give me a better understanding of the system and what went wrong in this particular issue.&lt;/p&gt;

&lt;p&gt;Now, I’ve been doing this for… decades, I guess.  And at this point I not only take notes this way but I think this way.  This is how I approach problems: I ask a question and either answer it or break the question down into smaller questions until I know the answer or know how to find the answer.  Finding the answer can mean&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Read some code&lt;/li&gt;
  &lt;li&gt;Stop something in a debugger and inspect the state&lt;/li&gt;
  &lt;li&gt;Ask someone&lt;/li&gt;
  &lt;li&gt;Etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if I know how to find the answer I do that.  I write down what I found in the debugger or what my coworker told me in the notes and the set the answer.  I continue this way until the problem is solved.&lt;/p&gt;

&lt;p&gt;Now, this idea of “I ask a question and either answer it or break the question down into smaller questions until I know the answer” is very similar to the way I write software.  We breakdown functionality using successive refinement making smaller and smaller functions until the function are easy to implement.&lt;/p&gt;

&lt;p&gt;Similarly to writing this code, this strategy is good for large problems.  It’s overkill for things that are very simple.  Starting a doc, writing down questions, curating answers doesn’t make sense if you already know what is wrong.  If you just wrote some new code and now it crashed and you can realize, “oops I passed the wrong argument here and it’s going to dereference a NULL pointer in this case” then just fix it.  Don’t write these notes and don’t refine any questions.  But, if you have a large problem to work on that might take a few days, or longer, then this system can be very powerful and this approach to systems thinking can really help you dig into problems, navigate systems, and build amazing things.&lt;/p&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

</description>
        <pubDate>Wed, 16 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/07/16/question-and-answer-thinking.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/07/16/question-and-answer-thinking.html</guid>
        
        
      </item>
    
      <item>
        <title>Cleaning a rusty_trap.</title>
        <description>&lt;p&gt;In my last post I came back to my debugger project and got set up to build it and tried to get the tests to pass.  In this post I’m doing the same with the rust version, &lt;a href=&quot;https://github.com/joekain/rusty_trap&quot;&gt;rusty_trap&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I want to get in the habit of tracking where I’m starting these posts.  So I’m at commit &lt;a href=&quot;https://github.com/joekain/rusty_trap/commit/6c22cbaad3ba97cefc3826e82176e99364bc9f7f&quot;&gt;6c22cba&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I try to compile I get a few errors:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-*- mode: compilation; default-directory: &quot;/plink:wsl:/home/jkain/projects/rusty_trap/&quot; -*-
Compilation started at Sun Jul  6 08:53:57

/home/jkain/.cargo/bin/cargo b
warning: no edition set: defaulting to the 2015 edition while the latest is 2024
   Compiling nix v0.3.9 (https://github.com/joekain/nix-rust.git?branch=WaitStatus#4ae37a8b)
error[E0591]: can&apos;t transmute zero-sized type
   --&amp;gt; /home/jkain/.cargo/git/checkouts/nix-rust-9e641bdbdbe8194f/4ae37a8/src/sched.rs:168:20
	|
168 | ...   ffi::clone(mem::transmute(callback), ptr as *mut c_void, flags, &amp;amp;...
	|                  ^^^^^^^^^^^^^^
	|
	= note: source type: for&amp;lt;&apos;a&amp;gt; extern &quot;C&quot; fn(*mut Box&amp;lt;(dyn FnMut() -&amp;gt; isize + &apos;a)&amp;gt;) -&amp;gt; i32 {callback}
	= note: target type: *const for&amp;lt;&apos;a&amp;gt; extern &quot;C&quot; fn(*const Box&amp;lt;(dyn FnMut() -&amp;gt; isize + &apos;a)&amp;gt;) -&amp;gt; i32
	= help: cast with `as` to a pointer instead

For more information about this error, try `rustc --explain E0591`.
error: could not compile `nix` (lib) due to 1 previous error

Compilation exited abnormally with code 101 at Sun Jul  6 08:53:58
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a problem building the nix crate which seems unexpected.  I think I have some old version locked.  Now that I think about it, everything must have some old version locked.  But, looking at Cargo.toml I have:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;nn&quot;&gt;[package]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;rusty_trap&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0.1.0&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;authors&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Joseph Kain &amp;lt;joekain@gmail.com&amp;gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[dependencies]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;libc&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0.1.8&quot;&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[dependencies.nix]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;https://github.com/joekain/nix-rust.git&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;0.3.8&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;branch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;WaitStatus&quot;&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[lib]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;rusty_trap&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;src/lib.rs&quot;&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[[bin]]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;twelve&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;tests/inferiors/twelve.rs&quot;&lt;/span&gt;

&lt;span class=&quot;nn&quot;&gt;[[bin]]&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;loop&quot;&lt;/span&gt;
&lt;span class=&quot;py&quot;&gt;path&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;tests/inferiors/loop.rs&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;So i have nix setup to track my repo with my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WaitStatus&lt;/code&gt; branch, which is woefully out of date.&lt;/p&gt;

&lt;p&gt;Now, I think there might have been a change I made that I never created a pull request for in proper nix.  But, let’s see what happens if I just list nix as a dependency and pick of the officilal version.  And I’ll do a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo update&lt;/code&gt; to get new versions of all the dependencies.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;@@ -4,12 +4,8 @@&lt;/span&gt; version = &quot;0.1.0&quot;
 authors = [&quot;Joseph Kain &amp;lt;joekain@gmail.com&amp;gt;&quot;]
 
 [dependencies]
&lt;span class=&quot;gd&quot;&gt;-libc     = &quot;0.1.8&quot;
-
-[dependencies.nix]
-git = &quot;https://github.com/joekain/nix-rust.git&quot;
-version = &quot;0.3.8&quot;
-branch = &quot;WaitStatus&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+libc = &quot;0.2.174&quot;
+nix = &quot;0.30.1&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ok, this still doesn’t build but at least it’s complaining about problems in rusty_trap now.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-*- mode: compilation; default-directory: &quot;/plink:wsl:/home/jkain/projects/rusty_trap/&quot; -*-
Compilation started at Sun Jul  6 09:08:55

/home/jkain/.cargo/bin/cargo b
warning: no edition set: defaulting to the 2015 edition while the latest is 2024
 Downloading crates ...
  Downloaded cfg_aliases v0.2.1
  Downloaded nix v0.30.1
   Compiling cfg_aliases v0.2.1
   Compiling libc v0.2.174
   Compiling cfg-if v1.0.1
   Compiling bitflags v2.9.1
   Compiling nix v0.30.1
   Compiling rusty_trap v0.1.0 (/home/jkain/projects/rusty_trap)
error[E0432]: unresolved import `nix::unistd::Fork`
 --&amp;gt; src/lib.rs:5:18
  |
5 | use nix::unistd::Fork::*;
  |                  ^^^^ could not find `Fork` in `unistd`

error[E0432]: unresolved import `nix::sys::wait`
 --&amp;gt; src/lib.rs:9:15
  |
9 | use nix::sys::wait::*;
  |               ^^^^ could not find `wait` in `sys`

error[E0433]: failed to resolve: could not find `ptrace` in `sys`
  --&amp;gt; src/ptrace_util/mod.rs:3:15
   |
3  | use nix::sys::ptrace::ptrace::*;
   |               ^^^^^^ could not find `ptrace` in `sys`
   |
note: found an item that was configured out
  --&amp;gt; /home/jkain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/sys/mod.rs:81:13
   |
81 |     pub mod ptrace;
   |             ^^^^^^
note: the item is gated behind the `ptrace` feature
  --&amp;gt; /home/jkain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/sys/mod.rs:79:8
   |
79 |     #![feature = &quot;ptrace&quot;]
   |        ^^^^^^^

error[E0432]: unresolved import `nix::sys::ptrace`
 --&amp;gt; src/ptrace_util/mod.rs:2:15
  |
2 | use nix::sys::ptrace::*;
  |               ^^^^^^ could not find `ptrace` in `sys`

warning: unused import: `nix::unistd::*`
 --&amp;gt; src/lib.rs:4:5
  |
4 | use nix::unistd::*;
  |     ^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `handle`
  --&amp;gt; src/lib.rs:22:18
   |
22 | use breakpoint::{handle, TrapBreakpoint};
   |                  ^^^^^^

warning: `extern` declarations without an explicit ABI are deprecated
  --&amp;gt; src/lib.rs:43:5
   |
43 |     extern {
   |     ^^^^^^ help: explicitly specify the &quot;C&quot; ABI: `extern &quot;C&quot;`
   |
   = note: `#[warn(missing_abi)]` on by default

error[E0599]: no variant or associated item named `Sys` found for enum `Errno` in the current scope
  --&amp;gt; src/lib.rs:82:24
   |
82 |             Err(Error::Sys(errno::EAGAIN)) =&amp;gt; continue,
   |                        ^^^ variant or associated item not found in `Errno`

warning: variable does not need to be mutable
  --&amp;gt; src/breakpoint/mod.rs:35:34
   |
35 | pub fn handle&amp;lt;F&amp;gt;(inf: Inferior,  mut callback: &amp;amp;mut F) -&amp;gt; InferiorState
   |                                  ----^^^^^^^^
   |                                  |
   |                                  help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

Some errors have detailed explanations: E0432, E0433, E0599.
For more information about an error, try `rustc --explain E0432`.
warning: `rusty_trap` (lib) generated 4 warnings
error: could not compile `rusty_trap` (lib) due to 5 previous errors; 4 warnings emitted

Compilation exited abnormally with code 101 at Sun Jul  6 09:08:56
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So it seems like either my additions to nix for wait, and ptrace aren’t present or they moved.  There is something about &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fork&lt;/code&gt; as well so maybe things just moved.  Let’s take a look at the &lt;a href=&quot;https://docs.rs/nix/latest/nix/unistd/fn.fork.html&quot;&gt;nix docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.rs/nix/latest/nix/unistd/fn.fork.html&quot;&gt;Fork&lt;/a&gt;, looks like this should be in lowercase.  No idea why it was capitalized before but let’s fix that.  Fixed.&lt;/p&gt;

&lt;p&gt;Also, this use line gives us some hints about waitpid as well:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;nix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::{&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;wait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waitpid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;unistd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fork&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ForkResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}};&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The documentation also poits out&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Available on crate feature process only.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is new, I guess, and also relevant beause just using these doesn’t work:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;warning: no edition set: defaulting to the 2015 edition while the latest is 2024
   Compiling rusty_trap v0.1.0 (/home/jkain/projects/rusty_trap)
error[E0432]: unresolved imports `nix::sys::wait`, `nix::unistd::fork`, `nix::unistd::ForkResult`, `nix::unistd::errno`
   --&amp;gt; src/lib.rs:4:16
	|
4   | ...sys::wait::waitpid,unistd::{fork, ForkResult, Error, errno}, sys::si...
	|         ^^^^                   ^^^^  ^^^^^^^^^^         ^^^^^
	|         |                      |     |                  |
	|         |                      |     |                  no `errno` in `unistd`
	|         |                      |     |                  help: a similar name exists in the module: `Errno`
	|         |                      |     no `ForkResult` in `unistd`
	|         |                      no `fork` in `unistd`
	|         could not find `wait` in `sys`
	|
note: found an item that was configured out
   --&amp;gt; /home/jkain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/sys/mod.rs:181:13
	|
181 |     pub mod wait;
	|             ^^^^
note: the item is gated behind the `process` feature
   --&amp;gt; /home/jkain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/sys/mod.rs:180:8
	|
180 |     #![feature = &quot;process&quot;]
	|        ^^^^^^^
note: found an item that was configured out
   --&amp;gt; /home/jkain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/unistd.rs:278:15
	|
278 | pub unsafe fn fork() -&amp;gt; Result&amp;lt;ForkResult&amp;gt; {
	|               ^^^^
note: the item is gated behind the `process` feature
   --&amp;gt; /home/jkain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/unistd.rs:159:4
	|
159 | #![feature = &quot;process&quot;]
	|    ^^^^^^^
note: found an item that was configured out
   --&amp;gt; /home/jkain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/unistd.rs:209:10
	|
209 | pub enum ForkResult {
	|          ^^^^^^^^^^
note: the item is gated behind the `process` feature
   --&amp;gt; /home/jkain/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/unistd.rs:159:4
	|
159 | #![feature = &quot;process&quot;]
	|    ^^^^^^^
	= help: consider importing this module instead:
			nix::errno
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Oh, I need to add this to the Cargo file on the nix dependency.  Looks like I need something similar for ptrace.  This is new to me and seems pretty cool that I can pick and choose pieces of nix (and other crates I assume) to include.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   Cargo.toml
@@ -5,7 +5,7 @@&lt;/span&gt; authors = [&quot;Joseph Kain &amp;lt;joekain@gmail.com&amp;gt;&quot;]
 
 [dependencies]
 libc = &quot;0.2.174&quot;
&lt;span class=&quot;gd&quot;&gt;-nix = &quot;0.30.1&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+nix = {version = &quot;0.30.1&quot;, features = [&quot;process&quot;, &quot;ptrace&quot;]}
&lt;/span&gt; 
 [lib]
 name = &quot;rusty_trap&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, &lt;a href=&quot;https://docs.rs/nix/latest/nix/sys/ptrace/index.html&quot;&gt;ptrace is interesting&lt;/a&gt;.  It looks like someone had a similar idea to my idea to &lt;a href=&quot;/2015/09/07/refactoring-ptrace-code.html&quot;&gt;create separate functions for each of the different ptrace requests&lt;/a&gt; (PTRACE_CONT, PTRACE_PEEKTEXT, etc.) with proper type checking.  That’s fantastic.&lt;/p&gt;

&lt;p&gt;These are perfect and I’ll use them directly.&lt;/p&gt;

&lt;p&gt;Well, in the short term I’ll implement ptrace_util on top of them which will&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Let me review the types and see how they compare to the types I choose.&lt;/li&gt;
  &lt;li&gt;Leave the code that uses ptrace_util alone for now.  I like to mimize changes until we get the tests running and passing again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hmm, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PEEKTEXT&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POKETEXT&lt;/code&gt; don’t seem to have coresponding functions in the new nix ptrace.  However, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PEEKDATA&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POKEDATA&lt;/code&gt; do have functions (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;read&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;write&lt;/code&gt;) and Linux doesn’t actually separate text and data so I should be able to use these instead.&lt;/p&gt;

&lt;p&gt;And ok, now it builds at least.  No idea if it works but these seem pretty straightfoward so I hope they do.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   Cargo.toml
@@ -5,7 +5,7 @@&lt;/span&gt; authors = [&quot;Joseph Kain &amp;lt;joekain@gmail.com&amp;gt;&quot;]
 
 [dependencies]
 libc = &quot;0.2.174&quot;
&lt;span class=&quot;gd&quot;&gt;-nix = &quot;0.30.1&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+nix = {version = &quot;0.30.1&quot;, features = [&quot;process&quot;, &quot;ptrace&quot;]}
&lt;/span&gt; 
 [lib]
 name = &quot;rusty_trap&quot;
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;modified   src/ptrace_util/mod.rs
@@ -1,8 +1,6 @@&lt;/span&gt;
 use libc::pid_t;
&lt;span class=&quot;gd&quot;&gt;-use nix::sys::ptrace::*;
-use nix::sys::ptrace::ptrace::*;
-use std::ptr;
-use libc::c_void;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+use nix::sys::ptrace;
+use nix::unistd::Pid;
&lt;/span&gt; 
 use inferior::InferiorPointer;
 
&lt;span class=&quot;p&quot;&gt;@@ -39,44 +37,49 @@&lt;/span&gt; pub mod user {
 }
 
 pub fn trace_me() -&amp;gt; () {
&lt;span class=&quot;gd&quot;&gt;-    ptrace(PTRACE_TRACEME, 0, ptr::null_mut(), ptr::null_mut())
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    ptrace::traceme()
&lt;/span&gt;         .ok()
         .expect(&quot;Failed PTRACE_TRACEME&quot;);
 }
 
 pub fn get_instruction_pointer(pid: pid_t) -&amp;gt; InferiorPointer {
&lt;span class=&quot;gd&quot;&gt;-    let raw = ptrace(PTRACE_PEEKUSER, pid, user::regs::RIP as * mut c_void, ptr::null_mut())
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let raw = ptrace::read_user(Pid::from_raw(pid), user::regs::RIP as ptrace::AddressType)
&lt;/span&gt;         .ok()
         .expect(&quot;Failed PTRACE_PEEKUSER&quot;);
     InferiorPointer(raw as u64)
 }
 
 pub fn set_instruction_pointer(pid: pid_t, ip: InferiorPointer) -&amp;gt; () {
&lt;span class=&quot;gd&quot;&gt;-    ptrace(PTRACE_POKEUSER, pid, user::regs::RIP as * mut c_void, ip.as_voidptr())
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    ptrace::write_user(Pid::from_raw(pid), user::regs::RIP as ptrace::AddressType, ip.as_i64())
&lt;/span&gt;         .ok()
         .expect(&quot;Failed PTRACE_POKEUSER&quot;);
 }
 
 pub fn cont(pid: pid_t) -&amp;gt; () {
&lt;span class=&quot;gd&quot;&gt;-    ptrace(PTRACE_CONT, pid, ptr::null_mut(), ptr::null_mut())
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    ptrace::cont(Pid::from_raw(pid), None)
&lt;/span&gt;         .ok()
         .expect(&quot;Failed PTRACE_CONTINUE&quot;);
 }
 
 pub fn peek_text(pid: pid_t, address: InferiorPointer) -&amp;gt; i64 {
&lt;span class=&quot;gd&quot;&gt;-    ptrace(PTRACE_PEEKTEXT, pid, address.as_voidptr(), ptr::null_mut())
-        .ok()
-        .expect(&quot;Failed PTRACE_PEEKTEXT&quot;)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    // From ptrace(2) regarding PTRACE_PEEKTEXT and PTRACE_PEEKDATA
+    //   Linux does not have separate text and data address spaces,
+    //   so these two operations are currently equivalent.
+    // So use ptrace::read which is ptrace(PTRACE_PEEKDATA, ...)
+    // An alterantive would be to use libc::ptrace.
+    ptrace::read(Pid::from_raw(pid),  address.as_voidptr())
+	.ok()
+	.expect(&quot;Failed PTRACE_PEEKTEXT&quot;)
&lt;/span&gt; }
 
 pub fn poke_text(pid: pid_t, address: InferiorPointer, value: i64) -&amp;gt; () {
&lt;span class=&quot;gd&quot;&gt;-    ptrace(PTRACE_POKETEXT, pid, address.as_voidptr(), value as * mut c_void)
-        .ok()
-        .expect(&quot;Failed PTRACE_POKETEXT&quot;);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    ptrace::write(Pid::from_raw(pid), address.as_voidptr(), value)
+	.ok()
+	.expect(&quot;Failed PTRACE_POKETEXT&quot;)
&lt;/span&gt; }
 
 pub fn single_step(pid: pid_t) -&amp;gt; () {
&lt;span class=&quot;gd&quot;&gt;-    ptrace(PTRACE_SINGLESTEP, pid, ptr::null_mut(), ptr::null_mut())
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    ptrace::step(Pid::from_raw(pid), None)
&lt;/span&gt;         .ok()
         .expect(&quot;Failed PTRACE_SINGLESTEP&quot;);
 }&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now the compilation errors go back to lib.rs, I fixed the problems with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;use&lt;/code&gt; earlier but now it’s looking at the code itself.  On of the main issues I faced was that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;execve&lt;/code&gt; changed the types it expects.  I had a lot of trouble getting this right and I think part of this is I have forgotten almost all the rust I knew and I might be doing this wrong.  But these type conversions to pass to nix::unistd::execve are too much IMHO.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;exec_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// let c_filename = &amp;amp;CStr::from_ptr(filename.to_str().unwrap().as_ptr() as *const i8);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cstring_filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filename&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
					&lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to get string from filename&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed to create CString from filename&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cstr_filename&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;CStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cstring_filename&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_ptr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

    &lt;span class=&quot;nf&quot;&gt;disable_address_space_layout_randomization&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;ptrace_util&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trace_me&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;execve&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CString&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cstr_filename&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.ok&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Failed execve&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;unreachable!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I kind worked through this by brute force / trial and error and am not proud of it.  But, let’s move on.&lt;/p&gt;

&lt;p&gt;On the bright side, libc now includes personality and I can use that.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;@@ -37,35 +34,32 @@&lt;/span&gt; static mut global_breakpoint : Breakpoint = Breakpoint {
 };
 static mut global_inferior : Inferior = Inferior { pid: 0, state: InferiorState::Stopped };
 
&lt;span class=&quot;gd&quot;&gt;-mod ffi {
-    use libc::{c_int, c_long};
-
-    extern {
-        pub fn personality(persona: c_long) -&amp;gt; c_int;
-    }
-}
-
&lt;/span&gt; fn disable_address_space_layout_randomization() -&amp;gt; () {
     unsafe {
&lt;span class=&quot;gd&quot;&gt;-        let old = ffi::personality(0xffffffff);
-        ffi::personality((old | 0x0040000) as i64);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+	let old = libc::personality(0xffffffff);
+	libc::personality((old | libc::ADDR_NO_RANDOMIZE) as u64);
&lt;/span&gt;     }
 }
 &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Happy to be rid of the hard coded &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x0040000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The rest were some easier updates to the types.  I do wonder if I should push &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix::unistd::Pid&lt;/code&gt; all the way up and be consistent about using it.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/lib.rs
@@ -1,15 +1,12 @@&lt;/span&gt;
 extern crate nix;
 extern crate libc;
 
&lt;span class=&quot;gd&quot;&gt;-use nix::unistd::*;
-use nix::unistd::Fork::*;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+use nix::{Error, sys::wait::waitpid,unistd::{execve, fork, ForkResult}, sys::signal};
&lt;/span&gt; use libc::pid_t;
&lt;span class=&quot;gd&quot;&gt;-use nix::Error;
-use nix::errno;
&lt;/span&gt; use nix::sys::wait::*;
&lt;span class=&quot;gd&quot;&gt;-use std::ffi::CString;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+use nix::unistd::Pid;
+use std::ffi::{CString, CStr};
&lt;/span&gt; use std::path::Path;
&lt;span class=&quot;gd&quot;&gt;-use nix::sys::signal;
&lt;/span&gt; 
 mod ptrace_util;
 
&lt;span class=&quot;p&quot;&gt;@@ -19,7 +16,7 @@&lt;/span&gt; use inferior::*;
 mod breakpoint;
 
 pub use self::breakpoint::trap_inferior_set_breakpoint;
&lt;span class=&quot;gd&quot;&gt;-use breakpoint::{handle, TrapBreakpoint};
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+use breakpoint::{TrapBreakpoint};
&lt;/span&gt; 
 #[derive(Copy, Clone)]
 struct Breakpoint {
&lt;span class=&quot;p&quot;&gt;@@ -37,35 +34,32 @@&lt;/span&gt; static mut global_breakpoint : Breakpoint = Breakpoint {
 };
 static mut global_inferior : Inferior = Inferior { pid: 0, state: InferiorState::Stopped };
 
&lt;span class=&quot;gd&quot;&gt;-mod ffi {
-    use libc::{c_int, c_long};
-
-    extern {
-        pub fn personality(persona: c_long) -&amp;gt; c_int;
-    }
-}
-
&lt;/span&gt; fn disable_address_space_layout_randomization() -&amp;gt; () {
     unsafe {
&lt;span class=&quot;gd&quot;&gt;-        let old = ffi::personality(0xffffffff);
-        ffi::personality((old | 0x0040000) as i64);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+	let old = libc::personality(0xffffffff);
+	libc::personality((old | libc::ADDR_NO_RANDOMIZE) as u64);
&lt;/span&gt;     }
 }
 
 fn exec_inferior(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; () {
&lt;span class=&quot;gd&quot;&gt;-    let c_filename = &amp;amp;CString::new(filename.to_str().unwrap()).unwrap();
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    // let c_filename = &amp;amp;CStr::from_ptr(filename.to_str().unwrap().as_ptr() as *const i8);
+    let cstring_filename = CString::new(filename.to_str()
+					.expect(&quot;Failed to get string from filename&quot;))
+	.expect(&quot;Failed to create CString from filename&quot;);
&lt;/span&gt;     disable_address_space_layout_randomization();
     ptrace_util::trace_me();
&lt;span class=&quot;gd&quot;&gt;-    execve(c_filename, &amp;amp;[], &amp;amp;[])
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let cstr_filename = unsafe { CStr::from_ptr(cstring_filename.as_ptr()) };
+    execve::&amp;lt;CString, CString&amp;gt;(cstr_filename, &amp;amp;[], &amp;amp;[])
&lt;/span&gt;         .ok()
         .expect(&quot;Failed execve&quot;);
     unreachable!();
 }
 
&lt;span class=&quot;gd&quot;&gt;-fn attach_inferior(pid: pid_t) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
-    match waitpid(pid, None) {
-        Ok(WaitStatus::Stopped(pid, signal::SIGTRAP)) =&amp;gt;
-            return Ok(Inferior {pid: pid, state: InferiorState::Running}),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn attach_inferior(raw_pid: pid_t) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
+    let nix_pid = Pid::from_raw(raw_pid);
+    match waitpid(nix_pid, None) {
+        Ok(WaitStatus::Stopped(pid, signal::Signal::SIGTRAP)) =&amp;gt;
+            return Ok(Inferior {pid: pid.into(), state: InferiorState::Running}),
&lt;/span&gt;         Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in attach_inferior&quot;),
         Err(e) =&amp;gt; return Err(e)
     }
&lt;span class=&quot;p&quot;&gt;@@ -73,25 +67,25 @@&lt;/span&gt; fn attach_inferior(pid: pid_t) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
 
 pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;TrapInferior, Error&amp;gt; {
     loop {
&lt;span class=&quot;gd&quot;&gt;-        match fork() {
-            Ok(Child)                      =&amp;gt; exec_inferior(filename, args),
-            Ok(Parent(pid))                =&amp;gt; {
-                unsafe { global_inferior = attach_inferior(pid).ok().unwrap() };
-                return Ok(pid)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        match unsafe { fork() } {
+            Ok(ForkResult::Child)                      =&amp;gt; exec_inferior(filename, args),
+            Ok(ForkResult::Parent{child: pid})         =&amp;gt; {
+                unsafe { global_inferior = attach_inferior(pid.into()).ok().unwrap() };
+                return Ok(pid.into())
&lt;/span&gt;             },
&lt;span class=&quot;gd&quot;&gt;-            Err(Error::Sys(errno::EAGAIN)) =&amp;gt; continue,
-            Err(e)                         =&amp;gt; return Err(e)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            Err(Error::EAGAIN) =&amp;gt; continue,
+            Err(e)             =&amp;gt; return Err(e)
&lt;/span&gt;         }
     }
 }
 
&lt;span class=&quot;gd&quot;&gt;-pub fn trap_inferior_continue&amp;lt;F&amp;gt;(inferior: TrapInferior, callback: &amp;amp;mut F) -&amp;gt; i8
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn trap_inferior_continue&amp;lt;F&amp;gt;(inferior: TrapInferior, callback: &amp;amp;mut F) -&amp;gt; i32
&lt;/span&gt;     where F: FnMut(TrapInferior, TrapBreakpoint) -&amp;gt; () {
 
     let mut inf = unsafe { global_inferior };
     ptrace_util::cont(inf.pid);
     loop {
&lt;span class=&quot;gd&quot;&gt;-        inf.state = match waitpid(inf.pid, None) {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        inf.state = match waitpid(Pid::from_raw(inf.pid), None) {
&lt;/span&gt;             Ok(WaitStatus::Exited(_pid, code)) =&amp;gt; return code,
             Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt;
                 breakpoint::handle(inf, callback),&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now, it compiles.  But the tests fail.  Before even running the tests I get some loader issues:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Inconsistency detected by ld.so: ../sysdeps/x86_64/dl-machine.h: 541: elf_machine_rela_relative: Assertion `ELFW(R_TYPE) (reloc-&amp;gt;r_info) == R_X86_64_RELATIVE&apos; failed!
Inconsistency detected by ld.so: ../sysdeps/x86_64/dl-machine.h: 541: elf_machine_rela_relative: Assertion `ELFW(R_TYPE) (reloc-&amp;gt;r_info) == R_X86_64_RELATIVE&apos; failed!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;These are weird, unless I &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptrace&lt;/code&gt; pokeed something that I shouldn’t have.  And, I am suspicious that the breakpoint offsets would have moved, so let’s look into that first.&lt;/p&gt;

&lt;p&gt;I used gdb to find the proper address of the breakpoints.  And this elimited the inconsistencies detected by ld.so.&lt;/p&gt;

&lt;p&gt;But the tests still don’t pass.  Well, one of them doesn’t:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;running 3 tests
test it_can_exec ... ok
test it_can_handle_a_breakpoint_more_than_once ... FAILED
test it_can_set_breakpoints ... ok

failures:

---- it_can_handle_a_breakpoint_more_than_once stdout ----

thread &apos;it_can_handle_a_breakpoint_more_than_once&apos; panicked at /home/jkain/projects/rusty_trap/src/lib.rs:93:17:
Unexpected stop on signal SIGSEGV in trap_inferior_continue.  State: 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ok, so &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inferior_continue&lt;/code&gt; found the inferior stopped on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIGSEGV&lt;/code&gt;.  We could very well cause this by patching/restoring the breakpoint word incorrectly.&lt;/p&gt;

&lt;p&gt;Is this the first time the breakpoint triggered?&lt;/p&gt;

&lt;p&gt;Using printouts it seems that we’ve handled the breakpoint twice already. But stopping on the breakpoint calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint::handle&lt;/code&gt; once and the single stepping calls it a second time.  Let’s confirm with better prints.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;in breakpoint::handle inf.state = 0
in breakpoint::handle inf.state = 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Right, so 0 is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Running&lt;/code&gt; and 2 is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SingleStepping&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint::handle&lt;/code&gt; when single stepping we would attempt to set the breakpoint again and then continue.&lt;/p&gt;

&lt;p&gt;Oh, this is the test that only has a breakpoint in main.  Why did I think it was the other one?&lt;/p&gt;

&lt;p&gt;Given that the test of a breakpoint multiple times works but the one with a single breakpoint occurance does not, it seems that either the address is wrong or something is wrong with trying to write this.  Let me look at the main function disassembly in gdb:&lt;/p&gt;

&lt;p&gt;I do see one bug and have fixed it but it isn’t the problem I’m looking for.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/breakpoint/mod.rs
@@ -27,7 +27,7 @@&lt;/span&gt; fn step_over(inferior: TrapInferior, bp: Breakpoint) -&amp;gt; () {
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt; fn set(inferior: TrapInferior, bp: Breakpoint) -&amp;gt; () {
     let mut modified = bp.original_breakpoint_word;
&lt;span class=&quot;gd&quot;&gt;-    modified &amp;amp;= !0xFFi64 &amp;lt;&amp;lt; bp.shift;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    modified &amp;amp;= !(0xFFi64 &amp;lt;&amp;lt; bp.shift);
&lt;/span&gt;  
  modified |= 0xCCi64 &amp;lt;&amp;lt; bp.shift;
     poke_text(inferior, bp.aligned_address, modified);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;I think this was never an issue because the target and aligned address were always the same (and therefor &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;shift&lt;/code&gt; was 0) becauze functions are usually aligned to 16 bytes.  Either that or I don’t understand the operator precedence here.  Eitehr way I think this is a good change but it doesn’t fix the test.&lt;/p&gt;

&lt;p&gt;At this point I took a diversion to setup a cleaner development system with a newer version of Ubuntu.  And then came back to working on this on the new system.  Strangely, the test that was failing changed:  The basic breakpoint test worked but the multiple breakpoint test stopped working.&lt;/p&gt;

&lt;p&gt;One issue is that again compilers and systems have changed so the breakpoint addresses have moved.  Also, the addresses now have namespaces so the symbol name is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;loop::foo&lt;/code&gt; and it gets C++-name-mangled.  With this new name I’m able to find the address in gdb and plug it into the test.&lt;/p&gt;

&lt;p&gt;After that, I wanted to focus only on the one test that is failing.  I commented out the other tests (there must a better way to do this).  Then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;it_can_handle_a_breakpoint_more_than_once&lt;/code&gt; passed!?!?&lt;/p&gt;

&lt;p&gt;I asked, are these tests run concurrently?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch11-02-running-tests.html&quot;&gt;Yes&lt;/a&gt;, concurrent use of rusty_trap isn’t going to work yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo test -- --test-threads=1&lt;/code&gt; to run them sequentially.&lt;/p&gt;

&lt;p&gt;Now this passes!  Well, I learned something about cargo testing and we have new code smell to encourage us to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rusty_trap&lt;/code&gt; thread safe.  The biggest road block here is the global breakpoint.&lt;/p&gt;

&lt;p&gt;Also, from the &lt;a href=&quot;https://doc.rust-lang.org/book/ch11-02-running-tests.html&quot;&gt;same documentation&lt;/a&gt; I see that there is a better way to run a single test: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo test it_can_handle_a_breakpoint_more_than_once&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href=&quot;https://github.com/joekain/rusty_trap/pull/2&quot;&gt;pull request&lt;/a&gt; for all of the changes we made for this post.&lt;/p&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;There are a number of to do items we can carry forward with us.&lt;/p&gt;

&lt;p&gt;TODO:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Go over the warnings&lt;/li&gt;
  &lt;li&gt;Consider &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nix::unistd::Pid&lt;/code&gt; rather than pid_t.&lt;/li&gt;
  &lt;li&gt;Look at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ptrace::AddressType&lt;/code&gt; and consider replacing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InferiorPointer&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Get rid of the global breakpoint and make the system thread safe.&lt;/li&gt;
  &lt;li&gt;Handle multiple breakpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We should be able to tackle some of these in the next post.  I hope to see you there.&lt;/p&gt;

&lt;div class=&quot;debugger-callout&quot; style=&quot;
	    border: 1px solid #eee;
	    padding: 1em;
	    border-radius: 6px;
	    background-color: #f9f9f9;
	    display: flex;
	    align-items: center;
	    gap: 1em;
	    margin: 2em 0;
&quot;&gt;
  &lt;img src=&quot;/rusty_trap.png&quot; alt=&quot;Rusty trap icon&quot; style=&quot;width: 40px; height: 40px; flex-shrink: 0;&quot; /&gt;
  &lt;div&gt;
    &lt;strong&gt;This post is part of my &lt;em&gt;Writing a Debugger&lt;/em&gt; series.&lt;/strong&gt;&lt;br /&gt;
    &lt;a href=&quot;/debugger/&quot;&gt;View all posts and upcoming topics →&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

</description>
        <pubDate>Wed, 09 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/07/09/cleaning-up-a-rusty-trap.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/07/09/cleaning-up-a-rusty-trap.html</guid>
        
        
      </item>
    
      <item>
        <title>Trap - Back Up and Running</title>
        <description>&lt;p&gt;It’s been a long time and I’m finally coming back to this project and need to get things building again.  I tried compiling the project and ran into the following errors:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-- Configuring done
-- Generating done
-- Build files have been written to: /home/jkain/projects/trap/_out
Scanning dependencies of target trap
[  4%] Building C object src/CMakeFiles/trap.dir/inferior_load.c.o
/home/jkain/projects/trap/src/inferior_load.c: In function ‘attach_to_inferior’:
/home/jkain/projects/trap/src/inferior_load.c:24:49: error: ‘SIGTRAP’ undeclared (first use in this function)
   24 |   if (WIFSTOPPED(status) &amp;amp;&amp;amp; WSTOPSIG(status) == SIGTRAP) {
  |                                                 ^~~~~~~
/home/jkain/projects/trap/src/inferior_load.c:24:49: note: each undeclared identifier is reported only once for each function it appears in
/home/jkain/projects/trap/src/inferior_load.c: In function ‘trap_inferior_continue’:
/home/jkain/projects/trap/src/inferior_load.c:71:51: error: ‘SIGTRAP’ undeclared (first use in this function)
   71 |     if (WIFSTOPPED(status) &amp;amp;&amp;amp; WSTOPSIG(status) == SIGTRAP) {
  |                                                   ^~~~~~~
make[2]: *** [src/CMakeFiles/trap.dir/build.make:63: src/CMakeFiles/trap.dir/inferior_load.c.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:149: src/CMakeFiles/trap.dir/all] Error 2
make: *** [Makefile:95: all] Error 2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SIGTRAP&lt;/code&gt; isn’t defined?  I guess I need to include signal.h but I don’t understand why I didn’t need this before.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;modified   src/inferior_load.c
@@ -5,6 +5,7 @@&lt;/span&gt;
 #include &amp;lt;sys/wait.h&amp;gt;
 #include &amp;lt;assert.h&amp;gt;
 #include &amp;lt;errno.h&amp;gt;
&lt;span class=&quot;gi&quot;&gt;+#include &amp;lt;signal.h&amp;gt;
&lt;/span&gt; #include &amp;lt;stdio.h&amp;gt;
 #include &amp;lt;stdlib.h&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now it builds, great!  But the tests fail:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Test project /home/jkain/projects/trap/_out
	Start 1: test_exec
1/7 Test #1: test_exec ............................   Passed    0.00 sec
	Start 2: test_breakpoint
2/7 Test #2: test_breakpoint ......................Child aborted***Exception:   0.01 sec
PTRACE_POKETEXT: : Input/output error
Hello World!

	Start 3: test_multi_breakpoint
3/7 Test #3: test_multi_breakpoint ................Child aborted***Exception:   0.01 sec
PTRACE_POKETEXT: : Input/output error

	Start 4: test_multiple_breakpoints
4/7 Test #4: test_multiple_breakpoints ............Child aborted***Exception:   0.01 sec
PTRACE_POKETEXT: : Input/output error

	Start 5: test_delete_breakpoint
5/7 Test #5: test_delete_breakpoint ...............Child aborted***Exception:   0.01 sec
PTRACE_POKETEXT: : Input/output error

	Start 6: test_delete_breakpoint_in_callback
6/7 Test #6: test_delete_breakpoint_in_callback ...Child aborted***Exception:   0.01 sec
PTRACE_POKETEXT: : Input/output error

	Start 7: test_duplicate_breakpoint
7/7 Test #7: test_duplicate_breakpoint ............Child aborted***Exception:   0.01 sec
PTRACE_POKETEXT: : Input/output error


14% tests passed, 6 tests failed out of 7

Total Test time (real) =   0.06 sec

The following tests FAILED:
	  2 - test_breakpoint (Child aborted)
	  3 - test_multi_breakpoint (Child aborted)
	  4 - test_multiple_breakpoints (Child aborted)
	  5 - test_delete_breakpoint (Child aborted)
	  6 - test_delete_breakpoint_in_callback (Child aborted)
	  7 - test_duplicate_breakpoint (Child aborted)
Errors while running CTest
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Oh, this is because the address for the breakpoints has changed because it’s been 10 years and I’m using a different system, compiler, etc. now.  This is a great reminder that we should implement symbol look up so that the breakpoints aren’t just hard coded addresses, but let’s get the basics working again before we take that on.&lt;/p&gt;

&lt;p&gt;Fixing the offsets isn’t enough to fix this.  There is probably address space randomization like we saw with Rust.  I’ve implemented &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;personality&lt;/code&gt; calls to disable address randomization.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;p&quot;&gt;@@ -12,6 +14,11 @@&lt;/span&gt; static inferior_t g_inferior;
 
 static void setup_inferior(const char *path, char *const argv[])
 {
&lt;span class=&quot;gi&quot;&gt;+  unsigned long old = personality(0xFFFFFFFF);
+  if (personality(old | ADDR_NO_RANDOMIZE) &amp;lt; 0) {
+    perror(&quot;Failed to set personality:&quot;);
+  }
+
&lt;/span&gt;   ptrace_util_traceme();
   execv(path, argv);
 }
 &lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But this still doesn’t work.  So, what is the address of main?&lt;/p&gt;

&lt;p&gt;From gdb &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main = 0x555555555169&lt;/code&gt;.  And it is always this value, it’s not random.  I am running on WSL, maybe that’s weird.  Also does this binary have a start address set?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Yes, it does.  The start address is 0x0000000000001080.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which coresponds with the normal offset of .text&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Sections:
Idx Name          Size      VMA               LMA               File off  Algn
 15 .text         00000195  0000000000001080  0000000000001080  00001080  2**4
				  CONTENTS, ALLOC, LOAD, READONLY, CODE
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ok, so what’s going on?  Maybe another look at gdb can help:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ gdb inferiors/hello
(No debugging symbols found in inferiors/hello)
(gdb) b main
Breakpoint 1 at 0x1169
(gdb)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So gdb gets the basic address (note this is different than the 0x1149 we saw before because I added a printf call and string.).&lt;/p&gt;

&lt;p&gt;Hang on, let’s run under gdb:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;(gdb) r
Starting program: /home/jkain/projects/trap/_out/test/inferiors/hello

Breakpoint 1, 0x0000555555555169 in main ()
(gdb)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Ok, so object is being loaded into a different address as it runs.  And at the start gdb doesn’t know where it will be loaded (just like trap).&lt;/p&gt;

&lt;p&gt;For now, I can just hard code this address, I guess.  I’ve removed the printout from the inferior and use 0x555555555149 as the breakpoint address.&lt;/p&gt;

&lt;p&gt;Now that I think about it, I saw the same thing in Rust when I last worked on it.  This makes sense in that the binary wouldn’t necessarily get loaded at address 0.  If I understand correctly, there are non-relocatable types of binaries and that must have been what I was building before and the newer tool chain allows for relocation.  But, that’s just speculation on my part.&lt;/p&gt;

&lt;p&gt;Even with these new addresses the I see an assertion failure in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt;.  I added a printout in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; to print the instruction pointer.  I see two calls:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;breakpoint_resolve: ip = 0x555555555149
breakpoint_resolve: ip = 0x55555555514c
test_breakpoint: /home/jkain/projects/trap/src/breakpoint.c:102: breakpoint_resolve: Assertion `result&apos; failed.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The first call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; must be when the breakpoint in main is reached.  And the second call is 3 bytes later.  I think this is trying to resolve the stop in execution that happens when we are in state &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;INFERIOR_SINGLE_STEPPING&lt;/code&gt; to step over the breakpoint.  And in this case the instruction pointer will have moved past the byte where we inserted the breakpoint – past the entire original instruction.  So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint_resolve&lt;/code&gt; should fail as this address isn’t one of our breakpoints.  That is,&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; breakpoint_resolve&lt;/code&gt; assumes we executed a one byte instruction and subtracts 1 from the instruction pointer to find the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;target_address&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is not correct and wasn’t correct when I originally wrote it.  I don’t know how this worked before.&lt;/p&gt;

&lt;p&gt;Is this something I never wrote about (and presumably didn’t test properly)?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It looks like this was added in the &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;multiple-breakpoints&lt;/a&gt; post.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think I need to back up to the previous post and, make these breakpoint address tweaks, and then retrace my steps through &lt;a href=&quot;/2015/08/22/multiple-breakpoints.html&quot;&gt;multiple-breakpoints&lt;/a&gt;.  I also should get in the habit of announcing which commit I’m starting with in each post and have a PR at the end of each post.&lt;/p&gt;

&lt;p&gt;I’ll take this work on in a separate post.  For now, I’ve pushed the fixes as a &lt;a href=&quot;https://github.com/joekain/trap/pull/12&quot;&gt;PR&lt;/a&gt; and we should get started on getting the Rust version working again.&lt;/p&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;But, before, I go I have one question for you.  At some point in the future I plan to port trap and rusty_trap to ARM.  Do you have a favorite ARM board?  I have a few: an old Raspberry Pi 2, an &lt;a href=&quot;https://amzn.to/44qYpL5&quot;&gt;odroid XU4&lt;/a&gt;, and an &lt;a href=&quot;https://amzn.to/3U5Mlsy&quot;&gt;Orange Pi Zero 3&lt;/a&gt;.  I’m thinking about getting an &lt;a href=&quot;https://amzn.to/3GHuTHN&quot;&gt;Orange Pi 5&lt;/a&gt;.  Let me know your thoughts in the comments.&lt;/p&gt;

&lt;div class=&quot;debugger-callout&quot; style=&quot;
	    border: 1px solid #eee;
	    padding: 1em;
	    border-radius: 6px;
	    background-color: #f9f9f9;
	    display: flex;
	    align-items: center;
	    gap: 1em;
	    margin: 2em 0;
&quot;&gt;
  &lt;img src=&quot;/rusty_trap.png&quot; alt=&quot;Rusty trap icon&quot; style=&quot;width: 40px; height: 40px; flex-shrink: 0;&quot; /&gt;
  &lt;div&gt;
    &lt;strong&gt;This post is part of my &lt;em&gt;Writing a Debugger&lt;/em&gt; series.&lt;/strong&gt;&lt;br /&gt;
    &lt;a href=&quot;/debugger/&quot;&gt;View all posts and upcoming topics →&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

</description>
        <pubDate>Tue, 08 Jul 2025 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2025/07/08/trap-back-up-and-running.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2025/07/08/trap-back-up-and-running.html</guid>
        
        
      </item>
    
      <item>
        <title>Refactor fail</title>
        <description>&lt;p&gt;In the last post we implemented &lt;a href=&quot;/2015/09/16/rust-state-machine.html&quot;&gt;a state machine to manage breakpoints&lt;/a&gt; in rusty_trap.  We also added support for hitting a single breakpoint multiple times.  Now, I want to start adding support for multiple distinct breakpoints.&lt;/p&gt;

&lt;p&gt;One way to approach adding a new feature is to refactor to make the code easily accept the new feature.  That’s what I set out to do today, but it didn’t go the way I planned.&lt;/p&gt;

&lt;!--break--&gt;

&lt;h2 id=&quot;multiple-breakpoints&quot;&gt;Multiple Breakpoints&lt;/h2&gt;

&lt;p&gt;I still start with writing a test for my goal - to support multiple breakpoints.  This will help me identify the refactorings I need:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;nd&quot;&gt;#[test]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;it_can_handle_multiple_breakpoints&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;foo_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_exec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;Path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;./target/debug/loop&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0x5555555587b0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_set_breakpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0x555555558bd0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nn&quot;&gt;rusty_trap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;trap_inferior_continue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_passed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;passed_inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inferior&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_passed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_foo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;foo_count&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_passed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
             &lt;span class=&quot;n&quot;&gt;main_count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;nd&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Unknown breakpoint {}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bp_passed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;foo_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;assert_eq!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;main_count&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;and of course this test fails which is expected.  Though the error isn’t exactly what I expected:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;---- it_can_handle_multiple_breakpoints stdout ----
	thread &apos;it_can_handle_multiple_breakpoints&apos; panicked at &apos;assertion failed: `(left == right)` (left: `2`, right: `5`)&apos;, tests/lib.rs:63
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So it looks like we only detected 2 two hits to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bp_foo&lt;/code&gt;.  I would have expected to get all 5 an maybe more (due to misattribution of bp_main).&lt;/p&gt;

&lt;p&gt;I also see this&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;test it_can_handle_multiple_breakpoints ... thread &apos;&amp;lt;main&amp;gt;&apos; panicked at &apos;assertion failed: c.borrow().is_none()&apos;, ../src/libstd/sys/common/thread_info.rs:54
FAILED
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’m not sure exactly what this is about or when it happened.  The test must have gotten at least two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert_eq!(foo_count, 5);&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;implementation&quot;&gt;Implementation&lt;/h2&gt;

&lt;p&gt;As I mentioned, the test should point me in the direction I need to refactor.  So, I need to consider: hat do we need to do in order to implement this feature?&lt;/p&gt;

&lt;p&gt;Looking over breakpoint/mod.rs I think we need to&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Remove global_breakpoint in favor of a vector of breakpoints&lt;/li&gt;
  &lt;li&gt;Update &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;breakpoint::handle&lt;/code&gt; to lookup the correct breakpoint&lt;/li&gt;
  &lt;li&gt;Fix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap_inferior_set_breakpoint&lt;/code&gt; to add to the vector of breakpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, instead of having a global vector of breakpoints.  I’ll keep the vector in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt;.  This will help when we need to support multiple inferiors.  One problem is that this will create a circular dependency between inferior and breakpoint modules.&lt;/p&gt;

&lt;p&gt;I need some private structure for breakpoint module that fits into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt;.  How do I do this in Rust?  Maybe I can use a generic that lets me store/fetch it in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vector&lt;/code&gt;.  A little googling turns up that I can use the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Any&lt;/code&gt; trait.&lt;/p&gt;

&lt;p&gt;If I want to put a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vector&lt;/code&gt; inside &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; then I will need to remove the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Copy&lt;/code&gt; trait from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt;.  I don’t want to to be copying the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vector&lt;/code&gt;s.  Given this I should remove &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global_inferior&lt;/code&gt;.&lt;/p&gt;

&lt;h3 id=&quot;remove-global_inferior&quot;&gt;Remove global_inferior&lt;/h3&gt;

&lt;p&gt;I’ll do this by dynamically allocating an inferior and returning it to the application.  This eliminates the need for Copy trait on Inferior.&lt;/p&gt;

&lt;p&gt;I start by commenting out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global_inferior&lt;/code&gt; and letting the compiler tell me what to fix:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Compiling rusty_trap v0.1.0 (file:///home/joseph/src/rust/rusty_trap)
src/lib.rs:79:26: 79:41 error: unresolved name `global_inferior` [E0425]
src/lib.rs:79                 unsafe { global_inferior = attach_inferior(pid).ok().unwrap() };
                                    ^~~~~~~~~~~~~~~
src/lib.rs:79:26: 79:41 help: run `rustc --explain E0425` to see a detailed explanation
src/lib.rs:91:28: 91:43 error: unresolved name `global_inferior` [E0425]
src/lib.rs:91     let mut inf = unsafe { global_inferior };
                                      ^~~~~~~~~~~~~~~
src/lib.rs:91:28: 91:43 help: run `rustc --explain E0425` to see a detailed explanation
src/lib.rs:105:18: 105:33 error: unresolved name `global_inferior` [E0425]
src/lib.rs:105         unsafe { global_inferior = inf };
                             ^~~~~~~~~~~~~~~
src/lib.rs:105:18: 105:33 help: run `rustc --explain E0425` to see a detailed explanation
error: aborting due to 3 previous errors
Could not compile `rusty_trap`.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Changes to inferior/mod.rs:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;gh&quot;&gt;diff --git a/src/inferior/mod.rs b/src/inferior/mod.rs
index 17eaa77..e5eaa55 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/src/inferior/mod.rs
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/src/inferior/mod.rs
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -1,6 +1,9 @@&lt;/span&gt;
 use libc::pid_t;
 use libc::c_void;
 use std::ops::{Add, Sub};
&lt;span class=&quot;gi&quot;&gt;+use std::any::Any;
+use std::vec::Vec;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt; #[derive(Copy, Clone)]
 pub enum InferiorState {
&lt;span class=&quot;p&quot;&gt;@@ -9,13 +12,14 @@&lt;/span&gt; pub enum InferiorState {
     SingleStepping
 }
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-#[derive(Copy, Clone)]
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+//#[derive(Copy, Clone)]
&lt;/span&gt; pub struct Inferior {
     pub pid: pid_t,
&lt;span class=&quot;gd&quot;&gt;-    pub state: InferiorState
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    pub state: InferiorState,
+    pub privates: Vec&amp;lt;Box&amp;lt;Any&amp;gt;&amp;gt;
&lt;/span&gt; }
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-pub type TrapInferior = pid_t;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub type TrapInferior = Box&amp;lt;Inferior&amp;gt;;
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt; #[derive(Copy, Clone)]
 pub struct InferiorPointer(pub u64);&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Right now, the only important changes is the change to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TrapInferior&lt;/code&gt;.  The changes to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; are still work in progress.&lt;/p&gt;

&lt;p&gt;Changes to lib.rs:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;gh&quot;&gt;diff --git a/src/lib.rs b/src/lib.rs
index ac9e444..70ded88 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/src/lib.rs
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/src/lib.rs
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -29,13 +29,13 @@&lt;/span&gt; struct Breakpoint {
     original_breakpoint_word : i64
 }
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-static mut global_breakpoint : Breakpoint = Breakpoint {
-    shift: 0,
-    target_address: InferiorPointer(0),
-    aligned_address: InferiorPointer(0),
-    original_breakpoint_word: 0
-};
-static mut global_inferior : Inferior = Inferior { pid: 0, state: InferiorState::Stopped };
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+// static mut global_breakpoint : Breakpoint = Breakpoint {
+//     shift: 0,
+//     target_address: InferiorPointer(0),
+//     aligned_address: InferiorPointer(0),
+//     original_breakpoint_word: 0
+// };
+//static mut global_inferior : Inferior = Inferior { pid: 0, state: InferiorState::Stopped, privates: vec![] };
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt; mod ffi {
     use libc::{c_int, c_long};
&lt;span class=&quot;p&quot;&gt;@@ -62,10 +62,10 @@&lt;/span&gt; fn exec_inferior(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; () {
     unreachable!();
 }
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-fn attach_inferior(pid: pid_t) -&amp;gt; Result&amp;lt;Inferior, Error&amp;gt; {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn attach_inferior(pid: pid_t) -&amp;gt; Result&amp;lt;Box&amp;lt;Inferior&amp;gt;, Error&amp;gt; {
&lt;/span&gt;     match waitpid(pid, None) {
         Ok(WaitStatus::Stopped(pid, signal::SIGTRAP)) =&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-            return Ok(Inferior {pid: pid, state: InferiorState::Running}),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            return Ok(Box::new(Inferior {pid: pid, state: InferiorState::Running, privates: vec![]})),
&lt;/span&gt;         Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in attach_inferior&quot;),
         Err(e) =&amp;gt; return Err(e)
     }
&lt;span class=&quot;p&quot;&gt;@@ -76,8 +76,8 @@&lt;/span&gt; pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;TrapInferior
         match fork() {
             Ok(Child)                      =&amp;gt; exec_inferior(filename, args),
             Ok(Parent(pid))                =&amp;gt; {
&lt;span class=&quot;gd&quot;&gt;-                unsafe { global_inferior = attach_inferior(pid).ok().unwrap() };
-                return Ok(pid)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+                let inf = attach_inferior(pid).ok().unwrap();
+                return Ok(inf)
&lt;/span&gt;             },
             Err(Error::Sys(errno::EAGAIN)) =&amp;gt; continue,
             Err(e)                         =&amp;gt; return Err(e)
&lt;span class=&quot;p&quot;&gt;@@ -86,22 +86,20 @@&lt;/span&gt; pub fn trap_inferior_exec(filename: &amp;amp;Path, args: &amp;amp;[&amp;amp;str]) -&amp;gt; Result&amp;lt;TrapInferior
 }
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt; pub fn trap_inferior_continue&amp;lt;F&amp;gt;(inferior: TrapInferior, callback: &amp;amp;mut F) -&amp;gt; i8
&lt;span class=&quot;gd&quot;&gt;-    where F: FnMut(TrapInferior, TrapBreakpoint) -&amp;gt; () {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    where F: FnMut(&amp;amp;TrapInferior, TrapBreakpoint) -&amp;gt; () {
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-    let mut inf = unsafe { global_inferior };
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    let mut inf = inferior;
&lt;/span&gt;     ptrace_util::cont(inf.pid);
     loop {
         inf.state = match waitpid(inf.pid, None) {
             Ok(WaitStatus::Exited(_pid, code)) =&amp;gt; return code,
             Ok(WaitStatus::Stopped(_pid, signal::SIGTRAP)) =&amp;gt;
&lt;span class=&quot;gd&quot;&gt;-                breakpoint::handle(inf, callback),
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+                breakpoint::handle(&amp;amp;inf, callback),
&lt;/span&gt;             Ok(WaitStatus::Stopped(_pid, signal)) =&amp;gt; {
               panic!(&quot;Unexpected stop on signal {} in trap_inferior_continue.  State: {}&quot;, signal, inf.state as i32)
           },
           Ok(_) =&amp;gt; panic!(&quot;Unexpected stop in trap_inferior_continue&quot;),
           Err(_) =&amp;gt; panic!(&quot;Unhandled error in trap_inferior_continue&quot;)
       };
&lt;span class=&quot;gd&quot;&gt;-
-        unsafe { global_inferior = inf };
&lt;/span&gt;   }
}&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Here I’ve commened out &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global_inferior&lt;/code&gt; and allocated an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; dynamically in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;attach_inferior&lt;/code&gt; using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&lt;/code&gt;.  I’ve also started passing around references to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&amp;lt;Inferior&amp;gt;&lt;/code&gt; rather than moving the pointer.&lt;/p&gt;

&lt;p&gt;Changes to breakpoint/mod.rs:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;gh&quot;&gt;diff --git a/src/breakpoint/mod.rs b/src/breakpoint/mod.rs
index a069427..1d4aa1d 100644
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;--- a/src/breakpoint/mod.rs
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+++ b/src/breakpoint/mod.rs
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;@@ -19,33 +19,33 @@&lt;/span&gt; static mut global_breakpoint : Breakpoint = Breakpoint {
 };
&lt;span class=&quot;err&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-fn step_over(inferior: TrapInferior, bp: Breakpoint) -&amp;gt; () {
-    poke_text(inferior, bp.aligned_address, bp.original_breakpoint_word);
-    set_instruction_pointer(inferior, bp.target_address);
-    single_step(inferior);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn step_over(inferior: &amp;amp;TrapInferior, bp: Breakpoint) -&amp;gt; () {
+    let pid = inferior.pid;
+    poke_text(pid, bp.aligned_address, bp.original_breakpoint_word);
+    set_instruction_pointer(pid, bp.target_address);
+    single_step(pid);
&lt;/span&gt; }
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-fn set(inferior: TrapInferior, bp: Breakpoint) -&amp;gt; () {
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+fn set(inferior: &amp;amp;TrapInferior, bp: Breakpoint) -&amp;gt; () {
&lt;/span&gt;     let mut modified = bp.original_breakpoint_word;
     modified &amp;amp;= !0xFFi64 &amp;lt;&amp;lt; bp.shift;
     modified |= 0xCCi64 &amp;lt;&amp;lt; bp.shift;
&lt;span class=&quot;gd&quot;&gt;-    poke_text(inferior, bp.aligned_address, modified);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    poke_text(inferior.pid, bp.aligned_address, modified);
&lt;/span&gt; }
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-pub fn handle&amp;lt;F&amp;gt;(inf: Inferior,  mut callback: &amp;amp;mut F) -&amp;gt; InferiorState
-    where F: FnMut(TrapInferior, TrapBreakpoint) -&amp;gt; () {
-    let inferior = inf.pid;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+pub fn handle&amp;lt;F&amp;gt;(inf: &amp;amp;TrapInferior,  mut callback: &amp;amp;mut F) -&amp;gt; InferiorState
+    where F: FnMut(&amp;amp;TrapInferior, TrapBreakpoint) -&amp;gt; () {
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;     let bp = unsafe { global_breakpoint };
     match inf.state {
         InferiorState::Running =&amp;gt; {
&lt;span class=&quot;gd&quot;&gt;-            callback(inferior, 0);
-            step_over(inferior, bp);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            callback(&amp;amp;inf, 0);
+            step_over(&amp;amp;inf, bp);
&lt;/span&gt;             InferiorState::SingleStepping
         },
         InferiorState::SingleStepping =&amp;gt; {
&lt;span class=&quot;gd&quot;&gt;-            set(inferior, bp);
-            cont(inferior);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+            set(&amp;amp;inf, bp);
+            cont(inf.pid);
&lt;/span&gt;             InferiorState::Running
         },
         _ =&amp;gt; panic!(&quot;Unsupported breakpoint encountered during supported inferior state&quot;)
&lt;span class=&quot;p&quot;&gt;@@ -58,10 +58,10 @@&lt;/span&gt; pub fn trap_inferior_set_breakpoint(inferior: TrapInferior, location: u64) -&amp;gt; Tr
         shift : (location - aligned_address) * 8,
         aligned_address: InferiorPointer(aligned_address),
         target_address: InferiorPointer(location),
&lt;span class=&quot;gd&quot;&gt;-        original_breakpoint_word: peek_text(inferior, InferiorPointer(aligned_address))
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+        original_breakpoint_word: peek_text(inferior.pid, InferiorPointer(aligned_address))
&lt;/span&gt;     };
&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;gd&quot;&gt;-    set(inferior, bp);
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    set(&amp;amp;inferior, bp);
&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;
&lt;/span&gt;     unsafe {
         global_breakpoint = bp;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;These changes are mostly of one of two forms:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Taking the pid field from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; rather than assuming &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InferiorTrap&lt;/code&gt; is a pid.&lt;/li&gt;
  &lt;li&gt;Passing references to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&amp;lt;Inferior&amp;gt;&lt;/code&gt; around instead of moving.  Previously these were copies.  Passing references fixes up ownership now that we don’t copy.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These changes all seem well and good until I get to the tests.  I expected to have to fix up a few things (in fact the API changed in thatthe callback now takes a reference).  But I didn’t understand the real problem until I saw the error:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cargo test
   Compiling rusty_trap v0.1.0 (file:///home/joseph/src/rust/rusty_trap)
&amp;lt;std macros&amp;gt;:5:8: 5:18 error: binary operation `==` cannot be applied to type `&amp;amp;Box&amp;lt;rusty_trap::inferior::Inferior&amp;gt;` [E0369]
&amp;lt;std macros&amp;gt;:5 if ! ( * left_val == * right_val ) {
                      ^~~~~~~~~~
&amp;lt;std macros&amp;gt;:1:1: 9:39 note: in expansion of assert_eq!
tests/lib.rs:19:9: 19:47 note: expansion site
note: in expansion of closure expansion
tests/lib.rs:18:55: 22:6 note: expansion site
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So, it seems that these references are not comparable.  Which I guess makes some sense.  I actually, shouldn’t have more than one copy of the pointer or reference to the pointer in the first place.  That must be violating the borrow rules.&lt;/p&gt;

&lt;div style=&quot;
  border: 1px solid #eee;
  padding: 0.75em 1em;
  margin: 2em 0;
  font-size: 0.9rem;
  background-color: #f9f9f9;
  border-radius: 4px;
&quot;&gt;
  &lt;p style=&quot;margin: 0;&quot;&gt;
    Enjoying the post?
    &lt;a href=&quot;/newsletter-signup&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-newsletter-signup-link&quot;&gt;
       Sign up for the newsletter
    &lt;/a&gt;
    or
    &lt;a href=&quot;https://buymeacoffee.com/joekain&quot; target=&quot;_blank&quot; data-umami-click=&quot;clicked-buymeacoffee-link&quot;&gt;
       support the work on Buy Me a Coffee
    &lt;/a&gt;.
  &lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;

&lt;p&gt;I can’t really proceed with this refactoring.  I’m going to need to go back to where I started and try something different.  I’m thinking I will go back to returning the pid, or some opaque handle, to the application and then using that to lookup the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Inferior&lt;/code&gt; data structure.&lt;/p&gt;

&lt;p&gt;I’ll try again next week.&lt;/p&gt;

&lt;div class=&quot;debugger-callout&quot; style=&quot;
	    border: 1px solid #eee;
	    padding: 1em;
	    border-radius: 6px;
	    background-color: #f9f9f9;
	    display: flex;
	    align-items: center;
	    gap: 1em;
	    margin: 2em 0;
&quot;&gt;
  &lt;img src=&quot;/rusty_trap.png&quot; alt=&quot;Rusty trap icon&quot; style=&quot;width: 40px; height: 40px; flex-shrink: 0;&quot; /&gt;
  &lt;div&gt;
    &lt;strong&gt;This post is part of my &lt;em&gt;Writing a Debugger&lt;/em&gt; series.&lt;/strong&gt;&lt;br /&gt;
    &lt;a href=&quot;/debugger/&quot;&gt;View all posts and upcoming topics →&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

</description>
        <pubDate>Fri, 25 Sep 2015 00:00:00 +0000</pubDate>
        <link>https://system.joekain.com/2015/09/25/refactor-fail.html</link>
        <guid isPermaLink="true">https://system.joekain.com/2015/09/25/refactor-fail.html</guid>
        
        
      </item>
    
  </channel>
</rss>
