From 2dfbc312e6ccb88f838170d8e777d48aacde2ff5 Mon Sep 17 00:00:00 2001 From: johannst Date: Sun, 13 Nov 2022 14:13:35 +0000 Subject: deploy: 026d679006e5d470bacdc74bb3082072edf31e36 --- print.html | 475 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 470 insertions(+), 5 deletions(-) (limited to 'print.html') diff --git a/print.html b/print.html index 5f6b2fa..abeecd8 100644 --- a/print.html +++ b/print.html @@ -76,7 +76,7 @@ @@ -1722,6 +1722,75 @@ define hook-quit break-save quit end +

Watchpoint on struct / class member

+

A symbolic watchpoint defined on a member variable for debugging is only valid +as long as the expression is in scope. Once out of scope the watchpoint gets +deleted.

+

When debugging some memory corruption we want to keep the watchpoint even the +expression goes out of scope to find the location that overrides the variable +and introduces the corruption.

+
(gdb) l
+1  struct S { int v; };
+2
+3  void set(struct S* s, int v) {
+4      s->v = v;
+5  }
+6
+7  int main() {
+8      struct S s;
+9      set(&s, 1);
+10     set(&s, 2);
+11     set(&s, 3);
+...
+
+(gdb) s
+set (s=0x7fffffffe594, v=1) at test.c:4
+4    s->v = v;
+
+# Define a new watchpoint on the member of the struct. The expression however
+# is only valid in the current functions scope.
+
+(gdb) watch s->v
+Hardware watchpoint 2: s->v
+
+(gdb) c
+Hardware watchpoint 2: s->v
+Old value = 0
+New value = 1
+set (s=0x7fffffffe594, v=1) at test.c:5
+5   }
+
+# The watchpoint gets deleted as soon as we leave the function scope.
+
+(gdb) c
+Watchpoint 2 deleted because the program has left the block in
+which its expression is valid.
+main () at test.c:10
+10      set(&s, 2);
+
+(gdb) p &s->v
+$1 = (int *) 0x7fffffffe594
+
+# Define a watchpoint o the address of the member variable of the s instance.
+# This of course only makes sense as long as the s instance is not moved in memory.
+
+(gdb) watch *0x7fffffffe594
+Hardware watchpoint 3: *0x7fffffffe594
+
+(gdb) c
+Hardware watchpoint 3: *0x7fffffffe594
+Old value = 1
+New value = 2
+set (s=0x7fffffffe594, v=2) at test.c:5
+5   }
+
+(gdb) c
+Hardware watchpoint 3: *0x7fffffffe594
+Old value = 2
+New value = 3
+set (s=0x7fffffffe594, v=3) at test.c:5
+5   }
+

Know Bugs

Workaround command + finish bug

When using finish inside a command block, commands after finish are not @@ -2626,6 +2695,112 @@ int main() { return 0; } +

Example: Minimal templatized test registry

+

A small test function registry bringing together a few different template +features.

+
#include <cstdio>
+#include <functional>
+#include <map>
+#include <string>
+#include <type_traits>
+
+template<typename R, typename... P>
+struct registry {
+    using FUNC = R (*)(P...);
+    using SELF = registry<R, P...>;
+    using RET = R;
+
+    static SELF& get() {
+        static SELF r;
+        return r;
+    }
+
+    bool add(std::string nm, FUNC fn) {
+        const auto r = m_fns.insert({std::move(nm), std::move(fn)});
+        return r.second;
+    }
+
+    R invoke(const std::string& nm, P... p) const { return invoke_impl<R>(nm, p...); }
+
+    void dump() const {
+        for (const auto& it : m_fns) {
+            std::puts(it.first.c_str());
+        }
+    }
+
+  private:
+    std::map<std::string, FUNC> m_fns;
+
+    template<typename RET>
+    std::enable_if_t<std::is_same_v<RET, void>> invoke_impl(const std::string& nm, P... p) const {
+        const auto it = m_fns.find(nm);
+        if (it == m_fns.end()) {
+            return;
+        }
+        std::invoke(it->second, p...);
+    }
+
+    template<typename RET>
+    std::enable_if_t<!std::is_same_v<RET, void>, RET> invoke_impl(const std::string& nm,
+                                                                  P... p) const {
+        const auto it = m_fns.find(nm);
+        if (it == m_fns.end()) {
+            static_assert(std::is_default_constructible_v<RET>,
+                          "RET must be default constructible");
+            return {};
+        }
+        return std::invoke(it->second, p...);
+    }
+};
+
+#define TEST_REGISTER(REGISTRY, NAME)                                                      \
+    static bool regfn_##REGISTRY##NAME() {                                                 \
+        const bool r = REGISTRY::get().add(#NAME, NAME);                                   \
+        if (!r) {                                                                          \
+            std::puts("Failed to register test " #NAME ", same name already registered!"); \
+            std::abort();                                                                  \
+        }                                                                                  \
+        return r;                                                                          \
+    }                                                                                      \
+    static const bool reg_##REGISTRY##NAME = regfn_##REGISTRY##NAME();
+
+#define TEST(REGISTRY, NAME, ...)    \
+    REGISTRY::RET NAME(__VA_ARGS__); \
+    TEST_REGISTER(REGISTRY, NAME);   \
+    REGISTRY::RET NAME(__VA_ARGS__)
+
+// -- Usage 1 simple usage.
+
+using REG1 = registry<void>;
+TEST(REG1, test1) {
+    std::puts("REG1::test1");
+}
+TEST(REG1, test2) {
+    std::puts("REG1::test2");
+}
+
+// -- Usage 2 with convenience macro wrapper.
+
+using REG2 = registry<void, bool>;
+#define TEST2(NAME, ...) TEST(REG2, NAME, ##__VA_ARGS__)
+
+TEST2(test1, bool val) {
+    printf("REG2::test1 val %d\n", val);
+}
+
+int main() {
+    const auto& R1 = REG1::get();
+    R1.dump();
+    R1.invoke("test1");
+    R1.invoke("test2");
+
+    const auto& R2 = REG2::get();
+    R2.dump();
+    R2.invoke("test1", true);
+
+    return 0;
+}
+

Example: Concepts pre c++20

Prior to c++20's concepts, SFINAE and std::void_t can be leveraged to build something similar allowing to define an interface (aka trait) for a template @@ -3304,6 +3479,7 @@ def sum(a: int, b: int) -> int:

  • ptrace_scope
  • cryptsetup
  • swap
  • +
  • input
  • systemd

    systemctl

    @@ -3566,9 +3742,129 @@ For example as systemd service:

    systemctl status dphys-swapfile
     
    +

    Linux input

    +

    Some notes on using /dev/input/* device driver files.

    +

    mouseX / mice

    +

    These device files are created by the [mousedev] driver.

    + +

    The data stream consists of 3 bytes per event. An event is encoded as (BTN, X, Y).

    + +

    The raw data stream can be inspected as follows.

    +
    sudo cat /dev/input/mice | od -tx1 -w3 -v
    +
    +

    eventX

    +

    These device files are created by the [evdev] driver.

    + +

    Input events are encoded as given by the input_event struct below. Reading +from the eventX device file will always yield whole number of input events.

    +
    struct input_event {
    +    struct timeval time;
    +    unsigned short type;
    +    unsigned short code;
    +    unsigned int value;
    +};
    +
    +

    On most 64bit machines the raw data stream can be inspected as follows.

    +
    sudo cat /dev/input/event4 | od -tx1 -w24 -v
    +
    +

    Identifyin device files.

    +

    To find out which device file is assigned to which input device the following +file /proc/bus/input/devices in the proc filesystem can be consulted.

    +

    This yields entries as follows and shows which Handlers are assigned to which +Name.

    +
    I: Bus=0018 Vendor=04f3 Product=0033 Version=0000
    +N: Name="Elan Touchpad"
    +...
    +H: Handlers=event15 mouse0
    +...
    +
    +

    Example: Toying with /dev/input/eventX

    +

    Once compiled, the example should be run as sudo ./event /dev/input/eventX.

    +
    #include <stdio.h>
    +#include <fcntl.h>
    +#include <assert.h>
    +#include <unistd.h>
    +#include <time.h>
    +
    +#include <sys/time.h>
    +#include <linux/input-event-codes.h>
    +
    +struct input_event {
    +    struct timeval time;
    +    unsigned short type;
    +    unsigned short code;
    +    unsigned int value;
    +};
    +
    +const char* type(unsigned short t) {
    +    static char buf[32];
    +    const char* fmt = "0x%x";
    +    switch (t) {
    +#define FMT(TYPE) case TYPE: fmt = #TYPE"(0x%x)"; break
    +        FMT(EV_SYN);
    +        FMT(EV_KEY);
    +        FMT(EV_REL);
    +        FMT(EV_ABS);
    +#undef FMT
    +    }
    +    snprintf(buf, sizeof(buf), fmt, t);
    +    return buf;
    +}
    +
    +const char* code(unsigned short c) {
    +    static char buf[32];
    +    const char* fmt = "0x%x";
    +    switch (c) {
    +#define FMT(CODE) case CODE: fmt = #CODE"(0x%x)"; break
    +        FMT(BTN_LEFT);
    +        FMT(BTN_RIGHT);
    +        FMT(BTN_MIDDLE);
    +        FMT(REL_X);
    +        FMT(REL_Y);
    +#undef FMT
    +    }
    +    snprintf(buf, sizeof(buf), fmt, c);
    +    return buf;
    +}
    +
    +const char* timefmt(const struct timeval* t) {
    +    assert(t);
    +    struct tm* lt = localtime(&t->tv_sec); // Returns pointer to static tm object.
    +    static char buf[64];
    +    strftime(buf, sizeof(buf), "%H:%M:%S", lt);
    +    return buf;
    +}
    +
    +int main(int argc, char* argv[]) {
    +    assert(argc == 2);
    +
    +    int fd = open(argv[1], O_RDONLY);
    +    assert(fd != -1);
    +
    +    struct input_event inp;
    +    while (1) {
    +        int ret = read(fd, &inp, sizeof(inp));
    +        assert(ret == sizeof(inp));
    +        printf("time: %s type: %s code: %s value: 0x%x\n",
    +               timefmt(&inp.time), type(inp.type), code(inp.code), inp.value);
    +    }
    +}
    +
    +

    [mousedev]: TODO /home/johannst/dev/linux/drivers/input/mousedev.c +[evdev]: TODO /home/johannst/dev/linux/drivers/input/evdev.c

    Network

    tcpdump(1)

    CLI

    @@ -3593,9 +3889,42 @@ tcp/udp/icmp Filter for protocol.
    # -k: Start capturing immediately.
     ssh <host> tcpdump -i <IF> -w - | sudo wireshark -k -i -
     
    +

    firewall-cmd(1)

    +

    Command line interface to the firewalld(1) daemon.

    +

    List current status of the firewall

    +
    # List all services and ports for all zones.
    +firewall-cmd --list-all
    +# List all services.
    +firewall-cmd --list-services
    +# List all ports.
    +firewall-cmd --list-ports
    +
    +
    +

    Add --zone <ZONE> to limit output to a given ZONE. Use --get-zones to +see all available zones.

    +
    +

    Add entries

    +
    # Add a service to the firewall, use `--get-services` to list all available
    +# service names.
    +firewall-cmd --add-service <SERVICE>
    +# Add a specific port.
    +firewall-cmd --add-port 8000/tcp
    +
    +

    Remove entries

    +
    # Remove service.
    +firewall-cmd --remove-service <SERVICE>
    +# Remove port.
    +firewall-cmd --remove-port 8000/tcp
    +
    +

    References

    +

    Web

    html

    Collapsible element

    @@ -3693,6 +4022,142 @@ p { </div> </div> + +

    Minimal tabs

    +

    Rendered html

    +
    <script>
    +const showTab = (E, T) => {
    +    const TABS = Array.from(document.getElementsByClassName("content"));
    +    TABS.forEach(T => {
    +        T.style.display = "none";
    +    });
    +
    +    document.getElementById(T).style.display = "block";
    +};
    +
    +window.onload = () => {
    +    document.getElementById("bTab1").onclick = (E) => {
    +        showTab(E, "tTab1");
    +    };
    +    document.getElementById("bTab2").onclick = (E) => {
    +        showTab(E, "tTab2");
    +    };
    +}
    +</script>
    +
    +<button type="button" id="bTab1">Tab1</button>
    +<button type="button" id="bTab2">Tab2</button>
    +
    +<div id="tTab1" class="content" style="display: block;">
    +    <p>Some content goes here ...</p>
    +</div>
    +
    +<div id="tTab2" class="content" style="display: none;">
    +    <p>... and there.</p>
    +</div>
    +
    +

    Chart.js

    +

    Minimal example with external tooltips

    +

    Rendered html

    +
    <canvas id="myChart" style="margin:5em;"></canvas>
    +
    +<script>
    +const get_or_create_tooltip = (id) => {
    +  if (tooltip = document.getElementById(id)) {
    +    return tooltip;
    +  } else {
    +    // -- Create a new Tooltip element.
    +    const tooltip = document.createElement('div');
    +    tooltip.id = id;
    +    document.body.appendChild(tooltip);
    +
    +    // -- Some minimal styling.
    +    tooltip.style.background = 'rgba(0, 0, 0, 0.1)';
    +    tooltip.style.position   = 'absolute';
    +    tooltip.style.transition = 'all .2s ease';
    +
    +    // -- Add a table element for the tooltip content.
    +    const table = document.createElement('table');
    +    tooltip.appendChild(table);
    +
    +    return tooltip
    +  }
    +}
    +
    +const render_tooltip = (context) => {
    +  const {chart, tooltip} = context;
    +
    +  // -- Get Tooltip element.
    +  const tooltip_elem = get_or_create_tooltip('myTooltip');
    +
    +  // -- Get data point values (only one data point).
    +  const {label: x, formattedValue: y} = tooltip.dataPoints[0];
    +
    +  // -- Format new tooltip.
    +  const link = document.createElement('a');
    +  link.href =  "https://github.com/johannst";
    +  link.innerHTML = "X:" + x + " Y:" + y;
    +
    +  // -- Remove previous child element and add new one.
    +  const table = tooltip_elem.querySelector('table');
    +  table.innerHTML = ""; 
    +  table.appendChild(link);
    +
    +  // -- Get absolute X/Y position of the top left corner of the canvas.
    +  const {offsetLeft: canvas_x, offsetTop: canvas_y} = chart.canvas;
    +
    +  // -- Set position and minimal style for the tooltip.
    +  tooltip_elem.style.left = canvas_x + tooltip.caretX + 'px';
    +  tooltip_elem.style.top  = canvas_y + tooltip.caretY + 'px';
    +  tooltip_elem.style.font = tooltip.options.bodyFont.string;
    +
    +  // -- Place the tooltip (I) left or (II) right of the data point.
    +  if (tooltip.xAlign === "right") {
    +    tooltip_elem.style.transform = 'translate(-100%, 0)'; // (I)
    +  } else if (tooltip.xAlign === "left") {
    +    tooltip_elem.style.transform = 'translate(0%, 0)';    // (II)
    +  }
    +}
    +
    +// -- Render a chart with some dummy data on the canvas.
    +const chart = new Chart(
    +  document.getElementById('myChart'),
    +  {
    +    data: {
    +      datasets: [{
    +        // -- A single dataset.
    +        label: 'Just some values',
    +        type: 'scatter',
    +        data: [
    +          {x: 4, y: 4},
    +          {x: 5, y: 1},
    +          {x: 7, y: 6},
    +          {x: 10, y: 8},
    +          {x: 10, y: 7},
    +          {x: 10, y: 3},
    +        ],
    +        backgroundColor: 'rgba(255, 99, 132, 0.5)',
    +        borderColor: 'rgb(255, 99, 132)',
    +      }],
    +    },
    +    options: {
    +      scales: {
    +        y: {
    +          beginAtZero: true, // -- Start the Y-Axis at zero instead min(y) of dataset.
    +        }
    +      },
    +      plugins: {
    +        tooltip: {
    +          enabled: false, // -- Disable builtin tooltips.
    +          mode: 'nearest', // -- Get the item that is nearest to the mouse.
    +          intersect: false, // -- 'mode' is active also when the mouse doesnt intersect with an item on the chart.
    +          external: render_tooltip, // -- External tooltip handler, allows to create own HTML.
    +        }
    +      }
    +    }
    +  }
    +);
    +</script>
     

    Arch