<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Debugging on Roberto Selbach</title><link>https://rselbach.com/tags/debugging/</link><description>Recent content in Debugging on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Fri, 02 May 2008 22:40:00 +0000</lastBuildDate><atom:link href="https://rselbach.com/tags/debugging/index.xml" rel="self" type="application/rss+xml"/><item><title>When Rendering Bugs Move</title><link>https://rselbach.com/when-rendering-bugs-move/</link><pubDate>Fri, 02 May 2008 22:40:00 +0000</pubDate><guid>https://rselbach.com/when-rendering-bugs-move/</guid><description>&lt;p&gt;A Qt application of mine began drawing stale text after I scrolled a custom widget. The bug appeared only with desktop compositing enabled, so I immediately blamed the driver. Then I ran it on a different card and saw the same stale text in a slightly different place.&lt;/p&gt;
&lt;p&gt;My naive fix was to call &lt;code&gt;update()&lt;/code&gt; repeatedly after scrolling. That reduced the frequency without curing it. Repainting more often can hide an invalid region briefly, but it cannot make incorrect geometry correct.&lt;/p&gt;
&lt;p&gt;I reduced the widget until it painted only two rectangles and a label. The problem remained when one rectangle moved outside the area returned by the widget&amp;rsquo;s internal item bounds. My clipping assumptions were wrong: I invalidated the old logical rectangle but painted a pen and text extending beyond it. Compositing changed timing and exposure enough to reveal the mistake more reliably.&lt;/p&gt;
&lt;p&gt;After expanding the dirty region to include every painted pixel, both drivers behaved. I then tested without compositing, with XRender compositing and with OpenGL compositing. That matrix is useful because a defect that follows one backend suggests a different layer from a defect that follows the application everywhere.&lt;/p&gt;
&lt;p&gt;Adding a plain background behind the moving item made stale areas obvious and gave the reduced test a result anyone could recognize immediately.&lt;/p&gt;
&lt;p&gt;Driver debugging still requires exact information. Card family, kernel module, X driver, Mesa version, colour depth and compositor backend can all matter. &amp;ldquo;Latest driver&amp;rdquo; is not a version, especially a week later. A minimal program is worth far more than a description of a large application that sometimes looks odd.&lt;/p&gt;
&lt;p&gt;The most useful distinction is between wrong commands and wrong execution. If the application issues the wrong clipping, geometry or lifetime operations, changing drivers may only move the symptom. If a tiny correct sequence fails on one driver and succeeds elsewhere, the driver report becomes persuasive.&lt;/p&gt;
&lt;p&gt;My practical conclusion is to simplify before assigning blame. Disable layers one at a time, record the exact matrix, and inspect the application&amp;rsquo;s painted bounds even when hardware acceleration appears implicated. Graphics stacks are complicated enough without accusing the innocent portion first. Unfortunately, every portion has previous convictions.&lt;/p&gt;</description></item><item><title>Chasing Black Windows</title><link>https://rselbach.com/chasing-black-windows/</link><pubDate>Thu, 20 Dec 2007 22:05:00 +0000</pubDate><guid>https://rselbach.com/chasing-black-windows/</guid><description>&lt;p&gt;After a driver update, newly opened windows sometimes became black under KWin&amp;rsquo;s OpenGL compositor. The same windows were fine with XRender. Turning off every effect changed nothing, which at least got the animations out of the suspect list.&lt;/p&gt;
&lt;p&gt;The useful split was the texture-from-pixmap path. Under X11 compositing, the server redirects a window into a pixmap. KWin&amp;rsquo;s OpenGL path then uses the driver&amp;rsquo;s texture-from-pixmap support to bind that pixmap as a texture for the scene. XRender composites the pixmap through a different route. Applications had painted the right pixels; the OpenGL path was failing to present them.&lt;/p&gt;
&lt;p&gt;I could make it happen by opening two terminals, covering one, and uncovering it. Damage arrived, but the exposed window stayed black or returned in strips. Switching to the previous driver fixed that exact sequence. The newer driver also behaved if I disabled its acceleration option, slowly.&lt;/p&gt;
&lt;p&gt;That made a much better report than &amp;ldquo;KWin draws black windows.&amp;rdquo; I included the X server, Mesa, kernel and driver versions, the OpenGL backend, hardware identifiers, and the short cover/uncover sequence. I also noted that XRender worked. That last fact tells a driver developer far more than a screenshot of a black rectangle.&lt;/p&gt;
&lt;p&gt;Screenshots were awkward: some captured the correct window even while the display showed black. The redirected pixmap could be fine while the texture or final scanout was stale. A camera photo was ugly but honest.&lt;/p&gt;
&lt;p&gt;I am using XRender for normal work and keeping the failing setup available for testing. Reinstalling KWin would only replace the code that asks the driver to bind the same pixmap. The break is farther down the path, and now there is a reproducible way to reach it.&lt;/p&gt;</description></item><item><title>Debugging the Desktop From evdev to X.Org</title><link>https://rselbach.com/debugging-the-desktop-from-evdev-to-xorg/</link><pubDate>Fri, 07 Oct 2005 21:30:00 +0000</pubDate><guid>https://rselbach.com/debugging-the-desktop-from-evdev-to-xorg/</guid><description>&lt;p&gt;An extra mouse button worked in one application but not another, which sent me directly to the KDE control center. I changed mappings until ordinary buttons became surprising too. This was useful only as a demonstration that random adjustments scale badly.&lt;/p&gt;
&lt;p&gt;I started over at the bottom of the stack. The kernel input layer exposes event devices and reports key, button, and relative motion events. X.Org opens an input device through its configured driver, translates those events into the X input model, and sends them to clients. KDE and its applications then apply shortcuts or application behavior.&lt;/p&gt;
&lt;p&gt;That sequence gives each test a specific question. Kernel event diagnostics answer whether hardware input reaches the kernel. The X.Org log answers which device and driver the server opened. &lt;code&gt;xev&lt;/code&gt; answers which X events a client receives. An application test answers what that client does with them.&lt;/p&gt;
&lt;p&gt;The button appeared at the kernel layer but was mapped unexpectedly by X.Org. I had copied a configuration stanza from a different mouse, including a button mapping whose numbers reflected that device. Removing the borrowed assumption and mapping the observed buttons deliberately fixed the problem.&lt;/p&gt;
&lt;p&gt;The exercise also exposed a trap in logs. A warning near the end of &lt;code&gt;Xorg.0.log&lt;/code&gt; looked relevant because it mentioned input, but the earlier device initialization lines showed that it belonged to another configured device. Timestamps, identifiers, and full initialization sections matter more than a conveniently alarming word.&lt;/p&gt;
&lt;p&gt;I saved a small checklist with the configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;1. Does the kernel report the event?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2. Which device does X.Org open?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;3. What does xev receive?
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4. How does the application interpret it?
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is not sophisticated debugging, and that is why it works. Each layer has a narrow contract. The first failed contract determines where to investigate; layers above it cannot repair missing information.&lt;/p&gt;
&lt;p&gt;Desktop Linux can feel complicated because these boundaries are visible. I prefer visible complexity to one giant mystery, but only when I respect the boundaries. Starting at the application and changing every layer on the way down is not diagnosis. It is redecorating the staircase while looking for a leak.&lt;/p&gt;</description></item><item><title>Making a Use-After-Free Leave Tracks</title><link>https://rselbach.com/making-a-use-after-free-leave-tracks/</link><pubDate>Fri, 12 Aug 2005 17:50:00 +0000</pubDate><guid>https://rselbach.com/making-a-use-after-free-leave-tracks/</guid><description>&lt;p&gt;A driver crashed several calls after an object had been freed. The final bad pointer looked plausible, and ordinary logs only showed that teardown had happened sometime earlier. I needed the stale access to leave a clearer fingerprint.&lt;/p&gt;
&lt;p&gt;I rebuilt the test kernel with &lt;code&gt;CONFIG_DEBUG_SLAB&lt;/code&gt;. The slab allocator&amp;rsquo;s debugging checks and poisoning replace freed storage with a recognizable pattern instead of leaving yesterday&amp;rsquo;s fields looking valid. The next failure stopped looking like random corruption: the pointer led into an object full of poison bytes.&lt;/p&gt;
&lt;p&gt;That didn&amp;rsquo;t identify the owner by itself. I decoded the complete oops against the matching unstripped &lt;code&gt;vmlinux&lt;/code&gt;, then followed the call chain back to a timer callback. The timer retained the private pointer after the remove path had called &lt;code&gt;kfree()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The repair was pleasantly unoriginal: prevent rearming, delete the timer with the synchronizing form, and only then free the object. I ran repeated load, activity, and unload cycles with slab debugging still enabled. I also forced probe failures after each acquisition to make sure partially built objects followed the same lifetime rules.&lt;/p&gt;
&lt;p&gt;Poisoning is a diagnostic, not memory safety. It works when the freed bytes haven&amp;rsquo;t already been reused, and it makes a debug kernel slower. Here it turned a believable stale structure into an unmistakably dead one, which was exactly the clue I needed.&lt;/p&gt;</description></item><item><title>A 2.6 Driver and the Trail of Evidence</title><link>https://rselbach.com/a-2-6-driver-and-the-trail-of-evidence/</link><pubDate>Fri, 11 Feb 2005 20:45:00 +0000</pubDate><guid>https://rselbach.com/a-2-6-driver-and-the-trail-of-evidence/</guid><description>&lt;p&gt;I ported a small driver experiment to a newer 2.6 kernel this week. The old code compiled after a few mechanical edits, so I congratulated myself much too early. Loading the module produced an oops during device removal, which is the kernel&amp;rsquo;s way of reviewing code without softening the language.&lt;/p&gt;
&lt;p&gt;My first attempt was to stare at the final faulting line and add a null check. That stopped one crash but left the design wrong. The pointer was not unexpectedly null; it referred to an object whose lifetime had ended. A defensive condition would merely make the race less visible.&lt;/p&gt;
&lt;p&gt;The 2.6 device model asks drivers to express relationships among buses, devices, and drivers. Registration is not just bookkeeping. It establishes ownership and determines which callbacks can run as devices appear and disappear. I had treated &lt;code&gt;remove&lt;/code&gt; as the reverse spelling of &lt;code&gt;probe&lt;/code&gt;, but not as the reverse sequence of acquired resources.&lt;/p&gt;
&lt;p&gt;I wrote down each successful step in &lt;code&gt;probe&lt;/code&gt;: enable the device, reserve its region, allocate private state, initialize synchronization, and expose the interface. Then I made every failure path unwind only the steps already completed. &lt;code&gt;remove&lt;/code&gt; performs the same cleanup in reverse order after preventing new work from entering.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sample_probe&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; device &lt;span style="color:#f92672"&gt;*&lt;/span&gt;device)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; sample &lt;span style="color:#f92672"&gt;*&lt;/span&gt;sample;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; error;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sample &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;kmalloc&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;sample), GFP_KERNEL);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;sample)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;ENOMEM;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;memset&lt;/span&gt;(sample, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;sizeof&lt;/span&gt;(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;sample));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;reserve_hardware&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;goto&lt;/span&gt; free_sample;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* Published callbacks must be able to find fully initialized state. */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dev_set_drvdata&lt;/span&gt;(device, sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; error &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;register_interface&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (error)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;goto&lt;/span&gt; clear_drvdata;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clear_drvdata:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dev_set_drvdata&lt;/span&gt;(device, NULL);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;release_hardware:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;release_hardware&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;free_sample:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;kfree&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; error;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is shortened code, but the order matters: private data is attached before &lt;code&gt;register_interface()&lt;/code&gt; makes callbacks possible. If publication fails, the failure path clears that pointer before releasing and freeing the state. In C, a linear chain of labels is often clearer than nested cleanup conditionals.&lt;/p&gt;
&lt;p&gt;Removal uses the other half of the same rule:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sample_remove&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; device &lt;span style="color:#f92672"&gt;*&lt;/span&gt;device)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; sample &lt;span style="color:#f92672"&gt;*&lt;/span&gt;sample &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dev_get_drvdata&lt;/span&gt;(device);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;unregister_interface&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;stop_hardware&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cancel_deferred_work&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;dev_set_drvdata&lt;/span&gt;(device, NULL);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;release_hardware&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;kfree&lt;/span&gt;(sample);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;unregister_interface()&lt;/code&gt; first prevents new user callbacks. The hardware is then stopped and outstanding work is drained while &lt;code&gt;drvdata&lt;/code&gt; still points to live memory. Only after no callback can use it do I detach and free it.&lt;/p&gt;
&lt;p&gt;For evidence, I enabled the kernel debugging options relevant to memory and locking and built a kernel specifically for this work. They cost performance, but a development kernel is a measuring instrument, not a racing bicycle. I also kept the unstripped &lt;code&gt;vmlinux&lt;/code&gt; and the exact &lt;code&gt;.config&lt;/code&gt; beside the test build.&lt;/p&gt;
&lt;p&gt;When the next oops appeared, the symbolic trace identified both the callback and its caller. &lt;code&gt;printk()&lt;/code&gt; markers around acquisition and release confirmed that queued work could still run after teardown began. The repair was to stop new requests, cancel or drain deferred activity, and only then free the state it could reference.&lt;/p&gt;
&lt;p&gt;The order now looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;block new work
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stop device activity
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;wait for deferred work
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;remove user-visible interface
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;release hardware resources
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;free private state
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I tested failure paths too. Returning an artificial error after each acquisition showed whether cleanup restored the previous state. Module load and unload in a loop then exercised the unremarkable path repeatedly. Repetition is valuable because races dislike appointments.&lt;/p&gt;
&lt;p&gt;I included reference counts in the review even though the first crash did not mention them. A reference is a claim that an object must remain alive, not merely an integer to increment near a pointer. Every path acquiring one needs a visible release, and teardown must prevent new claims before waiting for old ones. Counting without a shutdown rule only measures how confused the lifetime has become.&lt;/p&gt;
&lt;p&gt;Lock choice followed the same reasoning. I did not replace every lock with a larger one to make the warning disappear. I listed which fields formed one invariant and which contexts accessed them. A spinlock suited the short state transition shared with interrupt context; work that could sleep stayed outside it. Narrow critical sections made both the rules and the traces easier to read.&lt;/p&gt;
&lt;p&gt;One subtle lesson was to distrust logs as timing-neutral. A &lt;code&gt;printk()&lt;/code&gt; can alter scheduling enough to hide a race. I used it to establish broad ordering, then relied on assertions, debug checks, and repeated tests rather than declaring victory when extra logging made the crash vanish.&lt;/p&gt;
&lt;p&gt;An oops after testing still required a disciplined capture. I saved the complete report, not only the final address, and matched it to the exact kernel image. A decoded call chain is context: it shows how execution arrived, which often matters more than the instruction that finally touched invalid memory.&lt;/p&gt;
&lt;p&gt;The kernel APIs will continue to evolve, and copying a driver from an older tree will continue to be tempting. The durable knowledge is not a particular field name. It is the discipline of checking the current in-tree callers, understanding callback context, defining object lifetime, and making cleanup the exact inverse of setup.&lt;/p&gt;
&lt;p&gt;I still prefer user-space debugging, where a bad pointer usually ruins one process rather than the afternoon. The kernel does leave a trail if the build preserves symbols and the code preserves invariants. That&amp;rsquo;s better than adding null checks until the machine goes quiet.&lt;/p&gt;</description></item><item><title>X.Org Configuration With Less Guessing</title><link>https://rselbach.com/xorg-configuration-with-less-guessing/</link><pubDate>Fri, 17 Dec 2004 20:15:00 +0000</pubDate><guid>https://rselbach.com/xorg-configuration-with-less-guessing/</guid><description>&lt;p&gt;I switched one machine to X.Org and immediately lost the mouse wheel. Naturally, I responded by changing three unrelated options in &lt;code&gt;xorg.conf&lt;/code&gt; at once. The wheel remained decorative, and I no longer knew which edit had caused the pointer acceleration to feel strange.&lt;/p&gt;
&lt;p&gt;Starting over one change at a time worked better. X.Org&amp;rsquo;s log records each configuration source, loaded module, detected device, warning, and error. The prefixes are particularly useful: &lt;code&gt;(EE)&lt;/code&gt; deserves immediate attention, while &lt;code&gt;(WW)&lt;/code&gt; is a clue rather than a conviction.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ less /var/log/Xorg.0.log
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The mouse was using the wrong protocol. Once the &lt;code&gt;InputDevice&lt;/code&gt; section selected the correct protocol and enabled &lt;code&gt;ZAxisMapping&lt;/code&gt;, wheel events arrived as expected. I confirmed that with &lt;code&gt;xev&lt;/code&gt;, which is not beautiful but is wonderfully literal: move, click, or type, and it shows what the X server reports.&lt;/p&gt;
&lt;p&gt;Display problems benefit from the same restraint. Modelines are timings, not decorative strings, and monitor ranges are safety limits rather than values to invent competitively. I let detection provide what it could, compared the resulting mode against the log, and added explicit values only when I had the monitor&amp;rsquo;s specifications in front of me.&lt;/p&gt;
&lt;p&gt;This also clarified the layers for me. The kernel exposes the input device, the X server translates input and drives the display, and KDE consumes the resulting events. Poking a KDE setting cannot repair an X protocol mismatch. Likewise, changing an X option will not help if the kernel never reports the device.&lt;/p&gt;
&lt;p&gt;I kept a minimal known-good configuration and added only the monitor ranges and device sections I actually needed. Generated configurations tend to contain enough commented examples to make every problem look plausible. More text is not more diagnosis.&lt;/p&gt;
&lt;p&gt;Keeping the known-good file also made testing reversible without making every edit timid. I could change one section, restart the server, inspect the new log, and return to a working baseline if the result was worse. That is a much tighter loop than reconstructing yesterday&amp;rsquo;s guesses from memory.&lt;/p&gt;
&lt;p&gt;The move to X.Org has been less dramatic than I expected, which is favorable. It feels familiar, the source is moving in an open direction, and the logging is adequate when I resist random editing. My new rule is log first, &lt;code&gt;xev&lt;/code&gt; second, configuration change third. Rebooting repeatedly and hoping the mouse develops self-awareness is no longer on the list.&lt;/p&gt;</description></item><item><title>Let DCOP Tell You the Signature</title><link>https://rselbach.com/let-dcop-tell-you-the-signature/</link><pubDate>Sun, 02 Mar 2003 11:44:53 +0000</pubDate><guid>https://rselbach.com/let-dcop-tell-you-the-signature/</guid><description>&lt;p&gt;A DCOP call failed today even though the method name was correct. The method name, unfortunately, was only part of the name that mattered. I had sent an &lt;code&gt;int&lt;/code&gt;; the exported function expected an unsigned value.&lt;/p&gt;
&lt;p&gt;Before changing code, I ask the running object what it exposes:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dcop myapp MainIface
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The output includes complete function signatures. In this case it shows &lt;code&gt;void setPage(unsigned int)&lt;/code&gt;. I then call it from the command line with a small test argument:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dcop myapp MainIface setPage &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;DCOP identifies methods by their signatures and marshals known Qt types through &lt;code&gt;QDataStream&lt;/code&gt;. The sending and receiving sides must therefore agree on argument types, order, and return type. A visually similar C++ declaration is not necessarily the same wire contract.&lt;/p&gt;
&lt;p&gt;For custom values, both programs need compatible streaming operators and type registration. I avoid custom structures in small public interfaces when a &lt;code&gt;QString&lt;/code&gt;, &lt;code&gt;QCString&lt;/code&gt;, or simple list expresses the operation clearly. Fewer private types also make the command-line client useful.&lt;/p&gt;
&lt;p&gt;When writing the C++ caller, I keep the request and reply types visible next to each other:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QByteArray data;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QDataStream &lt;span style="color:#a6e22e"&gt;args&lt;/span&gt;(data, IO_WriteOnly);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;args &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; page;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCString replyType;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QByteArray replyData;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;client&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;call(app, &lt;span style="color:#e6db74"&gt;&amp;#34;MainIface&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;setPage(unsigned int)&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data, replyType, replyData)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kdWarning() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;DCOP setPage call failed&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; endl;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For a function with a return value, I verify &lt;code&gt;replyType&lt;/code&gt; before extracting from &lt;code&gt;replyData&lt;/code&gt;. Deserializing a reply under the wrong assumption merely converts a useful interface error into strange application state.&lt;/p&gt;
&lt;p&gt;The application and object names should not be copied from a single run if multiple instances are possible. I list registered clients and decide whether the program is meant to address one instance, every instance, or the instance owning a particular document. A suffix added to keep names unique is normal, not a DCOP server losing its arithmetic.&lt;/p&gt;
&lt;p&gt;I also preserve failures in scripts. The command-line client returns useful status, and a shell script should check it rather than carrying on after the target application has exited. Interprocess calls cross a lifetime boundary; yesterday&amp;rsquo;s object name is not a reservation for today.&lt;/p&gt;
&lt;p&gt;I keep the exported interface narrow and test it independently of the window. If the command works but a toolbar action does not, DCOP has been acquitted and I can inspect the connection instead.&lt;/p&gt;
&lt;p&gt;I let &lt;code&gt;dcop&lt;/code&gt; provide the authoritative spelling instead of copying it from memory. Memory is fast, local, and apparently not type-safe.&lt;/p&gt;</description></item><item><title>A Small DCOP Command Is a Good Test</title><link>https://rselbach.com/a-small-dcop-command-is-a-good-test/</link><pubDate>Tue, 14 May 2002 18:36:52 +0000</pubDate><guid>https://rselbach.com/a-small-dcop-command-is-a-good-test/</guid><description>&lt;p&gt;I needed to tell a running application to refresh a document, and my first instinct was to add a socket and invent a tiny protocol. Then I remembered that a KDE session already has DCOP, including discovery, calls, and argument marshalling. It seemed wasteful to build a worse version before lunch.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;dcop&lt;/code&gt; command is the quickest way to see what is available:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dcop
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dcop konqueror
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dcop konqueror KonquerorIface
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The first command lists registered applications. Supplying an application lists its objects and interfaces; adding an object shows callable functions. This is useful beyond scripting. It tells me whether the object registered under the name I expected and whether the signature exported by the program matches my mental version of it.&lt;/p&gt;
&lt;p&gt;For a simple application object, I derive from &lt;code&gt;DCOPObject&lt;/code&gt; and name the interface:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;RefreshIface&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;virtual&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; DCOPObject
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; K_DCOP
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; k_dcop:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;virtual&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; refresh(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QString &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;path) &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The implementation registers through the application&amp;rsquo;s &lt;code&gt;DCOPClient&lt;/code&gt;. Calls are sent through the session&amp;rsquo;s DCOP server, which locates the target client and carries the serialized arguments. The caller does not need the target&amp;rsquo;s process identifier, and the receiver can expose a small interface rather than its internal objects.&lt;/p&gt;
&lt;p&gt;I keep DCOP methods coarse. Sending &lt;code&gt;refresh(QString)&lt;/code&gt; is reasonable; reproducing every setter on a widget is not. Once the interface mirrors the user interface, outside programs depend on details I will want to change.&lt;/p&gt;
&lt;p&gt;There is also a choice between a call that waits for a reply and one that merely sends a message. I use a synchronous call only when the result is genuinely needed. Blocking while another desktop process opens a file or asks a question can make both programs feel frozen.&lt;/p&gt;
&lt;p&gt;Application names need care as well. A second instance may register with a numbered name rather than replacing the first one. If the operation concerns a particular document, guessing the first process in the list is unsafe. I either arrange single-instance behavior deliberately or discover the intended application and object from information I already have. DCOP removes the need for process identifiers; it does not remove the need to choose the right process.&lt;/p&gt;
&lt;p&gt;Failures should remain visible. A call can fail because the application exited, the object disappeared, or the signature changed. I report that to the caller instead of treating a missing reply as an empty successful result.&lt;/p&gt;
&lt;p&gt;My rule now is to prove the exchange with the command-line client first. If &lt;code&gt;dcop&lt;/code&gt; cannot see or call the object, another page of C++ is unlikely to improve matters. This is one of those rare debugging techniques that removes code, which makes it suspiciously pleasant.&lt;/p&gt;</description></item></channel></rss>