<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title></title>
		<description>Sharing stuff (by Bertrand Drouvot)</description>
		<link>https://bdrouvot.github.io</link>
		<atom:link href="https://bdrouvot.github.io/feed.xml" rel="self" type="application/rss+xml" />
		
			<item>
				<title>Welcome to pg_shmemviz: PostgreSQL shared memory visualizer</title>
				<description>&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;

&lt;p&gt;The purpose of this blog post is to introduce &lt;strong&gt;pg_shmemviz&lt;/strong&gt;, a new tool to
visualize PostgreSQL shared memory.&lt;/p&gt;

&lt;p&gt;It follows the same approach as
&lt;a href=&quot;/2026/08/13/welcome-to-pg-walviz-postgresql-wal-segment-visualizer/&quot;&gt;pg_walviz&lt;/a&gt;,
bringing physical layout and byte level navigation to PostgreSQL shared memory
instead of WAL segments.&lt;/p&gt;

&lt;p&gt;Views such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_shmem_allocations&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_buffercache&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_shmem_allocations_numa&lt;/code&gt; are useful to inspect selected aspects of shared
memory. However, sometimes we also want to see where allocations are physically
located, which C structures they contain, their exact fields and padding, the
regions reached through pointers and the corresponding raw bytes.&lt;/p&gt;

&lt;h3 id=&quot;welcome-to-pg_shmemviz&quot;&gt;Welcome to pg_shmemviz&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;pg_shmemviz&lt;/strong&gt; is a development and debugging tool that captures PostgreSQL’s
main and dynamic shared memory segments into an offline snapshot and displays
them in a local browser.&lt;/p&gt;

&lt;p&gt;The interface combines a shared memory map, an allocation table, a structure
inspector and a Physical Bytes view. They are synchronized: selecting an
allocation, structure field or byte updates the other views. Pointer and history
navigation can also cross captured segments.&lt;/p&gt;

&lt;p&gt;As a picture is worth a thousand words, let’s have a look at it:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-overview.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz overview&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;shared-memory-overview&quot;&gt;Shared memory overview&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-shared-memory-overview.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz shared memory overview&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The map displays named allocations, allocator padding and unused ranges. Main
shared memory, DSM control, DSM and DSA segments can be selected independently.
One can filter the allocation table, select an allocation or zoom into a
physical range.&lt;/p&gt;

&lt;h4 id=&quot;structure-fields-and-padding&quot;&gt;Structure Fields and padding&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-structure-fields.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz Structure Fields and padding&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The Structure Fields panel uses DWARF from the exact &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postgres&lt;/code&gt; executable to
display nested C structures, field offsets, values, compiler padding and array
stride padding.&lt;/p&gt;

&lt;p&gt;Pointer targets with known bounds appear as referenced regions. Selecting one
highlights its source pointer and opens the target bytes. Specialized discovery
covers PostgreSQL statistics, WAL, process, SLRU, dynahash and DSM registry
structures.&lt;/p&gt;

&lt;h4 id=&quot;physical-bytes&quot;&gt;Physical Bytes&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-physical-bytes.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz Physical Bytes&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The Physical Bytes panel displays bounded byte windows classified by structure
field and padding. Selecting a byte finds its deepest known field, while
selecting a field brackets its bytes. Moving the mouse over a byte displays its
address, offset, value, structure field, NUMA node and comparison information
when available.&lt;/p&gt;

&lt;h4 id=&quot;buffer-cache&quot;&gt;Buffer Cache&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-buffer-cache.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz Buffer Cache&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_buffercache&lt;/code&gt; is installed, the snapshot includes a buffer cache
summary. Optional per buffer details report the buffer identifier, database,
tablespace, relation, fork, block, dirty state, usage count and pinning
backends. Resolved object names and direct navigation between a buffer
descriptor and its page can also be captured.&lt;/p&gt;

&lt;h4 id=&quot;dynamic-shared-memory-and-dsa&quot;&gt;Dynamic shared memory and DSA&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-dynamic-shared-memory.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz dynamic shared memory and DSA&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Dynamic shared memory segments are displayed separately from the main segment.
DSM registry names identify plain DSM segments, named DSA areas and DSA areas
backing known dshash tables.&lt;/p&gt;

&lt;h4 id=&quot;numa-placement&quot;&gt;NUMA placement&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-numa-placement.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz NUMA placement&quot; /&gt;&lt;/p&gt;

&lt;p&gt;On a PostgreSQL build with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--with-libnuma&lt;/code&gt;, the map can display captured page
placement. NUMA information is also propagated to allocations, structure
instances, fields, referenced regions and physical bytes. A range crossing a
page boundary can therefore show several nodes.&lt;/p&gt;

&lt;p&gt;The default scope captures &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProcGlobal-&amp;gt;allProcs&lt;/code&gt;. The shared buffers or all
named main shared memory allocations can also be requested explicitly.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-numa-structure-fields.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz PGPROC structure and field NUMA placement&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this capture from a multi node host, the selected &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PGPROC&lt;/code&gt; spans nodes 0
and 1. Its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subxids&lt;/code&gt; member and nested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xids&lt;/code&gt; array also cross the page
boundary. Individual array elements show their own placement, so an element
can show N0 while its parent shows N0+N1.&lt;/p&gt;

&lt;h4 id=&quot;snapshot-comparison&quot;&gt;Snapshot comparison&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;/assets/images/pg_shmemviz-snapshot-comparison.png&quot; class=&quot;aligncenter size-full&quot; alt=&quot;pg_shmemviz snapshot comparison&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Two compatible snapshots can be compared at allocation, field and byte levels.
The Before and After control preserves matched selections while navigating
between both captures.&lt;/p&gt;

&lt;p&gt;In this example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xact_commit&lt;/code&gt; changes from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1736&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1952&lt;/code&gt;. The selected
eight-byte field is outlined in the Physical Bytes view, while purple marks
only the bytes whose stored values changed.&lt;/p&gt;

&lt;h3 id=&quot;how-to-use-it&quot;&gt;How to use it&lt;/h3&gt;

&lt;p&gt;The current release is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.1.0-beta.1&lt;/code&gt; and can be found in this
&lt;a href=&quot;https://github.com/bdrouvot/pg_shmemviz&quot;&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Do not run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_shmemviz&lt;/code&gt; on a production PostgreSQL instance.&lt;/strong&gt;
It is intended only for development and debugging on a disposable or
otherwise isolated instance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The PostgreSQL server must have debug information, and its matching
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_config&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postgres&lt;/code&gt; executable and development files must be available.
The tool uses LLDB on macOS and GDB elsewhere. The debugger reads DWARF from
the executable and does not attach to the running server.&lt;/p&gt;

&lt;p&gt;Build and install the extension with:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ~/pg_shmemviz
make &lt;span class=&quot;nv&quot;&gt;PG_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/postgres-install/bin/pg_config
make &lt;span class=&quot;nv&quot;&gt;PG_CONFIG&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/path/to/postgres-install/bin/pg_config &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then create the extension in the database used for capture:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/path/to/postgres-install/bin/psql &lt;span class=&quot;nt&quot;&gt;-d&lt;/span&gt; postgres &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;CREATE EXTENSION pg_shmemviz&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Create a snapshot with:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/pg_shmemviz/bin/pg_shmemviz capture &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--pg-config&lt;/span&gt; /path/to/postgres-install/bin/pg_config &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--dbname&lt;/span&gt; postgres &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  /path/to/new-snapshot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The viewer can then be started with:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/pg_shmemviz/bin/pg_shmemviz serve /path/to/new-snapshot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It starts a local HTTP server, opens the browser and binds to
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;127.0.0.1:8765&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;Two snapshots can be compared with:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/pg_shmemviz/bin/pg_shmemviz serve &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  /path/to/before-snapshot &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  /path/to/after-snapshot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For a remote or headless development server, one can use:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;~/pg_shmemviz/bin/pg_shmemviz serve &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--no-open&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--port&lt;/span&gt; 8765 /path/to/new-snapshot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;and create an SSH tunnel from the workstation:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ssh &lt;span class=&quot;nt&quot;&gt;-N&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt; 8765:127.0.0.1:8765 user@server
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://127.0.0.1:8765/&lt;/code&gt; can be opened locally.&lt;/p&gt;

&lt;h3 id=&quot;remarks&quot;&gt;Remarks&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;A capture is a sequential, lock free copy of live shared memory. PostgreSQL
continues to modify it, so related fields can represent different instants
and a multi byte non atomic field can be torn.&lt;/li&gt;
  &lt;li&gt;Structure layouts require the exact &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;postgres&lt;/code&gt; executable used by the
captured server.&lt;/li&gt;
  &lt;li&gt;The complete main shared memory segment is copied, including shared buffers
and unused reserved space. Snapshots can therefore be large and contain
sensitive data.&lt;/li&gt;
  &lt;li&gt;NUMA capture can fault untouched pages and establish their first touch
placement.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_buffercache&lt;/code&gt; metadata and the copied shared memory bytes can represent
different instants.&lt;/li&gt;
  &lt;li&gt;The viewer has no authentication, authorization or TLS. It should remain on
loopback, with an SSH tunnel used for remote access.&lt;/li&gt;
  &lt;li&gt;Automatic structure discovery depends on PostgreSQL internal names and has
currently been validated against PostgreSQL 20devel.&lt;/li&gt;
  &lt;li&gt;Please refer to the
&lt;a href=&quot;https://github.com/bdrouvot/pg_shmemviz#important-limitations&quot;&gt;README&lt;/a&gt;
for the complete compatibility and limitations list.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;pg_shmemviz&lt;/strong&gt; is a new utility that can be used to visualize PostgreSQL
shared memory, from complete segments and named allocations down to C
structure fields, padding and raw bytes.&lt;/p&gt;

&lt;p&gt;Optional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pg_buffercache&lt;/code&gt; and NUMA information add more context, while snapshot
comparison helps identify what changed between two captures.&lt;/p&gt;

&lt;p&gt;This is a first beta release for development and debugging, and feedback is
welcome.&lt;/p&gt;
</description>
				<pubDate>Wed, 19 Aug 2026 20:00:00 -0500</pubDate>
				<link>https://bdrouvot.github.io/2026/08/20/welcome-to-pg-shmemviz-postgresql-shared-memory-visualizer/</link>
				<guid isPermaLink="true">https://bdrouvot.github.io/2026/08/20/welcome-to-pg-shmemviz-postgresql-shared-memory-visualizer/</guid>
			</item>
		
	</channel>
</rss>

