<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Peter McConnell</title>
    <description>The latest articles on Forem by Peter McConnell (@pemcconnell).</description>
    <link>https://forem.com/pemcconnell</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F202788%2F3b435efb-ccfb-47be-936a-2d730797c00d.jpeg</url>
      <title>Forem: Peter McConnell</title>
      <link>https://forem.com/pemcconnell</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/pemcconnell"/>
    <language>en</language>
    <item>
      <title>Building an XDP eBPF Program with C and Golang: A Step-by-Step Guide</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Thu, 18 May 2023 14:09:52 +0000</pubDate>
      <link>https://forem.com/pemcconnell/building-an-xdp-ebpf-program-with-c-and-golang-a-step-by-step-guide-4hoa</link>
      <guid>https://forem.com/pemcconnell/building-an-xdp-ebpf-program-with-c-and-golang-a-step-by-step-guide-4hoa</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In today's highly connected and data-driven world, network performance is crucial for ensuring efficient communication and optimal user experience. XDP (eXpress Data Path) and eBPF (extended Berkeley Packet Filter) have emerged as powerful technologies that enable high-performance packet processing and network optimization. In this step-by-step guide, we will explore the process of building an XDP eBPF program using C and Golang. XDP allows for early packet interception at the network interface driver level, while eBPF provides a flexible and efficient execution environment for custom packet processing logic. Together, they offer an unprecedented level of control and performance in networking applications. Our project, named "dilih" (drop it like it's hot), demonstrates how to build a simple chaos engineering tool that arbitrarily drops packets on a given network interface, which can be a useful tool to allow application developers to understand how their products behave when the network isn't. Through this guide, you will gain a basic understanding of XDP, eBPF, and their practical applications in network manipulation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;note: you can find the entire codebase for this article here: &lt;a href="https://github.com/peter-mcconnell/dilih" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/dilih&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;The project aims to build an XDP (eXpress Data Path) eBPF (extended Berkeley Packet Filter) program using C and Golang. Named "dilih" (drop it like it's hot, as it drops packets like ... they're hot?), the program serves as a simple chaos engineering tool that randomly drops around 50% of packets on a given network interface. This project demonstrates the power and flexibility of XDP and eBPF in controlling packet processing at high speed, making it an ideal starting point for understanding these technologies.&lt;/p&gt;

&lt;p&gt;The XDP eBPF program, implemented in C, hooks into the Linux kernel's networking stack at an early stage to intercept packets and decide their fate. Using a simple randomization mechanism the program selectively drops packets allowing for controlled chaos in network traffic. Additionally, the program utilizes eBPF's perf event mechanism to gather statistics and measure the processing time for dropped and passed packets.&lt;/p&gt;

&lt;p&gt;The accompanying Golang application interacts with the XDP eBPF program, providing a user-friendly interface to monitor the packet drop behavior and visualize performance statistics. It leverages eBPF maps to extract and aggregate the collected data from kernel space, allowing users to gain insights into the impact of dropped packets and the efficiency of packet processing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Development Environment
&lt;/h2&gt;

&lt;p&gt;To get started with building the XDP eBPF program with C and Golang, you need to set up your development environment. Follow these steps to ensure that you have all the necessary tools and dependencies in place:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install Development Tools&lt;/p&gt;

&lt;p&gt;First, ensure that you have the required development tools installed on your system. This includes packages like clang, llvm, and bpftool. You can install these tools using the package manager available on your Linux distribution however I would recommend investing a little time to build these tools from source as it will give you greater control over the flags and features built into these tools.&lt;/p&gt;

&lt;p&gt;If you are curious about my &lt;em&gt;exact&lt;/em&gt; LLVM / Clang setup, I use the following ansible tasks for my configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/llvm.yaml" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/llvm.yaml&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/debugtools.yaml" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/debugtools.yaml&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/docker.yaml" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/docker.yaml&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Golang&lt;/p&gt;

&lt;p&gt;Next, you need to install Golang, which is the programming language used for the accompanying Golang application. Visit the official Golang website at &lt;a href="https://golang.org" rel="noopener noreferrer"&gt;https://golang.org&lt;/a&gt; and follow the installation instructions specific to your operating system. Once installed, make sure the go command is accessible from the command line by adding the appropriate binary directory to your system's PATH.&lt;/p&gt;

&lt;p&gt;If you are curious about my &lt;em&gt;exact&lt;/em&gt; Golang setup, I use the following ansible task for my configuration:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/golang.yaml" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/.dotfiles/blob/master/tasks/golang.yaml&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Project Dependencies&lt;/p&gt;

&lt;p&gt;&lt;em&gt;go dependencies&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Navigate to the project's root directory and install the required Golang dependencies by running the following command:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go mod download
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This command will fetch and install the necessary Golang packages defined in the project's go.mod file.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;libbpf&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We are going to use libbpf in our C code. In the dilih repo we added this as a git submodule but you can choose to manage it elsewhere if you like. When we build our C program later we'll include libbpf with &lt;code&gt;-I../libbpf/src&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(Optional) IDE configuration&lt;/p&gt;

&lt;p&gt;Whatever your editor of choice should be, invest some time in making sure it is set up for C and Golang. Particularly for autocomplete, linting, symbol detection etc. This will make your life much easier.&lt;/p&gt;

&lt;p&gt;If you are curious about my &lt;em&gt;exact&lt;/em&gt; setup, I use the following repo to install neovim, configure my LSP and setup everything else I need for development:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/peter-mcconnell/.dotfiles/" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/.dotfiles/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Writing the XDP eBPF Program in C
&lt;/h2&gt;

&lt;p&gt;The XDP (eXpress Data Path) program is implemented using the eBPF (extended Berkeley Packet Filter) framework in C, with some help from libbpf. It allows us to intercept packets at an early stage in the Linux kernel's networking stack and perform custom packet processing logic. In this section, we will walk through the steps to write the XDP eBPF program in C.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Understanding the Program Logic&lt;/p&gt;

&lt;p&gt;Before diving into the code, let's understand the logic of our XDP program. The goal is to randomly drop ~50% of packets on a given network interface. We will use a randomization mechanism to decide whether to drop or pass each packet ("is random number even?"). The program will also collect statistics and measure the processing time for dropped and passed packets using eBPF's perf event mechanism. Our BPF program runs in kernel space but we'll want to expose data to userspace, so we'll use BPF maps to expose data to our Go program.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Creating the Program Source File&lt;/p&gt;

&lt;p&gt;Start by creating a new file called dilih_kern.c in a ./bpf/ directory in your project. This file will contain our XDP eBPF program logic. Open the file in your favorite text editor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Defining the required headers and structures&lt;/p&gt;

&lt;p&gt;To begin, include the necessary headers and define the required structures for our XDP program. We need bpf.h and bpf_helpers.h which provide useful structures and helper functions.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;linux/bpf.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;bpf_helpers.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Defining Data Structures and Maps&lt;/p&gt;

&lt;p&gt;Next, define the necessary data structures and maps that our XDP program will utilize. We will use a struct to represent the perf event data, and a BPF_MAP_TYPE_PERF_EVENT_ARRAY map to store the perf events. Define the following structures and maps:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;perf_trace_event&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;__u64&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;__u32&lt;/span&gt; &lt;span class="n"&gt;processing_time_ns&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;__u8&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cp"&gt;#define TYPE_ENTER 1
#define TYPE_DROP 2
#define TYPE_PASS 3
&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;__uint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BPF_MAP_TYPE_PERF_EVENT_ARRAY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;__uint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;__uint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;perf_trace_event&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="n"&gt;__uint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_entries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;output_map&lt;/span&gt; &lt;span class="nf"&gt;SEC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".maps"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The output_map map will be used to store the perf events generated by our XDP program. The TYPE_* definitions will make our code more readable later.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Implementing the XDP Program Function&lt;/p&gt;

&lt;p&gt;Now, it's time to implement the XDP program function itself. Begin by declaring the XDP function with the appropriate signature:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;SEC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"xdp"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;xdp_dilih&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;xdp_md&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Add program logic here ... detailed in the next step&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The xdp_dilih function will serve as our XDP eBPF program entry point. It will be called for every incoming packet.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Handling Perf Events and Collecting Data&lt;/p&gt;

&lt;p&gt;Inside the xdp_dilih function, we can handle perf events to collect data and measure processing time. We have already defined the output_map to store these events. Use the bpf_perf_event_output helper function to emit perf events to the map.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;perf_trace_event&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="c1"&gt;// Perf event for entering xdp program&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bpf_ktime_get_ns&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TYPE_ENTER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processing_time_ns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;bpf_perf_event_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;output_map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BPF_F_CURRENT_CPU&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Packet dropping logic&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bpf_get_prandom_u32&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Perf event for dropping packet&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TYPE_DROP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;__u64&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bpf_ktime_get_ns&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processing_time_ns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;bpf_perf_event_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;output_map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BPF_F_CURRENT_CPU&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;XDP_DROP&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Perf event for passing packet&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TYPE_PASS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;__u64&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bpf_ktime_get_ns&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;processing_time_ns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;bpf_perf_event_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;output_map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BPF_F_CURRENT_CPU&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;XDP_PASS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;In this section of the code, we handle the perf events to collect data and measure the processing time of dropped and passed packets. We first emit a perf event when entering the XDP program (type 1). Then, we use a randomization mechanism to decide whether to drop or pass the packet. If the packet is dropped, we emit a perf event with type 2 and return XDP_DROP. If the packet is passed, we emit a perf event with type 3 and return XDP_PASS.&lt;/p&gt;

&lt;p&gt;The bpf_ktime_get_ns() function is used to measure the timestamp (nanoseconds since system boot, excluding suspend time) and processing time of the packet. The bpf_get_prandom_u32() function generates a random value that helps in deciding whether to drop or pass the packet - the "is this random number even?" part.&lt;/p&gt;

&lt;p&gt;Additionally, we use bpf_printk() to print debug messages that can be accessed through the kernel's trace buffer.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That concludes the implementation of the XDP eBPF program in C. This program will selectively drop packets based on a randomization mechanism and emit perf events for collecting data and measuring processing time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compiling and Loading the XDP eBPF Program
&lt;/h2&gt;

&lt;p&gt;Once we have written the XDP eBPF program in C, the next step is to compile it and load it into the kernel. In this section, we will walk through the steps to compile and load the XDP eBPF program.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compiling the XDP Program
&lt;/h3&gt;

&lt;p&gt;To compile the XDP program, we will use the LLVM Clang compiler with the appropriate flags. Open a terminal and navigate to the bpf directory where the dilih_kern.c file is located. Then, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;clang &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-target&lt;/span&gt; bpf &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-I&lt;/span&gt;../libbpf/src&lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-Werror&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-emit-llvm&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; dilih_kern.ll dilih_kern.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me explain the flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-S&lt;/code&gt; Emit an intermediate representation (IR) assembly code file instead of generating object code. This step is used to generate LLVM IR code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-g&lt;/code&gt; Include BTF information&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-target bpf&lt;/code&gt; Specify the target architecture as "bpf" (Berkeley Packet Filter), indicating that the code being compiled is intended to run on eBPF.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-I../libbpf/src&lt;/code&gt; Add the path ../libbpf/src to the include search paths. This allows the compiler to find the necessary header files (bpf helper files) from the libbpf library.`&lt;code&gt;Wall&lt;/code&gt; Enable all compiler warning messages.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-Werror&lt;/code&gt; Treat all warnings as errors, causing the compilation process to fail if any warnings are encountered.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-O2&lt;/code&gt; Apply optimization level 2 to the generated code. This level of optimization focuses on improving performance without sacrificing code size. This is actually a requirement for some BPF usecases, though I'm struggling to recall right now what they are. TODO&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-emit-llvm&lt;/code&gt; Instruct the compiler to emit LLVM IR code as the output.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c&lt;/code&gt; Compile the input source file without linking, producing an object file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-o&lt;/code&gt; dilih_kern.ll: Specify the output file name for the generated LLVM IR code as dilih_kern.ll.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we use the llc command is used to further process the LLVM IR code and generate the final object file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;llc -march=bpf -filetype=obj -O2 -o dilih_kern.o dilih_kern.ll&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let me explain the flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-march=bpf&lt;/code&gt; Specify the target architecture as "bpf" for the code generation stage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-filetype=obj&lt;/code&gt; Specify the desired output file type as an object file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-O2&lt;/code&gt; Apply optimization level 2 to the generated code during the code generation stage.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-o&lt;/code&gt; Specify the output file name for the generated object code as dilih_kern.o.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This command compiles the dilih_kern.c file into a BPF object file named dilih_kern.o. The -target bpf flag specifies the target architecture as BPF, and the -O2 flag enables optimization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading the XDP Program
&lt;/h3&gt;

&lt;p&gt;To load the XDP program into the kernel, we will use the bpftool command-line utility. Ensure that you have the bpftool utility installed on your system. If it's not already installed, you can typically install it using your distribution's package manager.&lt;/p&gt;

&lt;p&gt;In the terminal, run the following command to load the XDP program:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo bpftool prog load dilih\_kern.o /sys/fs/bpf/dilih&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This command loads the dilih_kern.o object file and pins it into the /sys/fs/bpf/dilih location. Adjust the path as necessary based on your system configuration. The bpftool utility will handle the loading process and verify the program's validity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Attaching the XDP Program
&lt;/h3&gt;

&lt;p&gt;After loading the XDP program, we need to attach it to a network interface to start intercepting packets. To attach the XDP program, run the following command:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo bpftool net attach xdp pinned /sys/fs/bpf/dilih dev &amp;lt;interface&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  you can get  by running `ip link'
&lt;/h1&gt;

&lt;p&gt;Replace  with the name of the network interface you want to attach the XDP program to. For example, eth0. This command attaches the XDP program to the specified interface, enabling it to intercept incoming packets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Makefile
&lt;/h3&gt;

&lt;p&gt;For convenience, lets throw some of what we learned above into a Makefile at &lt;code&gt;./bpf/Makefile&lt;/code&gt;. I'll not go into depths about how Makefiles work in this article but will summarise the functionality after the code snippet:&lt;br&gt;
``&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;TARGET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; dilih
&lt;span class="nv"&gt;BPF_TARGET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;${&lt;/span&gt;TARGET:&lt;span class="o"&gt;=&lt;/span&gt;_kern&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;BPF_C&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;${&lt;/span&gt;BPF_TARGET:&lt;span class="o"&gt;=&lt;/span&gt;.c&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;BPF_OBJ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;${&lt;/span&gt;BPF_C:.c&lt;span class="o"&gt;=&lt;/span&gt;.o&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;BPF_PINNED_PATH&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; /sys/fs/bpf/&lt;span class="p"&gt;$(&lt;/span&gt;TARGET&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;XDP_NAME&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; dilih
&lt;span class="nv"&gt;DEV&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; ens160

&lt;span class="nl"&gt;xdp&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;$(BPF_OBJ)&lt;/span&gt;
        &lt;span class="p"&gt;-&lt;/span&gt;bpftool net detach xdpgeneric dev &lt;span class="p"&gt;$(&lt;/span&gt;DEV&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;BPF_PINNED_PATH&lt;span class="p"&gt;)&lt;/span&gt;
        bpftool prog load &lt;span class="p"&gt;$(&lt;/span&gt;BPF_OBJ&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;BPF_PINNED_PATH&lt;span class="p"&gt;)&lt;/span&gt;
        bpftool net attach xdpgeneric pinned &lt;span class="p"&gt;$(&lt;/span&gt;BPF_PINNED_PATH&lt;span class="p"&gt;)&lt;/span&gt; dev &lt;span class="p"&gt;$(&lt;/span&gt;DEV&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;$(BPF_OBJ)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;%.o: %.c&lt;/span&gt;
        clang &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;-target&lt;/span&gt; bpf &lt;span class="se"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;-I&lt;/span&gt;../libbpf/src&lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;-Werror&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
                &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-emit-llvm&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="p"&gt;${&lt;/span&gt;@:.o&lt;span class="o"&gt;=&lt;/span&gt;.ll&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nv"&gt;$&amp;lt;&lt;/span&gt;
        llc &lt;span class="nt"&gt;-march&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bpf &lt;span class="nt"&gt;-filetype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;obj &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;$@&lt;/span&gt; &lt;span class="p"&gt;${&lt;/span&gt;@:.o&lt;span class="o"&gt;=&lt;/span&gt;.ll&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="p"&gt;-&lt;/span&gt;bpftool net detach xdpgeneric dev &lt;span class="p"&gt;$(&lt;/span&gt;DEV&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;BPF_PINNED_PATH&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;BPF_OBJ&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="p"&gt;${&lt;/span&gt;BPF_OBJ:.o&lt;span class="o"&gt;=&lt;/span&gt;.ll&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this file in place and &lt;code&gt;make&lt;/code&gt; installed you can run something like &lt;code&gt;DEV=eth0 make&lt;/code&gt; to compile and load the eBPF program and &lt;code&gt;DEV=eth0 make clean&lt;/code&gt; to remove the files and unload the eBPF program.&lt;/p&gt;

&lt;p&gt;Congratulations! You have successfully compiled and loaded the XDP eBPF program into the kernel and attached it to a network interface. The program is now ready to intercept and process packets based on your defined logic.&lt;/p&gt;

&lt;p&gt;Please note that the compilation and loading process may vary slightly depending on your system configuration and specific requirements. Make sure to adjust the commands accordingly and refer to the documentation of the tools and utilities used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the Golang Application
&lt;/h2&gt;

&lt;p&gt;In this section, we will write a Golang application that interacts with the XDP eBPF program and collects metrics. The Golang application will communicate with the loaded XDP program, read the perf events, and display statistics based on the collected data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the Golang Application Code
&lt;/h3&gt;

&lt;p&gt;Let's create a new file named &lt;code&gt;main.go&lt;/code&gt; and open it in a text editor. This file will contain the code for our Golang application. Copy and paste the following code into main.go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"encoding/binary"&lt;/span&gt;
        &lt;span class="s"&gt;"fmt"&lt;/span&gt;
        &lt;span class="s"&gt;"net"&lt;/span&gt;
        &lt;span class="s"&gt;"os"&lt;/span&gt;
        &lt;span class="s"&gt;"os/signal"&lt;/span&gt;
        &lt;span class="s"&gt;"syscall"&lt;/span&gt;

        &lt;span class="s"&gt;"github.com/cilium/ebpf"&lt;/span&gt;
        &lt;span class="s"&gt;"github.com/cilium/ebpf/link"&lt;/span&gt;
        &lt;span class="s"&gt;"github.com/cilium/ebpf/perf"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;TYPE_ENTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="n"&gt;TYPE_DROP&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;TYPE_PASS&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TimeSinceBoot&lt;/span&gt;  &lt;span class="kt"&gt;uint64&lt;/span&gt;
        &lt;span class="n"&gt;ProcessingTime&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt;
        &lt;span class="n"&gt;Type&lt;/span&gt;           &lt;span class="kt"&gt;uint8&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ringBufferSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;128&lt;/span&gt; &lt;span class="c"&gt;// size of ring buffer used to calculate average processing times&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;ringBuffer&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ringBufferSize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;uint32&lt;/span&gt;
        &lt;span class="n"&gt;start&lt;/span&gt;  &lt;span class="kt"&gt;int&lt;/span&gt;
        &lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
        &lt;span class="n"&gt;filled&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ringBuffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;ringBufferSize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
                &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ringBuffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;float32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filled&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="kt"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ringBufferSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="kt"&gt;float32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ebpf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LoadCollectionSpec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bpf/dilih_kern.o"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;coll&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ebpf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to create new collection: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;coll&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;prog&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;coll&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Programs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"xdp_dilih"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prog&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No program named 'xdp_dilih' found in collection"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;iface&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"INTERFACE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;iface&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No interface specified. Please set the INTERFACE environment variable to the name of the interface to be use"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;iface_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterfaceByName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to get interface %s: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;iface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;XDPOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Program&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;prog&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Interface&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;iface_idx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c"&gt;// Flags is one of XDPAttachFlags (optional).&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;lnk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;link&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AttachXDP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;lnk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Successfully loaded and attached BPF program."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c"&gt;// handle perf events&lt;/span&gt;
        &lt;span class="n"&gt;outputMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;coll&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Maps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"output_map"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No map named 'output_map' found in collection"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;perfEvent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;perf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outputMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to create perf event reader: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;perfEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;buckets&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;uint32&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;TYPE_ENTER&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// bpf program entered&lt;/span&gt;
                &lt;span class="n"&gt;TYPE_DROP&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// bpf program dropped&lt;/span&gt;
                &lt;span class="n"&gt;TYPE_PASS&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// bpf program passed&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;processingTimePassed&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ringBuffer&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;processingTimeDropped&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;ringBuffer&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

        &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c"&gt;// var event event&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;perfEvent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="k"&gt;continue&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;

                        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawSample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;12&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid sample size"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="k"&gt;continue&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="c"&gt;// time since boot in the first 8 bytes&lt;/span&gt;
                        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimeSinceBoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LittleEndian&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uint64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawSample&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                        &lt;span class="c"&gt;// processing time in the next 4 bytes&lt;/span&gt;
                        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessingTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;binary&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LittleEndian&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Uint32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawSample&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                        &lt;span class="c"&gt;// type in the last byte&lt;/span&gt;
                        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;uint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RawSample&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                        &lt;span class="n"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;

                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TYPE_ENTER&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="k"&gt;continue&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TYPE_DROP&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="n"&gt;processingTimeDropped&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessingTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;TYPE_PASS&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="n"&gt;processingTimePassed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ProcessingTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;

                        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="err"&gt;\0&lt;/span&gt;&lt;span class="s"&gt;33[H&lt;/span&gt;&lt;span class="err"&gt;\0&lt;/span&gt;&lt;span class="s"&gt;33[2J"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"total: %d. passed: %d. dropped: %d. passed processing time avg (ns): %f. dropped processing time avg (ns): %f&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TYPE_ENTER&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TYPE_PASS&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;buckets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TYPE_DROP&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;processingTimePassed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;processingTimeDropped&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}()&lt;/span&gt;

        &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;chan&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Interrupt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SIGTERM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may need to run &lt;code&gt;go mod init &amp;amp;&amp;amp; go mod tidy&lt;/code&gt; if you haven't already done so.&lt;/p&gt;

&lt;p&gt;The code sets up the necessary components for the Golang application. It loads the BPF program, attaches it to the specified network interface, and initializes the perf event reader. However, the code to read and process the perf events is yet to be implemented.&lt;/p&gt;

&lt;p&gt;That concludes the content for the "Writing the Golang Application" section. Feel free to modify and customize the content according to your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building and Running the Project
&lt;/h2&gt;

&lt;p&gt;Now that we have implemented the XDP eBPF program in C and the Golang application, let's build and run the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the XDP eBPF Program
&lt;/h3&gt;

&lt;p&gt;You can skip this step if you have already compiled the dilih_kern.o from the steps above.&lt;/p&gt;

&lt;p&gt;Before building the XDP eBPF program, ensure that you have the necessary build tools and dependencies installed on your system. You can refer to the project's README or documentation for the specific requirements.&lt;/p&gt;

&lt;p&gt;To build the XDP eBPF program, navigate to the bpf directory and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will compile the C code and generate the dilih_kern.o object file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Golang Application
&lt;/h3&gt;

&lt;p&gt;To build the Golang application, make sure you are in the root directory of the project. Run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CGO_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 go build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will compile the Golang code and generate an executable binary file. Note: we do not need CGO for our application. We could leave it enabled if we wish, but I like to use CGO_ENABLED=0 when I can as it results in a statically compiled binary that I can easily load into containers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the Go Project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; ./dilih
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should start to see a summary of the packets processed on the given interface:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fblob%2Fmain%2Fassets%2Fdilih_run.png%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fblob%2Fmain%2Fassets%2Fdilih_run.png%3Fraw%3Dtrue" title="sample output" alt="sample output" width="800" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure to run the application with elevated privileges (sudo) to access the necessary resources.&lt;/p&gt;

&lt;p&gt;The Golang application will start collecting data from the XDP program and display statistics based on the received perf events.&lt;/p&gt;

&lt;p&gt;Cleaning Up&lt;br&gt;
To clean up the project and remove the XDP program from the network interface, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;make clean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will detach the XDP program from the network interface and remove any associated artifacts.&lt;/p&gt;

&lt;p&gt;That's it! You have successfully built and run the project. Experiment with different network interfaces and observe the packet drop statistics displayed by the Golang application.&lt;/p&gt;

&lt;p&gt;Feel free to explore additional features and modifications to enhance the project further.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and Verifying the XDP eBPF Program
&lt;/h2&gt;

&lt;p&gt;Testing and verifying the functionality of the XDP eBPF program is an essential step to ensure its correctness and effectiveness. In this section, we'll cover some testing techniques and verification methods for the XDP program.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test Environment Setup
&lt;/h3&gt;

&lt;p&gt;To create a suitable test environment, we'll utilize virtual network interfaces (veth devices) to simulate network traffic and observe the behavior of the XDP program.&lt;/p&gt;

&lt;p&gt;Install the iproute2 package if it's not already installed on your system. This package provides the necessary tools to manage network interfaces.&lt;/p&gt;

&lt;p&gt;Create a pair of veth devices using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link &lt;/span&gt;add veth0 &lt;span class="nb"&gt;type &lt;/span&gt;veth peer name veth1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create two virtual network interfaces (veth0 and veth1) that are connected to each other.&lt;/p&gt;

&lt;p&gt;Set the interfaces up and assign IP addresses to them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth0 up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip &lt;span class="nb"&gt;link set &lt;/span&gt;veth1 up
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip addr add 10.0.0.1/24 dev veth0
&lt;span class="nb"&gt;sudo &lt;/span&gt;ip addr add 10.0.0.2/24 dev veth1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will bring up the interfaces and assign IP addresses (10.0.0.1 and 10.0.0.2) to them.&lt;/p&gt;

&lt;p&gt;With the veth devices set up, we can proceed to test and verify the XDP eBPF program's functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Packet Drop Verification
&lt;/h3&gt;

&lt;p&gt;One of the primary functionalities of the XDP program is to drop a certain percentage of packets. We can verify this behavior by sending packets between the veth devices and observing the packet drop rate.&lt;/p&gt;

&lt;p&gt;Open two terminal windows and navigate to the project directory in both of them.&lt;/p&gt;

&lt;p&gt;In the first terminal, run the following command to listen for ICMP echo requests (ping):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; veth1 icmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the second terminal, send ICMP echo requests (ping) from veth0 to veth1 using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ip netns &lt;span class="nb"&gt;exec &lt;/span&gt;veth0 ping 10.0.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe the output in the first terminal. You should see the ICMP echo requests being captured.&lt;/p&gt;

&lt;p&gt;Analyze the packet capture to verify the packet drop rate. If the XDP program is working correctly, approximately 50% of the ICMP echo requests should be dropped, resulting in a reduced number of captured packets.&lt;/p&gt;

&lt;p&gt;By performing packet drop verification tests, you can ensure that the XDP program is functioning as expected and dropping packets according to the specified percentage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Analysis
&lt;/h3&gt;

&lt;p&gt;In addition to functional verification, it's crucial to analyze the performance impact of the XDP eBPF program. This analysis helps evaluate the efficiency and overhead introduced by the program.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Use the provided Golang application to collect performance metrics and statistics from the XDP program. Refer to the "Building and Running the Project" section for instructions on how to run the Golang application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Monitor and observe the average processing time for both passed and dropped packets. The Golang application displays the average processing time in nanoseconds (ns) for each packet type.&lt;/p&gt;

&lt;p&gt;If the average processing time is consistently low, it indicates that the XDP program is performing efficiently and causing minimal processing overhead.&lt;/p&gt;

&lt;p&gt;If the average processing time is significantly high, it may indicate that the XDP program is introducing a considerable processing overhead, which may require optimization or further investigation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Collect data and analyze the performance metrics over an extended period of network traffic to identify any patterns or trends. Look for anomalies or deviations in the processing time that could indicate potential bottlenecks or inefficiencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Experiment with different packet drop percentages and observe their impact on the average processing time. By varying the drop rate, you can assess the trade-off between packet loss and processing efficiency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Performing performance analysis allows you to gain insights into the impact of the XDP eBPF program on network performance and make informed decisions about its optimization and tuning.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Integration and System Testing&lt;br&gt;
To ensure the proper integration of the XDP eBPF program into the overall system, it's essential to perform integration and system testing. This involves testing the interaction between the XDP program, the network stack, and other components of the system.&lt;/p&gt;

&lt;p&gt;Construct a test scenario that closely resembles the production environment in which the XDP program will operate. Consider factors such as network traffic patterns, system load, and the presence of other networking components.&lt;/p&gt;

&lt;p&gt;Generate realistic network traffic using tools such as packet generators, traffic simulators, or actual production traffic if available.&lt;/p&gt;

&lt;p&gt;Monitor the system's behavior, including packet processing, performance metrics, and system resource utilization. Ensure that the XDP program functions as expected and does not introduce any adverse effects on the system.&lt;/p&gt;

&lt;p&gt;Test corner cases and edge conditions to validate the robustness and resilience of the XDP program. This includes scenarios such as high network traffic volumes, unusual packet structures, or unexpected network events.&lt;/p&gt;

&lt;p&gt;By conducting integration and system testing, you can ensure that the XDP eBPF program seamlessly integrates into the broader system and operates reliably under various conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we explored the process of building an XDP eBPF program with C and Golang. We started by understanding the basics of XDP and eBPF, followed by setting up the development environment and writing the XDP eBPF program in C. We then integrated the program with a Golang application to collect and analyze performance metrics.&lt;/p&gt;

&lt;p&gt;Throughout the journey, we learned how to compile and load the XDP program, build the Golang application, and leverage the power of eBPF and XDP to manipulate network packets and introduce controlled chaos. We also discussed testing methodologies to ensure the correctness, efficiency, and integration of the XDP program within the system.&lt;/p&gt;

&lt;p&gt;The ability to leverage eBPF and XDP opens up a world of possibilities for network programmability, performance optimization, and security enhancements. By harnessing the flexibility and programmability of eBPF, developers can create powerful and efficient networking applications.&lt;/p&gt;

&lt;p&gt;We encourage you to explore further possibilities with XDP and eBPF, experiment with different scenarios, and dive deeper into the rich ecosystem of eBPF tools and libraries available. Embrace the power of XDP and eBPF to unlock new horizons in network programming and performance optimization.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;p&gt;To further expand your knowledge and explore the world of XDP, eBPF, and network programming, here are some valuable resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The Cilium Project: Cilium is an open-source project that provides networking and security capabilities powered by eBPF. Their documentation and codebase offer in-depth insights into eBPF and its applications. Visit their website at cilium.io for more information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The iovisor Project: iovisor is an open-source project that focuses on building tools, libraries, and infrastructure for eBPF-based tracing, monitoring, and networking. Their website at iovisor.org hosts a wealth of resources, including tutorials, documentation, and sample code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The BCC (BPF Compiler Collection): BCC is a collection of powerful command-line tools and libraries that leverage eBPF for various tracing and performance analysis tasks. The official GitHub repository at github.com/iovisor/bcc provides extensive documentation and examples to help you dive deep into eBPF.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;eBPF.io: eBPF.io is a community-driven website dedicated to providing resources, tutorials, and news about eBPF. It features articles, case studies, and a curated list of tools and libraries related to eBPF. Explore the website at ebpf.io to stay up-to-date with the latest developments in the eBPF ecosystem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Linux Kernel Documentation: The Linux kernel documentation includes a comprehensive section on eBPF and XDP, covering various aspects, including API references, usage examples, and implementation details. Access the documentation at &lt;a href="http://www.kernel.org/doc/html/latest/bpf" rel="noopener noreferrer"&gt;www.kernel.org/doc/html/latest/bpf&lt;/a&gt; to gain a deep understanding of the underlying mechanisms.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These resources serve as valuable references and provide opportunities for further learning and exploration. Delve into the world of XDP, eBPF, and network programming, and unlock the full potential of these technologies in your networking projects.&lt;/p&gt;

&lt;p&gt;You can also find the entire codebase for this article here: &lt;a href="https://github.com/peter-mcconnell/dilih" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/dilih&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ebpf</category>
      <category>go</category>
      <category>clang</category>
      <category>xdp</category>
    </item>
    <item>
      <title>Docker Overlayfs: How filesystems work in Docker</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Thu, 19 Jan 2023 12:53:51 +0000</pubDate>
      <link>https://forem.com/pemcconnell/docker-overlayfs-network-namespaces-docker-bridge-and-dns-52jo</link>
      <guid>https://forem.com/pemcconnell/docker-overlayfs-network-namespaces-docker-bridge-and-dns-52jo</guid>
      <description>&lt;p&gt;This is a brief follow up to my article on &lt;a href="https://www.petermcconnell.com/posts/linux_networking/" rel="noopener noreferrer"&gt;Docker networking: Network Namespaces, Docker Bridge and DNS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docker uses the OverlayFS file system to manage the file system of its containers. When a container is run, Docker creates a new layer for the container's file system on top of the base image. This allows the container to have its own file system that is isolated from the host system and other containers.&lt;/p&gt;

&lt;p&gt;Running the &lt;code&gt;ubuntu:22.04&lt;/code&gt; image we can see the root file system differs from the host where I'm running it. Below you can see there is a file in root called &lt;code&gt;/.dockerenv&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; ubuntu:22.04 bash
root@541cc3b62543:/# &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-al&lt;/span&gt; /
total 56
drwxr-xr-x   1 root root 4096 Jan 19 11:51 &lt;span class="nb"&gt;.&lt;/span&gt;
drwxr-xr-x   1 root root 4096 Jan 19 11:51 ..
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt;   1 root root    0 Jan 19 11:51 .dockerenv
lrwxrwxrwx   1 root root    7 Nov 30 02:04 bin -&amp;gt; usr/bin
drwxr-xr-x   2 root root 4096 Apr 18  2022 boot
drwxr-xr-x   5 root root  360 Jan 19 11:51 dev
drwxr-xr-x   1 root root 4096 Jan 19 11:51 etc
drwxr-xr-x   2 root root 4096 Apr 18  2022 home
lrwxrwxrwx   1 root root    7 Nov 30 02:04 lib -&amp;gt; usr/lib
lrwxrwxrwx   1 root root    9 Nov 30 02:04 lib32 -&amp;gt; usr/lib32
lrwxrwxrwx   1 root root    9 Nov 30 02:04 lib64 -&amp;gt; usr/lib64
lrwxrwxrwx   1 root root   10 Nov 30 02:04 libx32 -&amp;gt; usr/libx32
drwxr-xr-x   2 root root 4096 Nov 30 02:04 media
drwxr-xr-x   2 root root 4096 Nov 30 02:04 mnt
drwxr-xr-x   2 root root 4096 Nov 30 02:04 opt
dr-xr-xr-x 491 root root    0 Jan 19 11:51 proc
drwx------   2 root root 4096 Nov 30 02:07 root
drwxr-xr-x   5 root root 4096 Nov 30 02:07 run
lrwxrwxrwx   1 root root    8 Nov 30 02:04 sbin -&amp;gt; usr/sbin
drwxr-xr-x   2 root root 4096 Nov 30 02:04 srv
dr-xr-xr-x  13 root root    0 Jan 19 11:51 sys
drwxrwxrwt   2 root root 4096 Nov 30 02:07 tmp
drwxr-xr-x  14 root root 4096 Nov 30 02:04 usr
drwxr-xr-x  11 root root 4096 Nov 30 02:07 var
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which does not exist at root on the host running the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;root@541cc3b62543:/# 
root@541cc3b62543:/# &lt;span class="nb"&gt;exit
exit&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; /.dockerenv
&lt;span class="nb"&gt;stat&lt;/span&gt;: cannot statx &lt;span class="s1"&gt;'/.dockerenv'&lt;/span&gt;: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So ... &lt;em&gt;where&lt;/em&gt; does it exist? To inspect the layers of a running container, you can use the "docker inspect" command followed by the container ID or name. This will return a JSON object containing information about the container, including its layers. To view this we'll re-run our &lt;code&gt;ubuntu:22.04&lt;/code&gt; container, grab the ID and inspect it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; ubuntu:22.04 bash
6a9014d7ebfddb3a107b29aca3764f24e51f64fda1e8b8cec135c18923daefeb
&lt;span class="c"&gt;# lower directory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect 6a9014d7ebfddb3a107b29aca3764f24e51f64fda1e8b8cec135c18923daefeb &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.GraphDriver.Data.LowerDir}}'&lt;/span&gt;
/dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a-init/diff:/dockerstore/overlay2/bb9057b4f1980fe004301f181c3313c15c2a75b7c7b7c5a6fe80159d2275f0d3/diff

&lt;span class="c"&gt;# upper directory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect 6a9014d7ebfddb3a107b29aca3764f24e51f64fda1e8b8cec135c18923daefeb &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.GraphDriver.Data.UpperDir}}'&lt;/span&gt;
/dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a/diff

&lt;span class="c"&gt;# merged directory&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect 6a9014d7ebfddb3a107b29aca3764f24e51f64fda1e8b8cec135c18923daefeb &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.GraphDriver.Data.MergedDir}}'&lt;/span&gt;
/dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a/merged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'll keep this container running and we'll dig into these contents shortly.&lt;/p&gt;

&lt;p&gt;When a container is run, its layers are stored in the host system's file system, typically in the &lt;code&gt;/var/lib/docker/overlay2&lt;/code&gt; directory. You can see mine is in &lt;code&gt;/dockerstore/&lt;/code&gt; as I have manually set &lt;code&gt;data-root&lt;/code&gt; in &lt;code&gt;/etc/docker/daemon.json&lt;/code&gt; for the host that I'm testing this on. Each layer is represented by a directory that contains the files and directories that make up that layer. The topmost layer is the one that the container is currently using, and the lower layers are the ones that are inherited from the base image.&lt;/p&gt;

&lt;p&gt;The advantages of using layers in Docker include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller image size, since multiple containers can share a common base image&lt;/li&gt;
&lt;li&gt;Faster container startup time, since only the changes made to the container are stored in new layers&lt;/li&gt;
&lt;li&gt;Easier to manage and update containers, since changes can be made to a container's layer without affecting the base image&lt;/li&gt;
&lt;li&gt;Greater security, since each container's file system is isolated from other containers and the host system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please keep in mind that the information is general and may vary depending on specific scenarios.&lt;/p&gt;

&lt;p&gt;Now lets take a deeper look at the filesystem for our running container.&lt;/p&gt;

&lt;h2&gt;
  
  
  LowerDir
&lt;/h2&gt;

&lt;p&gt;This value is unique in the outputs above in that it's actually two paths, separated by a colon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect 6a9014d7ebfddb3a107b29aca3764f24e51f64fda1e8b8cec135c18923daefeb &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.GraphDriver.Data.LowerDir}}'&lt;/span&gt;
/dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a-init/diff:/dockerstore/overlay2/bb9057b4f1980fe004301f181c3313c15c2a75b7c7b7c5a6fe80159d2275f0d3/diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first part (left side of &lt;code&gt;:&lt;/code&gt;) is the path to the init layer of the container. this is the layer that contains the initial filesystem of the container, which is based on the base image. We can take a look at the contents of that layer with &lt;code&gt;ls&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ls&lt;/span&gt; /dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a-init/diff
dev  etc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second part (right side of &lt;code&gt;:&lt;/code&gt;) is the path to the layer of the container that includes changes from the rest of the Dockerfile. Again we can take a look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo ls&lt;/span&gt; /dockerstore/overlay2/bb9057b4f1980fe004301f181c3313c15c2a75b7c7b7c5a6fe80159d2275f0d3/diff
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To better visualise this, lets create our own Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:22.04&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /testinglowerdir/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"hellothere"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /testinglowerdir/foo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, given what we learned above, when we run this container the first part of &lt;code&gt;LowerDir&lt;/code&gt; should contain &lt;em&gt;all&lt;/em&gt; the contents for &lt;code&gt;ubuntu:22.04&lt;/code&gt; and the second part of &lt;code&gt;LowerDir&lt;/code&gt; should contain only &lt;code&gt;/testinglowerdir/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM ubuntu:22.04
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6b7dfa7e8fdb
Step 2/2 : RUN &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /testinglowerdir/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"hellothere"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /testinglowerdir/foo
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Running &lt;span class="k"&gt;in &lt;/span&gt;e71a7cd5541c
Removing intermediate container e71a7cd5541c
 &lt;span class="nt"&gt;---&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; df924945a2b0
Successfully built df924945a2b0
Successfully tagged &lt;span class="nb"&gt;test&lt;/span&gt;:latest
&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nb"&gt;test &lt;/span&gt;bash
9c9fe0bcd283bc0c9649b77246115e3a09e8885efd53f0e9de09de537bea9188
&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect 9c9fe0bcd283bc0c9649b77246115e3a09e8885efd53f0e9de09de537bea9188 &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.GraphDriver.Data.LowerDir}}'&lt;/span&gt;
/dockerstore/overlay2/5501fd185b14a60317f3e0db485bb8f8c5cf41b7cb1ed0688526ba918938b7bf-init/diff:/dockerstore/overlay2/4d49e9a62bad55c3761ab08ded87f56010b28a40f264896c01e5c1c653b826a8/diff:/dockerstore/overlay2/bb9057b4f1980fe004301f181c3313c15c2a75b7c7b7c5a6fe80159d2275f0d3/diff
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="c"&gt;# show directory contents for second part of LowerDir&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo ls&lt;/span&gt; /dockerstore/overlay2/4d49e9a62bad55c3761ab08ded87f56010b28a40f264896c01e5c1c653b826a8/diff
testinglowerdir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  UpperDir
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect 6a9014d7ebfddb3a107b29aca3764f24e51f64fda1e8b8cec135c18923daefeb &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.GraphDriver.Data.UpperDir}}'&lt;/span&gt;
/dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a/diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UpperDir contains changes that we've made at runtime. To see this in action we can exec into our container and create a simple directory with a file in the root directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; 6a9 bash
root@6a9014d7ebfd:/# &lt;span class="nb"&gt;mkdir&lt;/span&gt; /tutorial
root@6a9014d7ebfd:/# &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'iseeyou'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tutorial/ohai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now see this in our UpperDir directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo ls&lt;/span&gt; /dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a/diff/
root  tutorial
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo cat&lt;/span&gt; /dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a/diff/tutorial/ohai
iseeyou
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want to quickly see what files are being created by a running container? This is something the &lt;code&gt;UpperDir&lt;/code&gt; can tell you.&lt;/p&gt;

&lt;h2&gt;
  
  
  MergedDir
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect 6a9014d7ebfddb3a107b29aca3764f24e51f64fda1e8b8cec135c18923daefeb &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.GraphDriver.Data.MergedDir}}'&lt;/span&gt;
/dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a/merged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm sure you've guessed what this one is... This is the merged structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo ls&lt;/span&gt; /dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a/merged
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  tutorial  usr  var
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here you can see all of the directories from the LowerDir and UpperDir together. We can chroot into this directory to "see what docker sees":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chroot&lt;/span&gt; /dockerstore/overlay2/268eb11c54948d6293aa3947b7a2c83b1395b18509518e26487f0e79997f787a/merged /bin/bash
root@pete:/# &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-al&lt;/span&gt;
total 72
drwxr-xr-x  1 root root 4096 Jan 19 12:21 &lt;span class="nb"&gt;.&lt;/span&gt;
drwxr-xr-x  1 root root 4096 Jan 19 12:21 ..
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt;  1 root root    0 Jan 19 11:56 .dockerenv
lrwxrwxrwx  1 root root    7 Nov 30 02:04 bin -&amp;gt; usr/bin
drwxr-xr-x  2 root root 4096 Apr 18  2022 boot
drwxr-xr-x  1 root root 4096 Jan 19 11:56 dev
drwxr-xr-x  1 root root 4096 Jan 19 11:56 etc
drwxr-xr-x  2 root root 4096 Apr 18  2022 home
lrwxrwxrwx  1 root root    7 Nov 30 02:04 lib -&amp;gt; usr/lib
lrwxrwxrwx  1 root root    9 Nov 30 02:04 lib32 -&amp;gt; usr/lib32
lrwxrwxrwx  1 root root    9 Nov 30 02:04 lib64 -&amp;gt; usr/lib64
lrwxrwxrwx  1 root root   10 Nov 30 02:04 libx32 -&amp;gt; usr/libx32
drwxr-xr-x  2 root root 4096 Nov 30 02:04 media
drwxr-xr-x  2 root root 4096 Nov 30 02:04 mnt
drwxr-xr-x  2 root root 4096 Nov 30 02:04 opt
drwxr-xr-x  2 root root 4096 Apr 18  2022 proc
drwx------  1 root root 4096 Jan 19 12:16 root
drwxr-xr-x  5 root root 4096 Nov 30 02:07 run
lrwxrwxrwx  1 root root    8 Nov 30 02:04 sbin -&amp;gt; usr/sbin
drwxr-xr-x  2 root root 4096 Nov 30 02:04 srv
drwxr-xr-x  2 root root 4096 Apr 18  2022 sys
drwxrwxrwt  2 root root 4096 Nov 30 02:07 tmp
drwxr-xr-x  2 root root 4096 Jan 19 12:20 tutorial
drwxr-xr-x 14 root root 4096 Nov 30 02:04 usr
drwxr-xr-x 11 root root 4096 Nov 30 02:07 var
root@pete:/# &lt;span class="nb"&gt;cat&lt;/span&gt; /tutorial/ohai 
iseeyou
root@pete:/#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty sweet! Another way / a "better" way that we can get this view is with &lt;code&gt;nsenter&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nsenter &lt;span class="nt"&gt;--target&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="o"&gt;{{&lt;/span&gt;.State.Pid&lt;span class="o"&gt;}}&lt;/span&gt; 6a9&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nt"&gt;--uts&lt;/span&gt; &lt;span class="nt"&gt;--ipc&lt;/span&gt; &lt;span class="nt"&gt;--net&lt;/span&gt; &lt;span class="nt"&gt;--pid&lt;/span&gt;
root@6a9014d7ebfd:/# &lt;span class="nb"&gt;cat&lt;/span&gt; /tutorial/ohai 
iseeyou
root@6a9014d7ebfd:/#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Do it yourself
&lt;/h2&gt;

&lt;p&gt;This has been a quick look into how Docker avails of OverlayFS, but you can of course do this yourself. The basic syntax is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; overlay overlay &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;lowerdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;lower,upperdir&lt;span class="o"&gt;=&lt;/span&gt;upper,workdir&lt;span class="o"&gt;=&lt;/span&gt;workdir target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lowerdir&lt;/code&gt; is the lower filesystem&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;upperdir&lt;/code&gt; is the upper filesystem&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;workdir&lt;/code&gt; is a directory where the OverlayFS stores metadata about the overlay&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;target&lt;/code&gt; is the mount point where the overlay will be mounted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if you have two directories, /mnt/lower and /mnt/upper, you can create an OverlayFS file system that combines them at /mnt/overlay with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mount &lt;span class="nt"&gt;-t&lt;/span&gt; overlay overlay &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;lowerdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/mnt/lower,upperdir&lt;span class="o"&gt;=&lt;/span&gt;/mnt/upper,workdir&lt;span class="o"&gt;=&lt;/span&gt;/mnt/workdir /mnt/overlay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To view the contents of the overlay, you can simply navigate to the mount point (in this example, /mnt/overlay) and use standard Linux commands to view the files and directories.&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;lsblk&lt;/code&gt; command to view the mounted overlays in your system and also you can unmount the overlays using umount command.&lt;/p&gt;

&lt;p&gt;Please keep in mind that this is a basic example and there are many other options and settings that can be used when creating an OverlayFS file system.&lt;/p&gt;

</description>
      <category>welcome</category>
      <category>community</category>
    </item>
    <item>
      <title>Docker networking: Network Namespaces, Docker and DNS</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Thu, 19 Jan 2023 08:37:22 +0000</pubDate>
      <link>https://forem.com/pemcconnell/docker-networking-network-namespaces-docker-and-dns-19f1</link>
      <guid>https://forem.com/pemcconnell/docker-networking-network-namespaces-docker-and-dns-19f1</guid>
      <description>&lt;p&gt;Ever wondered how &lt;code&gt;docker compose&lt;/code&gt; lets you communicate between services? This article takes a high level look at network namespaces, Dockers internal DNS and Docker bridge.&lt;/p&gt;

&lt;p&gt;Network namespaces are a powerful feature in Linux that allows for the isolation of network stacks, creating multiple virtual networks on a single host. This concept is particularly useful for scenarios such as containerization, where each container needs its own independent network stack. In this article we'll take a look at how &lt;code&gt;docker&lt;/code&gt; / &lt;code&gt;docker compose&lt;/code&gt; utilize this technology to grant containers network isolation and also take a look at how docker handles cross-container networking.&lt;/p&gt;

&lt;h2&gt;
  
  
  simple docker run
&lt;/h2&gt;

&lt;p&gt;To see network namespaces in action, we'll run a simple container (without the &lt;code&gt;--network=host&lt;/code&gt; flag):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# run nginx in detached mode on host port 8080&lt;/span&gt;
docker run &lt;span class="nt"&gt;--name&lt;/span&gt; dummynginx &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:80 &lt;span class="nt"&gt;-d&lt;/span&gt; nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we ran this with &lt;code&gt;--network=host&lt;/code&gt; it would share the network namespace of the host and therefore would not have it's own network namespace.&lt;/p&gt;

&lt;p&gt;Now we can check to see if we any network namespaces were created for this container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;lsns &lt;span class="nt"&gt;-t&lt;/span&gt; net | &lt;span class="nb"&gt;grep &lt;/span&gt;nginx
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; password &lt;span class="k"&gt;for &lt;/span&gt;pete: 
        NS TYPE NPROCS   PID USER     NETNSID NSFS                           COMMAND
...
4026533009 net      17 52981 root           0 /run/docker/netns/3161e4b47f76 nginx: master process nginx &lt;span class="nt"&gt;-g&lt;/span&gt; daemon off&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the output above we can see a network namespace was created for a docker &lt;code&gt;nsfs&lt;/code&gt; with the command &lt;code&gt;nginx&lt;/code&gt;. This seems like a likely culprit. We could confirm this by checking for the &lt;code&gt;nsfs&lt;/code&gt; in &lt;code&gt;docker inspect &amp;lt;container id&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Each container runs in its own network namespace, which means it has its own set of network interfaces, IP addresses, and routing tables. This isolation allows each container to have its own network configuration, without interfering with the host's network or other containers. You can view these resources from the network namespace by using the &lt;code&gt;ip&lt;/code&gt; command with &lt;code&gt;nsenter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In addition, the isolation also allows you to use the same port on the host for different services running in different containers. This is useful when you want to run multiple instances of the same service on a single host, each with its own IP address and port.&lt;/p&gt;

&lt;p&gt;Also, this isolation helps in providing security boundaries between different containers, as they can't see or interact with each other's network stack unless explicitly configured to do so.&lt;/p&gt;

&lt;h2&gt;
  
  
  docker compose
&lt;/h2&gt;

&lt;p&gt;Let's remove that container and see how things look with &lt;code&gt;docker compose&lt;/code&gt; which can allow multiple containers to communicate with eachother:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# remove the old nginx container&lt;/span&gt;
docker &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; dummynginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll create a simple &lt;code&gt;docker-compose.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.1'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nginx_a&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nginx:latest'&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8080:80'&lt;/span&gt;
  &lt;span class="na"&gt;nginx_b&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nginx:latest'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this file stored as &lt;code&gt;docker-compose.yaml&lt;/code&gt; we'll spin it up then check &lt;code&gt;lsns&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;+] Running 3/3
 ⠿  Network linuxnetworks_default      Created  0.0s
 ⠿ Container linuxnetworks-nginx_b-1   Started  0.3s
 ⠿ Container linuxnetworks-nginx_a-1   Started  0.6s
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;lsns &lt;span class="nt"&gt;-t&lt;/span&gt; net | &lt;span class="nb"&gt;grep &lt;/span&gt;nginx
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; password &lt;span class="k"&gt;for &lt;/span&gt;pete: 
        NS TYPE NPROCS   PID USER     NETNSID NSFS                           COMMAND
...
4026533008 net      17 57327 root           0 /run/docker/netns/20790e4f73be nginx: master process nginx &lt;span class="nt"&gt;-g&lt;/span&gt; daemon off&lt;span class="p"&gt;;&lt;/span&gt;
4026533108 net      17 57501 root           1 /run/docker/netns/11b21af68bd5 nginx: master process nginx &lt;span class="nt"&gt;-g&lt;/span&gt; daemon off&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two network namespaces; one for each container. It's worth noting if we had replicas in each service, each container within that service would get their own network namespace. So how does &lt;code&gt;docker compose&lt;/code&gt; allow &lt;code&gt;nginx_a&lt;/code&gt; to speak to &lt;code&gt;nginx_b&lt;/code&gt; given each container has it's own network namespace?&lt;/p&gt;

&lt;p&gt;Firstly lets clarify the mystery we're trying to solve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;nginx_a curl nginx_b
&amp;lt;&lt;span class="o"&gt;!&lt;/span&gt;DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;&lt;span class="nb"&gt;head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&amp;lt;title&amp;gt;Welcome to nginx!&amp;lt;/title&amp;gt;
&amp;lt;style&amp;gt;
html &lt;span class="o"&gt;{&lt;/span&gt; color-scheme: light dark&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
body &lt;span class="o"&gt;{&lt;/span&gt; width: 35em&lt;span class="p"&gt;;&lt;/span&gt; margin: 0 auto&lt;span class="p"&gt;;&lt;/span&gt;
font-family: Tahoma, Verdana, Arial, sans-serif&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Welcome to nginx!&amp;lt;/h1&amp;gt;
&amp;lt;p&amp;gt;If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;For online documentation and support please refer to
&amp;lt;a &lt;span class="nv"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://nginx.org/"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;nginx.org&amp;lt;/a&amp;gt;.&amp;lt;br/&amp;gt;
Commercial support is available at
&amp;lt;a &lt;span class="nv"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://nginx.com/"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;nginx.com&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Thank you &lt;span class="k"&gt;for &lt;/span&gt;using nginx.&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We connected to the &lt;code&gt;nginx_a&lt;/code&gt; container, then curl'ed &lt;code&gt;nginx_b&lt;/code&gt;. How does it work? We'll dig in over the next few sections to understand how Docker makes this possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  a quick note on the 'links:' property
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;docker compose&lt;/code&gt; supports a &lt;code&gt;links:&lt;/code&gt; property. This &lt;em&gt;is not&lt;/em&gt; required to allow services to speak to each other. It's main purpose is to allow you to create aliases or to use hostname resolution, linking between containers in different docker compose files.&lt;/p&gt;

&lt;p&gt;more info: &lt;a href="https://docs.docker.com/compose/compose-file/#links"&gt;https://docs.docker.com/compose/compose-file/#links&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  dockers internal DNS server
&lt;/h3&gt;

&lt;p&gt;Docker runs its own internal DNS server at &lt;code&gt;127.0.0.11&lt;/code&gt;. How do we know this? We can see it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; nginx_a bash
root@b315617ce0dd:/# &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/resolv.conf 
nameserver 127.0.0.11
options ndots:0
root@b315617ce0dd:/# 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can query this from &lt;code&gt;nginx_a&lt;/code&gt; by installing &lt;code&gt;dnsutils&lt;/code&gt; and using &lt;code&gt;nslookup&lt;/code&gt; or &lt;code&gt;dig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; nginx_a bash
root@b315617ce0dd:/# apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt &lt;span class="nb"&gt;install &lt;/span&gt;dnsutils
root@b315617ce0dd:/# nslookup nginx_b 127.0.0.11
Server:         127.0.0.11
Address:        127.0.0.11#53

Non-authoritative answer:
Name:   nginx_b
Address: 172.21.0.2

root@b315617ce0dd:/#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just to be sure that IP is correct we can validate the IP for &lt;code&gt;nginx_b&lt;/code&gt; with &lt;code&gt;docker inspect&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker inspect &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'Name=nginx_b'&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.Networks.linuxnetworks_default.IPAddress}}'&lt;/span&gt;
172.21.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks good.&lt;/p&gt;

&lt;p&gt;When a container is created and connected to a network, Docker automatically creates a DNS record for that container, using the container name as the hostname and the IP address of the container as the record's value. This enables other containers on the same network to access each other by name, rather than needing to know the IP address of the target container.&lt;/p&gt;

&lt;h3&gt;
  
  
  virtual network interfaces
&lt;/h3&gt;

&lt;p&gt;When we ran &lt;code&gt;docker compose&lt;/code&gt; it created a virtual network interface inside each containers network namespace. To look at the resources in a network namespace we first need to know where the &lt;code&gt;nsfs&lt;/code&gt; is. Looking at the example output above from &lt;code&gt;lsns&lt;/code&gt; above we seen this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;4026533008 net      17 57327 root           0 /run/docker/netns/20790e4f73be nginx: master process nginx &lt;span class="nt"&gt;-g&lt;/span&gt; daemon off&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see in the &lt;code&gt;nsfs&lt;/code&gt; column we have: &lt;code&gt;/run/docker/netns/20790e4f73be&lt;/code&gt;. With this we can enter the namespace and run some commands from that namespace.&lt;/p&gt;

&lt;p&gt;You can also get this from &lt;code&gt;docker&lt;/code&gt; itself. Running &lt;code&gt;docker ps&lt;/code&gt; you can get the CONTAINER ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                                   NAMES
b315617ce0dd   nginx:latest   &lt;span class="s2"&gt;"/docker-entrypoint.…"&lt;/span&gt;   16 minutes ago   Up 16 minutes   0.0.0.0:8080-&amp;gt;80/tcp, :::8080-&amp;gt;80/tcp   linuxnetworks-nginx_a-1
da9caf14c90c   nginx:latest   &lt;span class="s2"&gt;"/docker-entrypoint.…"&lt;/span&gt;   16 minutes ago   Up 16 minutes   80/tcp                                  linuxnetworks-nginx_b-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can run &lt;code&gt;inspect&lt;/code&gt; to see the network settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker inspect b315617ce0dd &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.SandboxKey}}'&lt;/span&gt;
/var/run/docker/netns/11b21af68bd5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or the terribly ugly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker inspect &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'Name=nginx_a'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.SandboxKey}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Armed with the &lt;code&gt;nsfs&lt;/code&gt; path we can enter the namespace and run commands. Lets take a quick look at what interfaces are available in this namespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nsenter &lt;span class="nt"&gt;--net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/docker/netns/11b21af68bd5 ip &lt;span class="nb"&gt;link
&lt;/span&gt;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    &lt;span class="nb"&gt;link&lt;/span&gt;/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
47: eth0@if48: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    &lt;span class="nb"&gt;link&lt;/span&gt;/ether 02:42:ac:15:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool! We're finally peaking behind the curtain.&lt;/p&gt;

&lt;h3&gt;
  
  
  exploring the namespace
&lt;/h3&gt;

&lt;p&gt;Now that we have the ability to run commands from the namespace we can request some more information about this interface with &lt;code&gt;ip addr show&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nsenter &lt;span class="nt"&gt;--net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/docker/netns/11b21af68bd5 ip addr show eth0
47: eth0@if48: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP group default 
    &lt;span class="nb"&gt;link&lt;/span&gt;/ether 02:42:ac:15:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.21.0.3/16 brd 172.21.255.255 scope global eth0
       valid_lft forever preferred_lft forever
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also get the route table information with &lt;code&gt;ip route show&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nsenter &lt;span class="nt"&gt;--net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/docker/netns/11b21af68bd5 ip route show
default via 172.21.0.1 dev eth0 
172.21.0.0/16 dev eth0 proto kernel scope &lt;span class="nb"&gt;link &lt;/span&gt;src 172.21.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we can even run good old faithful &lt;code&gt;netstat&lt;/code&gt; to check out which ports we're listening on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nsenter &lt;span class="nt"&gt;--net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/docker/netns/11b21af68bd5 netstat &lt;span class="nt"&gt;-tulnp&lt;/span&gt;
Active Internet connections &lt;span class="o"&gt;(&lt;/span&gt;only servers&lt;span class="o"&gt;)&lt;/span&gt;
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.11:32889        0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;               LISTEN      16568/dockerd       
tcp        0      0 0.0.0.0:80              0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;               LISTEN      57501/nginx: master 
tcp6       0      0 :::80                   :::&lt;span class="k"&gt;*&lt;/span&gt;                    LISTEN      57501/nginx: master 
udp        0      0 127.0.0.11:51543        0.0.0.0:&lt;span class="k"&gt;*&lt;/span&gt;                           16568/dockerd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  docker bridge
&lt;/h3&gt;

&lt;p&gt;Using the &lt;code&gt;ip route&lt;/code&gt; command above we seen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nsenter &lt;span class="nt"&gt;--net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/run/docker/netns/11b21af68bd5 ip route show
default via 172.21.0.1 dev eth0 
172.21.0.0/16 dev eth0 proto kernel scope &lt;span class="nb"&gt;link &lt;/span&gt;src 172.21.0.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what is &lt;code&gt;172.21.0.1&lt;/code&gt;? That is the default gateway, the docker bridge. How can we know? Well, firstly we can list &lt;code&gt;docker&lt;/code&gt; networks with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker network &lt;span class="nb"&gt;ls
&lt;/span&gt;NETWORK ID     NAME                    DRIVER    SCOPE
a52a287eb9f2   bridge                  bridge    &lt;span class="nb"&gt;local
&lt;/span&gt;8f5afced6d3f   host                    host      &lt;span class="nb"&gt;local
&lt;/span&gt;19508f629e30   none                    null      &lt;span class="nb"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then we can &lt;code&gt;inspect&lt;/code&gt; the networks, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker network inspect bridge
&lt;span class="o"&gt;[&lt;/span&gt;
    &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"Name"&lt;/span&gt;: &lt;span class="s2"&gt;"bridge"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Id"&lt;/span&gt;: &lt;span class="s2"&gt;"a52a287eb9f2af463e14ac4a97583b96a9dc66e28b5ac67967321386834e4e24"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Created"&lt;/span&gt;: &lt;span class="s2"&gt;"2023-01-18T20:00:03.09775174Z"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Scope"&lt;/span&gt;: &lt;span class="s2"&gt;"local"&lt;/span&gt;,
        &lt;span class="s2"&gt;"Driver"&lt;/span&gt;: &lt;span class="s2"&gt;"bridge"&lt;/span&gt;,
        &lt;span class="s2"&gt;"EnableIPv6"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"IPAM"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Driver"&lt;/span&gt;: &lt;span class="s2"&gt;"default"&lt;/span&gt;,
            &lt;span class="s2"&gt;"Options"&lt;/span&gt;: null,
            &lt;span class="s2"&gt;"Config"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
                &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="s2"&gt;"Subnet"&lt;/span&gt;: &lt;span class="s2"&gt;"172.17.0.0/16"&lt;/span&gt;,
                    &lt;span class="s2"&gt;"Gateway"&lt;/span&gt;: &lt;span class="s2"&gt;"172.17.0.1"&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;]&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"Internal"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"Attachable"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"Ingress"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"ConfigFrom"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"Network"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"ConfigOnly"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;,
        &lt;span class="s2"&gt;"Containers"&lt;/span&gt;: &lt;span class="o"&gt;{}&lt;/span&gt;,
        &lt;span class="s2"&gt;"Options"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"com.docker.network.bridge.default_bridge"&lt;/span&gt;: &lt;span class="s2"&gt;"true"&lt;/span&gt;,
            &lt;span class="s2"&gt;"com.docker.network.bridge.enable_icc"&lt;/span&gt;: &lt;span class="s2"&gt;"true"&lt;/span&gt;,
            &lt;span class="s2"&gt;"com.docker.network.bridge.enable_ip_masquerade"&lt;/span&gt;: &lt;span class="s2"&gt;"true"&lt;/span&gt;,
            &lt;span class="s2"&gt;"com.docker.network.bridge.host_binding_ipv4"&lt;/span&gt;: &lt;span class="s2"&gt;"0.0.0.0"&lt;/span&gt;,
            &lt;span class="s2"&gt;"com.docker.network.bridge.name"&lt;/span&gt;: &lt;span class="s2"&gt;"docker0"&lt;/span&gt;,
            &lt;span class="s2"&gt;"com.docker.network.driver.mtu"&lt;/span&gt;: &lt;span class="s2"&gt;"1500"&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;,
        &lt;span class="s2"&gt;"Labels"&lt;/span&gt;: &lt;span class="o"&gt;{}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see the Gateway for &lt;code&gt;bridge&lt;/code&gt; is set to &lt;code&gt;172.17.0.1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The Docker bridge default gateway is responsible for connecting containers running in separate network namespaces, using the IP addresses and MAC addresses of the containers connected to it to forward packets to the correct container. When a packet is received by the bridge, it checks the destination IP address of the packet and compares it to the IP addresses of the connected containers. If a match is found, the packet is then forwarded to the corresponding container using the container's MAC address. Additionally, the bridge also uses network address translation (NAT) to rewrite the source IP address of the packet to that of the bridge, allowing the containers to communicate with external networks.&lt;/p&gt;

&lt;h3&gt;
  
  
  so ... how does nginx_a speak to nginx_b? (recap)
&lt;/h3&gt;

&lt;p&gt;The request starts from &lt;code&gt;nginx_a&lt;/code&gt; with &lt;code&gt;curl nginx_a&lt;/code&gt;. &lt;code&gt;nginx_a&lt;/code&gt; is configured to resolve DNS with Dockers internal DNS, as defined in its &lt;code&gt;/etc/resolv.conf&lt;/code&gt; file. Dockers Internal DNS resolves &lt;code&gt;nginx_b&lt;/code&gt; to &lt;code&gt;172.21.0.2&lt;/code&gt; which is within the range of the default gateway in, the docker bridge, as defined in this containers route table. The Docker Bridge acts as a gateway and rewrites the source IP of the packet to the NAT to allow for return traffic using NAT, then forwards the request onto the desired destination at &lt;code&gt;172.21.0.2&lt;/code&gt;. The request then works its way back to the real source, via the default gateway once more.&lt;/p&gt;

&lt;h3&gt;
  
  
  network_mode
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;docker compose&lt;/code&gt; supports another interesting property that we'll take a quick look at: &lt;code&gt;network_mode:&lt;/code&gt;. With this setting we can tell services to share the same network resources. As we're using &lt;code&gt;nginx&lt;/code&gt; in both services currently they will both try to bind on port &lt;code&gt;80&lt;/code&gt; which will cause problems if they are sharing the same resources so for &lt;code&gt;nginx_b&lt;/code&gt; we'll create a unique nginx config file &lt;code&gt;netsvc.conf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen       9080;
    server_name  localhost;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is as simple an nginx config as you can get. Notably we're telling it to listen on &lt;code&gt;9080&lt;/code&gt; instead of the default &lt;code&gt;80&lt;/code&gt; so that they can run in the same network namespace without colliding.&lt;/p&gt;

&lt;p&gt;Now we'll create an updated &lt;code&gt;docker-compose-netsvc.yaml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.1'&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nginx_a&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nginx:latest'&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8080:80'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;9080:9080'&lt;/span&gt;
  &lt;span class="na"&gt;nginx_b&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;nginx:latest'&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./netsvc.conf:/etc/nginx/conf.d/default.conf:ro&lt;/span&gt;
    &lt;span class="na"&gt;network_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;service:nginx_a"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See how we put our port-mapping for &lt;code&gt;nginx_b&lt;/code&gt; (to &lt;code&gt;:9080&lt;/code&gt;) in &lt;code&gt;nginx_a&lt;/code&gt;? With this file in place we'll run our new compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; docker-compose-netsvc.yaml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;+] Running 2/2
 ⠿ Container linuxnetworks-nginx_a-1  Started  0.4s
 ⠿ Container linuxnetworks-nginx_b-1  Started  0.6s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;lsns&lt;/code&gt; this time we can see that we only have one network namespace (compared to two that we seen with the original &lt;code&gt;docker-compose.yaml&lt;/code&gt; file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;lsns &lt;span class="nt"&gt;-t&lt;/span&gt; net | &lt;span class="nb"&gt;grep &lt;/span&gt;nginx
4026533007 net      34 148278 root           0 /run/docker/netns/1e55105e0804 nginx: master process nginx &lt;span class="nt"&gt;-g&lt;/span&gt; daemon off&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We do still have two containers of course:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose ps
NAME                      IMAGE               COMMAND                  SERVICE             CREATED             STATUS              PORTS
linuxnetworks-nginx_a-1   nginx:latest        &lt;span class="s2"&gt;"/docker-entrypoint.…"&lt;/span&gt;   nginx_a             5 minutes ago       Up 5 minutes        0.0.0.0:8080-&amp;gt;80/tcp, :::8080-&amp;gt;80/tcp, 0.0.0.0:9080-&amp;gt;9080/tcp, :::9080-&amp;gt;9080/tcp
linuxnetworks-nginx_b-1   nginx:latest        &lt;span class="s2"&gt;"/docker-entrypoint.…"&lt;/span&gt;   nginx_b             5 minutes ago       Up 5 minutes        
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see from &lt;code&gt;docker inspect&lt;/code&gt; the &lt;code&gt;nginx_b&lt;/code&gt; container has no visible network settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'Name=nginx_b'&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.Networks.linuxnetworks_default.IPAddress}}'&lt;/span&gt;
&amp;lt;no value&amp;gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'Name=nginx_b'&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.SandboxKey}}'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can check the &lt;code&gt;nginx_a&lt;/code&gt; network settings and see that all looks good there:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'Name=nginx_a'&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.Networks.linuxnetworks_default.IPAddress}}'&lt;/span&gt;
172.22.0.2
&lt;span class="nv"&gt;$ &lt;/span&gt;docker inspect &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'Name=nginx_a'&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s1"&gt;'{{.NetworkSettings.SandboxKey}}'&lt;/span&gt;
/var/run/docker/netns/1e55105e0804

&lt;span class="nb"&gt;sudo &lt;/span&gt;nsenter &lt;span class="nt"&gt;--net&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/run/docker/netns/1e55105e0804 ip &lt;span class="nb"&gt;link
&lt;/span&gt;1: lo: &amp;lt;LOOPBACK,UP,LOWER_UP&amp;gt; mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    &lt;span class="nb"&gt;link&lt;/span&gt;/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
80: eth0@if81: &amp;lt;BROADCAST,MULTICAST,UP,LOWER_UP&amp;gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    &lt;span class="nb"&gt;link&lt;/span&gt;/ether 02:42:ac:16:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can imagine, say, running a VPN as a service and then forcing all other services to use that network namespace and resources. Super cool.&lt;/p&gt;

&lt;p&gt;I hope this article has taught you a little about network namespaces and Dockers networking. Happy Hacking o/&lt;/p&gt;

</description>
      <category>docker</category>
      <category>linux</category>
      <category>networking</category>
      <category>compose</category>
    </item>
    <item>
      <title>Advice for engineers wanting to 'make it'</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Tue, 10 Jan 2023 11:28:43 +0000</pubDate>
      <link>https://forem.com/pemcconnell/advise-for-engineers-wanting-to-make-it-4c9b</link>
      <guid>https://forem.com/pemcconnell/advise-for-engineers-wanting-to-make-it-4c9b</guid>
      <description>&lt;h2&gt;
  
  
  The advice I wish I had been given
&lt;/h2&gt;

&lt;p&gt;As someone with two decades of experience in the tech industry, I have been fortunate to have enjoyed success, but my path has been far from deliberate. In hindsight, there are some steps I could have taken to have increased my ability to be a better engineer and ultimately to have reached my goals sooner. If I could time travel, this is the advice I would give to my younger self:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Understand what you want and how to get it: Working in tech because it's your hobby is great, but you should spend some time thinking about how you want to optimise your time. Consider what you want from your career as you (usually) only get one and time is short. Whether it's an early retirement, mass influence on tech, or reaching millions of users, understanding what you want and using it as the guiding light for decision making is a much more efficient strategy than just going with the flow. And remember, "money" is a valid answer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set goals: Now that you know what you want, determine the incremental steps you need to take to get there. Consider who can help you achieve those goals and how you can position yourself to achieve them. Think about what you need to provide to ensure it's a good deal for all parties involved.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It's a craft: So, build on it. That means deliberately doing the things you don't enjoy too. If you don't understand a part of the stack that affects your job, don't ignore it. Force yourself to learn the hard things. Getting better and learning things you don't want to learn is possibly the hardest, yet most important skill I can recommend developing. Being able to deeply understand the work you're doing gives you the confidence to do so much more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be someone that's easy to work with: You spend 40+ hours a week with your colleagues, so be someone they want to have around. Being kind, easy to work with, fun, dependable, diligent, a great communicator and having their backs are all extremely important for your career. Sadly, this is an area where some people lack. I've seen technically brilliant individuals held back due to issues with soft skills. I found Simon Sinek's talk on Performance vs. Trust a good example of this: &lt;a href="https://www.youtube.com/watch?v=kJdXjtSnZTI&amp;amp;ab_channel=MikeKnight" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=kJdXjtSnZTI&amp;amp;ab_channel=MikeKnight&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You're a service: To state the obvious, an employment is a contract an employer creates in exchange for a service (that's the work that you do). If you want to raise your price, it had better be a good deal for the employer. So, what are they getting? Consider this:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Bob was hired at $100k per year. In a year, Bob developed four features. None of these features brought the business any value. Was that a good deal for the business?&lt;/p&gt;

&lt;p&gt;Jane was also hired at $100k per year. In a year, Jane developed a single feature that increased revenue by $300k. Was that a good deal for the business?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's obvious when in black and white but I know I've had times early in my career when I was accidentally coasting, not really thinking about how good of a deal I was for my employer. Try to ensure that you are working on a) increasing revenue, b) decreasing the cost of business, or c) acting as a catalyst for many others. Talk to your manager about wanting to optimize your effectiveness in these areas. Ensure that you can demonstrate this value on demand if required. Always try to remember that you're being paid for a return of value and it is your duty to ensure you can maximise that value.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Overtime: If your goal is work/life balance, this may not be relevant. However, the reality is that you often get out what you put in. You can have an entire career clocking in 40-hour weeks, but from what I've observed, everyone who gets ahead does so by digging in and putting in the extra work. Don't burn yourself out, of course! Manage your time and take it easy when you can, but when the pressure is on for you or your team, be prepared to knuckle down and put in some overtime. This both solidifies your position as someone who can be depended on and also gives you more time in the saddle.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In summary, the steps outlined above can be distilled into a single plan:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand your goals and what you want to achieve&lt;/li&gt;
&lt;li&gt;Set specific and measurable goals to get there&lt;/li&gt;
&lt;li&gt;Continuously improve your craft and skills, even if it's uncomfortable&lt;/li&gt;
&lt;li&gt;Be a valuable team member and easy to work with&lt;/li&gt;
&lt;li&gt;Clearly communicate and demonstrate your value to your employer&lt;/li&gt;
&lt;li&gt;Be prepared to put in the extra work when necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these steps, you can increase your chances of success and reach your desired career outcomes, whether that's financial stability, influence in the industry or a balance lifestyle. Remember that career paths are rarely straight and it's not uncommon to change direction or re-evaluate goals, so be flexible and always keep learning.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Golang debugging tips</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Sun, 08 Jan 2023 11:54:27 +0000</pubDate>
      <link>https://forem.com/pemcconnell/golang-debugging-tips-10lh</link>
      <guid>https://forem.com/pemcconnell/golang-debugging-tips-10lh</guid>
      <description>&lt;p&gt;&lt;em&gt;the 'code' for this article can be found here: &lt;a href="https://github.com/peter-mcconnell/petermcconnell.com/blob/main/assets/dummy/godebug/main.go"&gt;https://github.com/peter-mcconnell/petermcconnell.com/blob/main/assets/dummy/godebug/main.go&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  debugging Golang - the context
&lt;/h2&gt;

&lt;p&gt;This is the tool I reach for when a program isn't behaving how I expect it to and I want to dig into the internals / browse the state of the program at particular points so as to realise why my understanding of the program is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  what are the requirements?
&lt;/h2&gt;

&lt;p&gt;The debugger of choice (for me) is &lt;code&gt;dlv&lt;/code&gt; / &lt;code&gt;delve&lt;/code&gt;. The reasons for this are at the end of the article.&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;delve&lt;/code&gt;: &lt;a href="https://github.com/go-delve/delve"&gt;https://github.com/go-delve/delve&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/go-delve/delve
&lt;span class="nb"&gt;cd &lt;/span&gt;delve
go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/go-delve/delve/cmd/dlv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Have the following pieces of information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a list of 1 or more points in my code where I'd like to set breakpoints

&lt;ul&gt;
&lt;li&gt;filename and line numbers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  example program
&lt;/h2&gt;

&lt;p&gt;For the sake of this article we'll create a simple application to debug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// main.go&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;  &lt;span class="c"&gt;// should be * 2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doubleit 2: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doubleit 4: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"doubleit 8: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we run this with &lt;code&gt;go run main.go&lt;/code&gt; we want it to double the numbers we pass but for &lt;em&gt;some reason&lt;/em&gt; we're getting different results.&lt;/p&gt;

&lt;p&gt;We will also need a &lt;code&gt;go.mod&lt;/code&gt; for Delve to run so also perform a &lt;code&gt;go mod init&lt;/code&gt; from this directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  debug it
&lt;/h2&gt;

&lt;p&gt;To get started from the project directory&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dlv debug
Type &lt;span class="s1"&gt;'help'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;list of commands.
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now set some breakpoints. For this example we'll say we want to debug our &lt;code&gt;doubleit&lt;/code&gt; method - the first line of which is at &lt;code&gt;:7&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dlv debug
Type &lt;span class="s1"&gt;'help'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;list of commands.
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; b main.go:7
Breakpoint 1 &lt;span class="nb"&gt;set &lt;/span&gt;at 0x49c8bb &lt;span class="k"&gt;for &lt;/span&gt;main.doubleit&lt;span class="o"&gt;()&lt;/span&gt; ./main.go:7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we've added all of the breakpoints that we need we can instruct the program to run with &lt;code&gt;c&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dlv debug
Type &lt;span class="s1"&gt;'help'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;list of commands.
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; b main.go:7
Breakpoint 1 &lt;span class="nb"&gt;set &lt;/span&gt;at 0x49c8bb &lt;span class="k"&gt;for &lt;/span&gt;main.doubleit&lt;span class="o"&gt;()&lt;/span&gt; ./main.go:7
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; c
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; main.doubleit&lt;span class="o"&gt;()&lt;/span&gt; ./main.go:7 &lt;span class="o"&gt;(&lt;/span&gt;hits goroutine&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;:1 total:1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;PC: 0x49c8bb&lt;span class="o"&gt;)&lt;/span&gt;
     2:package main
     3:
     4:import &lt;span class="s2"&gt;"fmt"&lt;/span&gt;
     5:
     6:func doubleit&lt;span class="o"&gt;(&lt;/span&gt;val int&lt;span class="o"&gt;)&lt;/span&gt; int &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   7: &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3 // should be &lt;span class="k"&gt;*&lt;/span&gt; 2
     8:&lt;span class="o"&gt;}&lt;/span&gt;
     9:
    10:func main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    11: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 2: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;))&lt;/span&gt;
    12: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 4: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we've ran our program with an attached debugger and it has paused execution at the breakpoint we set. We can run &lt;code&gt;args&lt;/code&gt; to see which arguments where passed to the method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; c
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; main.doubleit&lt;span class="o"&gt;()&lt;/span&gt; ./main.go:7 &lt;span class="o"&gt;(&lt;/span&gt;hits goroutine&lt;span class="o"&gt;(&lt;/span&gt;1&lt;span class="o"&gt;)&lt;/span&gt;:1 total:1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;PC: 0x49c8bb&lt;span class="o"&gt;)&lt;/span&gt;
     2:package main
     3:
     4:import &lt;span class="s2"&gt;"fmt"&lt;/span&gt;
     5:
     6:func doubleit&lt;span class="o"&gt;(&lt;/span&gt;val int&lt;span class="o"&gt;)&lt;/span&gt; int &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   7: &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3 // should be &lt;span class="k"&gt;*&lt;/span&gt; 2
     8:&lt;span class="o"&gt;}&lt;/span&gt;
     9:
    10:func main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    11: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 2: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;))&lt;/span&gt;
    12: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 4: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; args
val &lt;span class="o"&gt;=&lt;/span&gt; 2
~r0 &lt;span class="o"&gt;=&lt;/span&gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in this point in the program we're in the &lt;code&gt;doubleit&lt;/code&gt; method when it was invoked with a &lt;code&gt;val&lt;/code&gt; value of &lt;code&gt;2&lt;/code&gt;. We can print this and other variables using &lt;code&gt;p&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; p val
2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even call methods from this point using &lt;code&gt;call&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; call doubleit&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; main.doubleit&lt;span class="o"&gt;()&lt;/span&gt; ./main.go:7 &lt;span class="o"&gt;(&lt;/span&gt;hits goroutine&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;:1 total:2&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;PC: 0x49c8bb&lt;span class="o"&gt;)&lt;/span&gt;
     2:package main
     3:
     4:import &lt;span class="s2"&gt;"fmt"&lt;/span&gt;
     5:
     6:func doubleit&lt;span class="o"&gt;(&lt;/span&gt;val int&lt;span class="o"&gt;)&lt;/span&gt; int &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   7: &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3 // should be &lt;span class="k"&gt;*&lt;/span&gt; 2
     8:&lt;span class="o"&gt;}&lt;/span&gt;
     9:
    10:func main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    11: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 2: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;))&lt;/span&gt;
    12: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 4: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; args
val &lt;span class="o"&gt;=&lt;/span&gt; 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above we hit our own breakpoint set earlier allowing us to print the &lt;code&gt;args&lt;/code&gt; for the &lt;code&gt;call&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To walk over the execution we can press &lt;code&gt;n&lt;/code&gt; to go to the next point of execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; n
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; main.doubleit&lt;span class="o"&gt;()&lt;/span&gt; ./main.go:6 &lt;span class="o"&gt;(&lt;/span&gt;PC: 0x49c8a0&lt;span class="o"&gt;)&lt;/span&gt;
     1:// main.go
     2:package main
     3:
     4:import &lt;span class="s2"&gt;"fmt"&lt;/span&gt;
     5:
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   6:func doubleit&lt;span class="o"&gt;(&lt;/span&gt;val int&lt;span class="o"&gt;)&lt;/span&gt; int &lt;span class="o"&gt;{&lt;/span&gt;
     7: &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3 // should be &lt;span class="k"&gt;*&lt;/span&gt; 2
     8:&lt;span class="o"&gt;}&lt;/span&gt;
     9:
    10:func main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    11: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 2: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; n
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; main.doubleit&lt;span class="o"&gt;()&lt;/span&gt; ./main.go:7 &lt;span class="o"&gt;(&lt;/span&gt;hits goroutine&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;:2 total:3&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;PC: 0x49c8bb&lt;span class="o"&gt;)&lt;/span&gt;
     2:package main
     3:
     4:import &lt;span class="s2"&gt;"fmt"&lt;/span&gt;
     5:
     6:func doubleit&lt;span class="o"&gt;(&lt;/span&gt;val int&lt;span class="o"&gt;)&lt;/span&gt; int &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   7: &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3 // should be &lt;span class="k"&gt;*&lt;/span&gt; 2
     8:&lt;span class="o"&gt;}&lt;/span&gt;
     9:
    10:func main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    11: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 2: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;))&lt;/span&gt;
    12: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 4: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and view the backtrace with &lt;code&gt;bt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; bt
0  0x000000000049c8bb &lt;span class="k"&gt;in &lt;/span&gt;main.doubleit
   at ./main.go:7
1  0x000000000046251f &lt;span class="k"&gt;in &lt;/span&gt;debugCall256
   at :0
2  0x0000000000407484 &lt;span class="k"&gt;in &lt;/span&gt;runtime.debugCallWrap2
   at /usr/local/go/src/runtime/debugcall.go:251
3  0x00000000004073b3 &lt;span class="k"&gt;in &lt;/span&gt;runtime.debugCallWrap1
   at /usr/local/go/src/runtime/debugcall.go:203
4  0x0000000000464ca1 &lt;span class="k"&gt;in &lt;/span&gt;runtime.goexit
   at /usr/local/go/src/runtime/asm_amd64.s:1594
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To view the code around the current point of execution just press &lt;code&gt;l&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; l
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; main.doubleit&lt;span class="o"&gt;()&lt;/span&gt; ./main.go:7 &lt;span class="o"&gt;(&lt;/span&gt;hits goroutine&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;:3 total:4&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;PC: 0x49c8bb&lt;span class="o"&gt;)&lt;/span&gt;
     2:package main
     3:
     4:import &lt;span class="s2"&gt;"fmt"&lt;/span&gt;
     5:
     6:func doubleit&lt;span class="o"&gt;(&lt;/span&gt;val int&lt;span class="o"&gt;)&lt;/span&gt; int &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;   7: &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3 // should be &lt;span class="k"&gt;*&lt;/span&gt; 2
     8:&lt;span class="o"&gt;}&lt;/span&gt;
     9:
    10:func main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    11: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 2: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;))&lt;/span&gt;
    12: fmt.Printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 4: %d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which of course shows our very hard to find logic error, &lt;code&gt;* 3&lt;/code&gt; instead of &lt;code&gt;* 2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note: you can also set breakpoints in the stdlib functions (paths will vary depending on your setup):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;dlv&lt;span class="o"&gt;)&lt;/span&gt; b src/net/http/request.go:899
Breakpoint 1 &lt;span class="nb"&gt;set &lt;/span&gt;at 0x794599 &lt;span class="k"&gt;for &lt;/span&gt;net/http.NewRequestWithContext&lt;span class="o"&gt;()&lt;/span&gt; /usr/local/go./net/http/request.go:899
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  summary
&lt;/h2&gt;

&lt;p&gt;The example above is extremely trivial - where &lt;code&gt;dlv&lt;/code&gt; and it's ilk shine are on complex usecases where you may not even know what methods are between the input and output, such as debugging the stdlib. Just this week I used &lt;code&gt;dlv&lt;/code&gt; to identify why a &lt;code&gt;POST&lt;/code&gt; wasn't honouring a &lt;code&gt;307&lt;/code&gt; temporary redirect - on inspection, using &lt;code&gt;dlv&lt;/code&gt;, I learned that the &lt;code&gt;body&lt;/code&gt; is disregarded if it is an unrecognised &lt;code&gt;type&lt;/code&gt; &lt;a href="https://github.com/golang/go/blob/master/src/net/http/request.go#L899"&gt;https://github.com/golang/go/blob/master/src/net/http/request.go#L899&lt;/a&gt;. Having to do this without a debugger would have taken quite a bit of code hopping - the debugger took care of that for me and allowed me to validate argument values as I did it.&lt;/p&gt;

&lt;h2&gt;
  
  
  why not gdb?
&lt;/h2&gt;

&lt;p&gt;I know some folk feel strongly that &lt;code&gt;gdb&lt;/code&gt; is the tool to use for debugging go code, but given the Golang docs itself encourage you to use Delve over GDB I personally stay away from it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that Delve is a better alternative to GDB when debugging Go programs built with the standard toolchain. It understands the Go runtime, data structures, and expressions better than GDB. Delve currently supports Linux, OSX, and Windows on amd64. For the most up-to-date list of supported platforms, please see the Delve documentation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Exceptions here may be usage of cgo but I'll leave that out for now.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;source: &lt;a href="https://tip.golang.org/doc/gdb"&gt;https://tip.golang.org/doc/gdb&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>debugging</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Python debugging tips</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Sun, 08 Jan 2023 11:53:10 +0000</pubDate>
      <link>https://forem.com/pemcconnell/python-debugging-tips-4n57</link>
      <guid>https://forem.com/pemcconnell/python-debugging-tips-4n57</guid>
      <description>&lt;p&gt;&lt;em&gt;the 'code' for this article can be found here: &lt;a href="https://github.com/peter-mcconnell/petermcconnell.com/blob/main/assets/dummy/pydebug/main.py" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/petermcconnell.com/blob/main/assets/dummy/pydebug/main.py&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  debugging Python - the context
&lt;/h2&gt;

&lt;p&gt;This is the flow I take when faced with a new Python codebase. I often find myself having to debug codebases I've never seen before which has forced me to become very comfortable being lost in code and to develop some patterns that help me find my way. This is what I'm sharing with you today.&lt;/p&gt;

&lt;p&gt;I should note that I live in terminals - constantly connecting to servers, containers, colleagues machines, my own homelab etc. To compound this fact my editor of choice also lives in the terminal (Neovim). For that reason &lt;strong&gt;this guide is TERMINAL based&lt;/strong&gt; and as such does not include IDE-based debugging flows (which are solid from what I've seen).&lt;/p&gt;

&lt;h2&gt;
  
  
  what are the requirements?
&lt;/h2&gt;

&lt;p&gt;The debugger of choice (for me) is &lt;code&gt;ipdb&lt;/code&gt;. The reasons for this are at the end of the article.&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;ipdb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; ipdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll also need to gather information from the &lt;code&gt;refining scope&lt;/code&gt; section below.&lt;/p&gt;

&lt;h2&gt;
  
  
  refining scope
&lt;/h2&gt;

&lt;p&gt;Often (my own usecase) my Python debugging story typically starts with: "This app is broken. It's doing X" which tells me very little about what's wrong and where to look. My first objective is to make the size of the problem statement as small / tight as possible. To do so, before I've looked at any code I try to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validate that it appears to be an issue with the code and categorise it

&lt;ul&gt;
&lt;li&gt;perf issue&lt;/li&gt;
&lt;li&gt;logic issue&lt;/li&gt;
&lt;li&gt;flakiness&lt;/li&gt;
&lt;li&gt;dependency issue&lt;/li&gt;
&lt;li&gt;etc&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;identify which version of that app I need to debug &amp;amp; where I can get it&lt;/li&gt;

&lt;li&gt;identify which part of the codebase (file location, method, line)&lt;/li&gt;

&lt;li&gt;identify required inputs (method arguments, environment variables, third party sources etc)&lt;/li&gt;

&lt;li&gt;understand what has been tried already to fix the problem&lt;/li&gt;

&lt;li&gt;identify stakeholders, urgency etc ...&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This serves a few purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ensure I can reproduce the bug&lt;/li&gt;
&lt;li&gt;reduce the scope of things that I need to look at&lt;/li&gt;
&lt;li&gt;help me understand the business logic / expected results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point I should have the confidence to know that the problem requires debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  example application
&lt;/h2&gt;

&lt;p&gt;To get started create the following file. This is the simplest possible example I could create so as to keep signal/noise ratio in favour of the actual debugging steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python
# main.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doubleit 2: %d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doubleit 4: %d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doubleit 8: %d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll use this simple example for our debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  using ipdb
&lt;/h2&gt;

&lt;p&gt;From the information gathered earlier lets imagine the outputs were that the program above is spitting out the wrong values. We expect the &lt;code&gt;doubleit&lt;/code&gt; lines to show their values being doubled but instead they seem to be trebled (yes, it's obvious why, but imagine this is a very large program and you don't know why the output is what it is).&lt;/p&gt;

&lt;p&gt;With that information to hand we can look for the &lt;code&gt;doubleit&lt;/code&gt; method and add set some breakpoints so that we can explore the program as it's running to understand the state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python
# main.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ipdb&lt;/span&gt;       &lt;span class="c1"&gt;# &amp;lt; added this line
&lt;/span&gt;    &lt;span class="n"&gt;ipdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_trace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt; added this line
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doubleit 2: %d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doubleit 4: %d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doubleit 8: %d&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;doubleit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can continue to add &lt;code&gt;ipdb.set_trace()&lt;/code&gt; points throughout our code. Generally speaking when I am running this for the first time I'll tend to just drop one or two points in the codebase that I know are going to be in the path, with the expectation that I'll manually step through the execution to learn how it flows. When we've added all of the breakpoints that we need we can instruct the program to run with &lt;code&gt;python main.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;python main.py
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /home/pete/go/src/github.com/peter-mcconnell/petermcconnell.com/assets/dummy/pydebug/main.py&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;doubleit&lt;span class="o"&gt;()&lt;/span&gt;
      5     ipdb.set_trace&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6     &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3
      7

ipdb&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we've ran our program with an attached debugger and it has paused execution at the breakpoint we set. We can run &lt;code&gt;args&lt;/code&gt; to see which arguments where passed to the method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /home/pete/go/src/github.com/peter-mcconnell/petermcconnell.com/assets/dummy/pydebug/main.py&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;doubleit&lt;span class="o"&gt;()&lt;/span&gt;
      5     ipdb.set_trace&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6     &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3
      7

ipdb&amp;gt; args
val &lt;span class="o"&gt;=&lt;/span&gt; 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in this point in the program we're in the &lt;code&gt;doubleit&lt;/code&gt; method when it was invoked with a &lt;code&gt;val&lt;/code&gt; value of &lt;code&gt;2&lt;/code&gt;. We can print this and other variables using &lt;code&gt;p&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ipdb&amp;gt; p val
2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or just the variable name on its own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ipdb&amp;gt; val
2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even call methods from this point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ipdb&amp;gt; doubleit&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To walk over the execution we can press &lt;code&gt;n&lt;/code&gt; to go to the next point of execution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ipdb&amp;gt; doubleit&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;
18
ipdb&amp;gt; n
&lt;span class="nt"&gt;--Return--&lt;/span&gt;
6
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /home/pete/go/src/github.com/peter-mcconnell/petermcconnell.com/assets/dummy/pydebug/main.py&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;doubleit&lt;span class="o"&gt;()&lt;/span&gt;
      5     ipdb.set_trace&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6     &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3
      7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and view the backtrace with &lt;code&gt;bt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ipdb&amp;gt; bt
  /home/pete/go/src/github.com/peter-mcconnell/petermcconnell.com/assets/dummy/pydebug/main.py&lt;span class="o"&gt;(&lt;/span&gt;9&lt;span class="o"&gt;)&lt;/span&gt;&amp;lt;module&amp;gt;&lt;span class="o"&gt;()&lt;/span&gt;
      8 &lt;span class="k"&gt;if &lt;/span&gt;__name__ &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;:
&lt;span class="nt"&gt;----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 9     print&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 2: %d"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;))&lt;/span&gt;
     10     print&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 4: %d"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;))&lt;/span&gt;

6
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /home/pete/go/src/github.com/peter-mcconnell/petermcconnell.com/assets/dummy/pydebug/main.py&lt;span class="o"&gt;(&lt;/span&gt;6&lt;span class="o"&gt;)&lt;/span&gt;doubleit&lt;span class="o"&gt;()&lt;/span&gt;
      5     ipdb.set_trace&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6     &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3
      7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To view the code around the current point of execution just press &lt;code&gt;l&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ipdb&amp;gt; l
      1 &lt;span class="c"&gt;#!/usr/bin/env python&lt;/span&gt;
      2 &lt;span class="c"&gt;# main.py&lt;/span&gt;
      3 def doubleit&lt;span class="o"&gt;(&lt;/span&gt;val&lt;span class="o"&gt;)&lt;/span&gt;:
      4     import ipdb
      5     ipdb.set_trace&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="nt"&gt;----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6     &lt;span class="k"&gt;return &lt;/span&gt;val &lt;span class="k"&gt;*&lt;/span&gt; 3
      7
      8 &lt;span class="k"&gt;if &lt;/span&gt;__name__ &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"__main__"&lt;/span&gt;:
      9     print&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 2: %d"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;))&lt;/span&gt;
     10     print&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 4: %d"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;4&lt;span class="o"&gt;))&lt;/span&gt;
     11     print&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"doubleit 8: %d"&lt;/span&gt;, doubleit&lt;span class="o"&gt;(&lt;/span&gt;8&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which of course shows our very hard to find logic error, &lt;code&gt;* 3&lt;/code&gt; instead of &lt;code&gt;* 2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note: you can also set breakpoints in the stdlib functions (paths will vary depending on your setup):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ipdb&amp;gt; b /home/pete/.local/lib/python3.10/site-packages/requests/api.py:14
Breakpoint 1 at /home/pete/.local/lib/python3.10/site-packages/requests/api.py:14
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  debug flow
&lt;/h2&gt;

&lt;p&gt;Using the commands above I can begin my cyclic process of narrowing in on the fix:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;repro -&amp;gt; explore -&amp;gt; understand -&amp;gt; tweak -&amp;gt; repeat&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;More often than not this means I only need to understand a very small part of the application and can ignore code that isn't relevant to the immediate issue.&lt;/p&gt;

&lt;p&gt;At a more detailed level this process looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(repro) write a test that triggers the bug in as simple terms as I can express&lt;/li&gt;
&lt;li&gt;(explore) set breakpoints&lt;/li&gt;
&lt;li&gt;(explore) run &lt;code&gt;pytest&lt;/code&gt; with the &lt;code&gt;-s&lt;/code&gt; flag so that I can interact with &lt;code&gt;ipdb&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;(explore) use &lt;code&gt;args&lt;/code&gt; to check the arguments for the method that I'm in&lt;/li&gt;
&lt;li&gt;(explore) print surrounding variable values&lt;/li&gt;
&lt;li&gt;(explore) ensure the state of the program makes sense for my current breakpoint. If not, I need an earlier breakpoint. If so, continue with &lt;code&gt;n&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;(explore) repeat these steps until I've reached the point that the program is in a seemingly erroneous state

&lt;ul&gt;
&lt;li&gt;(understand) it's at this stage I'll take time to properly read the surrounding code and experiment with variable values to see if I can get the program to act in the expected manner&lt;/li&gt;
&lt;li&gt;(understand) depending on the category of bug I'll look for algorithmic complexity issues, stack overflow issues, parameter edgecases, logging quality, randomness factors etc. This is when the editor setup shines. see neovim section&lt;/li&gt;
&lt;li&gt;(tweak) I'll make minor adjustments to the code which I believe will nudge the program into the right place&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Once I'm happy that my small tweaks are having the desired effect I'll perform some tidy ups and look for opportunities to harden the code with type checking / improved logging / more tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  neovim
&lt;/h2&gt;

&lt;p&gt;This section describes my &lt;code&gt;neovim&lt;/code&gt; configuration for Python debugging at a high level. In short my debugging / code exploration flow boils down to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;telescope&lt;/code&gt; &lt;a href="https://github.com/nvim-telescope/telescope.nvim" rel="noopener noreferrer"&gt;https://github.com/nvim-telescope/telescope.nvim&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;allows me to &lt;code&gt;ctrl + f&lt;/code&gt; scan directories for files&lt;/li&gt;
&lt;li&gt;allows me to set up keybindings for scanning any common directories&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;coc&lt;/code&gt; &lt;a href="https://github.com/neoclide/coc.nvim" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://github.com/neoclide/coc.nvim" rel="noopener noreferrer"&gt;https://github.com/neoclide/coc.nvim&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;code complete in all of the languages I need&lt;/li&gt;
&lt;li&gt;function descriptions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;gd&lt;/code&gt; - default vim keybinding for go-to-definition. Jumps me into a function that I'm wanting to understand&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;ctrl + o&lt;/code&gt; / &lt;code&gt;ctrl + i&lt;/code&gt; - default vim keybindings for go to last / next jump point. Really useful as I'm scanning code - I can keep jumping through definitions with &lt;code&gt;gd&lt;/code&gt; then &lt;code&gt;ctrl + o&lt;/code&gt; my way back / &lt;code&gt;ctrl + i&lt;/code&gt; my way back down as I'm trying to build an understanding&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;You can see my full Neovim config here: &lt;a href="https://github.com/peter-mcconnell/.dotfiles/blob/master/config/nvim/init.vim" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/.dotfiles/blob/master/config/nvim/init.vim&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  summary
&lt;/h2&gt;

&lt;p&gt;The example above is extremely trivial - where &lt;code&gt;ipdb&lt;/code&gt; and it's ilk shine are on complex usecases where you may not even know what methods are between the input and output, such as debugging the stdlib. Just this week I used &lt;code&gt;ipdb&lt;/code&gt; to identify why a codebase long forgotten was throwing an obscure error for a given dataset. By using &lt;code&gt;ipdb&lt;/code&gt; I reproduced the scenario and just before the point at which I knew it would error created a break point that allowed me to inspect program state and better understand the conditions leading to the error, resulting in a quick patch.&lt;/p&gt;

&lt;h2&gt;
  
  
  why not pdb?
&lt;/h2&gt;

&lt;p&gt;Bells and whistles; I like that ipdb has better color support and tab completion. You could absolutely get the same results with &lt;code&gt;pdb&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
    </item>
    <item>
      <title>Perf engineering with Python 3.12</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Sat, 31 Dec 2022 10:11:50 +0000</pubDate>
      <link>https://forem.com/pemcconnell/perf-engineering-with-python-312-2a1k</link>
      <guid>https://forem.com/pemcconnell/perf-engineering-with-python-312-2a1k</guid>
      <description>&lt;h2&gt;
  
  
  overview
&lt;/h2&gt;

&lt;p&gt;3.12 brings perf profiling! In this article we take a look at how the new perf profiling support helps reduce our dummy Python script from 36 seconds to 0.8 seconds. We'll introduce the Linux tool &lt;code&gt;perf&lt;/code&gt; and also &lt;code&gt;FlameGraph.pl&lt;/code&gt;, look at some disassembly and go bug hunting. You can view the code for this article here: &lt;a href="https://github.com/peter-mcconnell/petermcconnell.com/tree/main/assets/dummy/perf_py_proj" rel="noopener noreferrer"&gt;https://github.com/peter-mcconnell/petermcconnell.com/tree/main/assets/dummy/perf_py_proj&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a second to go check out &lt;a href="https://docs.python.org/3.12/howto/perf_profiling.html" rel="noopener noreferrer"&gt;https://docs.python.org/3.12/howto/perf_profiling.html&lt;/a&gt; and indeed the changelog at &lt;a href="https://www.python.org/downloads/release/python-3120a3/" rel="noopener noreferrer"&gt;https://www.python.org/downloads/release/python-3120a3/&lt;/a&gt;. The important part (for this post) from these links is:&lt;/p&gt;

&lt;p&gt;"""&lt;br&gt;
The Linux perf profiler is a very powerful tool that allows you to profile and obtain information about the performance of your application. perf also has a very vibrant ecosystem of tools that aid with the analysis of the data that it produces.&lt;/p&gt;

&lt;p&gt;The main problem with using the perf profiler with Python applications is that perf only allows to get information about native symbols, this is, the names of the functions and procedures written in C. This means that the names and file names of the Python functions in your code will not appear in the output of the perf.&lt;/p&gt;

&lt;p&gt;Since Python 3.12, the interpreter can run in a special mode that allows Python functions to appear in the output of the perf profiler. When this mode is enabled, the interpreter will interpose a small piece of code compiled on the fly before the execution of every Python function and it will teach perf the relationship between this piece of code and the associated Python function using perf map files.&lt;br&gt;
"""&lt;/p&gt;
&lt;h2&gt;
  
  
  writing a "bad" program
&lt;/h2&gt;

&lt;p&gt;I'm excited to try this, so lets get going. Firstly lets create a python script for us to profile. I'm doing this before installing Python 3.12 as I want to create a FlameGraph of how this process looks in 3.10 verses 3.12. Here we have a script that attempts to perform lookups against a large list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;findme&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;findme&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;missed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# create a large sized input to show off inefficiency
&lt;/span&gt;    &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20000000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

    &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# get the current time [start]
&lt;/span&gt;    &lt;span class="nf"&gt;run_dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# run our inefficient method
&lt;/span&gt;    &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# get the current time [end]
&lt;/span&gt;
    &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;  &lt;span class="c1"&gt;# Calculate the duration
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Duration: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Print the duration
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running this I get the following result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3.10 assets/dummy/perf_py_proj/before.py
...
found 99992
found 99993
found 99994
found 99995
found 99996
found 99997
found 99998
found 99999
Duration: 36.06884431838989 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;36 seconds is bad enough for us to pick up a reasonable amount of samples.&lt;/p&gt;

&lt;h2&gt;
  
  
  flamegraphs!
&lt;/h2&gt;

&lt;p&gt;Now we can create our &lt;a href="https://github.com/brendangregg/FlameGraph" rel="noopener noreferrer"&gt;FlameGraph&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# record profile to "perf.data" file (default output)&lt;/span&gt;
perf record &lt;span class="nt"&gt;-F&lt;/span&gt; 99 &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; python3.10 assets/dummy/perf_py_proj/before.py
&lt;span class="c"&gt;# read perf.data (created above) and display trace output&lt;/span&gt;
perf script &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.perf
&lt;span class="c"&gt;# fold stack samples into single lines&lt;/span&gt;
&lt;span class="c"&gt;# here I reference ~/FlameGraph/ - you can get this from https://github.com/brendangregg/FlameGraph&lt;/span&gt;
~/FlameGraph/stackcollapse-perf.pl out.perf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.folded
&lt;span class="c"&gt;# generate flamegraph&lt;/span&gt;
~/FlameGraph/flamegraph.pl out.folded &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./assets/perf_example_python3.10.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us a nice SVG that visualises the traces:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_example_python3.10.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_example_python3.10.svg" title="python 3.10 perf flamegraph" alt="python 3.10 perf flamegraph" width="1200" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This isn't useful ... I can see most of the time was spent in "new_keys_object.lto_priv.0" but that is meaningless in the context of the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  time for Python 3.12...
&lt;/h2&gt;

&lt;p&gt;First I need to install it - the steps for this vary depending on OS - follow the build instructions here for your environment: &lt;a href="https://github.com/python/cpython/tree/v3.12.0a3#build-instructions" rel="noopener noreferrer"&gt;https://github.com/python/cpython/tree/v3.12.0a3#build-instructions&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# for me on ubuntu:22.04&lt;/span&gt;
&lt;span class="c"&gt;# ensure I have python3-dbg installed&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;python3-dbg

&lt;span class="c"&gt;# build python&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-fno-omit-frame-pointer -mno-omit-leaf-frame-pointer"&lt;/span&gt;
./configure &lt;span class="nt"&gt;--enable-optimizations&lt;/span&gt;
make
make &lt;span class="nb"&gt;test
sudo &lt;/span&gt;make &lt;span class="nb"&gt;install
unset &lt;/span&gt;CFLAGS

&lt;span class="c"&gt;# after this I reset my systems python3 symlink to 3.10 as 3.12 isn't yet stable&lt;/span&gt;
&lt;span class="c"&gt;# for testing python3.12 I'll call "python3.12" instead of "python3"&lt;/span&gt;
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; /usr/local/bin/python3.10 /usr/local/bin/python3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that installed I first need to enable perf support. This is detailed in &lt;a href="https://docs.python.org/3.12/howto/perf_profiling.html" rel="noopener noreferrer"&gt;https://docs.python.org/3.12/howto/perf_profiling.html&lt;/a&gt; and there are three options: 1) an environment variable, 2) an -X option or 3) dynamically using &lt;code&gt;sys&lt;/code&gt;. I'll go for the environment variable approach as I don't mind &lt;em&gt;everything&lt;/em&gt; being profiled for a small script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PYTHONPERFSUPPORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we simply repeat the process above using the &lt;code&gt;python3.12&lt;/code&gt; binary instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# record profile to "perf.data" file (default output)&lt;/span&gt;
perf record &lt;span class="nt"&gt;-F&lt;/span&gt; 99 &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; python3.12 assets/dummy/perf_py_proj/before.py
&lt;span class="c"&gt;# read perf.data (created above) and display trace output&lt;/span&gt;
perf script &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.perf
&lt;span class="c"&gt;# fold stack samples into single lines&lt;/span&gt;
&lt;span class="c"&gt;# here I reference ~/FlameGraph/ - you can get this from https://github.com/brendangregg/FlameGraph&lt;/span&gt;
~/FlameGraph/stackcollapse-perf.pl out.perf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.folded
&lt;span class="c"&gt;# generate flamegraph&lt;/span&gt;
~/FlameGraph/flamegraph.pl out.folded &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./assets/perf_example_python3.12.before.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we'll take a peek at the report with &lt;code&gt;perf report -g -i perf.data&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_report.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_report.png" title="python 3.12 perf report" alt="python 3.12 perf report output" width="800" height="798"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! We can see our Python function names and script names!&lt;/p&gt;

&lt;p&gt;Now we can take a look at the updated SVG that visualises the traces with Python 3.12:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_example_python3.12.before.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_example_python3.12.before.svg" title="python 3.12 perf flamegraph" alt="python 3.12 perf flamegraph" width="1200" height="1414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is already looking much more useful. We see the majority of the time is being spent doing comparisons and in the list_contains method. We can also see the specific file &lt;code&gt;before.py&lt;/code&gt; and method &lt;code&gt;run_dummy&lt;/code&gt; that is calling it.&lt;/p&gt;

&lt;h2&gt;
  
  
  investigation time / the fix
&lt;/h2&gt;

&lt;p&gt;Now that we know where in our code the problem is, we can take a look at the source code in CPython to see why the &lt;code&gt;list_contains&lt;/code&gt; method would be so slow: &lt;a href="https://github.com/python/cpython/blob/199507b81a302ea19f93593965b1e5088195a6c5/Objects/listobject.c#L440" rel="noopener noreferrer"&gt;https://github.com/python/cpython/blob/199507b81a302ea19f93593965b1e5088195a6c5/Objects/listobject.c#L440&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;note: you may not always have access to the source code - in circumstances such as this you can view the disassembly in perf report directly to get some idea of what's going on. I'll add a quick section at the end showing how this looks&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// I found this by going to https://github.com/python/cpython/ and searching for "list_contains"&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="nf"&gt;list_contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PyListObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;PyObject&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;Py_ssize_t&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Py_SIZE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PyList_GET_ITEM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Py_INCREF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;cmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PyObject_RichCompareBool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Py_EQ&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Py_DECREF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nasty... looking at this code I can see that each time it is invoked it iterates over the array and performs a comparison against each item. That's far from ideal for our usecase, so lets go back to the Python code we wrote. Our Flamegraph shows us that the problem is in our &lt;code&gt;run_dummy&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;findme&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;findme&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;#  &amp;lt;- this is what triggers list_contains
&lt;/span&gt;            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;missed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;findme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can't really change that line as it is doing what we want it to do - identifying if an integer is in &lt;code&gt;numbers&lt;/code&gt;. Perhaps we can change the &lt;code&gt;numbers&lt;/code&gt; data type to one better suited to lookups. In our existing code we have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;numbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20000000&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

    &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# get the current time [start]
&lt;/span&gt;    &lt;span class="nf"&gt;run_dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# run our inefficient method
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we used a LIST data type for our "numbers", which under the hood (in CPython) is implemented as dynamically-sized arrays and as such are nowhere near as efficient (O(N)) as the likes of a Hashtable for looking up an item (which is O(1)). A SET on the other hand (another Python data type) is implemented as a Hashtable and would give us the fast lookup we're looking for. Lets change the data type in our Python code and see what the impact is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="c1"&gt;# we'll just change this line, casting numbers to a set before running run_dummy
&lt;/span&gt;    &lt;span class="nf"&gt;run_dummy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# passing a set() for fast lookups
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can repeat the steps as above to generate our new flamegraph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# record profile to "perf.data" file (default output)&lt;/span&gt;
perf record &lt;span class="nt"&gt;-F&lt;/span&gt; 99 &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; python3.12 assets/dummy/perf_py_proj/after.py
...
found 99998
found 99999
Duration: 0.8350753784179688 seconds
&lt;span class="o"&gt;[&lt;/span&gt; perf record: Woken up 1 &lt;span class="nb"&gt;times &lt;/span&gt;to write data &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; perf record: Captured and wrote 0.039 MB perf.data &lt;span class="o"&gt;(&lt;/span&gt;134 samples&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Already we can see that things have massively improved. Where previously this was taking 36 seconds to run it is now taking 0.8 seconds! Lets continue creating our flamegraph for the new improved code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# read perf.data (created above) and display trace output&lt;/span&gt;
perf script &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.perf
&lt;span class="c"&gt;# fold stack samples into single lines&lt;/span&gt;
&lt;span class="c"&gt;# here I reference ~/FlameGraph/ - you can get this from https://github.com/brendangregg/FlameGraph&lt;/span&gt;
~/FlameGraph/stackcollapse-perf.pl out.perf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; out.folded
&lt;span class="c"&gt;# generate flamegraph&lt;/span&gt;
~/FlameGraph/flamegraph.pl out.folded &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./assets/perf_example_python3.12.after.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_example_python3.12.after.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_example_python3.12.after.svg" title="python 3.12 perf flamegraph improved" alt="python 3.12 perf flamegraph improved" width="1200" height="902"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a much healthier looking Flamegraph and our application is now much faster as a result. The perf profiling support in Python 3.12 brings a tremendously useful tool to software engineers that want to deliver fast programs and I'm excited to see the impact this will have on the language.&lt;/p&gt;

&lt;h2&gt;
  
  
  bonus round: what to do when you can't access the source code?
&lt;/h2&gt;

&lt;p&gt;Sometimes you don't have access to the underlying code which can make trying to understand what's going on much more difficult. Thankfully &lt;code&gt;perf report&lt;/code&gt; allows us to view the dissassembled code which can help paint a picture of what the machine is actually doing. This is a reasonable first place to look - I tend to prefer the source code if I can get hold of it as it allows me to &lt;code&gt;git blame&lt;/code&gt; / view the associated commits and PRs. To view this you can do the following:&lt;/p&gt;

&lt;p&gt;Open the perf report and select the line we're interested in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# this assumes we have already ran 'perf record' to generate perf.data ...&lt;/span&gt;
perf report &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; perf.data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_report_dis.1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_report_dis.1.png" title="perf report dissassembly" alt="perf report dissassembly" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press enter and choose the annotate option:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_report_dis.2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_report_dis.2.png" title="perf report dissassembly" alt="perf report dissassembly" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Behold! Here we can see both the C code and the machine instructions. Super useful! You can compare the screenshot below against the code snippet we're interested in: &lt;a href="https://github.com/python/cpython/blob/199507b81a302ea19f93593965b1e5088195a6c5/Objects/listobject.c#L440" rel="noopener noreferrer"&gt;https://github.com/python/cpython/blob/199507b81a302ea19f93593965b1e5088195a6c5/Objects/listobject.c#L440&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_report_dis.3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_report_dis.3.png" title="perf report dissassembly" alt="perf report dissassembly" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  recommended reading
&lt;/h2&gt;

&lt;p&gt;If this article has given you a taste for performance engineering, I can recommend the following Systems Performance book:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.com/Systems-Performance-Brendan-Gregg/dp/0136820158?crid=2J7NSUPP1LBQ2&amp;amp;keywords=systems+performance+enterprise+and+the+cloud&amp;amp;qid=1672315747&amp;amp;sprefix=systems+performance%2Caps%2C167&amp;amp;sr=8-1&amp;amp;linkCode=li2&amp;amp;tag=mobile052c67f-20&amp;amp;linkId=042c48313bcd6eae20ae98499600e515&amp;amp;language=en_US&amp;amp;ref_=as_li_ss_il" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahtjj0y767irinzxbyp6.jpg" alt="Systems Performance by Brendan Gregg" width="648" height="846"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>top7</category>
      <category>developers</category>
    </item>
    <item>
      <title>chatGPT - building an automated database testing tool</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Sat, 31 Dec 2022 10:08:37 +0000</pubDate>
      <link>https://forem.com/pemcconnell/chatgpt-building-an-automated-database-testing-tool-2d99</link>
      <guid>https://forem.com/pemcconnell/chatgpt-building-an-automated-database-testing-tool-2d99</guid>
      <description>&lt;h2&gt;
  
  
  Creating an automated database testing tool with ChatGPT
&lt;/h2&gt;

&lt;p&gt;Last night I thought I'd try to get ChatGPT to make an automated database testing tool and the results were quite promising.&lt;/p&gt;

&lt;p&gt;In conclusion, with guidance, it was able to build a project from scratch that ran a python script and postgres database. It generated some random schema and values for the randomly generated tables. It provided a Python script which would introspect the database and execute queries against it.&lt;/p&gt;

&lt;p&gt;Did it all work out of the box? No. There are some bugs to fix in the python script it generated. However the effort to go in and fix those is not high and certainly the whole end-to-end process is cheaper, time-wise, compared to starting from scratch.&lt;/p&gt;

&lt;p&gt;I found that the bugs it encountered were largely due to my lack of clarity or ordering of questions posed to it. It was quite capable of fixing its own mistakes / updating the existing code to match the new requirements when requested to do so.&lt;/p&gt;

&lt;p&gt;The only &lt;em&gt;real&lt;/em&gt; issue I encountered were general API errors that one would expect of something so popular in an early preview state.&lt;/p&gt;

&lt;p&gt;I came away from this experiment viewing ChatGPT and whatever follows it as a really useful development aide for those who already know how to program. It helped me build a tool faster than I could have had I sat down to do it from scratch. I don't view it as a replacement for software engineers yet for two main reasons - firstly: for non-trivial applications I suspect the person feeding requirements into the system (or "prompt engineer") needs to have a reasonable idea of how to build software in the first place, so as to know how to form requests and to correct mistakes / close gaps. secondly: the code being generated isn't always sound - without an experienced engineer reviewing and taking ownership of whatever code is produced (ownership being important for maintainence reasons) then there's little guarantee that you will get what you are hoping for.&lt;/p&gt;

&lt;p&gt;However; this is still very early days. Can the problems outlined be closed further? Absolutely. Will this sort of tooling be "bad" for software engineering as a whole, long-term? Perhaps. Personally I'm very excited to have this tool in my arsenal - already it has allowed me to scaffold prototype applications quickly. Would I use it for production code in a workplace? No more or less than I would snippets from stackoverflow or it's ilk. For now.&lt;/p&gt;

&lt;p&gt;Github repository: &lt;a href="https://github.com/peter-mcconnell/gpt_sql_test_generator"&gt;https://github.com/peter-mcconnell/gpt_sql_test_generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Screenshots:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q3d5_OwP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q3d5_OwP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/2.png" alt="step 2" title="step 2" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bVSBEk7u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bVSBEk7u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/3.png" alt="step 3" title="step 3" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RmaezyFM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RmaezyFM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/4.png" alt="step 4" title="step 4" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1fXF81-1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1fXF81-1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/5.png" alt="step 5" title="step 5" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Jf6_J4q5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Jf6_J4q5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/6.png" alt="step 6" title="step 6" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vJfsrUpv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJfsrUpv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/7.png" alt="step 7" title="step 7" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CMJMCcqJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CMJMCcqJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/8.png" alt="step 8" title="step 8" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sJHxp3Bm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sJHxp3Bm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/9.png" alt="step 9" title="step 9" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yQ3qc-G5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yQ3qc-G5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/10.png" alt="step 10" title="step 10" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PvJVC0tD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/11.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PvJVC0tD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/11.png" alt="step 11" title="step 11" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GQUx5twf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/12.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GQUx5twf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/12.png" alt="step 12" title="step 12" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qmAniK34--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qmAniK34--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/13.png" alt="step 13" title="step 13" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iE14R_Lu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iE14R_Lu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/14.png" alt="step 14" title="step 14" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mq1L170U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mq1L170U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/15.png" alt="step 15" title="step 15" width="873" height="1005"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Dughuwv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Dughuwv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/peter-mcconnell/gpt_sql_test_generator/master/media/16.png" alt="step 16" title="step 16" width="873" height="1005"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>python</category>
      <category>database</category>
      <category>docker</category>
    </item>
    <item>
      <title>A quick introduction to basic debugging tools for Linux systems</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Sat, 31 Dec 2022 09:57:13 +0000</pubDate>
      <link>https://forem.com/pemcconnell/a-quick-introduction-to-basic-debugging-tools-for-linux-systems-6ch</link>
      <guid>https://forem.com/pemcconnell/a-quick-introduction-to-basic-debugging-tools-for-linux-systems-6ch</guid>
      <description>&lt;p&gt;Firstly: let me qualify "basic". I'm using this term because the breadth of debugging tools on Linux is so large. "basic" does not mean that these tools are super simple to understand deeply or if you aren't already an expert in them that you are somehow "noob"; that's not the case at all.&lt;/p&gt;

&lt;p&gt;Secondly: there is no inventing being done here. I'm just surfacing information in this article which you can already find in the &lt;code&gt;man&lt;/code&gt; pages. I would strongly recommend you check the relevant man page for the tool you find of interest in this article.&lt;/p&gt;

&lt;p&gt;Thirdly: for the curious, these tools generally get their information from existing counters and statistics in &lt;code&gt;/proc/&lt;/code&gt; and &lt;code&gt;/sys/&lt;/code&gt;. In the man pages for these tools you can see a &lt;code&gt;FILES&lt;/code&gt; section which details the data source for the given tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  "crisis tools"
&lt;/h2&gt;

&lt;p&gt;This section is taken from Brendan Gregg's book &lt;a href="https://amzn.to/3I9iU49]" rel="noopener noreferrer"&gt;'System Performance' (4.1.2)&lt;/a&gt;. It covers not only where you can install some of the binaries mentioned in this article but also other useful tools for debugging. All credit to him for this list.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;package&lt;/th&gt;
&lt;th&gt;provides&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;procps&lt;/td&gt;
&lt;td&gt;ps(1), vmstat(8), uptime(1), top(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;util-linux&lt;/td&gt;
&lt;td&gt;dmesg(1), lsblk(1), lscpu(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sysstat&lt;/td&gt;
&lt;td&gt;iostat(1), mpstat(1), pidstat(1), sar(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iproute2&lt;/td&gt;
&lt;td&gt;ip(8), ss(8), nstat(8), tc(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;numactl&lt;/td&gt;
&lt;td&gt;numastat(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;linux-tools-common linux-tools-$(uname -r)&lt;/td&gt;
&lt;td&gt;perf(1), turbostat(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bcc-tools (aka bpfcc-tools)&lt;/td&gt;
&lt;td&gt;opensnoop(8), execsnoop(8), runqlat(8), runqlen(8), softirqs(8), hardirqs(8), ext4slower(8), ext4dist(8), biotop(8), biosnoop(8), biolatency(8), tcptop(8), tcplife(8), trace(8), argdist(8), funccount(8), stackcount(8), profile(8) and more ...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bpftrace&lt;/td&gt;
&lt;td&gt;bpftrace, basic versions of opensnoop(8), iolatency(8), iosnoop(8), bitesize(8), funccount(8), kprobe(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;perf-tools-unstable&lt;/td&gt;
&lt;td&gt;Ftrace versions of opensnoop(8), execsnoop(8), iolatency(8), iosnoop(8), bitesize(8), funccount(8), kprobe(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;trace-cmd&lt;/td&gt;
&lt;td&gt;trace-cmd(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nicstat&lt;/td&gt;
&lt;td&gt;nicstat(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ethtool&lt;/td&gt;
&lt;td&gt;ethtool(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tiptop&lt;/td&gt;
&lt;td&gt;tiptop(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;msr-tools&lt;/td&gt;
&lt;td&gt;rdmsr(8), wrmsr(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;github.com/brendangregg/msr-cloud-tools&lt;/td&gt;
&lt;td&gt;showboost(8), cpuhot(8), cputemp(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;github.com/brendangregg/pmc-cloud-tools&lt;/td&gt;
&lt;td&gt;pmcarch(8), cpucache(8), lcache(8), tlbstat(8), resstalls(8)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  top
&lt;/h2&gt;

&lt;p&gt;When: A reasonable first place to look.&lt;/p&gt;

&lt;p&gt;This is a command-line utility in Linux that allows users to view the processes running on their system and monitor their resource usage in real-time. It can be used to identify performance bottlenecks, track the usage of system resources, and troubleshoot issues on a Linux system.&lt;/p&gt;

&lt;p&gt;The output of top will include a list of processes running on the system, along with the following information for each process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The process ID (PID)&lt;/li&gt;
&lt;li&gt;The user owning the process&lt;/li&gt;
&lt;li&gt;The CPU and memory usage of the process&lt;/li&gt;
&lt;li&gt;The command that started the process&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the output you can identify some performance issues with your system. For example, if a single process is consuming a large amount of CPU or memory resources, it could be causing performance issues for your system. Similarly, if multiple processes are consuming high amounts of resources, it could indicate that your system is struggling to keep up with demand and may be a bottleneck.&lt;/p&gt;

&lt;p&gt;One common problem that &lt;code&gt;top&lt;/code&gt; can help identify is resource contention which occurs when multiple processes or applications are competing for the same system resources.&lt;/p&gt;

&lt;p&gt;I suspect this tool is familiar to many of you already:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Ftop.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Ftop.jpg" title="top example" alt="top example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However what you may not realise is that you can pull much more data from it. Press 'f' when in top to access the fields management view and select other columns to display:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Ftop_fields.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Ftop_fields.jpg" title="top fields management view" alt="top fields management"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;htop&lt;/code&gt; is a visually prettier alternative to &lt;code&gt;top&lt;/code&gt; and that can make understanding the system at a glance easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  mpstat
&lt;/h2&gt;

&lt;p&gt;When: you think there's an issue with the CPU utilization.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mpstat&lt;/code&gt; is a command-line tool that is used to monitor the performance of a Linux system by displaying the CPU usage of each processor and the overall system.&lt;/p&gt;

&lt;p&gt;Imagine that you are the system administrator of a web server that is experiencing slow performance and perhaps even downtime. You check the web server logs but nothing obvious is standing out so you begin to suspect there's something else wrong with the system. One general check we can do is on the CPU cores - this is where &lt;code&gt;mpstat&lt;/code&gt; comes in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mpstat &lt;span class="nt"&gt;-P&lt;/span&gt; ALL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will display the CPU usage statistics for all CPU cores on the system. The output will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Linux 4.9.0-8-amd64 &lt;span class="o"&gt;(&lt;/span&gt;server.petermcconnell.com&lt;span class="o"&gt;)&lt;/span&gt;  01/01/2023  _x86_64_    &lt;span class="o"&gt;(&lt;/span&gt;2 CPU&lt;span class="o"&gt;)&lt;/span&gt;

02:34:56 AM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
02:34:56 AM  all    20.00    0.00    5.00    0.00    0.00    0.00    0.00    0.00    0.00   75.00
02:34:56 AM    0    22.50    0.00    6.25    0.00    0.00    0.00    0.00    0.00    0.00   71.25
02:34:56 AM    1     7.50    0.00    3.75    0.00    0.00    0.00    0.00    0.00    0.00   88.75
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line shows the Linux version, hostname, and CPU architecture. The second line displays the column headers, which show the various metrics that are being measured. The third line, labeled "all," shows the overall CPU usage for all CPU cores combined. The fourth and fifth lines show the CPU usage for each individual CPU core.&lt;/p&gt;

&lt;p&gt;In this example, we can see that CPU 0 is running at a higher utilization than CPU 1. This could indicate that there is an issue with CPU 0 that is causing it to work harder than it should. To further investigate the issue we can opt to print these statistics every second by adding a "1" to the end of the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mpstat &lt;span class="nt"&gt;-P&lt;/span&gt; ALL 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will print statistics from each core every second, showing the current CPU usage. By watching the output over time, we can see if there are any patterns or spikes in the usage of a particular CPU core.&lt;/p&gt;

&lt;p&gt;If we notice that one of the CPU cores is consistently running at a higher utilization than the others, we can try to identify the cause of the problem. One way to do this is by using the &lt;code&gt;top&lt;/code&gt; command to see which processes are using the most CPU resources. It could be that the program itself is misconfigured and not properly distributing load over the available cores.&lt;/p&gt;

&lt;h2&gt;
  
  
  iostat
&lt;/h2&gt;

&lt;p&gt;When: you think there's an issue with IO.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;iostat&lt;/code&gt; tool is a command-line utility in Linux that allows users to monitor input/output (I/O) statistics for devices, partitions, and network filesystems. It can be used to identify performance bottlenecks and track the usage of system resources by I/O operations.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;iostat&lt;/code&gt;, you need to specify the interval at which you want to collect data, as well as the devices or partitions you want to monitor. For example, the following command will display I/O statistics for all devices every 2 seconds, 5 times:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;iostat &lt;span class="nt"&gt;-p&lt;/span&gt; ALL 2 5
Linux 5.4.0-72-generic &lt;span class="o"&gt;(&lt;/span&gt;myputer&lt;span class="o"&gt;)&lt;/span&gt;    30/12/22    _x86_64_    &lt;span class="o"&gt;(&lt;/span&gt;4 CPU&lt;span class="o"&gt;)&lt;/span&gt;

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.40   15.48    4.48    0.99    0.00   77.66

Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
loop0             0.00         0.00         0.00         0.00          0          0          0
loop1             0.00         0.00         0.00         0.00          0          0          0
loop2             0.00         0.00         0.00         0.00          0          0          0
loop3             0.00         0.00         0.00         0.00          0          0          0
loop4             0.00         0.00         0.00         0.00          0          0          0
loop5             0.00         0.00         0.00         0.00          0          0          0
loop6             0.00         0.00         0.00         0.00          0          0          0
loop7             0.00         0.00         0.00         0.00          0          0          0
sda               4.83        79.27       106.76         0.00     858983    1156820          0

... repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also get more simple summaries - for example, CPU utilization report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;iostat &lt;span class="nt"&gt;-c&lt;/span&gt;
Linux 5.4.0-72-generic &lt;span class="o"&gt;(&lt;/span&gt;urputer&lt;span class="o"&gt;)&lt;/span&gt;    30/12/22    _x86_64_    &lt;span class="o"&gt;(&lt;/span&gt;4 CPU&lt;span class="o"&gt;)&lt;/span&gt;

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.36   15.09    4.37    0.95    0.00   78.22
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or a device utilization report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iostat &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ALL
Linux 5.4.0-72-generic &lt;span class="o"&gt;(&lt;/span&gt;xerxes&lt;span class="o"&gt;)&lt;/span&gt;     30/12/22    _x86_64_    &lt;span class="o"&gt;(&lt;/span&gt;4 CPU&lt;span class="o"&gt;)&lt;/span&gt;

      tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd Device
     0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k loop0
     0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k loop1
     0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k loop2
     0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k loop3
     0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k loop4
     0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k loop5
     0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k loop6
     0.00         0.0k         0.0k         0.0k       0.0k       0.0k       0.0k loop7
     4.82        73.5k       103.6k         0.0k     841.2M       1.2G       0.0k sda
     0.01         0.3k         0.0k         0.0k       3.1M       0.5k       0.0k sda1
     0.00         0.0k         0.0k         0.0k       2.0k       0.0k       0.0k sda2
     4.71        73.1k       103.6k         0.0k     836.0M       1.2G       0.0k sda5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're seeing a lot of devices here which are showing to be inactive. We can easily hide those from view - rarely when debugging an issue do you want to see statistics for a device that isn't being used. To hide these, pass &lt;code&gt;-z&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iostat &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ALL &lt;span class="nt"&gt;-z&lt;/span&gt;
Linux 5.4.0-72-generic &lt;span class="o"&gt;(&lt;/span&gt;xerxes&lt;span class="o"&gt;)&lt;/span&gt;     30/12/22    _x86_64_    &lt;span class="o"&gt;(&lt;/span&gt;4 CPU&lt;span class="o"&gt;)&lt;/span&gt;

      tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd Device
     4.84        72.1k       103.1k         0.0k     841.2M       1.2G       0.0k sda
     0.01         0.3k         0.0k         0.0k       3.1M       0.5k       0.0k sda1
     0.00         0.0k         0.0k         0.0k       2.0k       0.0k       0.0k sda2
     4.73        71.7k       103.1k         0.0k     836.0M       1.2G       0.0k sda5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By analyzing this data, you can identify performance issues with your devices or partitions. For example, if the number of read or write operations is consistently high, it could indicate that the device or partition is heavily used and may be a bottleneck. Similarly, if the average time taken for each I/O operation is consistently high, it could indicate that the device or partition is struggling to keep up with demand.&lt;/p&gt;

&lt;p&gt;A common problem that &lt;code&gt;iostat&lt;/code&gt; can help identify is disk saturation, which occurs when a disk is being used to its maximum capacity and can't keep up with the demand for I/O operations. This can lead to slow performance and potential data loss. In &lt;code&gt;iostat&lt;/code&gt; output this problem may manifest as a consistently high percentage of time the device is busy with I/O operations and a high average time taken for each I/O operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  sar
&lt;/h2&gt;

&lt;p&gt;When: general purpose. you think there's an issue with CPU, Memory, Network or Block devices. Can record findings to files on disk for comparisson later (which is the main selling point of &lt;code&gt;sar&lt;/code&gt; over, say &lt;code&gt;vmstat&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sar&lt;/code&gt; (System Activity Report) tool is a command-line utility in Linux that allows users to monitor various system performance metrics over time. It can be used to identify performance bottlenecks, track the usage of system resources, and troubleshoot issues on a Linux system.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;sar&lt;/code&gt;, you need to specify the interval at which you want to collect data and the system performance metrics you want to monitor. For example, the following command will display CPU utilization statistics every 2 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sar 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of sar will include various performance metrics, depending on the options you specify. Some of the metrics that sar can monitor include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU utilization&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Disk I/O activity&lt;/li&gt;
&lt;li&gt;Network activity&lt;/li&gt;
&lt;li&gt;Load average (a measure of the system's CPU and I/O utilization)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By analyzing this data, you can identify any performance issues with your system. For example, if the CPU utilization is consistently high, it could indicate that your system is struggling to keep up with demand and may be a bottleneck. Similarly, if the memory usage is consistently high, it could indicate that your system is running out of available memory.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sar&lt;/code&gt; stores its reports in &lt;code&gt;/var/log/sysstat/&lt;/code&gt; by default and will look for files in this directory when you ask it for some reports. The file it looks for changes depending on the INTERVAL value passed to it. This can result in an error if a report isn't available from yesterday:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# -B tells sar to create a report on paging statistics&lt;/span&gt;
sar &lt;span class="nt"&gt;-B&lt;/span&gt;
Cannot open /var/log/sysstat/sa30: No such file or directory
Please check &lt;span class="k"&gt;if &lt;/span&gt;data collecting is enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can specify &lt;code&gt;0&lt;/code&gt; as the interval time which tells &lt;code&gt;sar&lt;/code&gt; to use statistics for the time since the system was started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sar 0 &lt;span class="nt"&gt;-B&lt;/span&gt;
Linux 5.4.0-72-generic &lt;span class="o"&gt;(&lt;/span&gt;herputer&lt;span class="o"&gt;)&lt;/span&gt;   30/12/22    _x86_64_    &lt;span class="o"&gt;(&lt;/span&gt;4 CPU&lt;span class="o"&gt;)&lt;/span&gt;

17:51:10     pgpgin/s pgpgout/s   fault/s  majflt/s  pgfree/s pgscank/s pgscand/s pgsteal/s    %vmeff
17:51:10        66.01     99.20   3074.99      0.38   1853.06      0.00      0.00      0.00      0.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sar&lt;/code&gt; can also be used to report on I/O and transfer rate statistics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sar 0 &lt;span class="nt"&gt;-b&lt;/span&gt;
Linux 5.4.0-72-generic &lt;span class="o"&gt;(&lt;/span&gt;hisputer&lt;span class="o"&gt;)&lt;/span&gt;   30/12/22    _x86_64_    &lt;span class="o"&gt;(&lt;/span&gt;4 CPU&lt;span class="o"&gt;)&lt;/span&gt;

17:53:49          tps      rtps      wtps      dtps   bread/s   bwrtn/s   bdscd/s
17:53:49         4.64      1.32      3.32      0.00    130.43    196.40      0.00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and block devices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sar 0 &lt;span class="nt"&gt;-d&lt;/span&gt;
Linux 5.4.0-72-generic &lt;span class="o"&gt;(&lt;/span&gt;ourputer&lt;span class="o"&gt;)&lt;/span&gt;   30/12/22    _x86_64_    &lt;span class="o"&gt;(&lt;/span&gt;4 CPU&lt;span class="o"&gt;)&lt;/span&gt;

17:55:12          DEV       tps     rkB/s     wkB/s     dkB/s   areq-sz    aqu-sz     await     %util
17:55:12       dev8-0      4.63     64.81     97.66      0.00     35.13      0.05     11.69      2.11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and more. &lt;code&gt;sar&lt;/code&gt; is a very powerful tool - please check out the man page for more info.&lt;/p&gt;

&lt;h2&gt;
  
  
  vmstat
&lt;/h2&gt;

&lt;p&gt;When: you think there's an issue with memory usage or I/O activity.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;vmstat&lt;/code&gt; (Virtual Memory Statistics) tool is a command-line utility in Linux that allows users to monitor various system performance metrics, including memory usage and I/O activity. It can be used to identify performance bottlenecks, track the usage of system resources, and troubleshoot issues on a Linux system.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;vmstat&lt;/code&gt; you need to specify the interval at which you want to collect data. For example, the following command will display system performance statistics every 2 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vmstat 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of vmstat will include the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The number of processes in various states (e.g. running, waiting, blocked)&lt;/li&gt;
&lt;li&gt;The amount of memory being used, including the amount of available memory&lt;/li&gt;
&lt;li&gt;The number of page faults (when a process requests a page of memory that is not in physical memory and must be retrieved from disk)&lt;/li&gt;
&lt;li&gt;The amount of I/O activity, including the number of read and write operations per second&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  free
&lt;/h2&gt;

&lt;p&gt;When: you want to assess the state of memory on a machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;free &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
              total        used        free      shared     buffers       cache   available
Mem:           15Gi       1.1Gi        12Gi       224Mi        95Mi       1.4Gi        13Gi
Swap:         2.0Gi          0B       2.0Gi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The total amount of available and used memory&lt;/li&gt;
&lt;li&gt;The amount of used and available swap space (virtual memory on disk used to store data when the system's physical memory is full)&lt;/li&gt;
&lt;li&gt;The number of used and available buffers (a type of cache used to store data temporarily) and cache (data stored in memory to speed up access to frequently used data)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  lsblk
&lt;/h2&gt;

&lt;p&gt;When: you assume there's an issue with the block devices and want to first check everything is in place.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;lsblk&lt;/code&gt; (List Block Devices) command is a command-line utility in Linux that allows users to view a list of block devices (e.g. hard drives, SSDs, USB drives) attached to their system. It can be used to view the available block devices, their mount points, and their partition layouts. You may want to use this tool to validate if block devices are available and mounted.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;lsblk&lt;/code&gt;, you simply need to enter the command followed by any desired options. The following command will display a list of all block devices on the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lsblk &lt;span class="nt"&gt;-f&lt;/span&gt;
NAME   FSTYPE LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINT
sda
├─sda1 vfat         29C1-1F85                               511M     0% /boot/efi
├─sda2
└─sda5 ext4         e3a6ec94-05ab-4e15-a310-e5797a2c55e9    419G     3% /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of lsblk will include the following information for each block device:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The device name (e.g. sda, sdb)&lt;/li&gt;
&lt;li&gt;The device type (e.g. disk, partition)&lt;/li&gt;
&lt;li&gt;The size of the device&lt;/li&gt;
&lt;li&gt;The mount point (if the device is mounted)&lt;/li&gt;
&lt;li&gt;The filesystem type (if the device is formatted with a filesystem)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  df
&lt;/h2&gt;

&lt;p&gt;When: you're aware of disk space issues and want to check the current availability.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;df&lt;/code&gt; (Disk Free) command is a command-line utility in Linux that allows users to view the amount of available and used disk space on their system. It can be used to identify if a disk is approaching or at capacity.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;df&lt;/code&gt;, you simply need to enter the command followed by any desired options. The following command will display the total amount of available and used disk space on the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; /
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda5       457G   15G  420G   4% /
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of df will include the following information for each file system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The file system name (e.g. /dev/sda1)&lt;/li&gt;
&lt;li&gt;The total size of the file system&lt;/li&gt;
&lt;li&gt;The amount of used and available space on the file system&lt;/li&gt;
&lt;li&gt;The percentage of used space on the file system&lt;/li&gt;
&lt;li&gt;The mount point (the location where the file system is mounted in the directory structure)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  perf
&lt;/h2&gt;

&lt;p&gt;When: you are aware of a processing issue and want to understand which process it's coming from, or you want to drill into the process to understand why it's not performing to the expected standard.&lt;/p&gt;

&lt;p&gt;This tool allows users to monitor various performance metrics and events on their system. It can be used to identify performance bottlenecks, track the usage of system resources, and troubleshoot issues on a Linux system. Unlike the other tools in this article &lt;code&gt;perf&lt;/code&gt; allows us to actually peek into the running program to see which parts of it are consuming cpu cycles. This is an incredibly valuable tool to have for those wanting to performance engineer on Linux and I have a practical example of this detailed in &lt;a href="https://www.petermcconnell.com/posts/perf_eng_with_py12/" rel="noopener noreferrer"&gt;https://www.petermcconnell.com/posts/perf_eng_with_py12/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To use &lt;code&gt;perf&lt;/code&gt;, you need to specify the type of performance data you want to collect, as well as the specific events or metrics you want to monitor. To get general cpu profiling you can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perf top &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_top.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fpeter-mcconnell%2Fpetermcconnell.com%2Fmaster%2Fassets%2Fperf_top.jpg" title="perf top example" alt="perf top example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perf also allows you to gather performance statistics. For example, the following command will collect CPU performance data for the cpu-clock event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;perf &lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; cpu-clock
^C
 Performance counter stats &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s1"&gt;'system wide'&lt;/span&gt;:

         11,633.97 msec cpu-clock                 &lt;span class="c"&gt;#    3.998 CPUs utilized&lt;/span&gt;

       2.909972618 seconds &lt;span class="nb"&gt;time &lt;/span&gt;elapsed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scope of what you can do with &lt;code&gt;perf&lt;/code&gt; is huge - too large for this article, but I couldn't leave it off the list. Perhaps a &lt;code&gt;perf&lt;/code&gt; deep dive article will come here soon ...&lt;/p&gt;

&lt;h2&gt;
  
  
  uptime
&lt;/h2&gt;

&lt;p&gt;When: you want to get a quick understanding of system load over the past 1, 5, 15 minutes or want to see how long the machine has been up for.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;uptime
&lt;/span&gt;19:26:35 up  5:12,  1 user,  load average: 0.33, 0.37, 0.37
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  further reading
&lt;/h2&gt;

&lt;p&gt;The Linux Programming Interface book is a fantastic book to learn the Linux fundamentals. In particular it breaks down virtual memory into understandable chunks and also introduces some of the debugging tools mentioned in this article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.com/Systems-Performance-Brendan-Gregg/dp/0136820158?crid=2J7NSUPP1LBQ2&amp;amp;keywords=systems+performance+enterprise+and+the+cloud&amp;amp;qid=1672315747&amp;amp;sprefix=systems+performance%2Caps%2C167&amp;amp;sr=8-1&amp;amp;linkCode=li2&amp;amp;tag=mobile052c67f-20&amp;amp;linkId=042c48313bcd6eae20ae98499600e515&amp;amp;language=en_US&amp;amp;ref_=as_li_ss_il" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3a19fk2r0po296umd8iu.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Systems Performance by Brendan Gregg is full of amazing material that also covers the tools in this article but goes into much greater depth and covers the likes of &lt;code&gt;bpf&lt;/code&gt; which wasn't touched upon in this article:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.amazon.com/Systems-Performance-Brendan-Gregg/dp/0136820158?crid=2J7NSUPP1LBQ2&amp;amp;keywords=systems+performance+enterprise+and+the+cloud&amp;amp;qid=1672315747&amp;amp;sprefix=systems+performance%2Caps%2C167&amp;amp;sr=8-1&amp;amp;linkCode=li2&amp;amp;tag=mobile052c67f-20&amp;amp;linkId=042c48313bcd6eae20ae98499600e515&amp;amp;language=en_US&amp;amp;ref_=as_li_ss_il" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftaclrerxqjum8uuq70jv.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'd also recommend checking out Branden Gregg's website - lots of solid content in there: &lt;a href="https://www.brendangregg.com/" rel="noopener noreferrer"&gt;https://www.brendangregg.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.brendangregg.com%2FPerf%2Flinux_observability_tools.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.brendangregg.com%2FPerf%2Flinux_observability_tools.png" title="linux observability tools" alt="linux observability tools"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>performance</category>
      <category>systems</category>
      <category>operations</category>
    </item>
    <item>
      <title>What is DevOps?</title>
      <dc:creator>Peter McConnell</dc:creator>
      <pubDate>Sat, 31 Dec 2022 09:47:03 +0000</pubDate>
      <link>https://forem.com/pemcconnell/what-is-devops-nkl</link>
      <guid>https://forem.com/pemcconnell/what-is-devops-nkl</guid>
      <description>&lt;p&gt;The term “devops” has been floating around since the late 2000s and frankly has always annoyed me. Not that I think the intent was bad but rather the adoption was so varied and confusing that simply saying the word out loud seemed to make things worse and lead to fear and confusion. I’d like to make “DevOps” the “Voldermort” of tech buzzwords - “He-Who-Must-Not-Be-Named”. This happened with Agile also which is even more confusing given it has a “manifesto” that’s all but a single paragraph, but I’ll leave that for another day.&lt;/p&gt;

&lt;p&gt;The Wikipedia (&lt;a href="https://en.wikipedia.org/wiki/DevOps"&gt;https://en.wikipedia.org/wiki/DevOps&lt;/a&gt;) page for DevOps is fairly hand-wavey which somewhat highlights the issue. Like most documents talking to “organisational change” it’s a word-salad with no real actionable takeaways.&lt;/p&gt;

&lt;p&gt;I’ve had several “DevOps” jobs and each of them were functionally and organisationally different. All projects were delivered but done so more often than not via silos. Wasn’t this the problem “DevOps” was meant to solve?&lt;/p&gt;

&lt;p&gt;Speaking of … What is the problem to solve? Shipping code to production quickly and reliably. In ye olden times there were issues of siloed development teams and operations teams meaning the devs wouldn’t optimise for production workloads and the operators wouldn’t have a clue what they were shipping onto their servers. There’s a lot of domain expertise in both camps - asking people to become experts of both was unreasonable.&lt;/p&gt;

&lt;p&gt;But that was also a different time … we didn’t have the abundance of tooling and services that we do today. Kubernetes didn’t exist. Docker didn’t exist. Cloud offerings were pretty light. With the tools available today, asking SWEs to take on more responsibility to account for their applications in production is a reasonable ask.&lt;/p&gt;

&lt;p&gt;"so what should we do?"&lt;/p&gt;

&lt;p&gt;Frankly I think we’re now in a world that looks more like the pre-devops days. SWE teams are no longer handing over a package of code and saying “hey, take my source code, install these dependencies and run this” but are now in control of their applications deployment manifest and can containerise their applications. The original problem to solve feels like much smaller a problem now.&lt;/p&gt;

&lt;p&gt;Operators can run a platform such as Kubernetes which for the most part the other engineering teams can treat like a PAAS. “How will devs know how to configure their apps to run on the platform” - they’ll have to learn. Somehow. Guardrails should be put in place (e.g. policies) to stop people “doing bad things” but it shouldn’t go as far as “that devops engineer will write your cicd config” or “that devops engineer will write your kubernetes config”. Teams MUST own their config, and to own it they need to understand it.&lt;/p&gt;

&lt;p&gt;So it shouldn’t be a team or a job title. There shouldn’t be “devops tools” or a “devops environment”. A problem can never be “a devops issue”. You might have a kubernetes team, an OPA/IAM/network policy team, a streaming services team etc. Just don’t have a “devops” team.&lt;/p&gt;

&lt;p&gt;We shouldn’t give it a new name either. Stop giving everything a name just so you can sell books and tshirts. This just creates distance from the problem to solve - we all just want to ship code reliably so that our business meets its goals and so that we don’t spend overtime fixing bugs or doing manual work that could have been done by a computer.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>softwareengineering</category>
      <category>organisation</category>
    </item>
  </channel>
</rss>
