Files
nexus/sreweekly/articles/374/06-new-playground-memory-spy.html
2026-09-12 17:23:01 +08:00

323 lines
17 KiB
HTML

<!DOCTYPE html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8">
<title>New playground: memory spy</title>
<meta name="author" content="Julia Evans">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="description" content="New playground: memory spy">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta property="og:title" content='New playground: memory spy'>
<meta property="og:type" content="website" />
<meta property="og:url" content="https://jvns.ca/blog/2023/05/25/new-playground--memory-spy/" />
<meta property="og:site_name" content="Julia Evans" />
<link rel="canonical" href="https://jvns.ca/blog/2023/05/25/new-playground--memory-spy/">
<link href="/favicon.ico" rel="icon">
<link href="/stylesheets/screen.css" rel="preload" type="text/css" as="style">
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
<link href="/stylesheets/print.css" media="print" rel="stylesheet" type="text/css">
<link href="/atom.xml" rel="alternate" title="Julia Evans" type="application/atom+xml">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css" integrity="sha384-vKruj+a13U8yHIkAyGgK1J3ArTLzrFGBbBc0tDp4ad/EyewESeXE/Iv67Aj8gKZ0" crossorigin="anonymous">
<script defer data-domain="jvns.ca" src="https://plausible.io/js/script.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js" integrity="sha384-PwRUT/YqbnEjkZO0zZxNqcxACrXe+j766U2amXcgMg5457rve2Y7I6ZJSm2A0mS4" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
<script defer type="text/javascript">
window.heap=window.heap||[],heap.load=function(e,t){window.heap.appid=e,window.heap.config=t=t||{};var r=document.createElement("script");r.type="text/javascript",r.async=!0,r.src="https://cdn.heapanalytics.com/js/heap-"+e+".js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(r,a);for(var n=function(e){return function(){heap.push([e].concat(Array.prototype.slice.call(arguments,0)))}},p=["addEventProperties","addUserProperties","clearEventProperties","identify","resetIdentity","removeEventProperty","setEventProperties","track","unsetEventProperty"],o=0;o<p.length;o++)heap[p[o]]=n(p[o])};
heap.load("2242143965");
</script>
</head>
<body>
<div id="skiptocontent">
<a href="#main">Skip to main content</a>
</div>
<div id="wrap">
<header role="banner">
<hgroup>
<h1><a href="/">Julia Evans</a></h1>
</hgroup>
<ul class="header-links">
<li><a href="/about">About</a></li>
<li><a href="/talks">Talks</a></li>
<li><a href="/projects/">Projects</a></li>
<li><a rel="me" href="https://social.jvns.ca/@b0rk">Mastodon</a></li>
<li><a href="https://bsky.app/profile/b0rk.jvns.ca">Bluesky</a></li>
<li><a href="https://github.com/jvns">Github</a></li>
</ul>
</header>
<nav role="navigation" class="header-nav"><ul class="main-navigation">
<li><a href="/categories/favorite/">Favorites</a></li>
<li><a href="/til/">TIL</a></li>
<li><a href="https://wizardzines.com">Zines</a></li>
<li class="subscription" data-subscription="rss"><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
</ul>
</nav>
<div id="main">
<div id="content">
<div>
<article class="hentry" role="article">
<header>
<h1 class="entry-title">New playground: memory spy</h1>
<div class="post-tags">
•
<a class="post-tag" href="/categories/playgrounds">playgrounds</a> •
</div>
<p class="meta sans">
<time class="date" datetime="2023-05-25T08:05:31" pubdate data-updated="true">
May 25, 2023
</time>
</p>
</header>
<main>
<p>Hello! Today we&rsquo;re releasing a new playground called &ldquo;memory spy&rdquo;. It lets you run C programs
and see how their variables are represented in memory. It&rsquo;s designed to be
accessible to folks who don&rsquo;t know C &ndash; it comes with bunch of extremely simple
example C programs that you can poke at. Here&rsquo;s the link:</p>
<div style="margin: 1em 0" align="center">
&gt;&gt; <a href="https://memory-spy.wizardzines.com">Memory Spy</a> &lt;&lt;
</div>
<p>This is a companion to the &ldquo;how integers and floats work&rdquo; <a href="https://wizardzines.com">zine</a> we&rsquo;ve been
working on, so the goal is mostly to look at how number types (integers and
floats) are represented.</p>
<h3 id="why-spy-on-memory" class="post-heading">
<a href="#why-spy-on-memory">
why spy on memory?
</a>
</h3>
<p>How computers actually represent variables can seem kind of abstract, so I
wanted to make it easy for folks to see how a real computer actually represents
variables in memory.</p>
<h3 id="why-is-it-useful-to-look-at-c" class="post-heading">
<a href="#why-is-it-useful-to-look-at-c">
why is it useful to look at C?
</a>
</h3>
<p>You might be wondering &ndash; I don&rsquo;t write C! Why should I care how C programs
represent variables in memory?</p>
<p>In this playground I&rsquo;m mostly interested in showing people how integers and
floats are represented. And low-level languages generally all represent
integers and floats in the same way &ndash; a 32-bit unsigned int is going to be the
same in C, C++, Rust, Go, Swift, etc. The exact name of the type is different,
but the representation is the same.</p>
<p>In higher-level languages like Python it&rsquo;s a little different, but under the
hood a <code>float</code> in Python contains a C <code>double</code>, so the C representation is
still pretty relevant.</p>
<h3 id="you-don-t-have-to-know-c" class="post-heading">
<a href="#you-don-t-have-to-know-c">
you don&rsquo;t have to know C
</a>
</h3>
<p>It uses C because C is the language where it&rsquo;s the most straightforward to map
between &ldquo;the code in your program&rdquo; and &ldquo;what&rsquo;s in your computer&rsquo;s memory&rdquo;.</p>
<p>But if you&rsquo;re not comfortable with C, this playground is still for you! We put
together a bunch of example programs where you can run them and look at each
variable&rsquo;s value.</p>
<p>None of the example programs use any fancy features of C &ndash; a lot of the code
is extremely simple, like <code>char byte = 'a';</code>. So you should be mostly
able to understand what&rsquo;s going on even if you don&rsquo;t know C at all.</p>
<h3 id="how-does-it-work" class="post-heading">
<a href="#how-does-it-work">
how does it work?
</a>
</h3>
<p>Behind the scenes, there&rsquo;s a server that:</p>
<ul>
<li>compiles the program with <code>clang</code></li>
<li>runs the program with the C debugger <code>lldb</code> (using a Python lldb script)</li>
<li>returns a JSON file with the values of the variable on every line, as an array of bytes</li>
</ul>
<p>Then the frontend formats the array of bytes so you can look at it. The display
logic isn&rsquo;t very fancy &ndash; ultimately it&rsquo;s a pretty thin wrapper around lldb.</p>
<h3 id="some-limitations" class="post-heading">
<a href="#some-limitations">
some limitations
</a>
</h3>
<p>The two main limitations I can think of right now are:</p>
<ul>
<li>there&rsquo;s no support for loops (it&rsquo;ll run them, but it&rsquo;ll only tell you the value of the variable the first time through the loop)</li>
<li>it only supports defining one variable per line</li>
</ul>
<p>There are probably more, it&rsquo;s a very simple project.</p>
<h3 id="the-inspiration" class="post-heading">
<a href="#the-inspiration">
the inspiration
</a>
</h3>
<p><a href="https://pythontutor.com/">Python Tutor</a> by Philip Guo was a huge inspiration. It has a different focus &ndash; it also lets you step through programs in a
debugger, but it&rsquo;s more focused on helping the user build a mental model for
how variables and control flow work.</p>
<h3 id="what-about-security" class="post-heading">
<a href="#what-about-security">
what about security?
</a>
</h3>
<p>In general my approach to running arbitrary untrusted code is 20% sandboxing
and 80% making sure that it&rsquo;s an extremely low value attack target so it&rsquo;s not
worth trying to break in.</p>
<p>Programs are terminated after 1 second of runtime, they run in a container with
no network access, and the machine they&rsquo;re running on has no sensitive data on
it and a very small CPU.</p>
<h3 id="some-notes-on-the-tech-stack" class="post-heading">
<a href="#some-notes-on-the-tech-stack">
some notes on the tech stack
</a>
</h3>
<p>The backend is in Go, plus a Python script to script the interactions with
lldb. (here&rsquo;s <a href="https://gist.github.com/jvns/7f1eff7cdda26412cc8df280a1641fd4">the source for the lldb script</a> and <a href="https://gist.github.com/jvns/14b8f65537004a56013260d9219ef36f">the source for the Go server right now</a>). I&rsquo;m
using <a href="https://jvns.ca/blog/2022/06/28/some-notes-on-bubblewrap/">bubblewrap</a>
to sandbox lldb.</p>
<p>As always the frontend is using Vue. You can see the frontend source with &ldquo;view
source&rdquo; if you want.</p>
<p>The main fancy thing that happens on the frontend is that I use <a href="https://tree-sitter.github.io/tree-sitter/">tree sitter</a> to figure out which lines
of the code have variables defined on them.</p>
<h3 id="some-design-notes" class="post-heading">
<a href="#some-design-notes">
some design notes
</a>
</h3>
<p>As usual these days, I built this project with <a href="https://marieflanagan.com/">Marie Claire LeBlanc Flanagan</a>. I think the
design decision I&rsquo;m the happiest with is how we handled navigating the program you&rsquo;re running.
Instead of using next/previous arrows to step through the code one line at a
time, you can just click on a line to view its variables.</p>
<p>This &ldquo;click on a line&rdquo; design wouldn&rsquo;t make sense in a normal debugger context
because usually you have loops and a line might be run more than once. But our
focus here isn&rsquo;t on control flow, and none of the example programs have loops.</p>
<p>The other thing I&rsquo;m happy with is the decision to use regular links like (<code>&lt;a href=&quot;#example=hexadecimal&quot;&gt;</code>) for all the navigation. There&rsquo;s an
<code>onhashchange</code> Javascript event that takes care of making sure we update the
page to match the new URL.</p>
<p>I think there were more design struggles but I forget what they were right now.</p>
<h3 id="that-s-all" class="post-heading">
<a href="#that-s-all">
that&rsquo;s all!
</a>
</h3>
<p>Here&rsquo;s the link again:</p>
<div style="margin: 1em 0" align="center">
&gt;&gt; <a href="https://memory-spy.wizardzines.com">Memory Spy</a> &lt;&lt;
</div>
<p>Let me know on Twitter or Mastodon if you notice any problems.</p>
</main>
<footer>
<style type="text/css">
#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; display: inline;}
#mc_embed_signup {
display: inline;
}
#mc_embed_signup input.button {
background: #ff5e00;
display: inline;
color: white;
padding: 6px 12px;
}
</style>
<div class="sharing">
<style>
.form-inline {
display:flex; flex-flow: row wrap; justify-content: center;
}
.form-inline input, .form-inline span {
padding: 10px;
}
.form-inline input {
display:inline;
max-width:30%;
margin: 0 10px 0 0;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 5px;
padding: 10px;
}
button {
background-color: #f50;
box-shadow: none;
border: 0;
border-radius: 5px;
color: white;
padding: 5px 10px;
}
@media (max-width: 800px) {
.form-inline input {
margin: 10px 0;
max-width:100% !important;
}
.form-inline {
flex-direction: column;
align-items: stretch;
}
}
</style>
<div align="center">
<form class="form-inline" action="https://app.convertkit.com/forms/1052396/subscriptions" method="post" data-uid="8884355abb" data-format="inline" data-version="5">
<span> Want a weekly digest of this blog?</span>
<input name="email_address" type="text" placeholder="Email address" />
<button type="submit" data-element="submit">Subscribe</button>
</form>
</div>
</div>
<p class="meta">
<a class="basic-alignment left" href="https://jvns.ca/blog/2023/05/12/introducing-implement-dns-in-a-weekend/" title="Previous Post: Introducing &#34;Implement DNS in a Weekend&#34;">Introducing &#34;Implement DNS in a Weekend&#34;</a>
<a class="basic-alignment right" href="https://jvns.ca/blog/2023/06/05/some-blogging-myths/" title="Next Post: Some blogging myths">Some blogging myths</a>
</p>
</footer>
</article>
</div>
</div>
</div>
<nav role="navigation" class="footer-nav"> <a href="/">Archives</a>
</nav>
<footer role="contentinfo"><span class="credit">&copy; Julia Evans. </span>
<span>If you like this, you may like <a href="https://web.archive.org/web/20181228051203/http://www.uliaea.ca/">Ulia Ea</a> or, more seriously, this list of <a href="https://jvns.ca/blogroll">blogs I love</a> or some <a href="https://jvns.ca/bookshelf">books I've read</a>. <br>
<p class="rc-scout__text"><i class="rc-scout__logo"></i>
You might also like the <a class="rc-scout__link" href="https://www.recurse.com/scout/click?t=546ea46360584b522270b8c3e5d830f8">Recurse Center</a>, my very favorite programming community <a href="/categories/hackerschool/">(my posts about it)</a></p>
</span>
<style class="rc-scout__style" type="text/css">.rc-scout{display:block;padding:0;border:0;margin:0;}.rc-scout__text{display:block;padding:0;border:0;margin:0;height:100%;font-size:100%;}.rc-scout__logo{display:inline-block;padding:0;border:0;margin:0;width:0.85em;height:0.85em;background:no-repeat center url('data:image/svg+xml;utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2012%2015%22%3E%3Crect%20x%3D%220%22%20y%3D%220%22%20width%3D%2212%22%20height%3D%2210%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%221%22%20width%3D%2210%22%20height%3D%228%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%228%22%20height%3D%226%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%223%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%225%22%20width%3D%222%22%20height%3D%221%22%20fill%3D%22%2361ae24%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%229%22%20width%3D%224%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%221%22%20y%3D%2211%22%20width%3D%2210%22%20height%3D%224%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%220%22%20y%3D%2212%22%20width%3D%2212%22%20height%3D%223%22%20fill%3D%22%23000%22%3E%3C%2Frect%3E%3Crect%20x%3D%222%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%223%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%224%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%225%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%226%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%227%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%228%22%20y%3D%2213%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3Crect%20x%3D%229%22%20y%3D%2212%22%20width%3D%221%22%20height%3D%221%22%20fill%3D%22%23fff%22%3E%3C%2Frect%3E%3C%2Fsvg%3E');}.rc-scout__link:link,.rc-scout__link:visited{color:#61ae24;text-decoration:underline;}.rc-scout__link:hover,.rc-scout__link:active{color:#4e8b1d;}</style>
</footer>
<script type="text/rocketscript">
(function(){
var twitterWidgets = document.createElement('script');
twitterWidgets.type = 'text/javascript';
twitterWidgets.async = true;
twitterWidgets.src = 'http://platform.twitter.com/widgets.js';
document.getElementsByTagName('head')[0].appendChild(twitterWidgets);
})();
</script>
</div>
</body>
</html>