<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" gd:etag="W/&quot;CUAFQHs5eSp7ImA9WxJUEUs.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401</id><updated>2009-07-09T11:21:51.521-07:00</updated><title>Coding Relic</title><subtitle type="html">Random musings on software in an embedded world.</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://codingrelic.geekhold.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>29</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><link rel="self" href="http://feeds.feedburner.com/CodingRelic" type="application/atom+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><entry gd:etag="W/&quot;DUYHQn0ycSp7ImA9WxJVE0s.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-3257500677352324741</id><published>2009-06-30T06:05:00.000-07:00</published><updated>2009-06-30T06:05:33.399-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-30T06:05:33.399-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="ELF" /><title>Post-mortem debugging: core files</title><content type="html">&lt;p&gt;When one has a core file, one runs gdb. Its simply the way things were Meant To Be, right? Yet gdb isn't the right tool for the job in all cases. If you're dealing with a corrupted heap, gdb is not very helpful. You can see the portion of the heap which caused the process to fault (most likely in malloc or free), but identifying the junk in memory is an exercise in puzzling it out and looking for patterns. It is often useful in such cases to search the rest of the process address space for pointers into the corrupted area of the heap. Though gdb can be used to search for patterns in memory, it isn't very good at it. For example consider the following macro:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: x-small; line-height: 1.1em; margin-left: 5em;"&gt;
define searchmem
    set $start = (char *) $arg0
    set $end = (char *) $arg1
    set $pattern = (unsigned int) $arg2
    set $p = $start
    while $p &amp;lt; $end
        if (*(unsigned int *) $p) == $pattern
            printf "pattern 0x%x found at 0x%x\n", $pattern, $p
        end
        set $p++
    end
end

document searchmem
    search between $argv0 and $argv1 for pattern $argv2
end
&lt;/pre&gt;

&lt;p&gt;This macro can look only for 32 bit numbers, not any sort of regular expression, and it is very, &lt;i&gt;very&lt;/i&gt; slow. We'd really like to run grep, but if gdb provides a way to run grep over the core contents I haven't found it. Instead, Gentle Reader, we'll write a utility to output the core file to text so that grep or any other Unix tool can be used. This would be a job for od or hexdump except for two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We'd like to see the addresses of the data being dumped.&lt;/li&gt;
&lt;li&gt;The core might be in the wrong endianness, such as a MIPS-BE core file on an x86 host.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of using a generic binary dump like od we'll construct a tool specifically for core files,  but first we need to understand their contents.&lt;/p&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;Process Address Space&lt;/span&gt;
&lt;img style="padding:3px; border:0px;" src="http://2.bp.blogspot.com/_WibILqsOlLg/SknzIZFJAKI/AAAAAAAAAW0/R1U3-qCcezo/s800/addrspace.png" width="137" height="601" align="right" border="0" alt="Linux process address space" title="This is a PowerPC example, other architectures may vary."&gt;
&lt;p&gt;Linux and modern Unix-ish operating systems dynamically map shared libraries into the process address space. These libraries are not packed tightly up against one another. For alignment and page protection reasons, there are gaps between them.&lt;/p&gt;

&lt;p&gt;Each library generally consists of multiple segments:&lt;p&gt;
&lt;ul&gt;
&lt;li&gt;instructions, called the TEXT segment.&lt;/li&gt;
&lt;li&gt;uninitialized data to be zero filled, called BSS&lt;/li&gt;
&lt;li&gt;initialized data (i.e. variables initialized to a non-zero value), called DATA&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can see the memory segments for a running process in /proc/&amp;lt;pid&amp;gt;/maps. Here is an example:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 2em;"&gt;
# cat /proc/1261/maps
0fc73000-0fc80000 r-xp 00000000 01:00 713        /lib/libA.so.1
0fc80000-0fc83000 ---p 0000d000 01:00 713        /lib/libA.so.1
0fc83000-0fc92000 rwxp 00000000 01:00 713        /lib/libA.so.1
...deleted...
10000000-1000c000 r-xp 00000000 01:00 1190       /bin/myapp
1001b000-1001c000 rwxp 0000b000 01:00 1190       /bin/myapp
...deleted...
&lt;/pre&gt;

&lt;p&gt;libA's callable functions are in the TEXT segment, mapped at addresses 0x0fc73000 through 0x0fc80000. Note the permission bits on these pages: read-only plus executable. Write permission is denied, making it safe to share the same physical pages of RAM amongst multiple processes.&lt;/p&gt;

&lt;p&gt;The libA BSS segment extends from 0x0fc80000 through 0x0fc83000. These pages are mapped with no permissions at all, even a read access will trigger a page fault. The kernel will supply a zero-filled page on the first fault, and mark its permissions as read+write.&lt;/p&gt;

&lt;p&gt;The libA DATA segment is at 0x0fc83000 though 0x0fc92000. This region is  populated with the initialized data, so it has read+write permissions already. No page fault will be triggered on access to these pages.&lt;/p&gt;

&lt;p&gt;When the process dies, the core file needs to preserve these scattered memory areas. Core files from a Linux process are written in ELF format, which I will stubbornly continue to refer to as the &lt;a href="http://en.wikipedia.org/wiki/Executable_and_Linkable_Format"&gt;Extensible Linking Format&lt;/a&gt;. ELF defines data sections with associated virtual addresses, allowing it to describe data scattered across a process address space. Let's examine the output of "readelf -l" on the core from this process:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 2em;"&gt;
Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz   Flg Align
  NOTE           0x0003f4 0x00000000 0x00000000 0x006f4 0x00000      0
  LOAD           0x001000 0x0fc73000 0x00000000 0x00000 0x0d000  R E 0x1000
  LOAD           0x001000 0x0fc80000 0x00000000 0x00000 0x03000      0x1000
  LOAD           0x001000 0x0fc83000 0x00000000 0x0f000 0x0f000  RWE 0x1000
  ..etc...
&lt;/pre&gt;

&lt;p&gt;The NOTE section contains various global information about the dead process, including its name and the contents of the CPU registers at the time it died. If you use &amp;quot;objdump -h&amp;quot; to list the core sections you'll see two additional sections: reg0/2 and reg0. These sections don't actually exist in the file, objdump breaks out the register contents from the NOTE section into their own pseudo-sections.&lt;/p&gt;

&lt;p&gt;The LOAD sections contain the data from the process address space. The first one starts at a virtual address of 0xfc73000. That is the TEXT segment for libA, which we looked at earlier. Note the FileSiz is 0: to save space, the kernel skips dumping the contents of non-writable pages. gdb would fetch these sections from the executable file instead. The second LOAD section contains the libA BSS, and similarly its FileSiz is zero: this process died before it ever referenced anything in the BSS of libA. None of the pages had been faulted in and therefore none were writable.&lt;/p&gt;

&lt;p&gt;The third LOAD section is the interesting one. This is the initialized DATA section. Because these pages were writable, they were saved to the core file and so the FileSiz is 0x0f000 (which matches the size of the segment from the /proc/&amp;lt;pid&amp;gt;maps file, above).&lt;/p&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;BFD&lt;/span&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Binary_File_Descriptor_library"&gt;libbfd&lt;/a&gt; is one of several libraries available for working with ELF files. We'll use libbfd to process the core file, dumping it as hex words to a text file.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="http://www.cs.utah.edu/dept/old/texinfo/bfd/bfd.html"&gt;history&lt;/a&gt; I can find, libbfd was created at Cygnus Support to deal with the myriad binary formats that sprang up in the 1980s and 90s. In response to how difficult it would be to encapsulate the various formats in a single library &lt;a href="http://www.linkedin.com/in/henkelwallace"&gt;David Henkel-Wallace&lt;/a&gt; reportedly responded &amp;quot;big f---ing deal,&amp;quot; and thus libbfd was christened. The name has since been clarified as &amp;quot;binary file descriptor.&amp;quot;&lt;/p&gt;

&lt;br/&gt;
&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 1em;"&gt;
    #include &amp;lt;bfd.h&amp;gt;

    bfd        *abfd;
    asection   *sect;
    char       *corefilename;
    enum bfd_endian endian;

    if ((abfd = bfd_openr(corefilename, NULL)) == NULL) {
        /* ... error handling ... */
    }
&lt;/pre&gt;

&lt;p&gt;First we open the file using bfd_openr(). *abfd is the handle used to access the file, all other libbfd APIs take it as an argument.&lt;/p&gt;

&lt;br/&gt;
&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 1em;"&gt;
    if (!bfd_check_format (abfd, bfd_core)) {
        printf("%s does not appear to be a core file.\n", corefilename);
        /* ... error handling ... */
    }
&lt;/pre&gt;

&lt;p&gt;We check that the file is actually a core and not some other type of file. &lt;i&gt;bfd_core&lt;/i&gt; is part of an enumerated type; other types which libbfd can check for include bfd_object for ELF programs or .o files, and bfd_archive for ar-style arcives.&lt;/p&gt;


&lt;br/&gt;
&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 1em;"&gt;
    endian = abfd-&amp;gt;xvec-&amp;gt;byteorder;
&lt;/pre&gt;

&lt;p&gt;libbfd handles the endianness of fields in the program and section headers, returning the result in the CPU's native byte order regardless of the endianness of the ELF file. It doesn't do anything about the &lt;i&gt;data&lt;/i&gt; within those sections. Since we're going to dump the data in the core file, we need to know the endianness.&lt;/p&gt;


&lt;br/&gt;
&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 1em;"&gt;
    for (sect = abfd-&gt;sections; sect != NULL; sect = sect-&gt;next) {
        bfd_vma        vma  = bfd_section_vma (abfd, sect);
        bfd_size_type  size = bfd_section_size(abfd, sect);
        const char    *name = bfd_section_name(abfd, sect);
&lt;/pre&gt;

&lt;p&gt;We loop over each ELF section, pulling out the virtual address and size of the data it holds. The size will be zero in many cases, as shown above for the libA TEXT and BSS sections.&lt;/p&gt;


&lt;br/&gt;
&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 1em;"&gt;
        if (!bfd_get_section_contents(abfd, sect, buf, (file_ptr)0, size)) {
            fprintf(stderr, "Could not read section %s\n", name);
            exit(5);
        }
&lt;/pre&gt;

&lt;p&gt;We fetch the contents of each section, ready to print them to hex.&lt;/p&gt;


&lt;br/&gt;
&lt;p&gt;With a modicum of string manipulation we have a hex dump of the core contents:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 2em;"&gt;
0fc84710:  615f6669  6e616c69  7a65005f  4a765f52    a_finalize._Jv_R
0fc84720:  65676973  74657243  6c617373  6573006c    egisterClasses.l
0fc84730:  6962632e  736f2e36  0061646c  65723332    ibc.so.6.adler32
0fc84740:  00636f6d  70726573  73320064  65666c61    .compress2.defla
&lt;/pre&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;libbfd licensing&lt;/span&gt;
&lt;p&gt;libbfd is GPL. It is not LGPL, which allows dynamic linking to proprietary code, but the full GPL. Any use of libbfd encumbers the rest of the software linked to it with the GPL and obligates you to provide the source code to anyone to whom you provide a binary. Its important to consider what this means: if you're developing tools for internal use by developers, the GPL is not  onerous. Indeed, the source code of the utility will likely be checked into the version control system that all developers can access anyway.&lt;/p&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;More later...&lt;/span&gt;
&lt;p&gt;If you for need to incorporate ELF file support into a proprietary software tool, then libbfd is not useable. I intended to provide the same core2hex example using FreeBSD's &lt;a href="http://sourceforge.net/projects/elftoolchain/"&gt;libelf&lt;/a&gt;, but this article is already quite long so I'm going to defer it until next time.&lt;/p&gt;

&lt;!-- LGPL libelf: http://www.mr511.de/software/english.html --&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-3257500677352324741?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=MlwsM9mdGn8:QEFBhkzOFuU:Pt5fc6444Es"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=MlwsM9mdGn8:QEFBhkzOFuU:Pt5fc6444Es" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=MlwsM9mdGn8:QEFBhkzOFuU:bV-q3IutASs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=MlwsM9mdGn8:QEFBhkzOFuU:bV-q3IutASs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=MlwsM9mdGn8:QEFBhkzOFuU:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=MlwsM9mdGn8:QEFBhkzOFuU:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=MlwsM9mdGn8:QEFBhkzOFuU:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=MlwsM9mdGn8:QEFBhkzOFuU:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/MlwsM9mdGn8" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=3257500677352324741" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/3257500677352324741?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/3257500677352324741?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/06/post-mortem-debugging-core-files.html" title="Post-mortem debugging: core files" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_WibILqsOlLg/SknzIZFJAKI/AAAAAAAAAW0/R1U3-qCcezo/s72-c/addrspace.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;A08BR3c4eyp7ImA9WxJRE0U.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-6848441686602729878</id><published>2009-05-15T05:17:00.000-07:00</published><updated>2009-05-15T05:17:36.933-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-15T05:17:36.933-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><title>Pre-mortem Backtracing</title><content type="html">&lt;p&gt;A backtrace is often the first step in debugging a problem. Generating a backtrace is generally thought of as a function of the debugger, on a core file after a process has died. However it is sometimes quite useful to generate a live backtrace while a process runs. For example, crashing the process in the field may not be acceptable if a problem is survivable. Logging a backtrace and other information can provide enough to locate the root cause, without having to  trigger any customer downtime.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;gcc backtrace support&lt;/span&gt;
&lt;p&gt;&lt;img style="padding:3px; border:0px;" src="http://1.bp.blogspot.com/_WibILqsOlLg/Sg1ckJPBfEI/AAAAAAAAAWM/Ot9NyUxpmtE/s200/stack.png" width="150" height="100" align="right" border="0" title="Ooooh, illustrations!."&gt;
The simplest way to get a crude backtrace is the &lt;span style="font-family: Courier New, Courier, fixed;"&gt;__builtin_return_address()&lt;/span&gt; macro supplied by gcc. You provide the frame number you want to retrieve, and get the return address for that stack frame:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
void do_backtrace2()
{
    void *pc0 = __builtin_return_address(0);
    void *pc1 = __builtin_return_address(1);
    void *pc2 = __builtin_return_address(2);
    void *pc3 = __builtin_return_address(3);

    printf("Frame 0: PC=%p\n", pc0);
    printf("Frame 1: PC=%p\n", pc1);
    printf("Frame 2: PC=%p\n", pc2);
    printf("Frame 3: PC=%p\n", pc3);
}
&lt;/pre&gt;

&lt;p&gt;This code will produce the following output:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
Frame 0: PC=0x80483ca
Frame 1: PC=0x80483e1
Frame 2: PC=0x62079d
Frame 3: PC=0x80482b9
&lt;/pre&gt;

&lt;p&gt;&lt;span style="font-family: Courier New, Courier, fixed;"&gt;__builtin_return_address()&lt;/span&gt; has significant limitations. It constructs code at compile time to walk back through the stack frames. That means you cannot use a variable in a loop, you can only use integer constants like the 0,1,2,3 shown above. Also on some architectures, including &lt;a href="http://codingrelic.geekhold.com/2008/03/secret-life-of-volatile.html"&gt;my&lt;/a&gt; &lt;a href="http://codingrelic.geekhold.com/2008/07/gdb-lies-to-you.html"&gt;beloved&lt;/a&gt; &lt;a href="http://codingrelic.geekhold.com/2009/02/inadvisable-externing.html"&gt;MIPS&lt;/a&gt;, only &lt;span style="font-family: Courier New, Courier, fixed;"&gt;__builtin_return_address(0)&lt;/span&gt; works. MIPS has no frame pointer, making it difficult to walk back up the stack. Frame 0 can use the return address register directly.&lt;/p&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;glibc's backtrace()&lt;/span&gt;
&lt;p&gt;glibc contains a simple backtrace function, which is somewhat more powerful than &lt;span style="font-family: Courier New, Courier, fixed;"&gt;__builtin_return_address()&lt;/span&gt;. The &lt;span style="font-family: Courier New, Courier, fixed;"&gt;backtrace()&lt;/span&gt; call populates an array with the program counter of each calling function, while a separate &lt;span style="font-family: Courier New, Courier, fixed;"&gt;backtrace_symbols()&lt;/span&gt; call can look up the symbolic names for each address:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
#include &amp;lt;execinfo.h&amp;gt;

#define BACKTRACE_SIZ   64
void do_backtrace()
{
    void    *array[BACKTRACE_SIZ];
    size_t   size, i;
    char   **strings;

    size = backtrace(array, BACKTRACE_SIZ);
    strings = backtrace_symbols(array, size);

    for (i = 0; i &amp;lt; size; i++) {
        printf("%p : %s\n", array[i], strings[i]);
    }

    free(strings);  // malloced by backtrace_symbols
}
&lt;/pre&gt;

&lt;p&gt;The output shows the backtrace with the address of each function call site:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
# gcc -g -o backtrace ./backtrace.c
# ./backtrace 
0x8048422 : ./backtrace(backtrace_symbols+0xd6) [0x8048422]
0x80484be : ./backtrace(backtrace_symbols+0x172) [0x80484be]
0x80484d5 : ./backtrace(backtrace_symbols+0x189) [0x80484d5]
0x071479d : /lib/tls/libc.so.6(__libc_start_main+0xed) [0x71479d]
0x804837d : ./backtrace(backtrace_symbols+0x31) [0x804837d]
&lt;/pre&gt;

&lt;p&gt;To get useful symbolic names, the -rdynamic option must be passed to the linker:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
# gcc -g -rdynamic -o backtrace ./backtrace.c
# ./backtrace 
0x804874a : ./backtrace(do_backtrace+0x1a) [0x804874a]
0x80487e6 : ./backtrace(foo1+0xb) [0x80487e6]
0x80487fd : ./backtrace(main+0x15) [0x80487fd]
0x012679d : /lib/tls/libc.so.6(__libc_start_main+0xed) [0x12679d]
0x80486a5 : ./backtrace(backtrace_symbols+0x31) [0x80486a5]
&lt;/pre&gt;

&lt;p&gt;There is also a &lt;span style="font-family: Courier New, Courier, fixed;"&gt;backtrace_symbols_fd()&lt;/span&gt; function, which nicely prints the output to a file descriptor without having to malloc a table of strings. If thats all you're trying to do, it is a simpler API.&lt;/p&gt;

&lt;p&gt;As an aside: notice how the address of __libc_start_main varies in the examples above, 0x62079d versus 0x71479d versus 0x12679d? That is &lt;a href="http://en.wikipedia.org/wiki/Address_space_layout_randomization"&gt;address space randomization&lt;/a&gt; in action. libc is mapped at a randomized base address every time a binary is started. Yhe offset of __libc_start_main within the page is a constant 0x79d, but the upper bits of the address will vary from one run to the next. This helps prevent buffer overflow and other code injection attacks, by randomizing the address of library routines.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;span style="font-weight: bold; font-size: 120%;"&gt;libunwind&lt;/span&gt;
&lt;p&gt;&lt;a href="http://www.nongnu.org/libunwind/index.html"&gt;libunwind&lt;/a&gt; is a  library for extracting call chain information. It supports many different CPU architectures. Here is an example of using libunwind to accomplish a similar result as glibc's &lt;span style="font-family: Courier New, Courier, fixed;"&gt;backtrace()&lt;/span&gt; function:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
#include &amp;lt;libunwind.h&amp;gt;

void do_backtrace2()
{
    unw_cursor_t    cursor;
    unw_context_t   context;

    unw_getcontext(&amp;context);
    unw_init_local(&amp;cursor, &amp;context);

    while (unw_step(&amp;cursor) &amp;gt; 0) {
        unw_word_t  offset, pc;
        char        fname[64];

        unw_get_reg(&amp;cursor, UNW_REG_IP, &amp;pc);

        fname[0] = '\0';
        (void) unw_get_proc_name(&amp;cursor, fname, sizeof(fname), &amp;offset);

        printf ("%p : (%s+0x%x) [%p]\n", pc, fname, offset, pc);
    }
}
&lt;/pre&gt;

&lt;p&gt;The output:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
0x80486b3 : (foo+0xb) [0x80486b3]
0x80486ca : (main+0x15) [0x80486ca]
0x016379d : (__libc_start_main+0xed) [0x16379d]
0x80484c9 : (_start+0x21) [0x80484c9]
&lt;/pre&gt;

&lt;p&gt;That is quite a bit more code to get a simple backtrace, but libunwind offers more capability to examine the program state at each frame. For example, one can print the saved register values:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
#include &amp;lt;libunwind.h&amp;gt;

void do_backtrace2()
{
    unw_cursor_t    cursor;
    unw_context_t   context;

    unw_getcontext(&amp;context);
    unw_init_local(&amp;cursor, &amp;context);
    while (unw_step(&amp;cursor) &amp;gt; 0) {
        unw_word_t  offset;
        unw_word_t  pc, eax, ebx, ecx, edx;
        char        fname[64];

        unw_get_reg(&amp;cursor, UNW_REG_IP,  &amp;pc);
        unw_get_reg(&amp;cursor, UNW_X86_EAX, &amp;eax);
        unw_get_reg(&amp;cursor, UNW_X86_EDX, &amp;edx);
        unw_get_reg(&amp;cursor, UNW_X86_ECX, &amp;ecx);
        unw_get_reg(&amp;cursor, UNW_X86_EBX, &amp;ebx);

        fname[0] = '\0';
        unw_get_proc_name(&amp;cursor, fname, sizeof(fname), &amp;offset);
        printf ("%p : (%s+0x%x) [%p]\n", pc, fname, offset, pc);
        printf("\tEAX=0x%08x EDX=0x%08x ECX=0x%08x EBX=0x%08x\n",
                eax, edx, ecx, ebx);
    }
}
&lt;/pre&gt;

&lt;p&gt;The output:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
0x80486b3 : (foo1+0xb) [0x80486b3]
 EAX=0x00000000 EDX=0x00b548b0 ECX=0x00000000 EBX=0x00000000
0x80486ca : (main+0x15) [0x80486ca]
 EAX=0x00000000 EDX=0x00b548b0 ECX=0x00000000 EBX=0x00000000
0x044879d : (__libc_start_main+0xed) [0x44879d]
 EAX=0x00000000 EDX=0x003368b0 ECX=0x00000000 EBX=0x00000000
0x80484c9 : (_start+0x21) [0x80484c9]
 EAX=0x00000000 EDX=0x00b548b0 ECX=0x00000000 EBX=0x00000000
&lt;/pre&gt;

&lt;br/&gt;
&lt;p&gt;When would this be useful? Given the relative costs, it is not unusual to have an embedded CPU with considerably more RAM than flash. In the event of a crash there may not be enough flash to save a full core file. Having the process deliberately catch SIGSEGV and dump its own backtrace with register values means you'd at least have &lt;i&gt;something&lt;/i&gt; to work with even if there is no core file.&lt;/p&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;Conclusion&lt;/span&gt;
&lt;p&gt;Over time I think I have used &lt;span style="font-family: Courier New, Courier, fixed;"&gt;__builtin_return_address(0)&lt;/span&gt; more often than any of the other techniques. Whether constructing simple performance instrumentation or logging problems from the field, knowing the caller has often been sufficient. For more extensive backtrace functionality I end up using libunwind. The &lt;span style="font-family: Courier New, Courier, fixed;"&gt;backtrace()&lt;/span&gt; function in glibc always seems to be too heavy for the simple stuff yet not sufficient for the complex stuff.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-6848441686602729878?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=AL08bZ3-7t8:_o3g7jzjkjw:Pt5fc6444Es"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=AL08bZ3-7t8:_o3g7jzjkjw:Pt5fc6444Es" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=AL08bZ3-7t8:_o3g7jzjkjw:bV-q3IutASs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=AL08bZ3-7t8:_o3g7jzjkjw:bV-q3IutASs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=AL08bZ3-7t8:_o3g7jzjkjw:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=AL08bZ3-7t8:_o3g7jzjkjw:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=AL08bZ3-7t8:_o3g7jzjkjw:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=AL08bZ3-7t8:_o3g7jzjkjw:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/AL08bZ3-7t8" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=6848441686602729878" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/6848441686602729878?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/6848441686602729878?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/05/pre-mortem-backtracing.html" title="Pre-mortem Backtracing" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_WibILqsOlLg/Sg1ckJPBfEI/AAAAAAAAAWM/Ot9NyUxpmtE/s72-c/stack.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;A0cHQH8yeCp7ImA9WxJSEUo.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-8232353780895900506</id><published>2009-05-01T04:57:00.000-07:00</published><updated>2009-05-01T04:57:11.190-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-01T04:57:11.190-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Rant" /><title>Desperate Bugs Demand Desperate Measures</title><content type="html">&lt;p&gt;After chasing a pernicious bug long enough, there is a strong temptation to find a way to make it be someone else's problem for a while.&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
if (critical_operation() == FAILED) {
    /* Uh oh. Quick, we need a distraction! */
    switch((rand() % 4)) {
        case 0: blame = "killall -SEGV kick_watchdog";     break;
        case 1: blame = "killall -ABRT cli_process";       break;
        case 2: blame = "cat /dev/urandom &gt;/proc/kmem";    break;
        case 3: blame = "killall -FPE  hardware_manager";  break;
    }
    system(blame);
    system("rm /var/log/*.log"); /* Destroy the evidence */
    sleep(10); /* Wait for the other process to die first */
    return;
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-8232353780895900506?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=9CZ1iIs1Nt8:6qI_awS7UNY:Pt5fc6444Es"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=9CZ1iIs1Nt8:6qI_awS7UNY:Pt5fc6444Es" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=9CZ1iIs1Nt8:6qI_awS7UNY:bV-q3IutASs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=9CZ1iIs1Nt8:6qI_awS7UNY:bV-q3IutASs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=9CZ1iIs1Nt8:6qI_awS7UNY:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=9CZ1iIs1Nt8:6qI_awS7UNY:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=9CZ1iIs1Nt8:6qI_awS7UNY:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=9CZ1iIs1Nt8:6qI_awS7UNY:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/9CZ1iIs1Nt8" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=8232353780895900506" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/8232353780895900506?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/8232353780895900506?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/05/desperate-bugs-demand-desperate.html" title="Desperate Bugs Demand Desperate Measures" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;Dk4ARno6fCp7ImA9WxJRFUs.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-7676377261322324793</id><published>2009-04-09T05:27:00.000-07:00</published><updated>2009-05-17T05:55:47.414-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-17T05:55:47.414-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="AppEngine" /><category scheme="http://www.blogger.com/atom/ns#" term="friendfeed" /><category scheme="http://www.blogger.com/atom/ns#" term="Python" /><title>More Google App Engine - Feedflares</title><content type="html">&lt;p&gt;Today's article is a discussion about some of the infrastructure for running this site. So far as I can tell from the logs and analytics, nearly everyone reading this blog does so via RSS. The RSS feed for this site is provided by &lt;a href="http://feedburner.google.com"&gt;FeedBurner&lt;/a&gt;, now part of Google. FeedBurner supports &lt;a href="http://www.feedburner.com/fb/a/publishers/feedflare"&gt;FeedFlares&lt;/a&gt;, small widgets appended after the content which can supply additional information or link to other services. I currently use several FeedFlares in the RSS feed, for &lt;a href="http://www.delicious.com/"&gt;del.icio.us&lt;/a&gt; and &lt;a href="http://www.friendfeed.com/"&gt;friendfeed&lt;/a&gt;. The friendfeed flare is new, and is the topic of this writeup.&lt;/p&gt;

&lt;br/&gt;

&lt;center&gt;&lt;img style="padding:3px; border:0px;" src="http://4.bp.blogspot.com/_WibILqsOlLg/Sd1yv-FmXCI/AAAAAAAAAVg/hE4aVKnD9ww/s800/feedflares.png" width="408" height="110" border="0" alt="FeedFlare example" title="The FeedBurner sample has that cool fade out at the top. I tried to replicate it, but failed miserably."&gt;&lt;/center&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;p&gt;&lt;a href="http://friendfeed.com/"&gt;&lt;img style="padding:3px; border:0px;" src="http://4.bp.blogspot.com/_WibILqsOlLg/Sd3oOv5ADnI/AAAAAAAAAVo/EQmnrFikY-w/s200/friendfeed.png" width="160" height="34" align="right" border="0" alt="friendfeed.com" title="Catchy logo, really."&gt;&lt;/a&gt;&lt;a href="//friendfeed.com/"&gt;friendfeed&lt;/a&gt; is a social media aggregation service, collecting updates from services like &lt;a href="http://digg.com/"&gt;Digg&lt;/a&gt;, &lt;a href="http://flickr.com/"&gt;Flickr&lt;/a&gt;, and various blog platforms into a single stream of updates. Many good articles about friendfeed can be found on &lt;a href="http://www.google.com/search?q=site:louisgray.com+friendfeed"&gt;louisgray.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The RSS feed for this blog is imported into friendfeed where people can see it, mark it as something they liked, or leave comments. At the time I started working on this project there was not a FeedFlare for friendfeed. &lt;a href="http://www.louisgray.com/live/2009/03/new-feedflare-displays-friendfeed-likes.html"&gt;There is now&lt;/a&gt;, but I decided to finish my version anyway and post it here. As &lt;a href="http://appengine.google.com/"&gt;Google App Engine&lt;/a&gt; is my favorite &lt;a href="http://codingrelic.geekhold.com/2009/03/exploring-google-app-engine.html"&gt;new toy&lt;/a&gt;, the FeedFlare is a GAE application.&lt;/p&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;


&lt;p&gt;We'll go straight to the code which gathers information from friendfeed to create the FeedFlare. I'm going to skip the boilerplate code for an application on the Google App Engine. It can be found on an &lt;a href="http://codingrelic.geekhold.com/2009/03/exploring-google-app-engine.html"&gt;earlier article&lt;/a&gt; about the App Engine, if needed. The complete source for this feedflare is also &lt;a href="http://www.geekhold.com/codingrelic/26/friendfeedflare.zip"&gt;available&lt;/a&gt; for download.&lt;/p&gt;


&lt;pre style="margin-left: 2em; font-family: Courier New, Courier, fixed; font-size: small; font-weight: bold; line-height: 1.1em;"&gt;
class FriendfeedFlare(webapp.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = &amp;quot;text/plain&amp;quot;

    scheme, host, path, param, query, frag = urlparse.urlparse(self.request.url)
    args = cgi.parse_qs(query)

    url      = self.parseArg(args, &amp;quot;url&amp;quot;)
    nickname = self.parseArg(args, &amp;quot;nickname&amp;quot;)
    api_key  = self.parseArg(args, &amp;quot;api_key&amp;quot;)

    if (url == None):
        self.response.out.write(&amp;quot;&amp;lt;FeedFlare&amp;gt;&amp;lt;Text&amp;gt;No URL specified!&amp;lt;/Text&amp;gt;&amp;lt;/FeedFlare&amp;gt;\n&amp;quot;)
        return

    subscribed = 1 if (nickname != None and api_key != None) else 0
&lt;/pre&gt;

&lt;div style="font-size:small; font-style:italic; padding: 5px; border: 1px #ff7f50 solid; width: 90%; margin-left: 2em;"&gt;
&lt;p&gt;Three arguments are accepted, using the standard CGI convention of http://host/path?arg1=value&amp;arg2=value&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;url&lt;/b&gt; - the url of the RSS item this FeedFlare should reference. This argument is required.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nickname&lt;/b&gt; - the friendfeed account to authenticate as. If provided, the search will be restricted to friends of this nickname. If not provided, we search all entries on friendfeed.com.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;api_key&lt;/b&gt; - the &lt;a href="http://friendfeed.com/remotekey"&gt;API Key&lt;/a&gt; to authenticate us. If nickname is provided the api_key must also be provided.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; At the time of this writing (4/9/2009) the nickname functionality to restrict results to subscribers is not working. It was working a couple days ago, but seemed to break just as I posted this article. I'll update the post if I get it working again, for now the feedflare is useable when searching all entries on friendfeed.com.&lt;/p&gt;

&lt;p&gt;urlparse.urlparse() is employed to break the URL into its main components, and then cgi.parse_qs() pulls out the individual parameters. parse_qs() returns each argument as a list, because it allows multiple instances of an argument. In this case only one makes sense, so we get back a list with one member. self.parseArg() is a small helper routine to return None if the argument is not present, or the first element in the list returned from cgi.parse_qs().&lt;/p&gt;
&lt;/div&gt;

&lt;pre style="margin-left: 2em; font-family: Courier New, Courier, fixed; font-weight: bold; font-size: small; line-height: 1.1em;"&gt;
    try:
        ffsession = friendfeed.FriendFeed(nickname, api_key);
        entries   = ffsession.fetch_url_feed(url, subscribed);
    except IOError:
        self.error(503);
        return
&lt;/pre&gt;

&lt;div style="font-size:small; font-style:italic; padding: 5px; border: 1px #ff7f50 solid; width: 90%; margin-left: 2em;"&gt;
&lt;p&gt;Friendfeed supplies Python &lt;a href="http://code.google.com/p/friendfeed-api/"&gt;wrapper functions&lt;/a&gt; for their API. The wrapper functions are used here to connect to friendfeed.com, using the authorization credentials (if present). If friendfeed is not responding, a 503 response is sent to feedburner.com. This Service Unavailable result tells feedburner to continue to use its  cached information and to try again later.&lt;/p&gt;

&lt;p&gt;fetch_url_feed() is a function added to the friendfeed API, to support their &lt;a href="http://code.google.com/p/friendfeed-api/wiki/ApiDocumentation#/api/feed/url_-_Fetch_Entries_about_a_URL"&gt;/api/feed/url&lt;/a&gt; API. It fetches all entries which reference the given url.&lt;/p&gt;
&lt;/div&gt;

&lt;pre style="margin-left: 2em; font-family: Courier New, Courier, fixed; font-size: small; font-weight: bold; line-height: 1.1em;"&gt;
    totalshares   = 0
    totalcomments = 0
    likers        = set()
    linkurl       = &amp;quot;http://friendfeed.com/&amp;quot;
    linkcomments  = -1
    
    for entry in entries[&amp;quot;entries&amp;quot;]:
        totalshares   += 1
        numcomments    = len(entry[&amp;quot;comments&amp;quot;])
        totalcomments += numcomments
        if (numcomments &gt; linkcomments):
            linkurl = &amp;quot;http://friendfeed.com/e/&amp;quot; + entry[&amp;quot;id&amp;quot;]
            linkcomments = numcomments
        for like in entry[&amp;quot;likes&amp;quot;]:
            liker = like[&amp;quot;user&amp;quot;]
            likers.add(liker[&amp;quot;name&amp;quot;])

    totallikes = len(likers)
&lt;/pre&gt;

&lt;div style="font-size:small; font-style:italic; padding: 5px; border: 1px #ff7f50 solid; width: 90%; margin-left: 2em;"&gt;
&lt;p&gt;The friendfeed API returns entries in &lt;a href="http://www.json.org/"&gt;JSON&lt;/a&gt; format, which is parsed by their API and returned as nested Python lists. To count the number of likes and comments, one needs to iterate over each entry.&lt;/p&gt;

&lt;p&gt;likers is a Python set, a datatype I learned about while working on this project. A set is a group of objects which will contain no duplicates. If you add an item to the set which is already present the set will contain only one instance of the item, not two. This is used to avoid overcounting likes: if the URL we are looking for was shared multiple times in friendfeed and the same  user marked every one of them as liked, we only want to count that as one like not many.&lt;/p&gt;

&lt;p&gt;The linkurl is a compromise. I'd really like to direct the link to a page containing all of the results for this URL. Unfortunately only the friendfeed JSON API includes URL search functionality, the web search page does not. So far as I can tell there is no way to link back to friendfeed for more than one entry ID. So here we link to the entry with the most comments.&lt;/p&gt;
&lt;/div&gt;

&lt;pre style="margin-left: 2em; font-family: Courier New, Courier, fixed; font-size: small; font-weight: bold; line-height: 1.1em;"&gt;
    self.response.out.write(&amp;quot;&amp;lt;FeedFlare&amp;gt;\n&amp;quot;)
    if (totalshares == 0):
        self.response.out.write(&amp;quot;  &amp;lt;Text&amp;gt;On Friendfeed: 0 shares&amp;lt;/Text&amp;gt;\n&amp;quot;)
    else:
        self.response.out.write(&amp;quot;  &amp;lt;Text&amp;gt;On Friendfeed: &amp;quot; +                      \
                                self.fmtTotal(totalshares,   &amp;quot;Share&amp;quot;)   + &amp;quot;, &amp;quot; + \
                                self.fmtTotal(totallikes,    &amp;quot;Like&amp;quot;)    + &amp;quot;, &amp;quot; + \
                                self.fmtTotal(totalcomments, &amp;quot;Comment&amp;quot;) +        \
                                &amp;quot;&amp;lt;/Text&amp;gt;\n&amp;quot;);
    self.response.out.write(&amp;quot;  &amp;lt;Link href=\&amp;quot;&amp;quot; + linkurl + &amp;quot;\&amp;quot;/&amp;gt;\n&amp;quot;);
    self.response.out.write(&amp;quot;&amp;lt;/FeedFlare&amp;gt;&amp;quot;)
    return
&lt;/pre&gt;

&lt;div style="font-size:small; font-style:italic; padding: 5px; border: 1px #ff7f50 solid; width: 90%; margin-left: 2em;"&gt;
&lt;p&gt;Generate the XML output.  self.fmtTotal() is another little helper routine to pluralize the output correctly, &amp;quot;1 Comment&amp;quot; versus &amp;quot;2 Comments&amp;quot; The result of all this processing is a simple bit of XML:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; font-style: normal; line-height: 1.1em; margin-left: 2em;"&gt;
&amp;lt;FeedFlare&amp;gt;
  &amp;lt;Text&amp;gt;On Friendfeed: 5 Shares, 1 Like, 2 Comments&amp;lt;/Text&amp;gt;
  &amp;lt;Link href="http://friendfeed.com/e/1b0141a1-f6fa-1be2-e775-e5d36959e04c"/&amp;gt;
&amp;lt;/FeedFlare&amp;gt;
&lt;/pre&gt;

&lt;p&gt;This is all feedburner needs to create the FeedFlare. All formatting, including the font size and the blue text coloring, is hard-coded by feedburner. The FeedFlare does not get to supply any formatting, just some text and an optional link.&lt;/p&gt;
&lt;/div&gt;

&lt;pre style="margin-left: 2em; font-family: Courier New, Courier, fixed; font-size: small; font-weight: bold; line-height: 1.1em;"&gt;
  def fmtTotal(self, count, descr):
    suffix = &amp;quot;&amp;quot; if (count == 1) else &amp;quot;s&amp;quot;
    return str(count) + &amp;quot; &amp;quot; + descr + suffix

  def parseArg(self, args, argname):
    try:
        ret = args[argname][0]
    except:
        ret = None
    return ret
&lt;/pre&gt;

&lt;div style="font-size:small; font-style:italic; padding: 5px; border: 1px #ff7f50 solid; width: 90%; margin-left: 2em;"&gt;
The aforementioned helper routines.
&lt;/div&gt;

&lt;br/&gt;


&lt;br/&gt;

&lt;p&gt;Thats it, or rather thats the interesting part. The complete source can be &lt;a href="http://www.geekhold.com/codingrelic/26/friendfeedflare.zip"&gt;downloaded&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The next question is, what is missing? What does it not do, that perhaps it should?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There is no caching of the result. Every request for the FeedFlare results in another API request to friendfeed.com. I believe this is acceptable because FeedBurner limits the rate of FeedFlare requests to about one per two hours.&lt;/li&gt;
&lt;li&gt;The link in the generated FeedFlare points to the friendfeed entry with the most comments. This is a compromise. I'd rather to link to a search results page with all of the entries regarding the given URL, but can not find a good way to do it. I'd have to make the FeedFlare dynamically construct a page populated with all of the links, showing all of the likes and comments... and that is too much work for this little project. I hope that someday, friendfeed.com will provide a way to  supply multiple entry IDs to appear on a single page.&lt;/li&gt;
&lt;/ul&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;Using the FeedFlare&lt;/span&gt;
&lt;p&gt;If you are interested in using this FeedFlare on your own blog, please feel free. You have a few options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;To use it without a specific nickname (so the results will include Everyone on friendfeed whether they follow you or not) you can use &lt;a href="http://feedflare.geekhold.com/feedflareunit/friendfeed.xml"&gt;this link&lt;/a&gt; as the Flare Unit URL in the Feedburner -&amp;gt; Optimize -&amp;gt; FeedFlares page for your feed.&lt;/li&gt;
&lt;li&gt;&lt;strike&gt;To configure it to only include people who subscribe to you on friendfeed, &lt;b&gt;download&lt;/b&gt; http://feedflare.geekhold.com/feedflareunit/friendfeeduser.xml"&gt;friendfeeduser.xml. Replace MY_NICKNAME with your friendfeed account name, and MY_API_KEY with your &lt;a href="http://friendfeed.com/remote_key/"&gt;Remote API Key&lt;/a&gt;, and put the modified file somewhere on your own site to be used to configure FeedBurner.&lt;/strike&gt;&lt;br/&gt;
The functionality to restrict the results to your subscribers is not working right now. Please stay tuned. I'll post an update on friendfeed.com if I get it working again.&lt;/li&gt;
&lt;li&gt;If you don't like something about the way this code works, you're free to modify it. You can download the &lt;a href="http://www.geekhold.com/codingrelic/26/friendfeedflare.zip"&gt;source code&lt;/a&gt;, set up your own Google App Engine application, and modify it as desired.&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-7676377261322324793?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=H-_8_dEMMeI:JnavJ2T8Jtc:Pt5fc6444Es"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=H-_8_dEMMeI:JnavJ2T8Jtc:Pt5fc6444Es" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=H-_8_dEMMeI:JnavJ2T8Jtc:bV-q3IutASs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=H-_8_dEMMeI:JnavJ2T8Jtc:bV-q3IutASs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=H-_8_dEMMeI:JnavJ2T8Jtc:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=H-_8_dEMMeI:JnavJ2T8Jtc:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=H-_8_dEMMeI:JnavJ2T8Jtc:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=H-_8_dEMMeI:JnavJ2T8Jtc:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/H-_8_dEMMeI" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=7676377261322324793" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/7676377261322324793?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/7676377261322324793?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/04/more-google-app-engine-feedflares.html" title="More Google App Engine - Feedflares" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_WibILqsOlLg/Sd1yv-FmXCI/AAAAAAAAAVg/hE4aVKnD9ww/s72-c/feedflares.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;D0ADRXs5fip7ImA9WxJSGEo.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-30972290337307012</id><published>2009-04-01T05:01:00.000-07:00</published><updated>2009-05-09T06:29:34.526-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-09T06:29:34.526-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Product Development" /><title>The Target Market is Not Obvious</title><content type="html">&lt;!– google_ad_section_start(weight=ignore) –&gt;
&lt;p&gt;&lt;img style="border:0px; display: block; margin-left: auto; margin-right: auto;" width="168" height="205" border="0" alt="munchkin bottle warmer" title="It also waxes the floors." align="right" src="http://2.bp.blogspot.com/_WibILqsOlLg/ScuAwIFJbPI/AAAAAAAAAUM/ea3zSMh-DCw/s320/bottlewarmer.jpg"&gt;
&lt;b&gt;Exhibit A:&lt;/b&gt; a bottle warmer by &lt;a href="http://www.munchkin.com/"&gt;munchkin&lt;/a&gt;, a company focussed mainly on baby products. You pour water into the base, put the bottle in the holder, and press the button. A heating element boils the water to steam, which warms the bottle.&lt;/p&gt;
&lt;!– google_ad_section_end –&gt;

&lt;br clear="right"&gt;&amp;nbsp;&lt;br/&gt;

&lt;p&gt;&lt;img style="border:0px; display: block; margin-left: auto; margin-right: auto;" width="141" height="160" border="0" alt="piezoelectric buzzer" title="The Root of All Evil, so far as I am concerned." align="right" src="http://2.bp.blogspot.com/_WibILqsOlLg/ScuA0EELcbI/AAAAAAAAAUU/oHqU_swc4oM/s200/buzzer.jpg"&gt;
&lt;b&gt;Exhibit B:&lt;/b&gt; a piezoelectric buzzer. When the bottle is done heating, the lighted button on the front changes from red to green and the product emits a series of four shrill beeps to announce its successful completion. It is obviously very proud of itself.&lt;/p&gt;
&lt;br clear="right"&gt;&amp;nbsp;&lt;br/&gt;

&lt;!– google_ad_section_start(weight=ignore) –&gt;
&lt;p&gt;The issue, and point of today's rant? When using the bottle warmer you are likely holding a squirming, ravenously hungry baby who really isn't interested in the details of the food preparation equipment. You don't need an audible alarm, as you'll be staring desperately at the warmer ready to snatch the bottle out the instant it has made it from &amp;quot;cold&amp;quot; to &amp;quot;tepid.&amp;quot; So at best, the buzzer is annoying. With twins this feature is actively harmful: you really, &lt;u&gt;&lt;i&gt;really&lt;/i&gt;&lt;/u&gt; don't want to wake the second baby until the first is done eating. Trust me on this one.&lt;/p&gt;
&lt;!– google_ad_section_end –&gt;

&lt;br/&gt;
&lt;p&gt;&lt;img style="border:0px; display: block; margin-left: auto; margin-right: auto;" width="192" height="256" border="0" alt="PCB showing removed buzzer" title="Have soldering iron. Will travel." align="right" src="http://2.bp.blogspot.com/_WibILqsOlLg/ScuBF6TFmgI/AAAAAAAAAUs/USKhUoLLl4I/s320/pcb.jpg"&gt;&lt;b&gt;Exhibit C:&lt;/b&gt; the empty space on the PCB where the buzzer used to be.&lt;/p&gt;

&lt;p&gt;I have to say, I found their user interface to disable the buzzer feature somewhat difficult: a Torx screwdriver and soldering iron.&lt;/p&gt;
&lt;br clear="right"&gt;&amp;nbsp;&lt;br/&gt;


&lt;p&gt;Who was this thing designed for anyway, and why did they feel a buzzer was necessary? One can only speculate about the product requirements document and user story which led to this feature...&lt;/p&gt;

&lt;div style="width:220px; padding: 3px; border-width: 1px; border-color: #999999; border-style: solid; background-color: #cccccc; margin-left: auto; margin-right: auto; margin-top: 15px; margin-bottom: 15px;"&gt;&lt;p style="text-align: center;"&gt;This is Evilyn, a new parent.&lt;/p&gt;

&lt;a href="http://en.wikipedia.org/wiki/Evil-Lyn"&gt;&lt;img style="border:0px; display: block; margin-left: auto; margin-right: auto;" width="160" height="120" border="0" alt="Closeup of evil face" title="I recycled this from a vampire image. Can you tell?" src="http://2.bp.blogspot.com/_WibILqsOlLg/ScuBBdF7-jI/AAAAAAAAAUk/wd709OaJdw4/s200/evilmom.jpg"&gt;&lt;/a&gt;

&lt;!– google_ad_section_start(weight=ignore) –&gt;
&lt;p style="margin: 1em;"&gt;When the baby is hungry she puts a bottle in the warmer, turns on the vacuum cleaner to drown out the crying, and goes to watch a little TV. When the bottle is heated there needs to be an indication noticeable despite both the vacuum cleaner and Oprah.&lt;/p&gt;&lt;/div&gt;


&lt;p&gt;I'm kidding of course, I don't believe there was ever any such product plan. I think the problem is somewhat more subtle: the bottle warmer wasn't really designed for the parent at all &amp;mdash; and no, it wasn't designed for the baby either.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;span style="font-weight: bold; font-size: 110%;"&gt;Winning the Business&lt;/span&gt;
&lt;p&gt;The &lt;a href="http://www.munchkin.com/"&gt;munchkin corporation&lt;/a&gt; might well design some of its products, but as with any large company they would face the &amp;quot;build versus buy&amp;quot; decision for each niche product filling out their lineup. Their main concern is marketing and brand management. So the bottle warmer was quite possibly designed and manufactured by another company, and that other company does not sell directly to parents. Though the product obviously needs to perform its function, their customer is not the end user of the bottle warmer. Their customer is the buyer at the munchkin corporation. They might be designing it under contract or, quite possibly, designing it speculatively and hoping it will be picked up by one or more baby product companies. They have to make a good pitch, or be the most impressive offering at the trade show, to get the buyer to pick them instead of some other supplier.&lt;/p&gt;

&lt;p&gt;In that sort of competition, a longer feature list can be the key to winning the business. The munchkin bottle warmer has several extra features: it can sterilize pacifiers and bottle nipples using steam, it has a lighted button which changes from red to green during operation, and it has the aforementioned infernal buzzer.&lt;/p&gt;
&lt;!– google_ad_section_end –&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;span style="font-weight: bold; font-size: 110%;"&gt;Designed By &amp;lt;insert company name here&amp;gt;&lt;/span&gt;
&lt;p&gt;So there you have it. When you are vaguely dissatisfied with something you bought, if it just doesn't seem to have been completely thought through, this might be the reason. The designer's target was not you. It was targeted to catch the attention of the buyer for another company. This is also a primary reason why &lt;a href="http://www.apple.com/"&gt;Apple&lt;/a&gt; has been so successful in consumer electronics: though they might buy components and software from outside, they never pick up a complete product to slap their logo onto. They retain control of the product design.&lt;/p&gt;

&lt;img style="border:0px; display: block; margin-left: auto; margin-right: auto;" width="162" height="15" border="0" alt="Designed by Apple in California" title="An iPod Touch" src="http://2.bp.blogspot.com/_WibILqsOlLg/ScuA62A1wdI/AAAAAAAAAUc/w8udSanwvv0/s200/designedbyapple.jpg"&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-30972290337307012?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=yYx2SV7VJKc:3gFDL_sC8tI:Pt5fc6444Es"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=yYx2SV7VJKc:3gFDL_sC8tI:Pt5fc6444Es" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=yYx2SV7VJKc:3gFDL_sC8tI:bV-q3IutASs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=yYx2SV7VJKc:3gFDL_sC8tI:bV-q3IutASs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=yYx2SV7VJKc:3gFDL_sC8tI:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=yYx2SV7VJKc:3gFDL_sC8tI:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=yYx2SV7VJKc:3gFDL_sC8tI:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=yYx2SV7VJKc:3gFDL_sC8tI:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/yYx2SV7VJKc" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=30972290337307012" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/30972290337307012?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/30972290337307012?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/04/target-market-is-not-obvious.html" title="The Target Market is Not Obvious" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_WibILqsOlLg/ScuAwIFJbPI/AAAAAAAAAUM/ea3zSMh-DCw/s72-c/bottlewarmer.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;DUMAQnk5cSp7ImA9WxJSF0U.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-6379829991347243848</id><published>2009-03-19T05:21:00.000-07:00</published><updated>2009-05-08T05:57:23.729-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-08T05:57:23.729-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="AppEngine" /><category scheme="http://www.blogger.com/atom/ns#" term="Python" /><title>Exploring Google App Engine</title><content type="html">&lt;p&gt;
&lt;a href="http://www.openbsd.org/"&gt;&lt;img style="padding:3px; border:0px;" src="http://2.bp.blogspot.com/_WibILqsOlLg/ScDSKzDbUzI/AAAAAAAAAUE/R5v2wbYOCfU/s200/openbsd.png"  width="150" height="141" align="right" border="0" alt="OpenBSD" title="I hope the puffer fish logo is licensed under Creative Commons..."&gt;&lt;/a&gt;
I registered my domain in 1996, when Network Solutions was the only registrar and their domain registration forms were faxed in. Back then email and web service providers were rather expensive for a small private domain, so I set up the appropriate services on an &lt;a href="http://www.openbsd.org/"&gt;OpenBSD&lt;/a&gt; system at my house. We had a cable modem from &lt;a href="http://www.cable-modem.net/features/athome_story.html"&gt;@Home&lt;/a&gt;, with a static IP address because DHCP was new and unproven and PPPoE did not exist. We bought DNS service from &lt;a href="http://io.com/"&gt;Illuminati Online&lt;/a&gt;, and pointed it at the @Home static IP address.&lt;/p&gt;

&lt;p&gt;In 2009 running servers on a system in ones home is far less the entertaining pursuit than it was in 1996. Botnets launch constant automated sweeps looking for vulnerable machines, and the deluge of spam is ever-increasing. So I started looking for alternatives, and fortunately the intervening years have radically changed the ISP market. Lots of hosting providers are available for a small fee. However the trickle of traffic to our current web site comes entirely from family, friends, and botnets, and I'd prefer to avoid paying for such a small installation. &lt;a href="http://sites.google.com/"&gt;Google Sites&lt;/a&gt; is free of charge, but does not allow subdirectories. We have a modest collection of pages built up over a decade, I'm not interested in redoing all of the links to flatten the hierarchy.&lt;/p&gt;

&lt;p&gt;
&lt;a href="http://appengine.google.com/"&gt;&lt;img style="padding:3px; border:0px;" src="http://2.bp.blogspot.com/_WibILqsOlLg/Sb-TUcEMX1I/AAAAAAAAAT0/3ExU2V0q2j4/s200/appengine.gif" width="145" height="111" align="right" border="0" alt="Google App Engine" title="I hope the AppEngine logo is licensed under Creative Commons..."&gt;&lt;/a&gt;
There is another free option now: &lt;a href="http://code.google.com/appengine/"&gt;Google App Engine&lt;/a&gt;. Intended to run Python applications, it does handle static files and allows subdirectories in its file hierarchy. I started with the most straightforward approach for serving entirely static files, relying on &lt;a href="http://blog.engelke.com/2008/07/30/google-appengine-for-web-hosting/"&gt;Charles Engelke's&lt;/a&gt; writeup. You &lt;a href="http://appengine.google.com/"&gt;register&lt;/a&gt; for Google App Engine, put your HTML files in a subdirectory (which I've named named &amp;quot;static&amp;quot;), and create an app.yaml like so:&lt;/p&gt;

&lt;br/&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
application: my-static-webpages
version: 1
runtime: python
api_version: 1

- url: (.*)/
  static_files: static\1/index.html
  upload: static/index.html

- url: /.*
  static_dir: static&lt;/pre&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;p&gt;
&lt;a href="http://www.python.org/"&gt;&lt;img style="padding:3px; border:0px;" src="http://2.bp.blogspot.com/_WibILqsOlLg/Sb-UG5cH2TI/AAAAAAAAAT8/MrvcGtn-KLQ/s200/pythonpowered.png" width="50" height="65" align="right" border="0" alt="Python" title="Woot! Python.org explicitly _supplies_ logos for third parties like me to use!"&gt;&lt;/a&gt;
This works, albeit with drawbacks, the most serious being the handling of inexact links. There are external links to our pages which lack the trailing slash, pointing to &amp;quot;http://www.example.com/foo&amp;quot; where foo is a directory. The existing Apache server would kindly send a redirect to &amp;quot;http://www.example.com/foo/&amp;quot; but the App Engine static handler returns a 404 error. I want to be forgiving, and not break existing links. Charles Engelke wrote a &lt;a href="http://blog.engelke.com/2008/07/31/appengine-rewrite-rules/"&gt;followup&lt;/a&gt; article describing how to perform basic HTTP redirects, so I decided to tackle something similar. Rather than use the static handler, we'll use Python. We configure app.yaml to run the Python handler:&lt;/p&gt;

&lt;br/&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 2em;"&gt;
application: my-static-webpages
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: forgivedirectories.py&lt;/pre&gt;

&lt;br/&gt;

&lt;p&gt;Though I bought my first Python book many years ago, I've done relatively little work in the language. This was quite a learning experience.&lt;/p&gt;

&lt;br/&gt;

&lt;table border="0"&gt;
&lt;tr&gt;
&lt;td valign="center"&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 2em;"&gt;
forgivedirectories.py:

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
import os

class ForgiveDirectories(webapp.RequestHandler):
  def get(self):    
    fullPath = 'static/' + self.request.path
    if not os.path.exists(fullPath):
      self.redirect('/404.html')
      return
&lt;/pre&gt;&lt;/td&gt;

&lt;td valign="center"&gt;&lt;i&gt;If it really is a bad link, we send them to an &lt;a href="http://www.geekhold.com/404.html"&gt;error page&lt;/a&gt;.&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td valign="center"&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 2em;"&gt;
    if os.path.isdir(fullPath):
      if fullPath[-1] != '/':
        self.redirect(self.request.path + '/')
        return
      else:
        fullPath += '/index.html'
&lt;/pre&gt;&lt;/td&gt;

&lt;td valign="center"&gt;&lt;i&gt;This is the &amp;quot;forgive&amp;quot; part of forgivedirectories.py. If the requested path is a directory but does not end in a slash, send them a redirect to the proper path.&lt;br/&gt;
Note that its important to send a redirect, and not just send the index.html page. Relative links like &amp;quot;../bar/index.html&amp;quot; will only work if the browser's path ends in a slash.&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td valign="center"&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 2em;"&gt;
    # if Google App Engine adds a
    # sendfile equivalent, we should
    # use it here.
    fh = open(fullPath, 'r')
    while 1:
      block = fh.read(16384)
      if not block:
        break
      self.response.out.write(block)
    fh.close
&lt;/pre&gt;&lt;/td&gt;

&lt;td valign="center"&gt;&lt;i&gt;If we make it here, we have a page to send. To limit the memory footprint we loop over every 16 KBytes; slurping the entire file into memory is neither necessary nor beneficial.&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;td valign="center"&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 2em;"&gt;
application = webapp.WSGIApplication(
                [('.*', ForgiveDirectories)],
                debug=True)

def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()
&lt;/pre&gt;&lt;/td&gt;
&lt;td valign="center"&gt;&lt;i&gt;Boilerplate code to initialize the web services gateway and call into our function. Its possible to match regular expressions to dispatch to multiple different classes, but here we match everything and send it to ForgiveDirectories.&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;  
&lt;/table&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;p&gt;This works, sortof. You can see the web pages now. One problem is that the Content-Type is not being set, so browsers have to assume a default. Amusingly enough most browsers default to text/html, so at least the pages render even if images are broken. Google Chrome defaults to text/plain, so it shows the HTML source of the pages. So we obviously need to set the Content-Type, which we will do by examining the file's extension. Being new to Python, it took a few tries to get something reasonable.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;


&lt;table cellspacing="2" style="border-collapse: separate; border-spacing: 1;"&gt;

&lt;tr&gt;
&lt;td style="border-width: 1px 0px 1px 1px; border-color: #999999; border-style: dashed;" valign="top"&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em;"&gt;
  def contentTypeFromExt(self, extension):
    if extension=='.html':
      return 'text/html'
    elif extension=='.jpg':
      return 'image/jpeg'
    elif extension=='.gif':
      return 'image/gif'
    elif extension=='.png':
      return 'image/png'
    elif extension=='.js':
      return 'application/x-javascript'
    elif extension=='.css':
      return 'text/css'
    else:
      return 'text/plain'
&lt;/pre&gt;&lt;/td&gt;
&lt;td style="border-width: 1px 1px 1px 0px; border-color: #999999; border-style: dashed;"&gt;&lt;i&gt;The first attempt. Its basically C code, translated into Python.&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td style="border-width: 1px 0px 1px 1px; border-color: #999999; border-style: dashed;" valign="top"&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em;"&gt;
  def contentTypeFromExt(self, ext):
    contenttype = {
      ext == '.html': 'text/html',
      ext == '.jpg' : 'image/jpeg',
      ext == '.gif' : 'image/gif',
      ext == '.png' : 'image/png',
      ext == '.js'  : 'application/x-javascript',
      ext == '.css' : 'text/css'}[1]
    return contenttype
&lt;/pre&gt;&lt;/td&gt;
&lt;td style="border-width: 1px 1px 1px 0px; border-color: #999999; border-style: dashed;"&gt;&lt;i&gt;The second attempt. Python does not have a switch statement, but some Google searching showed how to construct something that looks like one if you squint at it just right.&lt;br/&gt;
Unfortunately this version doesn't supply 'text/plain' as a default, and its just... weird. Trying to torture the code to vaguely resemble a switch statement is not very pleasing.&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td style="border-width: 1px 0px 1px 1px; border-color: #999999; border-style: dashed;" valign="top"&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em;"&gt;
  contentExtensions = {
      '.html'  : 'text/html',
      '.jpg'   : 'image/jpeg',
      '.gif'   : 'image/gif',
      '.png'   : 'image/png',
      '.js'    : 'application/x-javascript',
      '.css'   : 'text/css'}

  def contentTypeFromExt(self, ext):
    try:
      return self.contentExtensions[ext]
    except KeyError:
      return 'text/plain'
&lt;/pre&gt;&lt;/td&gt;
&lt;td style="border-width: 1px 1px 1px 0px; border-color: #999999; border-style: dashed;"&gt;&lt;i&gt;The third attempt, and the first real effort to do it in a Pythonic way. Python has hash tables as a fundamental type in the language, so let's use them! If the key is not found in the dictionary an exception will be thrown... so I guess we're supposed to catch it? I dunno. Obviously, not finding the key in the dictionary is expected to be an unusual occurrence. There must be a better way to do it.&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;


&lt;tr&gt;
&lt;td style="border-width: 1px 0px 1px 1px; border-color: #999999; border-style: dashed;" valign="top"&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em;"&gt;
  contentExtensions = {
      '.html'  : 'text/html',
      '.jpg'   : 'image/jpeg',
      '.gif'   : 'image/gif',
      '.png'   : 'image/png',
      '.js'    : 'application/x-javascript',
      '.css'   : 'text/css'}

  def contentTypeFromExt(self, ext):
    return self.contentExtensions.get(ext, \
                               'text/plain')
&lt;/pre&gt;&lt;/td&gt;
&lt;td style="border-width: 1px 1px 1px 0px; border-color: #999999; border-style: dashed;"&gt;&lt;i&gt;The current version, which I'm pretty happy with. By calling the get method directly we can supply a default value to be returned, instead of having it throw an exception.&lt;/i&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;p&gt;Now we need to actually set the Content-Type header. I'm using os.path.splitext() to pull out the file extension, which is probably wrong: on Windows I think that method expects backslashes, where a URL will always be forward slashes. It is fine when used with the App Engine (which is not hosted on Windows), so I'll rely on it until I can figure out a more appropriate method to use.&lt;/p&gt;

&lt;br/&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em;"&gt;
  def contentTypeFromPath(self, path):
    (basename, extension) = os.path.splitext(path)
    return self.contentTypeFromExt(extension)
  
  # The following goes in the get() method
  # immediately before we open the fullPath file:
  self.response.headers['Content-Type'] = self.contentTypeFromPath(fullPath);
&lt;/pre&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;span style="font-weight: bold; font-size:115%;"&gt;Left To Be Implemented...&lt;/span&gt;
&lt;p&gt;The App Engine is a lot of fun. Its a way to experiment with web services without expense for hosting. The forgivedirectories.py script is now in service,handling our web site. There are a few things left to do, which I hope to cover in future updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After reorganizing the web site some time ago, the old Apache server was configured to send 302 redirects for the old hierarchy. The new AppEngine handler should too, just in case there are any links remaining to the old URLs.&lt;/li&gt;
&lt;li&gt;For efficiency we should allow the browser to cache the pages, by implementing Last-Modified-Since support. The &lt;a href="http://www.ipsojobs.com/blog/2008/06/17/how-to-create-a-simple-but-powerful-cdn-with-google-app-engine-gae/"&gt;ipsojobs blog&lt;/a&gt; has a writeup about this which I intend to leverage&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-6379829991347243848?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=2D0ODWvi3J0:Z-8eH12AbKM:Pt5fc6444Es"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=2D0ODWvi3J0:Z-8eH12AbKM:Pt5fc6444Es" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=2D0ODWvi3J0:Z-8eH12AbKM:bV-q3IutASs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=2D0ODWvi3J0:Z-8eH12AbKM:bV-q3IutASs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=2D0ODWvi3J0:Z-8eH12AbKM:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=2D0ODWvi3J0:Z-8eH12AbKM:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=2D0ODWvi3J0:Z-8eH12AbKM:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=2D0ODWvi3J0:Z-8eH12AbKM:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/2D0ODWvi3J0" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=6379829991347243848" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/6379829991347243848?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/6379829991347243848?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/03/exploring-google-app-engine.html" title="Exploring Google App Engine" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_WibILqsOlLg/ScDSKzDbUzI/AAAAAAAAAUE/R5v2wbYOCfU/s72-c/openbsd.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;D0AFR3o4eSp7ImA9WxVVFk0.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-6205758624835084403</id><published>2009-03-09T05:54:00.000-07:00</published><updated>2009-03-09T06:35:16.431-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-03-09T06:35:16.431-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Rant" /><title>When the Universe Conspires to Prevent Backups</title><content type="html">&lt;p&gt;You know you're having a bad day when your backup software requires 83 Terabytes of free space in order to proceed.&lt;/p&gt;

&lt;img style="padding:3px; border:0px;" src="http://4.bp.blogspot.com/_WibILqsOlLg/SareOI9_p3I/AAAAAAAAATk/09e00-OQxqM/s800/timemachine.png" width="420" height="218" border="0" alt="Time Machine" title="This is real, not Photoshop"&gt;

&lt;br clear=right&gt;&lt;br clear=left&gt;

&lt;p&gt;As it turns out, the filesystem on this disk needed repair. Disk Utility found an 82.95 terabyte file which appeared to be incorrect.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-6205758624835084403?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=NGZirXQg-ws:6pa7RZqeg6A:Pt5fc6444Es"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=NGZirXQg-ws:6pa7RZqeg6A:Pt5fc6444Es" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=NGZirXQg-ws:6pa7RZqeg6A:bV-q3IutASs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=NGZirXQg-ws:6pa7RZqeg6A:bV-q3IutASs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=NGZirXQg-ws:6pa7RZqeg6A:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=NGZirXQg-ws:6pa7RZqeg6A:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=NGZirXQg-ws:6pa7RZqeg6A:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=NGZirXQg-ws:6pa7RZqeg6A:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/NGZirXQg-ws" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=6205758624835084403" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/6205758624835084403?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/6205758624835084403?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/03/when-universe-conspires-to-prevent.html" title="When the Universe Conspires to Prevent Backups" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_WibILqsOlLg/SareOI9_p3I/AAAAAAAAATk/09e00-OQxqM/s72-c/timemachine.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;DE8CQH86fyp7ImA9WxVWFkg.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-868687161148772989</id><published>2009-02-26T06:01:00.000-08:00</published><updated>2009-02-26T06:01:01.117-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-26T06:01:01.117-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><title>Inadvisable Externing</title><content type="html">&lt;p&gt;The Principle of the Conservation of Software Quality postulates that for every &lt;i&gt;best&lt;/i&gt; practice there is an equal and opposite &lt;i&gt;worst&lt;/i&gt; practice  which will be easier to implement. We're here today to talk about one of those balancing factors: declaration of externs within the C code.&lt;/p&gt;

&lt;p&gt;Assume for a moment that foo() is defined somewhere within the codebase and, for reasons unclear, there is no header file declaring foo(). Perhaps foo() was not deemed important enough, or had been static when originally written and only later opened up to the rest of the module. However we got here if one has adopted the best practice of using -Wall -Werror when compiling, then calling foo() will require a declaration or result in a compilation error. One is then faced with several choices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add foo() to an existing header file&lt;/li&gt;
&lt;li&gt;Make a new header file for foo() and any similar routines&lt;/li&gt;
&lt;li&gt;Declare foo() as an extern in the C file where it will be called.&lt;/li&gt;
&lt;/ol&gt;

&lt;a href="http://en.wikipedia.org/wiki/The_Scream"&gt;&lt;img style="padding:3px; border:0px;" src="http://3.bp.blogspot.com/_WibILqsOlLg/SaFtJDQLNbI/AAAAAAAAATc/RWqeRHkQiGE/s200/scream.jpg" width="164" height="159" align="right" border="0" alt="The Scream" title="It was either this or the Code Complete/Coding Horror icon, but Jeff Atwood would possibly take offense."&gt;&lt;/a&gt;

&lt;p&gt;Perhaps the Gentle Reader is unfamiliar with that last option. If so, I salute you: your unfamiliarity with it speaks well of you. To summarize: "extern" means the function is not provided within this compilation unit and will be supplied later when linking. It is normally used in header files, but is also valid within C code:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
do {
    extern int foo(int a, int b);
    c = foo(a, b);
} while (c != 1);
&lt;/pre&gt;

&lt;p&gt;The benefit of a function declaration in a header file is that it can be included in multiple places, in the file which implements the function and any file which wants to use the function. If the implementation of the function changes such that it no longer matches the declaration, an error will result. If the header changes such that the callers no longer match, an error will also result. Declaring an extern &lt;b&gt;within a C file&lt;/b&gt; accomplishes none of these things, because the compiler does not get to see both the declaration and the implementation of foo at the same time.&lt;/p&gt;

&lt;p&gt;Lets examine what will happen if a third argument is added to foo() but the caller blissfully relies on an existing extern declaration with two arguments. We'll use one of my &lt;a href="http://codingrelic.geekhold.com/2008/03/secret-life-of-volatile.html"&gt;favorite&lt;/a&gt; &lt;a href="http://codingrelic.geekhold.com/2008/07/gdb-lies-to-you.html"&gt;techniques&lt;/a&gt;, disassembling a MIPS binary to see how it works. We'll use a ridiculously simple example for clarity:&lt;/p&gt;

&lt;p&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
int foo(int a, int b, int c)
{
    return (a + b + c);
}
&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&lt;table style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; margin-left: 5em; line-height: 1.1em;" border="0"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td colspan="2"&gt;&amp;lt;foo&amp;gt;:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;addu v0,a0,a1&lt;/td&gt; &lt;td&gt;&amp;nbsp;&lt;i&gt;tmp = a + b&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;addu v0,v0,a2&lt;/td&gt; &lt;td&gt;&amp;nbsp;&lt;i&gt;tmp = tmp + c&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;jr   ra&lt;/td&gt;       &lt;td&gt;&amp;nbsp;&lt;i&gt;return to caller&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt;

&lt;p&gt;foo() expects arguments in registers a0, a1, and a2, and sums them together. Now lets look at the code generated by an extern declaration with only two arguments:&lt;/p&gt;

&lt;p&gt;&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
extern int foo(int a, int b); /* Worst Practice */
int main()
{
    return foo(1, 2);
}&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;&lt;table style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px;  margin-left: 5em; line-height: 1.1em;" border="0"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td colspan="2"&gt;&amp;lt;main&amp;gt;:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;li   a0,1&lt;/td&gt;    &lt;td&gt;&amp;nbsp;&lt;i&gt;load 1 into the first arg register&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;li   a1,2&lt;/td&gt;    &lt;td&gt;&amp;nbsp;&lt;i&gt;load 2 into the second arg register&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;lw   t9,0(gp)&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;i&gt;load address of foo()&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;jalr t9&lt;/td&gt;      &lt;td&gt;&amp;nbsp;&lt;i&gt;jump to foo()&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt;

&lt;p&gt;Not surprisingly, only registers a0 and a1 are loaded with values. The important point to note is that register a2 is not touched &lt;i&gt;at all&lt;/i&gt;. One might have intuitively assumed that the third argument would be zero, but this is &lt;b&gt;not the case&lt;/b&gt;. The third argument will be whatever garbage happens to be in register a2.&lt;/p&gt;

&lt;p&gt;The third argument could be most anything, and might vary depending on the call chain or the data being processed. We end up with a sporadic and difficult to diagnose bug, hidden in a way that the compiler cannot help, and all because we didn't want to bother with a header file. This happened at a previous employer of mine, where a particular process would reliably crash at just one customer site because their particular environment ended up with a non-NULL value in the argument register. It was, of course, a crucial customer.&lt;/p&gt;

&lt;p&gt;The moral of this article is that header files are your friend.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:105%;"&gt;In Other News&lt;/span&gt;
&lt;p&gt;Our family became larger in January, and the resulting lack of free time means postings will be less frequent. Previously I'd aimed for two postings per month, but now I think I'll be lucky to manage just one. I wrote several articles in advance, thinking that would be sufficient, but alas it was not enough. The Gentle Reader might have noticed one of those prepared postings appear twice. I accidentally marked the &lt;a href="http://codingrelic.geekhold.com/2009/01/blogroll-january-2009.html"&gt;Blogroll&lt;/a&gt; article for 1/200&lt;u&gt;8&lt;/u&gt;, and blogger.com happily sent it out immediately with a publication date one year prior. It also went out with the corrected date. I seem unable to expunge the mistaken early posting, that article still shows up twice in Google Reader. Oh well.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-868687161148772989?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=BlDqiYDSqDw:QhTUld3xjjQ:Pt5fc6444Es"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=BlDqiYDSqDw:QhTUld3xjjQ:Pt5fc6444Es" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=BlDqiYDSqDw:QhTUld3xjjQ:bV-q3IutASs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=BlDqiYDSqDw:QhTUld3xjjQ:bV-q3IutASs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=BlDqiYDSqDw:QhTUld3xjjQ:F7zBnMyn0Lo"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=BlDqiYDSqDw:QhTUld3xjjQ:F7zBnMyn0Lo" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/CodingRelic?a=BlDqiYDSqDw:QhTUld3xjjQ:4cEx4HpKnUU"&gt;&lt;img src="http://feeds.feedburner.com/~ff/CodingRelic?i=BlDqiYDSqDw:QhTUld3xjjQ:4cEx4HpKnUU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/BlDqiYDSqDw" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=868687161148772989" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/868687161148772989?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/868687161148772989?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/02/inadvisable-externing.html" title="Inadvisable Externing" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://3.bp.blogspot.com/_WibILqsOlLg/SaFtJDQLNbI/AAAAAAAAATc/RWqeRHkQiGE/s72-c/scream.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;DEMCQH49cSp7ImA9WxVRFUk.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-922328707192389882</id><published>2009-01-21T06:01:00.000-08:00</published><updated>2009-01-21T06:01:01.069-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-01-21T06:01:01.069-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="blogroll" /><title>Blogroll, January 2009</title><content type="html">&lt;p&gt;Programming languages are a voyeuristic pursuit for me, as my daily work consists entirely of C and assembly. Nonetheless it is quite clear that the future belongs to high level languages. Garbage collection avoids a huge class of bugs which have vexed developers for decades. Exceptions likewise make the handling of errors simpler and more robust, and having associative arrays as a built-in capability makes them the obvious choice for many needs.&lt;/p&gt;

&lt;p&gt;Criticism is leveled at high level languages about their performance, but languages based on a virtual machine will eventually achieve higher performance than C++. Profile-driven optimization is a well known technique to improve software performance, by optimizing for code paths which are actually used. Achieving this with C/C++ compilers is possible by running a first pass binary and feeding back profile data to a second pass. This is such a &lt;i&gt;giant pain in the ass&lt;/i&gt; that it is practically never done. Virtual machines, by their very nature, constantly collect profile data while running the interpreter. The Just-In-Time compilation in a VM can take advantage of this information, resulting in &lt;a href="http://www.bailopan.net/blog/?p=84"&gt;straight line, tightly scheduled code&lt;/a&gt; with no branches. It is a beautiful thing... or rather, it will be once it all works.&lt;/p&gt;

&lt;p&gt;So without further ado, lets talk about blogs that talk about programming languages.&lt;/p&gt;

&lt;br/&gt;&lt;span style="font-weight: bold; font-size: 115%;"&gt;The Blogroll: January 2009&lt;/span&gt;&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;div style="margin-left: 2em; padding: 5px; border: 1px coral solid;"&gt;
1) &lt;b&gt;&lt;a href="http://armstrongonsoftware.blogspot.com/"&gt;armstrong on software&lt;/a&gt;, by Joe Armstrong&lt;/b&gt;
&lt;p&gt;Joe writes mostly about &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt;, a language which has been around since the &lt;a href="http://delivery.acm.org/10.1145/1240000/1238850/supp/Erlang.pdf?key1=1238850&amp;key2=3356740321&amp;coll=GUIDE&amp;dl=GUIDE&amp;CFID=9415887&amp;CFTOKEN=83409977"&gt;mid 1980s&lt;/a&gt;, though part of that time was hidden within the bowels of the Ericsson corporation. Erlang is very different from other programming languages. For example, there really are not variables in the normal sense: you can give a name to a value, like 'x', but you cannot change the value of x later. The language is single assignment only. Similarly there are no loops (you can't have a loop if you cannot have a loop variable), there are only list comprehensions. There is really no mutable state in an Erlang program, once created a data element is guaranteed not to change until it is garbage collected.&lt;/p&gt;

&lt;p&gt;Why does Erlang make these choices? Erlang's primary design features are all around making message passing very cheap and efficient. Complete lack of mutable state is one example: there is no need to copy the current values of variables into a message, as there is no chance the values will change. Data can be freely passed by reference. The messaging infrastructure drives Erlang's reliability features, where Erlang nodes monitor other Erlang nodes and take action if they stop responding. The messaging capabilities also allow excellent scalability across multiple CPUs, which has driven Erlang's recent resurgence (with high-visibility deployments in &lt;a href="http://www.facebook.com/notes.php?id=9445547199"&gt;Facebook chat&lt;/a&gt; and &lt;a href="http://www.satine.org/archives/2007/12/13/amazon-simpledb/"&gt;Amazon SimpleDB&lt;/a&gt;.&lt;/div&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;div style="margin-left: 2em; padding: 5px; border: 1px coral solid;"&gt;
2) &lt;b&gt;&lt;a href="http://blog.headius.com/"&gt;Headius&lt;/a&gt;, by Charles Nutter&lt;/b&gt;
&lt;p&gt;Ruby is an interesting language, driven to popularity by the excellent &lt;a href="http://rubyonrails.org/"&gt;Ruby on Rails&lt;/a&gt; web application framework. Ruby's primary implementation is &lt;a href="http://en.wikipedia.org/wiki/Ruby_MRI"&gt;mri&lt;/a&gt;, which only recently implemented a bytecoded virtual machine. Earlier mri versions were entirely interpreted, and this led to the development a number of alternate implementations on various VMs. &lt;a href="http://jruby.codehaus.org/"&gt;JRuby&lt;/a&gt; is the most interesting to me personally: it implements the Ruby language atop the Java Virtual Machine, a very mature and well-performing technology. Charles Nutter is driving the JRuby development effort, and joined the staff at Sun Microsystems to work on it full time.&lt;/p&gt;
&lt;/div&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;div style="margin-left: 2em; padding: 5px; border: 1px coral solid;"&gt;
3) &lt;b&gt;&lt;a href="http://www.codecommit.com/blog/"&gt;Code Commit&lt;/a&gt;, by Daniel Spiewak&lt;/b&gt;
&lt;p&gt;Erlang and Ruby are both dynamically typed languages: functions accept objects as arguments, and dynamically determine their type at runtime. This is certainly powerful, in that a single routine can handle a wide variety of inputs, but it also means that type errors will only be detected at runtime. Unit testing becomes essential in this environment... and I have trouble accepting the premise that I'm supposed to love doing manually that which the toolchain used to be able to do.&lt;/p&gt;

&lt;p&gt; Daniel Spiewak writes mainly about &lt;a href="http://www.scala-lang.org/"&gt;Scala&lt;/a&gt;. C is a weakly typed language, where it is trivial to cast one type to another. Scala is an example of a more strongly typed language, making the toolchain able to make more guarantees about what the valid inputs are. Scala is also interesting in that it is another language implemented atop the Java VM.&lt;/p&gt;
&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-922328707192389882?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=TbkxdPGY"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=TbkxdPGY" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=Mf3TFzgE"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=Mf3TFzgE" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/tcwTGCQV4iY" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=922328707192389882" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/922328707192389882?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/922328707192389882?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/01/blogroll-january-2009.html" title="Blogroll, January 2009" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;CUACR304eip7ImA9WxJTGUo.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-1594399259771240078</id><published>2009-01-07T06:01:00.000-08:00</published><updated>2009-04-28T19:56:06.332-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-28T19:56:06.332-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><title>Variable Scoping with gcc</title><content type="html">&lt;p&gt;Has this ever happened to you? You allocate some sort of resource which needs to be released before returning from the function. You can put the release at the end of the routine for the normal case, but there are a number of error checks which can cause it to return early. You consider calling the reclaim routine inside each error handler, but you're concerned that someone maintaining the code in the future will forget to do so. Instead you put all of the cleanup code at the end of the function, and have each error check use a goto.&lt;/p&gt;

&lt;p&gt;Then of course someone argues very strenuously that goto is evil incarnate and must never be used, one thing leads to another, and then you have to find somewhere to hide the body ... wait, nevermind that last bit.&lt;/p&gt;

&lt;p&gt;Consider this instead:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
int foo()
{
    int fd LOCAL_SCOPE_FD = open("/path/to/file");

    if (error1()) {
        return -1;
    }

    return 0;
}&lt;/pre&gt;

&lt;p&gt;Though it might appear that I'm suggesting the file descriptor be leaked in order to simplify the code, that is not the case. The magic happens in LOCAL_SCOPE_FD:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
#define LOCAL_SCOPE_FD __attribute__((cleanup(local_fd_close)))

void local_fd_close(int *fd)
{
    if (*fd &gt;= 0) close(*fd);
}
&lt;/pre&gt;

&lt;p&gt;__attribute__(cleanup) is a gcc extension. When an automatic variable goes out of scope, the function indicated by the cleanup attribute will be called. If the scope is exited unusually, such as via longjmp() or by calling exit(), the cleanup function does not get called, but normal return statements or falling off the end of the block do work.&lt;/p&gt;

&lt;p&gt;It also works within inner blocks. For example:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
int foo()
{
    if (do_something) {
        int fd LOCAL_SCOPE_FD = open("/path/to/file");
    }
    /* local_fd_close will be called here. */

    ... more code ...
}&lt;/pre&gt;

&lt;p&gt;Cleanup can only be applied to automatic variables, i.e. variables on the stack declared within a function. It cannot be used with global or static variables. The cleanup attribute can be used with any variable type, not just integers. The cleanup function receives a pointer to the automatic variable being cleaned up.&lt;/p&gt;

&lt;p&gt;I assume that if the Gentle Reader is reading this article, there are good reasons to use C in your problem space. Needless to say, if you want automatic resource reclamation a language other than C would provide more capabilities. Garbage collection and object finalizers are powerful constructs, given a problem space where they are appropriate.&lt;/p&gt;



&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;Acknowledgements&lt;/span&gt;
&lt;p&gt;Many thanks to Matt Peters for pointing out the __attribute__(cleanup) capability to me.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.airs.com/blog/archives/257"&gt;Ian Lance Taylor&lt;/a&gt; recently wrote on a similar topic, about support for destructors and exceptions in C.&lt;/p&gt;



&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;Updates&lt;/span&gt;
&lt;p&gt;This article was picked up on &lt;a href="http://www.reddit.com/r/programming/comments/8ftm2/error_handling_with_gcc_attributes/"&gt;reddit&lt;/a&gt;, with a few comments. One pertinent comment from &lt;a href="http://www.reddit.com/user/erikd/"&gt;erikd&lt;/a&gt;:&lt;/p&gt;
&lt;p style="margin-left: 3em; font-style: italic"&gt;His local-fd-close() function has a bug, it needs to check the return value of close(), because close can return an error of EINTR which means the prcoess received an interrupt and should retry the close operation.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-1594399259771240078?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=tiWqqNwD"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=tiWqqNwD" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=R4By0d4P"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=R4By0d4P" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/ZYTtj1wU_Tw" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=1594399259771240078" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/1594399259771240078?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/1594399259771240078?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2009/01/variable-scoping-with-gcc.html" title="Variable Scoping with gcc" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;Ak4HQncyeip7ImA9WxJSF0o.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-3874240343134215631</id><published>2008-12-19T08:42:00.000-08:00</published><updated>2009-05-08T03:35:33.992-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-08T03:35:33.992-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><title>printf-acular</title><content type="html">&lt;p&gt;Ahh, printf. What would we do without printf, sprintf, etc? It makes it easy to format integers, floats, strings, &lt;a href="http://en.wikipedia.org/wiki/MAC_address"&gt;MAC addresses&lt;/a&gt;... Oh wait, there isn't a format specifier for MAC addresses is there? Well, lets rectify this obvious omission.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.gnu.org/software/libc/"&gt;glibc&lt;/a&gt; (and &lt;a href="http://www.uclibc.org/"&gt;uClibc&lt;/a&gt;) implement a facility to add new format codes to the printf family of functions. It is even possible to override the builtin implementations of the C standard formats, if the Gentle Reader has an irrational hatred for some aspect of the output. The &lt;a href="http://www.gnu.org/software/libtool/manual/libc/Customizing-Printf.html"&gt;GNU documentation&lt;/a&gt; for how to do this is adequate, but a bit obscure. So lets dive into an example by adding a %M specifier to handle MAC addresses:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
uint8_t mac[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 };

if (&lt;b&gt;register_printf_function&lt;/b&gt; ('M',
        &lt;b&gt;printf_output_M&lt;/b&gt;, &lt;b&gt;printf_arginfo_M&lt;/b&gt;)) {
    ... error handling ...
}

printf("%M\n", mac);

&lt;b&gt;Output:&lt;/b&gt;
00:11:22:33:44:55
&lt;/pre&gt;

&lt;p&gt;Huzzah! Now we'll dig into the implementation.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;printf_arginfo_M&lt;/span&gt;
&lt;p&gt;The first routine to be implemented tells glibc the arguments required by the new specifier:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;printf.h&amp;gt;

static int
&lt;b&gt;printf_arginfo_M&lt;/b&gt;(const struct printf_info *info,
                 size_t      n,
                 int        *argtypes)
{
    /* "%M" always takes one argument, a pointer to uint8_t[6]. */
    if (n &gt; 0) {
        argtypes[0] = PA_POINTER;
    }

    return 1;
} /* printf_arginfo_M */
&lt;/pre&gt;

&lt;p&gt;The fields of the printf_info structure are described in the &lt;a href="http://www.gnu.org/software/libtool/manual/libc/Conversion-Specifier-Options.html"&gt;GNU manual&lt;/a&gt;. For the arginfo() implementation the only important field is spec, the character code of the specifier for which the information is sought. This allows a single arginfo() routine to implement support for multiple new specifiers.&lt;p&gt;

&lt;p&gt;glibc also passes in an array of argtypes[] with n members, which are to be filled in with the types of the arguments expected. The available types are &lt;a href="http://www.gnu.org/software/libtool/manual/libc/Parsing-a-Template-String.html"&gt;documented&lt;/a&gt; in the GNU manual. The arginfo() routine must populate the argtypes[] with the expected arguments, after checking to ensure there is room. It should return the number of arguments it expects. If you define a format specifier which takes a large number of arguments, there might not be sufficient room in argtypes[] to store them all. If the arginfo() routine returns a larger value than n, glibc will call it again with a larger argtypes[] array.&lt;/p&gt;

&lt;p&gt;For %M there is only one argument, the MAC address to be formatted. The ability to write a format specifier with more than one option is potentially useful, but also confusing as none of the standard format options do so.&lt;/p&gt;



&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;printf_output_M&lt;/span&gt;
&lt;p&gt;Next we need to implement the output routine, to generate the formatted output for the new specifier. Output always goes to a FILE *, no matter whether called from printf(), sprintf(), vfprintf(), etc.&lt;/p&gt;

&lt;p&gt;One is free to use the stdio.h routines like fprintf(), so long as you do not use your new specifier in the format string. If you do, fprintf will call back into your output routine, which will call fprintf() again, which will call your output routine again... and hijinks ensue.&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
static int
printf_output_M(FILE *stream,
                const struct printf_info *info,
                const void *const *args)
{
    const unsigned char *mac;
    int len;

    mac = *(unsigned char **)(args[0]);

    &lt;b&gt;len = fprintf(stream, "%02x:%02x:%02x:%02x:%02x:%02x",
                  mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);&lt;/b&gt;

    return len;
} /* printf_output_M */
&lt;/pre&gt;

&lt;p&gt;We only take one argument for %M, so we only look at args[0]. There is no need to check for NULL: glibc validates the format before calling your output routine, you'll never get a NULL pointer. We call fprintf() to do most of the work, and return the number of bytes output.&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
printf("%30M\n", mac);

00:11:22:33:44:55
&lt;/pre&gt;

&lt;p&gt;Voila! I hope you've enjoyed this article... oh wait, that wasn't very good was it? The &amp;quot;30&amp;quot; prepended to the M specifier was meant to right-justify the output within a 30 character region. The output did not reflect this. Oops.&lt;/p&gt;



&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;printf_output_M, with modifiers&lt;/span&gt;
&lt;p&gt;glibc parses out the modifiers prepended to the specifier, and passes them to the output routine via the printf_info structure. Many of the standard modifiers don't make sense for MAC addresses. For example, I have no idea what &amp;quot;%llM&amp;quot; would mean. A few modifiers do make sense, such as a field width and the '-' argument to left-justify.&lt;/p&gt;

&lt;p&gt;We certainly &lt;i&gt;could&lt;/i&gt; write a bunch of code to insert the proper number of spaces before or after the MAC address, but we'll use a slightly different approach. We call fprintf() to generate the output anyway, so we'll generate a format string for fprintf() to make it handle the width and justification.&lt;/p&gt;



&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
static int
printf_output_M(FILE *stream,
                const struct printf_info *info,
                const void *const *args)
{
    const unsigned char *mac;
    char macstr[18]; /* 6 hex bytes, with colon seperators and trailing NUL */
    char fmtstr[32];
    int len;

    mac = *(unsigned char **)(args[0]);
    snprintf(macstr, sizeof(macstr), "%02x:%02x:%02x:%02x:%02x:%02x",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);

    /* We handle some conversion specifier options as appropriate:
     * '-' and field width */
    &lt;b&gt;if (info-&gt;width &gt; 0) {
        snprintf(fmtstr, sizeof(fmtstr), "%%%s%ds",
                (info-&gt;left ? "-" : ""), info-&gt;width);
    } else {
        snprintf(fmtstr, sizeof(fmtstr), "%%s");
    }&lt;/b&gt;

    len = fprintf(stream, fmtstr, macstr);

    return len;
} /* printf_output_M */
&lt;/pre&gt;

&lt;p&gt;We generate the formatted MAC address as a string buffer on the stack. The bolded code examines the modifiers in printf_info, and generates a format string in fmtstr[]. For example if the arguments to printf were &amp;quot;"%-30M&amp;quot;, the fmtstr[] will be &amp;quot;%-30s&amp;quot;&lt;/p&gt;

&lt;p&gt;Now the output is much more pleasing:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
printf("%30M\n", mac);

               00:11:22:33:44:55
&lt;/pre&gt;



&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;printf_output_M, Now with More Cisco&lt;/span&gt;
&lt;p&gt;Which looks more natural to the Gentle Reader?&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
00:11:22:33:44:55

0011.2233.4455
&lt;/pre&gt;

&lt;p&gt;If you chose the first one, you likely come from a desktop background with Operating systems such as Windows, MacOS, Linux, Solaris, or the various other Unixes which all use a colon-seperated MAC address.&lt;/p&gt;

&lt;p&gt;If you chose the second one, you likely come from a Cisco networking background. Cisco uses a MAC address format which takes up three fewer columns on the screen, important if one's user interface is a text CLI.&lt;/p&gt;

&lt;p&gt;Could we accommodate this variation? Certainly it could be done using another conversion specifier, perhaps using &amp;quot;%C&amp;quot; for a Cisco-style MAC address, but this seems inelegant. Coming to our rescue is a little used format modifier in printf: the 'alternate representation' hash mark. This modifier implements optional formatting, for example when used with the hex specifier as &amp;quot;%#x&amp;quot; it will prepend 0x to the output.&lt;/p&gt;

&lt;p&gt;So we'll implement the Cisco MAC address format using &amp;quot;%#M&amp;quot; in printf_output_M, like so:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
    &lt;b&gt;if (info-&gt;alt) {
        /* Cisco style formatting */
        snprintf(macstr, sizeof(macstr), "%02x%02x.%02x%02x.%02x%02x",
                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    } else {
        /* Unix/Linux/Windows/MacOS style formatting */
        snprintf(macstr, sizeof(macstr), "%02x:%02x:%02x:%02x:%02x:%02x",
                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    }&lt;/b&gt;
&lt;/pre&gt;

&lt;p&gt;If invoked with &amp;quot;%#M&amp;quot; we'll take the first code path and produce a Cisco-style MAC address. Otherwise, we generate a colon separated MAC address.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;Whither -Wall?&lt;/span&gt;
&lt;p&gt;With the -Wformat (or -Wall) argument, gcc will sanity check the arguments passed to the printf family of routines. It can ensure that the number of arguments matches the number of specifiers in the format string, for example. Unfortunately, it does not know about custom additions:&lt;/p&gt;

&lt;pre style="font-family: Courier New, Courier, fixed; font-size: small; line-height: 1.1em; margin-left: 5em;"&gt;
gcc -Wall testM.c
testM.c:57: warning: unknown conversion type character 'M' in format
testM.c:57: warning: too many arguments for format
&lt;/pre&gt;

&lt;p&gt;I know of no way to teach gcc about the new specifiers, unfortunately. If the Gentle Reader knows of a way, please describe it in the comments.&lt;/p&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;GPL&lt;/span&gt;
&lt;p&gt;I am not a lawyer, I do not play one on TV, and I do not want to play one on TV. So my opinions about GPL issues are really just &lt;a href="http://codingrelic.geekhold.com/2008/08/opensourcemycompanycom.html"&gt;quaint little notions&lt;/a&gt; of How The World Should Work.&lt;/p&gt;

&lt;p&gt;An argument has been advanced in &lt;a href="http://lists.debian.org/debian-devel/1997/06/msg00088.html"&gt;other contexts&lt;/a&gt; that if the only implementations of a particular API are licensed under the GPL, then any caller of that API should be considered a derivative work and be subjected to the GPL. As the register_printf_function() API only exists in glibc and uClibc, both of which are licensed under the LGPL, it could be argued that code calling register_printf_function() is therefore encumbered by the LGPL. Opinions may differ, but this is one possible interpretation.&lt;/p&gt;

&lt;p&gt;Personally, I choose to treat custom printf formatters as LGPLd code.&lt;/p&gt;


&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;Closing Thoughts&lt;/span&gt;
&lt;p&gt;It is of course possible to implement routines to format a MAC address as a string, using &amp;quot;%s&amp;quot; in stdio.h routines for output. However I think it is more elegant to teach stdio.h how to deal with common output requirements for your application.&lt;/p&gt;

&lt;p&gt;The C standard may claim additional lower case specifiers for standard formats in the future. It is best to rely on upper-case codes for custom formats.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-3874240343134215631?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=mmZiGNNL"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=mmZiGNNL" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=zaNZaRLp"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=zaNZaRLp" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/bkn-cD-J-U0" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=3874240343134215631" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/3874240343134215631?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/3874240343134215631?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/12/printf-acular.html" title="printf-acular" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;A0QFRn84cSp7ImA9WxRbEUs.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-349031141659955914</id><published>2008-11-30T22:25:00.001-08:00</published><updated>2008-12-01T14:08:37.139-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-12-01T14:08:37.139-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="blogroll" /><title>Blogroll, November 2008</title><content type="html">&lt;p&gt;The half-life of engineering knowledge is depressingly short. The old school way to stay current is via books covering technical topics, such as those published by &lt;a href="http://oreilly.com/"&gt;O'Reilly and Associates&lt;/a&gt; and more recently by &lt;a href="http://www.pragprog.com/"&gt;The Pragmatic Programmers&lt;/a&gt;. Though the quality of the writing in published books is generally quite good owing to the involvement of an editor, the other drawbacks are legion. Books have very long lead times before publication, leading to dated material. The enormous burdens placed upon the author mean that some interesting topic areas will never have a book developed. Technical books carry a relatively high price tag, and will continue to do so as long as the volumes stay low (a run of ten thousand is considered a successful technical title).&lt;/p&gt;

&lt;p&gt;
&lt;img style="padding:6px; border:0px;" src="http://1.bp.blogspot.com/_WibILqsOlLg/STODmP-_0kI/AAAAAAAAAS0/fg5t3n3hEZM/s320/rss.png" border="0" width="28" height="28" alt="RSS" align="right" /&gt;
I recent years I have sought my continuing education online, as presumably does the Gentle Reader as well. Sometimes this is in the form of publication-ready papers, such as those &lt;a href="http://people.redhat.com/drepper/"&gt;Ulrich Drepper&lt;/a&gt; is fond of writing (for example, &lt;a href="http://people.redhat.com/drepper/cpumemory.pdf"&gt;&amp;quot;What Every Programmer Should Know About Memory&amp;quot;&lt;/a&gt; is excellent). I also check the links on &lt;a href="http://www.reddit.com"&gt;Reddit&lt;/a&gt; regularly; I have mixed feelings about Reddit, but it does draw links to quite a bit of interesting material. If I find a particularly good author I'll add their RSS feed to &lt;a href="http://www.google.com/reader/"&gt;Google Reader&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One blog I follow regularly is that of &lt;a href="http://www.louisgray.com/"&gt;Louis Gray&lt;/a&gt;, a prolific blogger about social media and web 2.0. 
&lt;a href="http://www.louisgray.com/live/2008/03/5-blog-candidates-for-tomorrows.html"&gt;Each&lt;/a&gt;
&lt;a href="http://www.louisgray.com/live/2008/04/five-more-blogs-you-should-be-reading.html"&gt;month&lt;/a&gt;
&lt;a href="http://www.louisgray.com/live/2008/05/five-social-media-bloggers-to-watch.html"&gt;he&lt;/a&gt;
&lt;a href="http://www.louisgray.com/live/2008/06/five-blogs-for-june-on-your-summer.html"&gt;highlights&lt;/a&gt; 
&lt;a href="http://www.louisgray.com/live/2008/07/julys-jewels-five-obscure-blogs-that.html"&gt;other&lt;/a&gt;
&lt;a href="http://www.louisgray.com/live/2008/08/five-cool-bloggers-for-hot-month-of.html"&gt;blogs&lt;/a&gt;
&lt;a href="http://louisgray.com/live/2008/09/five-blogs-to-take-back-to-school-in.html"&gt;in&lt;/a&gt;
&lt;a href="http://www.louisgray.com/live/2008/10/treat-or-treat-five-obscure-blogs-for.html"&gt;that&lt;/a&gt;
&lt;a href="http://www.louisgray.com/live/2008/11/five-blogs-to-be-checking-out-this.html"&gt;space.&lt;/a&gt;
I really like that idea: each link makes the entire genre just a little bit more richly connected. I'm going to try to regularly suggest other blogs which I follow, though I will probably not make these lists as frequently as Louis Gray does.&lt;/p&gt;

&lt;p&gt;Some starting assertions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I'm going to focus on blogs relevant to software development. For example I also read parenting blogs, and though the Gentle Reader might also be a parent you'll need to go elsewhere for that material.&lt;/li&gt;
&lt;li&gt;I'm not going to spend time on &lt;a href="http://www.codinghorror.com/blog/"&gt;Jeff Atwood&lt;/a&gt;, &lt;a href="http://steve-yegge.blogspot.com/"&gt;Steve Yegge&lt;/a&gt;, or other high profile writers. I suspect the Gentle Reader has already decided whether to follow those authors or not.&lt;/li&gt;
&lt;li&gt;Though I try to focus on embedded system articles and many amongst this first set of links are related to low-level systems programming, future blogrolls will run further afield. You have been warned.&lt;/li&gt;
&lt;/ul&gt;


&lt;br/&gt;&lt;span style="font-weight: bold; font-size: 115%;"&gt;The Blogroll:&lt;/span&gt;&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;div style="margin-left: 2em; padding: 5px; border: 1px coral solid;"&gt;
1) &lt;b&gt;&lt;a href="http://www.yosefk.com/blog/"&gt;Proper Fixation&lt;/a&gt;, by Yossi Kreinin&lt;/b&gt;
&lt;p&gt;Yossi writes from the perspective of a senior individual contributor, a point of view I greatly appreciate. He's covered close-to-the-metal topics like &lt;a href="http://www.yosefk.com/blog/the-high-level-cpu-challenge.html"&gt;CPU architecture&lt;/a&gt; and &lt;a href="http://www.yosefk.com/blog/i-love-globals-or-google-core-dump.html"&gt;debugging&lt;/a&gt;, but also other topics such as &lt;a href="http://www.yosefk.com/blog/the-internal-free-market.html"&gt;organizational challenges&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;


&lt;br&gt;&lt;div style="margin-left: 2em; padding: 5px; border: 1px coral solid;"&gt;
2) &lt;b&gt;&lt;a href="http://crankypm.com/"&gt;The Cranky Product Manager&lt;/a&gt;, by the Cranky PM&lt;/b&gt;
&lt;p&gt;Written from the perspective of a Product Manager at &lt;a href="http://crankypm.com/2008/06/a-parable-on-fake-release-dates/"&gt;Dysfunctosoft&lt;/a&gt;, a highly exaggerated (one hopes) parody of the CrankyPM's employer. Entertaining, with a dash of &lt;a href="http://crankypm.com/2008/10/customer-sabotage-of-product-management/"&gt;truthiness&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;


&lt;br&gt;&lt;div style="margin-left: 2em; padding: 5px; border: 1px coral solid;"&gt;
3) &lt;b&gt;&lt;a href="http://www.dadhacker.com/blog/"&gt;Dadhacker&lt;/a&gt;, by Landon Dyer&lt;/b&gt;
&lt;p&gt;Landon Dyer is another senior developer with a long history in the computer industry. In the 1980s he worked on video games at Atari, and &lt;a href="http://www.dadhacker.com/blog/?p=995"&gt;regularly&lt;/a&gt; posts &lt;a href="http://www.dadhacker.com/blog/?p=987"&gt;reminiscences&lt;/a&gt; about those days. He also writes about the experience of being a developer in the lower levels of the system, like   
&lt;a href="http://www.dadhacker.com/blog/?p=1052"&gt;writing software for broken hardware&lt;/a&gt;
and various
&lt;a href="http://www.dadhacker.com/blog/?p=1038"&gt;pearls of wisdom&lt;/a&gt;.
&lt;/p&gt;&lt;/div&gt;


&lt;br&gt;&lt;div style="margin-left:2em; padding: 5px; border: 1px coral solid;"&gt;
4) &lt;b&gt;&lt;a href="http://duartes.org/gustavo/blog/"&gt;Gustavo Duarte&lt;/a&gt;, by the eponymous Gustavo Duarte&lt;/b&gt;
&lt;p&gt;Gustavo is best known for his extraordinarily detailed descriptions of various facets of the x86 architecture, such as the &lt;a href="http://duartes.org/gustavo/blog/post/cpu-rings-privilege-and-protection"&gt;privilege levels&lt;/a&gt; and &lt;a href="http://duartes.org/gustavo/blog/post/memory-translation-and-segmentation"&gt;MMU hardware&lt;/a&gt;. He's also written on a wide range of topics such as the &lt;a href="http://duartes.org/gustavo/blog/post/the-divided-house-of-gpl"&gt;GPL&lt;/a&gt;, &lt;a href="http://duartes.org/gustavo/blog/post/certification-demand-and-quality"&gt;certifications&lt;/a&gt;, and &lt;a href="http://duartes.org/gustavo/blog/post/most-programming-is-not-core"&gt;software business philosophy&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;

&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;More to come&lt;/span&gt;
&lt;p&gt;That is it for this installment, but I'd like to make the blogroll a regular topic.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-349031141659955914?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=i2y48cIS"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=i2y48cIS" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=nwc9yuRL"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=nwc9yuRL" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/Oqfzsqp_AY4" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=349031141659955914" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/349031141659955914?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/349031141659955914?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/11/blogroll-november-2008.html" title="Blogroll, November 2008" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_WibILqsOlLg/STODmP-_0kI/AAAAAAAAAS0/fg5t3n3hEZM/s72-c/rss.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;A0ECRH0ycCp7ImA9WxJQFE8.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-2176587812842015743</id><published>2008-11-28T12:40:00.000-08:00</published><updated>2009-05-27T06:07:45.398-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-27T06:07:45.398-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><category scheme="http://www.blogger.com/atom/ns#" term="embedded" /><title>The Six Million Dollar LibC</title><content type="html">&lt;p&gt;&lt;img style="padding:3px; border:0px;" src="http://2.bp.blogspot.com/_WibILqsOlLg/STALnv4lJVI/AAAAAAAAASs/o2rBbHQqozk/s320/android.png" border="0" width="99" height="150" align=right alt="Android" title="Why is his skull hinged?"/&gt;
Today, Gentle Reader, we will examine the Bionic library, a slim libc developed by &lt;a href="http://www.google.com/"&gt;Google&lt;/a&gt; for use in the &lt;a href="http://source.android.com/"&gt;Android&lt;/a&gt; mobile software platform. Bionic is clearly tailored for supporting the Android system, but it is interesting to see what might be done with it in other embedded system contexts.&lt;/p&gt;

&lt;p&gt;Google's &lt;a href="http://blogs.zdnet.com/Burnette/?p=584"&gt;stated goals&lt;/a&gt; for Bionic include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;BSD license: Android uses a Linux kernel, but they wanted to keep the GPL and LGPL out of user space.&lt;/li&gt;
&lt;li&gt;Small size: &lt;a href="http://www.gnu.org/software/libc/"&gt;glibc&lt;/a&gt; is very large, and though &lt;a href="http://www.uclibc.org/"&gt;uClibC&lt;/a&gt; is considerably smaller it is encumbered by the LGPL.&lt;/li&gt;
&lt;li&gt;Speed: designed for CPUs at relatively low clock frequencies, Bionic needs to be fast. In practice this seems to drive the decisions of what to leave out, rather than any special Google pixie dust to make code go fast.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this article we'll delve into the Bionic libc via source inspection, retrieved from the git repository in October 2008. The library is written to support ARM CPUs, though some x86 support is also present. There is no support for other CPU architectures, which makes it a bit inconvenient as all of my current systems are PowerPC or MIPS. Nonetheless I'll concede that for the mobile phone market which Bionic targets, ARM is the only architecture which matters.&lt;/p&gt;

&lt;p&gt;As one might expect for a BSD-licensed libc, a significant amount of code is sourced from &lt;a href="http://www.openbsd.org/"&gt;OpenBSD&lt;/a&gt;, &lt;a href="http://www.netbsd.org/"&gt;NetBSD&lt;/a&gt;, and &lt;a href="http://www.freebsd.org/"&gt;FreeBSD&lt;/a&gt;. Additional BSD-licensed bits come from &lt;a href="http://www.sun.com/"&gt;Sun&lt;/a&gt; and public domain code like the &lt;a href="http://www.twinsun.com/tz/tz-link.htm"&gt;time zone&lt;/a&gt; package. There is also a significant amount of new code written by Google, particularly in the pthread implementation.&lt;/p&gt;



&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;C++ support&lt;/span&gt;
&lt;p&gt;So what is different about the Bionic libc versus glibc? The most striking differences are in the C++ support, as detailed in the CAVEATS file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Bionic libc routines do not handle C++ exceptions. They neither throw exceptions themselves, nor will they pass exceptions from a called function back through to their caller. So for example, if the cmp() routine passed to qsort() throws an exception the caller of qsort() will not see it.&lt;br&gt;

&amp;nbsp;&lt;br&gt;
Support for C++ exceptions adds significant overhead to function calls, even just to pass thrown exceptions back to the caller. As Android's primary programming language is Java, which handles exceptions entirely within the runtime package, the designers chose to omit the lower level exception support. C++ code can still use exceptions internally, so long as they do not cross a libc routine. In practice, it would be difficult to actually guarantee that exceptions never try to transit a library routine.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There is no C++ &lt;a href="http://en.wikipedia.org/wiki/Standard_Template_Library"&gt;Standard Template Library&lt;/a&gt; included. Developers are free supply their own, such as the free &lt;a href="http://www.sgi.com/tech/stl/"&gt;SGI implementation&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lack of exceptions is obviously a big deal for C++ programmers, but nonetheless we'll push on.&lt;/p&gt;



&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;libpthread&lt;/span&gt;
&lt;p&gt;The pthread implementation appears to be completely new and developed by Google specifically for Android. It is, quite deliberately, not a complete implementation of POSIX pthreads. It implements those features necessary to support threads in the &lt;a href="http://sites.google.com/site/io/dalvik-vm-internals"&gt;Dalvik JVM&lt;/a&gt;, and only selectively thereafter.&lt;/p&gt;

&lt;p&gt;In other embedded Linux environments, the pthread library is crucial. There are a large number of developers in this space from a vxWorks background, to whom threads are simply the way software &lt;i&gt;should&lt;/i&gt; be written. So we'll spend a bit more time delving into libpthread.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mutexes, rwlocks, condvars, etc are all implemented using &lt;a href="http://www.linux.org.uk/~ajh/ols2002_proceedings.pdf.gz"&gt;kernel futexes&lt;/a&gt;, which makes the user space implementation impressively simple. It seems a little too simple actually, I intend to spend a bit more time studying the implementation and &lt;a href="http://people.redhat.com/drepper/futex.pdf"&gt;Ulrich Drepper's futex whitepaper&lt;/a&gt;.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There is no pthread_cancel(). Threads can exit, but can not be killed by another thread.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There is no pthread_atfork(). This routine is useful if you're going to fork from a threaded process, allowing cleanups of resources which should not be held in the child. I've mostly seen pthread_atfork() used to deal with mutex locking issues, and need to study how the use of futexes affects fork().&lt;/li&gt;

&lt;!--
&lt;br&gt;&lt;li&gt;pthread_yield() is not present. In practice lack of pthread_yield for new code is not a big deal: trying to second guess the scheduler isn't a good idea,  set the thread priorities correctly and make the thread block on something when it doesn't have anything useful to do. If trying to port existing code which uses pthread_yield, I believe the sched_yield() system call could be used.&lt;/li&gt;
--&gt;

&lt;br&gt;&lt;li&gt;Thread local storage is implemented, with up to 64 keys handled. Android reserves several of these for its own use: the per-thread id and errno, as well as two variables related to OpenGL whose function I do not understand. Interestingly the ARM implementation places the TLS map at the magic address &lt;span style="font-family: Courier New, Courier, fixed;"&gt;0xffff0ff0&lt;/span&gt; in all processes. This technique is presumably part of the Google performance enhancing pixie dust.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;POSIX realtime thread extensions like pthread_attr_{set,get}inheritsched and pthread_attr_{set,get}scope are not implemented. Frankly I've never worked on a system which did implement these APIs and am completely unfamiliar with them, so I don't find their omission surprising.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I haven't drawn a final conclusion of the Bionic pthread implementation yet. It is pleasingly simple, but lack of pthread_atfork() is troublesome and use of a magic address for the TLS map may make porting to other architectures more difficult. I need to get this puppy running on a PowerPC system and see how well it works.&lt;/p&gt;


&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Miscellaneous notes&lt;/span&gt;
&lt;p&gt;In the course of digging through the library I generated a number of other notes, which don't really clump into categories. So I'm simply going to dump it all upon the Gentle Reader, in hopes that some of it is useful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The README says there is no libm, though the source for libm is present with a large number of math routines. I need to investigate further whether it really works, or whether the README is out of date.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There is no &lt;a href="http://en.wikipedia.org/wiki/Wide_character"&gt;wchar_t&lt;/a&gt; and no LOCALE support. I think this is fine: wchar_t is an idea whose time has come... and gone. The world has moved on to Unicode with its various fixed and variable width encodings, which the wide character type is not particularly useful for.
&lt;br&gt;
I've used &lt;a href="http://icu-project.org/"&gt;ICU&lt;/a&gt; in recent projects for internationalization support, and this is also what Google suggests in the README for Bionic.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There is a shared memory region of configuration properties. For example, DNS settings are stored in shared memory and not &lt;span style="font-family: Courier New, Courier, fixed;"&gt;/etc/resolv.conf&lt;/span&gt;. The Android API also makes this shared memory configuration store available to applications via property_get() and property_set().&lt;/li&gt;

&lt;br&gt;&lt;li&gt;As one might expect, the stdio/stdlib/string/unistd implementation comes from &lt;a href="http://www.openbsd.org/"&gt;OpenBSD&lt;/a&gt;, &lt;a href="http://www.netbsd.org/"&gt;NetBSD&lt;/a&gt;, and &lt;a href="http://www.freebsd.org/"&gt;FreeBSD&lt;/a&gt; with minimal changes. The only change I noticed was to remove the LOCALE support from strtod() (i.e., is the decimal point a period or a comma? In the Bionic library it is always a period).&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There is no openlog() or syslog() implementation. There is a __libc_android_log_print() routine, to support Android's own logging mechanism.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;Bionic uses &lt;a href="http://g.oswego.edu/dl/html/malloc.html"&gt;Doug Lea's malloc&lt;/a&gt;, dlmalloc. Bionic also provides a hash table to track allocations looking for leaks, in &lt;span style="font-family: Courier New, Courier, fixed;"&gt;malloc_leak.c&lt;/span&gt;.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There is no pty support that I can find, and no openpty(). There are reports of people starting an &lt;a href="http://forum.xda-developers.com/showthread.php?t=442754"&gt;SSH daemon&lt;/a&gt; on a jailbroken Android device, so presumably there is some pseudo-terminal implementation which I've missed.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There are no asynchronous AIO routines like aio_read() or aio_write().&lt;/li&gt;

&lt;br&gt;&lt;li&gt;Bionic contains an MD5 and SHA1 implementation, but no crypt(). Android uses OpenSSL for any cryptographic needs.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;Android dispenses with most file-based Unix administration. Bionic does not implement getfsent, because there is no /etc/fstab. Somewhat incongruously there is a /var/run/utmp, and so getutent() is implemented.&lt;/li&gt;

&lt;br&gt;&lt;li&gt;Android implements its own account management, and does not use /etc/passwd. There is no getpwent(), and getpwnam()/getpwuid() are implemented as wrappers around an Android ID service. At present, the Android ID service consists of 25 hard-coded accounts in &lt;span style="font-family: Courier New, Courier, fixed;"&gt;&amp;lt;android_filesystem_config.h&amp;gt;&lt;/span&gt;&lt;/li&gt;

&lt;br&gt;&lt;li&gt;Bionic isn't finished. getprotobyname(), for example, will simply print &lt;span style="font-family: Courier New, Courier, fixed;"&gt;"FIX ME! implement getprotobyname() __FILE__:__LINE__"&lt;/span&gt;&lt;/li&gt;

&lt;br&gt;&lt;li&gt;There is no &lt;a href="http://tldp.org/HOWTO/Serial-Programming-HOWTO/index.html"&gt;termios&lt;/a&gt; support (good riddance).&lt;/li&gt;
&lt;/ul&gt;


&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Conclusion&lt;/span&gt;
&lt;p&gt;Bionic is certainly interesting, and pleasingly small. It also represents a philosophical outlook of keeping the GPL some distance away from the application code.&lt;/p&gt;

&lt;p&gt;Bionic is a BSD-based libc with support for Linux system calls and interfaces. If the lack of C++ exceptions or other limitations prove untenable, the syscall and pthread mutex implementation could be repurposed into the heavier &lt;a href="http://www.freebsd.org/"&gt;FreeBSD&lt;/a&gt;/&lt;a href="http://www.netbsd.org/"&gt;NetBSD&lt;/a&gt;/&lt;a href="http://www.openbsd.org/"&gt;OpenBSD&lt;/a&gt; libc, though handling thread cancellation using the Bionic mutexes could require additional work.&lt;/p&gt;

&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Postscript&lt;/span&gt;
&lt;p&gt;If you don't understand the reference in the title of this article, don't fret: you have simply not watched enough &lt;a href="http://en.wikipedia.org/wiki/The_Six_Million_Dollar_Man"&gt;bad 1970's American television&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; In the comments, &lt;a href="http://darwish.07.googlepages.com/"&gt;Ahmed Darwish&lt;/a&gt; points out &lt;a href="http://mjg59.livejournal.com/100221.html"&gt;another Android-related article&lt;/a&gt; discussing the kernel and power management interfaces Google added.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update2:&lt;/b&gt; &lt;a href="http://www.embeddedalley.com/"&gt;Embedded Alley&lt;/a&gt; is working on a &lt;a href="http://www.theregister.co.uk/2009/04/24/android_mips/"&gt;MIPS port&lt;/a&gt; of the Android software.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update3:&lt;/b&gt; In the comments Shuhrat Dehkanov points out an interview with David Turner, who works at Google on the Bionic implementation. Shuhrat also notes that you might have to log in to Google Groups to see the attachment. &lt;i&gt;&amp;quot;&lt;a href="http://groups.google.com/group/android-platform/attach/0f8eba5ecb95c6f4/OVERVIEW.TXT?part=4&amp;view=1"&gt;Here&lt;/a&gt; is an overview by David Turner (though non-official) which answers some of the questions/unclear parts in your article.&amp;quot;&lt;/i&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-2176587812842015743?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=Wr7xWDkm"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=Wr7xWDkm" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=l15PKAlC"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=l15PKAlC" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/M2N1PnzTsbA" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=2176587812842015743" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/2176587812842015743?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/2176587812842015743?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/11/six-million-dollar-libc.html" title="The Six Million Dollar LibC" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_WibILqsOlLg/STALnv4lJVI/AAAAAAAAASs/o2rBbHQqozk/s72-c/android.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;DUYNQX46eSp7ImA9WxJSF0U.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-6133668497806286949</id><published>2008-10-31T06:50:00.000-07:00</published><updated>2009-05-08T05:53:10.011-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-08T05:53:10.011-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><title>Ode to Enum</title><content type="html">&lt;p&gt;Let's just get this out in the open: I &lt;u&gt;love&lt;/u&gt; enum. Enum is a friend of mine. Enum and I go way, way back.&lt;/p&gt;

&lt;p&gt;I also detest enum's evil twin, the series of #defines.&lt;/p&gt;

&lt;table border="0" cellpadding="0" cellspacing="0"&gt;
&lt;tr&gt;
&lt;td align=left style="background-color: #ddffdd; padding-left: 3em; padding-right: 3em;"&gt;&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;
typedef enum {
    OP_FOO,
    OP_BAR,
} operation_t;&lt;/pre&gt;&lt;/td&gt;

&lt;td align=left style="background-color: #ffdddd; padding-left: 3em; padding-right: 1em;"&gt;&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;
#define OP_FOO   0
#define OP_BAR   1&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td align=center style="background-color: #ddffdd;"&gt;&lt;span style="font-family: Arial, sans-serif; font-size: large; font-style: bold; text-align: center; color: green;"&gt;GOOD&lt;/span&gt;&lt;/td&gt;
&lt;td align=center style="background-color: #ffdddd;"&gt;&lt;span style="font-family: Arial, sans-serif; font-size: large; font-style: bold; text-align: center; color: red;"&gt;BAD&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;

&lt;br&gt;&amp;nbsp;

&lt;p&gt;Why this irrational hatred for poor #define? After all, it has had a rough life. It isn't even a real part of the C language, being completely a pre-processor construct. Allow me to explain how #define has wronged me, not just once but on multiple occasions.&lt;/p&gt;

&lt;br&gt;

&lt;table border="0" cellpadding="3" cellspacing="1"&gt;
&lt;tr&gt;
&lt;td style="border-color: #ccc; border-width: 1px 1px 0 1px; border-style: solid; "&gt;When one has a series of #defines and one needs to add a new code point, the natural inclination is to grab the next unused value:&lt;/td&gt;
&lt;td style="border-color: #ccc; border-width: 1px 1px 0 1px; border-style: solid;"&gt;Meanwhile on a different branch in the version control system, a colleague  needs to add a new code point:&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=left style="padding-left: 1em; padding-right: 1em; border-color: #ccc; border-width: 0 1px 1px 1px; border-style: solid;"&gt;&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;
#define OP_FOO       0
#define OP_BAR       1
&lt;b&gt;#define OP_BUCKAROO  2&lt;/b&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td align=left style="padding-left: 1em; padding-right: 1em; border-color: #ccc; border-width: 0 1px 1px 1px; border-style: solid;"&gt;&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;
#define OP_FOO       0
#define OP_BAR       1
&lt;b&gt;#define OP_BANZAI    2&lt;/b&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;br&gt;

&lt;p&gt;If you are lucky, this will be flagged as a merge conflict and someone will notice the duplicate values. If you are not lucky, the entries will have been added in slightly different locations such that there is no conflict... or perhaps the poor schmuck tasked to resolve the conflicts only cares about making the darn thing superficially compile so they can get back to their real work of pounding out new operation types.&lt;/p&gt;

&lt;pre style="padding-left: 6em; font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;
#define OP_FOO       0
#define OP_BAR       1
&lt;b&gt;#define OP_BUCKAROO  2&lt;/b&gt;
&lt;b&gt;#define OP_BANZAI    2&lt;/b&gt;&lt;/pre&gt;

&lt;p&gt;... and much mayhem ensues.&lt;/p&gt;

&lt;p&gt;Had we used enum instead, the bad outcome is prevented. The enumeration will assign unique values:&lt;/p&gt;

&lt;pre style="padding-left: 6em; font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;typedef enum {
    OP_FOO,
    OP_BAR,
    &lt;b&gt;OP_BUCKAROO,&lt;/b&gt;
    &lt;b&gt;OP_BANZAI,&lt;/b&gt;
} operation_t;&lt;/pre&gt;

&lt;p&gt;From this, Gentle Reader, we can conclude that version control systems must love enums.&lt;/p&gt;

&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;gcc loves enums&lt;/span&gt;
&lt;p&gt;gcc &lt;span style="white-space:nowrap"&gt;-Wswitch-enum&lt;/span&gt; provides a very nice benefit for enums and switch statements. The compiler will require that all enumerated values be handled in the switch, and will complain if they are not. Consider this code:&lt;/p&gt;

&lt;pre style="padding-left: 3em; font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;int test(operation_t op)
{
    switch(op) {
        case OP_FOO:
        case OP_BAR:
            return -2;
            break;
        default:
            return -3;
            break;
    }
 } /* test */&lt;/pre&gt;

&lt;p&gt;When new enumeration values are introduced, the compiler will flag the places where handling needs to be added:&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: x-small; line-height: 1.1em;"&gt;./m.c:137: warning: enumeration value 'OP_BUCKAROO' not handled in switch
./m.c:138: warning: enumeration value 'OP_BANZAI' not handled in switch&lt;/pre&gt;
&lt;br&gt;

&lt;p&gt;Older gcc releases provided a weaker version of this checking in the form of &lt;span style="white-space:nowrap"&gt;-Wswitch&lt;/span&gt;, where a &amp;quot;default&amp;quot; case is considered to handle all enumerated values. So the presence of a default case renders &lt;span style="white-space:nowrap"&gt;-Wswitch&lt;/span&gt; useless. If you want to take advantage of &lt;span style="white-space:nowrap"&gt;-Wswitch&lt;/span&gt; but also want to practice defensive programming, unexpected values need to be handled outside of the switch:&lt;/p&gt;

&lt;pre style="padding-left: 3em; font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;int test(operation_t op)
{
    switch(op) {
        case OP_FOO:
        case OP_BAR:
            return -2;
            break;
    }

    return -3;
 } /* test */&lt;/pre&gt;

&lt;p&gt;The &lt;span style="white-space:nowrap"&gt;-Wall&lt;/span&gt; argument to gcc enables &lt;span style="white-space:nowrap"&gt;-Wswitch&lt;/span&gt;. If you are using a recent gcc, explicitly adding &lt;span style="white-space:nowrap"&gt;-Wswitch-enum&lt;/span&gt; is a good idea.&lt;/p&gt;

&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;gdb loves enums&lt;/span&gt;
&lt;p&gt;Enums make debugging easier, with symbolic values:&lt;/p&gt;

&lt;pre style="padding-left: 3em; font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;(gdb) print mydefine
$1 = 3
(gdb) print myenum
$2 = &lt;b&gt;OP_BANZAI&lt;/b&gt;&lt;/pre&gt;

&lt;p&gt;Isn't that better?&lt;/p&gt;

&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;enums on a diet&lt;/span&gt;
&lt;p&gt;There is one minor disadvantage of using an enum: by default, an enum is the size of an int. So on a 32 bit machine an enum takes up 4 bytes of memory, even with only a small set of enumerations. However this bloat is easily eliminated, at least when using gcc:&lt;/p&gt;

&lt;pre style="padding-left: 3em; font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;typedef enum {
    OP_FOO,
    OP_BAR,
    OP_BUCKAROO,
    OP_BANZAI,
} &lt;b&gt;__attribute__((packed))&lt;/b&gt; operation_t;&lt;/pre&gt;

&lt;p&gt;A packed enum consumes the minimum footprint; in this example it is a single byte. If there are more than 256 enumerations, or if enumerations are explicitly assigned to a large absolute value, the footprint will grow to 2 or 4 bytes.&lt;/p&gt;


&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;Do you love enums?&lt;/span&gt;
&lt;p&gt;Enums have been very, very good to me.&lt;/p&gt;


&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 120%;"&gt;Updates&lt;/span&gt;
&lt;p&gt;In the comments, &lt;a href="http://disqus.com/people/f8759a5f27820d5afca518b967947d1b/"&gt;Rob&lt;/a&gt; asked:&lt;/p&gt;
&lt;blockquote&gt;&lt;i&gt;all very good - but can you do in c as you do in GDB i.e. can you show me a function myReverseEnumFuction() which when called:&lt;br&gt;

printf("enum string=%s", myReverseEnumFunction(2) );&lt;br&gt;

would output "OP_BUCKAROO" ???&lt;/i&gt;&lt;/blockquote&gt;

&lt;p&gt;Unfortunately no, I do not think there is a good way to do this in the general case. gdb is able to print the symbolic names for enumerations by referencing &lt;a href="http://en.wikipedia.org/wiki/DWARF"&gt;debug sections&lt;/a&gt; in the binary. It is certainly possible to use the same debug sections to construct myReverseEnumFunction(), but there are two drawbacks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Production binaries are routinely stripped of debugging information, which would render myReverseEnumFunction() inoperable.&lt;/li&gt;
&lt;li&gt;There are a number of different (and incompatible) formats for debugging sections. Even within the Unix-ish world using the ELF binary format there is STABS and several variations of DWARF. myReverseEnumFunction() would not be very portable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The DWARF format is an interesting topic, and I've put it on my list of things to research and write about. In the meantime I would like to mention the C pre-processor's stringify capability (&amp;quot;#&lt;i&gt;(argument)&lt;/i&gt;&amp;quot;), which may come in handy when manually constructing a myReverseEnumFunction() function. The MAP() macro below uses stringify to populate a structure with the enumeration name.&lt;/p&gt;

&lt;pre style="padding-left: 3em; font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;
#define MAP(x)  {x, #x}

struct enum_mapping {
    bb_t code;
    char *name;
} enum_map[] = {
    MAP(OP_BUCKAROO),
    MAP(OP_BANZAI)
};

char *myReverseEnumFuction(bb_t bb)
{
    int i, max = sizeof(enum_map) / sizeof(enum_map[0]);

    for (i = 0; i &lt; max; i++) {
        struct enum_mapping *emap = &amp;enum_map[i];
        if (emap-&gt;code == bb) {
            return emap-&gt;name;
        }
    }
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-6133668497806286949?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=JeXZNuVt"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=JeXZNuVt" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=7W7JU5Ee"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=7W7JU5Ee" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/treMYiZB3xI" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=6133668497806286949" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/6133668497806286949?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/6133668497806286949?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/10/ode-to-enum.html" title="Ode to Enum" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;CE4AQnY6fCp7ImA9WxRWFEs.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-1325416196140616314</id><published>2008-10-14T06:01:00.000-07:00</published><updated>2008-10-31T07:22:23.814-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-10-31T07:22:23.814-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><title>Aliasing By Any Other Name</title><content type="html">&lt;p&gt;This time, Gentle Reader, we'll delve into one of the sticky areas of the C language: pointer aliases. Let's dive directly into code:&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.1em;"&gt;void
printval(uint32_t val32)
{
    printf(&amp;quot;0x%08x\n&amp;quot;, val32);
}

int
main()
{
    uint32_t val32 = 0;
    uint16_t *p16 = (uint16_t *)&amp;amp;val32;

    p16[1] = 0xa1fa;

    printval(val32);
}&lt;/pre&gt;

&lt;p&gt;We have a 32 bit integer on the stack, to which we also direct a pointer to a 16 bit integer. Writing to p[1] overwrites 16 bits of the word.&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em; margin:1em 20px;"&gt;$ cc -O0 ./test.c
$ ./a.out
0x0000a1fa&lt;/pre&gt;

&lt;p&gt;I can hear the groans already: &lt;i&gt;not another little-versus-big endianness discussion.&lt;/i&gt; No, thats not it! It is true that the result would be different on a little endian machine, but the far more interesting discussion concerns what happens when we compile -O2:&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em; margin:1em 20px;"&gt;$ cc -O2 ./test.c
$ ./a.out
0x00000000&lt;/pre&gt;

&lt;p&gt;Why is the result different? The plot thickens...&lt;/p&gt;

&lt;p&gt;As it is one of my &lt;a href="http://codingrelic.geekhold.com/2008/03/secret-life-of-volatile.html"&gt;favorite&lt;/a&gt; &lt;a href="http://codingrelic.geekhold.com/2008/07/gdb-lies-to-you.html"&gt;techniques&lt;/a&gt;, we'll look at the generated MIPS instructions to see why this happens.&lt;/p&gt;

&lt;table style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.1em;" border="0" cellpadding="0" cellspacing="0"&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;00:&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;b&gt;li&amp;nbsp;&amp;nbsp; v1,0xa1fa&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;i&gt;load 0xa1fa into register v1&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;04:&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;b&gt;move a0,zero&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;i&gt;load 0 into  a0 (the 1st function argument)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;08:&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;b&gt;sh&amp;nbsp;&amp;nbsp; v1,26(sp)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;i&gt;store 0xa1fa to the stack, where val32 is stored&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0c:&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;b&gt;lw&amp;nbsp;&amp;nbsp; t9,0(gp)&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;i&gt;load address of printval&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;10:&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;b&gt;jalr t9&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&amp;nbsp;&lt;i&gt;jump to printval&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;14:&amp;nbsp;&lt;/td&gt;&lt;td&gt;&lt;b&gt;nop&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;The second instruction loads zero into register a0, which will be passed as the first argument to printval(). val32 at that moment is zero. We then store 0xa1fa to 16 bits on the stack, overwriting part of val32. The zero in the argument register is untouched, so we end up passing zero to printval() even though val32 on the stack has changed to 0x0000a1fa. Surely this is a bug in gcc's optimizer, right?&lt;/p&gt;

&lt;p&gt;No it isn't a flaw in gcc, but understanding this behavior will take us back in the history of the C language.&lt;/p&gt;



&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Set Wayback Machine to 1974&lt;/span&gt;

&lt;img style="padding:3px; border:0px;" src="http://1.bp.blogspot.com/_WibILqsOlLg/SPNXOsyp0fI/AAAAAAAAARA/a5grndlv_w8/s320/pdp1120a.jpg" border="0" width=201 height=134 align=right alt="DEC PDP11/20" title="I once used a PDP-11 in anger... really. It had RK-05 disk packs, too."/&gt;

&lt;p&gt;C has a reputation as a language for high performance code. Yet the language is over 30 years old, during which time computer architecture has changed considerably. Instruction execution and memory access performance have both increased phenomenally, but execution speed has improved far more rapidly than memory. Accesses to memory have become almost two orders of magnitude more expensive relative to the CPU speed compared to 30 years ago, and a great deal of silicon is now spent on data caches to avoid the cost of going to memory.&lt;/p&gt;

&lt;p&gt;One difficulty with optimizing C code is the free-wheeling use of pointers. Two pointers are called aliases if they point to the same memory location, and in general the compiler cannot really know whether any two arbitrary pointers might be aliases. To be completely safe, whenever a write is done to a pointer the compiler would have to forget any values fetched using other pointers. Those values would be re-fetched from memory, just in case they had been changed by the aliasing pointer.&lt;/p&gt;

&lt;p&gt;Unfortunately keeping values in registers is a key factor in good performance. Even if the CPU data cache still holds all of the data, the additional instructions to reload values into registers can easily bloat an inner loop and measurably slow its performance. For a long time C compilers were required to handle aliasing of arbitrary pointers. To get good performance they would &amp;quot;cheat&amp;quot; in specific cases, retaining values in registers when it was pretty certain there was no aliasing. Sometimes the compiler would be wrong, and you'd end up with incorrect behavior for a specific (and probably convoluted) segment of code with obscure aliasing somewhere. The subtle, inconsistent behavior of compiled code was becoming a problem.&lt;/p&gt;


&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Set Wayback Machine to 1999&lt;/span&gt;

&lt;p&gt;To try to resolve the ambiguous nature of this handling, the C99 specification clarifies how compilers are allowed to optimize with respect to pointer aliasing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;void * can alias anything&lt;/li&gt;
&lt;li&gt;char * can alias anything&lt;/li&gt;
&lt;li&gt;any other two distinct pointer types are assumed not to be aliases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two pointers of the same type must be treated as if they were aliases, reloading the value from memory whenever the other changes. Two pointers of different types are assumed not to alias: operations on a pointer will not invalidate the values of other pointers. In the vast majority of cases this assumption is true, and it helps the C compiler to generate faster code.&lt;/p&gt;

&lt;p&gt;The problematic statement in our example code is:&lt;/p&gt;
&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.0em; margin:1em 20px; "&gt;uint16_t *p16 = (uint16_t *)&amp;amp;val32;&lt;/pre&gt;

&lt;p&gt;&amp;quot;&amp;amp;val32&amp;quot; is a pointer to uint32_t. p16 is a pointer to uint16_t. Because they are different types, they are treated as non-aliasing. The fact that they obviously &lt;u&gt;are&lt;/u&gt; aliases due to the way they are assigned is not relevant. The point of specifying the aliasing rules in C99 was to get away from the hodgepodge of unclear assumptions and heuristics, to follow just one consistent set of rules. The pointers are different types, therefore they are assumed not to alias.&lt;/p&gt;


&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Solutions&lt;/span&gt;
&lt;p&gt;Leaving aside the contrived example which kicked off the article, how does one resolve this problem? One possible solution is the use of void or char pointers, which the compiler must treat as a potential alias of any other pointer. For complex casts of one structure to another, passing a void pointer around may be worth considering. Unfortunately pervasive use of void sacrifices most of the type checks the compiler offers, so you can have argument bugs creep in which would otherwise have been easily caught.&lt;/p&gt;

&lt;p&gt;Another, simpler solution is to have the compiler handle aliasing more leniently. Specifying C89 mode, for example, requires the compiler to operate by the earlier language rules where any pointer could potentially alias another. Also, gcc supports a -fno-strict-aliasing flag which disables only the aggressive alias optimizations without impacting the rest of the C99 handling.&lt;/p&gt;

&lt;p&gt;A solution which does &lt;u&gt;not&lt;/u&gt; work is passing the pointer around, for example if we passed a pointer to val32 to printval() in the example code. In theory this would require the called routine to fetch the value from memory (side-stepping any aliasing issues), but in practice the compiler could inline printval() and use the same registered value as caused the controversy in the first place. Even worse, the problem could suddenly appear years after the code is written when the compiler is updated or compilation flags are changed resulting in different inlining behavior.&lt;/p&gt;

&lt;p&gt;In most cases where you have an existing codebase with aliasing problems, -fno-strict-aliasing is the best way to go. Aliasing bugs can be incredibly difficult to find. There is a slight performance hit to -fno-strict-aliasing in the form of additional references to memory, but in most cases it will be small as the data cache will satisfy all requests.&lt;/p&gt;

&lt;br&gt;&amp;nbsp;&lt;br&gt;
&lt;P&gt;&lt;b&gt;Update:&lt;/b&gt; In the comments, &lt;a href="http://disqus.com/people/51ebb537b8188a3c58430a126c0e5f44/"&gt;Lance&lt;/a&gt; talks about compiler warnings:&lt;/p&gt;
&lt;blockquote&gt;&lt;i&gt;Also worth noting, compiling this bit of code with "-O2 -Wall" causes some versions of gcc to complain "warning: dereferencing type-punned pointer will break strict-aliasing rules", which can be useful when attempting to track down this type of problem. Interestingly, "-O0 -Wall" does not produce this warning because gcc only does the analysis required to detect this situation when optimizations requiring alias analysis are enabled.&lt;/i&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-1325416196140616314?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=t8Wl5rGE"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=t8Wl5rGE" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=pYxrnWWQ"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=pYxrnWWQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/C9gcWnyqYqo" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=1325416196140616314" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/1325416196140616314?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/1325416196140616314?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/10/aliasing-by-any-other-name.html" title="Aliasing By Any Other Name" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_WibILqsOlLg/SPNXOsyp0fI/AAAAAAAAARA/a5grndlv_w8/s72-c/pdp1120a.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;DU8ASXg7eip7ImA9WxRSFEk.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-1299934419710857181</id><published>2008-09-11T07:30:00.000-07:00</published><updated>2008-09-14T20:04:08.602-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-14T20:04:08.602-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="websites" /><title>Random Musings on stackoverflow.com</title><content type="html">&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://1.bp.blogspot.com/_WibILqsOlLg/SMknYdZsF6I/AAAAAAAAAPk/PoQNq31ByJ8/s320/stackoverflow.png" BORDER="0" WIDTH=125 HEIGHT=35 ALIGN=RIGHT ALT="stackoverflow.com" TITLE="Gads, I hope I'm not violating copyrights by using this."/&gt;

&lt;P&gt;&lt;A HREF="http://stackoverflow.com/"&gt;Stackoverflow.com&lt;/A&gt; is a site by programmers and for programmers, whose purpose is answering questions on topics relating to software development. At the time of this writing it has completed a closed beta test and is in a more extensive test phase, ramping up to open on September 15, 2008. Access to the beta site is  open to anyone who wants it during the test period, see &lt;A HREF="http://blog.stackoverflow.com/2008/09/help-us-beta-test/"&gt;blog.stackoverflow.com&lt;/A&gt; for details.&lt;/P&gt;

&lt;P&gt;Stackoverflow is the brainchild of &lt;A HREF="http://www.codinghorror.com/"&gt;Jeff Atwood&lt;/A&gt; and &lt;A HREF="http://www.joelonsoftware.com/"&gt;Joel Spolsky&lt;/A&gt;, long-time bloggers on software development topics. It is described by its creators as the intersection of a Wiki, blog, forum, and digg/reddit voting site. Users post questions and answers about programming topics, and the questions and answers are voted on by the community. The highest voted answers rise to the top.&lt;/P&gt; 

&lt;BR&gt;&amp;nbsp;&lt;BR CLEAR=RIGHT&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Incentives, aka crackoverflow.com&lt;/SPAN&gt;
&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://1.bp.blogspot.com/_WibILqsOlLg/SMknkG65HeI/AAAAAAAAAPs/8mWGeGqQD7c/s320/upvote.png" BORDER="0" WIDTH=46 HEIGHT=83 ALIGN=LEFT ALT="upvoting" TITLE="two votes == 20 reputation"/&gt;
&lt;P&gt;The site has a series of incentives to keep people coming back. The biggest incentive is the reputation score, which goes up and down depending on how other users rate your questions and answers. Reputation encourages asking questions and providing answers, in order to improve ones score.&lt;/P&gt;

&lt;P&gt;&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://1.bp.blogspot.com/_WibILqsOlLg/SMkn0iapyrI/AAAAAAAAAP0/bVYFtg4QHTI/s320/reputation.png" BORDER="0" WIDTH=150 HEIGHT=24 ALIGN=RIGHT ALT="reputation" TITLE="reputation=441, with 4 badges"/&gt;
Reputation is in many ways similar to the Karma awarded by &lt;A HREF="http://reddit.com/"&gt;reddit.com&lt;/A&gt; and &lt;A HREF="http://news.ycombinator.com/"&gt;Hacker News.&lt;/A&gt; Capabilities on the site are unlocked by having a high enough reputation. For example voting on posts requires a score of 15 or more, where new users start with a score of 1.&lt;/P&gt;

&lt;P&gt;The other, more unique rewards system on stackoverflow are the badges. Badges are directly modeled after the achievements awarded in &lt;A HREF="http://www.xbox.com/en-US/games/tips/achievements.htm"&gt;Xbox games&lt;/A&gt;, which encourage gameplay by granting an award when a level is completed or other objectives achieved. The achievements show up in a gamers profile on &lt;A HREF="http://www.xbox.com/en-US/LIVE/"&gt;Xbox Live&lt;/A&gt;, where they are used to talk smack about opponents.&lt;/P&gt;

&lt;P&gt;&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://1.bp.blogspot.com/_WibILqsOlLg/SMkn_aKWOlI/AAAAAAAAAP8/LH1nwJQeXMg/s320/badges.png" BORDER="0" WIDTH=82 HEIGHT=78 ALIGN=RIGHT ALT="badges" TITLE="we dont need no stinking badges!"/&gt;
The stackoverflow badges encourage exploration of the features of the site. The first few badges area easy to obtain: fill out your profile to obtain the Autobiographer badge, vote up to get the Supporter badge, and gain the Teacher badge by asking a question which is voted up. Later badges get harder, requiring larger numbers of users to vote on your submissions.&lt;/P&gt; 

&lt;P&gt;Are these mechanisms effective? I think the reputation score is, certainly. As in a video game, there is a certain thrill to seeing ones score go up. The badges seem, well, hokey to me. I'll grant that I'm probably not the target demographic anyway, badges might have more resonance with those who play games regularly.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR CLEAR=RIGHT&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Content&lt;/SPAN&gt;
&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://1.bp.blogspot.com/_WibILqsOlLg/SMkoKuzUHfI/AAAAAAAAAQE/_bb2iJ0Nyrg/s320/question.png" BORDER="0" WIDTH=83 HEIGHT=129 ALIGN=RIGHT ALT="question votes" TITLE="a popular question"/&gt;
&lt;P&gt;The questions during the stackoverflow beta have ranged widely, but by far the most common questions are about Microsoft topics like &lt;A HREF="http://www.microsoft.com/NET/"&gt;.Net&lt;/A&gt; and &lt;A HREF="http://www.microsoft.com/sqlserver/2008/en/us/default.aspx"&gt;SQL Server.&lt;/A&gt; This is likely a reflection of the audience of &lt;A HREF="http://www.codinghorror.com/"&gt;Jeff&lt;/A&gt; and &lt;A HREF="http://www.joelonsoftware.com/"&gt;Joel's&lt;/A&gt; respective blogs being heavily invested in these technologies.&lt;/P&gt;

&lt;P&gt;I've been pleasantly surprised by a number of the questions posed on the site. Where the &lt;A HREF="http://reddit.com/r/programming/"&gt;programming sub-reddit&lt;/A&gt; is now completely dominated by Ruby, Python, and Haskell, stackoverflow is covering a much wider mix of technologies. There have even been a few embedded system and assembly language questions.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR CLEAR=RIGHT&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Technical solutions to social problems&lt;/SPAN&gt;
&lt;P&gt;The history of web communities dedicated to software developers has not been pretty. The comment section of the &lt;A HREF="http://reddit.com/r/programming/"&gt;programming sub-reddit&lt;/A&gt; is now so filled with vitriol and casual slander as to be unpleasant to read, let alone participate in. &lt;A HREF="http://news.ycombinator.com/"&gt;Hacker News&lt;/A&gt; attempts to prevent this slide into toxicity by a combination of social pressure and technical measures like not allowing downvotes until a certain karma has been achieved.&lt;/P&gt;

&lt;P&gt;Stackoverflow is relying heavily on technical solutions to social problems. For example:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Downvotes are only allowed by those at higher reputations, and reduce the target reputation by two where an upvote adds ten. Additionally downvoting costs the voter one point of reputation, to discourage frequent use.&lt;/LI&gt;
&lt;LI&gt;To discourage trivial edits to bump a question to the top of the list, more than five edits to a post make it &amp;quot;community owned&amp;quot; and no longer grant reputation to its poster.&lt;/LI&gt;
&lt;LI&gt;Users with sufficiently high reputation can edit another users posts or comments, for example to remove mean-spirited remarks. It is hoped they will use these powers for Good and not Evil.&lt;/LI&gt;
&lt;LI&gt;Users can mark a question or answer as offensive. This does not merely signal a moderator; if enough users click offensive the question will be automatically removed.&lt;/LI&gt;
&lt;/OL&gt;


&lt;BR&gt;&amp;nbsp;&lt;BR CLEAR=RIGHT&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Speed bumps coming&lt;/SPAN&gt;
&lt;P&gt;I really like the premise behind stackoverflow, of a social media site designed specifically for programmers. I think there is definitely a need which &lt;A HREF="http://reddit.com/r/programming/"&gt;reddit&lt;/A&gt;, &lt;A HREF="http://www.dzone.com/links/index.html/"&gt;DZone&lt;/A&gt;, and other developer-focussed sites do not fill.&lt;/P&gt;
&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://1.bp.blogspot.com/_WibILqsOlLg/SMko6unDhTI/AAAAAAAAAQU/S29WjlpJTEQ/s320/editing.png" BORDER="0" WIDTH=274 HEIGHT=61 ALIGN=RIGHT ALT="Editing another users post" TITLE="Names blurred to protect the innocent... and the guilty."/&gt;
&lt;P&gt;However I think this initial iteration of stackoverflow has a psychological problem: it tries to simultaneously be a voting site like &lt;A HREF="http://reddit.com/"&gt;reddit&lt;/A&gt; and &lt;A HREF="http://www.digg.com/"&gt;digg&lt;/A&gt;, and a community site like &lt;A HREF="http://www.wikipedia.com/"&gt;Wikipedia&lt;/A&gt;. What you end up with is postings associated with a particular users name, and which provide benefits in the form of reputation, but which can be &lt;I&gt;edited by other users.&lt;/I&gt; The site attempts to ameliorate this by making a post &amp;quot;community owned&amp;quot; after five edits, so it no longer belongs to the posting user. This step function solution brings the opposite problem: the poster feels cheated out of the reputation from what has become a popular topic.&lt;/P&gt;

&lt;P&gt;This mismatch between the digg and wikipedia models is not working smoothly yet, in my opinion. The digg model thrives on ownership, the wikipedia model on anonymity.&lt;/P&gt;

&lt;P&gt;Some other thoughts about future issues:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;On other sites even replying to someone else's comment can elicit a defensive reaction. I suspect that editing another user's question or answer on stackoverflow will lead to a series of edit-revert-edit-revert struggles.&lt;/LI&gt;
&lt;LI&gt;There is an enormous incentive to answer quickly: the early answers accrue upvotes and gain significant inertia to accrue more upvotes. A number of users  answer immediately by quoting Wikipedia or the first Google result, and gain significant reputation by doing so. stackoverflow will have difficulty succeeding if the highest rated answers are mostly Wikipedia rehashes.&lt;/LI&gt;
&lt;LI&gt;The site is far more active during US business hours, and relatively idle during nights and weekends. When combined with the strong incentive to answer immediately, the site can become a productivity drain. We may see companies block stackoverflow in the same way that &lt;A HREF="//facebook.com/"&gt;Facebook&lt;/A&gt; is often blocked.&lt;/LI&gt;
&lt;/UL&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR CLEAR=RIGHT&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Conclusion&lt;/SPAN&gt;
&lt;P&gt;I really like stackoverflow, and I want it to succeed. I think its business model is sound: focus on a well-defined niche, provide real value, and carefully select advertising to target that specific audience.&lt;/P&gt;

&lt;P&gt;UPDATE: This article was picked up on reddit, with amusing &lt;A HREF="http://www.reddit.com/r/programming/comments/70xwc/random_musings_on_stackoverflowcom/"&gt;commentary&lt;/A&gt;. Also &lt;A HREF="http://girldeveloper.com/intar-social-commentary/stackoverflow-the-answer-to-quot-site-expertsexchange-quot/"&gt;Sara Chipps&lt;/A&gt; and &lt;A HREF="http://blog.uncommons.org/2008/09/12/stackoverflowcom-first-impressions/"&gt;Dan Dyer&lt;/A&gt; have written about their initial impressions of stackoverflow.&lt;/P&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-1299934419710857181?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=RNyEwktO"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=RNyEwktO" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=xJQerZGx"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=xJQerZGx" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/mBK1zp5EYnY" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=1299934419710857181" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/1299934419710857181?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/1299934419710857181?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/09/random-musings-on-stackoverflowcom.html" title="Random Musings on stackoverflow.com" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_WibILqsOlLg/SMknYdZsF6I/AAAAAAAAAPk/PoQNq31ByJ8/s72-c/stackoverflow.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;CUUCQXs9eip7ImA9WxRTFEk.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-7720085263068001701</id><published>2008-09-03T05:01:00.000-07:00</published><updated>2008-09-03T05:01:00.562-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-03T05:01:00.562-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Product Development" /><title>The Good, Bad, and Ugly of Gizmo Construction</title><content type="html">&lt;P&gt;Rather by definition, embedded software involves building a gizmo of some sort. Manufacturing the hardware portion of the gizmo turns out to be somewhat more complicated than writing a Makefile and starting the build... who knew?&lt;/P&gt;

&lt;P&gt;Today, Gentle Reader, we'll discuss the realities of building hardware products by meandering through a few topics:&lt;/P&gt;
&lt;UL&gt;
 &lt;LI&gt;Contract Manufacturing&lt;/LI&gt;
 &lt;LI&gt;Distributors&lt;/LI&gt;
 &lt;LI&gt;Contract Engineering&lt;/LI&gt;
 &lt;LI&gt;DVT and Compliance&lt;/LI&gt;
&lt;/UL&gt;


&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:140%;"&gt;Contract Manufacturing&lt;/SPAN&gt;
&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://2.bp.blogspot.com/_WibILqsOlLg/SLqsxI1B13I/AAAAAAAAAPE/37nRhi_351c/s320/PCB.png" BORDER="0" WIDTH=144 HEIGHT=229 ALIGN=RIGHT ALT="Printed Circuit Board" TITLE="This is some random board I had on my shelf. It was probably built by a CM... work with me here, people."/&gt;
&lt;P&gt;Contract manufacturers build a product according to a completed design including the Bill of Materials, the layout of the printed circuit board, assembly instructions, system tests, etc. They can do as much or as little as desired, from a single board up to a completed and tested system directly shipped to your customer.&lt;/P&gt;

&lt;P&gt;In the last 25 years contract manufacturing has essentially taken over the production of all electronic goods. The economies of scale from producing such large volumes are overwhelming, and there are very few companies who maintain their own production facilities now. The capital expenses for kitting out an assembly line are daunting, the CM can amortize the cost over a huge number of products. Some well known contract manufacturing firms are &lt;A HREF="http://www.foxconn.com"&gt;Foxconn&lt;/A&gt; (also known as Hon Hai), &lt;A HREF="http://www.flextronics.com"&gt;Flextronics&lt;/A&gt; (which owns Solectron, another large CM), &lt;A HREF="http://www.celestica.com"&gt;Celestica&lt;/A&gt;, and &lt;A HREF="http://www.sanmina.com"&gt;Sanmina-SCI&lt;/A&gt;.&lt;/P&gt;

&lt;P&gt;Contract Manufacturers thrive on volume. If building something in really small volumes it is worth looking into production shops aimed at hobbyists, such &lt;A HREF="http://www.expresspcb.com/"&gt;ExpressPCB.&lt;/A&gt;&lt;/P&gt;

&lt;P&gt;One thing to keep firmly in mind: the value of a customer to a CM is measured entirely by the volume of &lt;I&gt;future&lt;/I&gt; business they expect. If order volumes drop off, the CM will rapidly lose interest. If your market suffers a serious downturn you may find that the quality of the product drops precipitously as the CM rushes through the build in order to get to another, more profitable customer. Similarly if you decide to terminate business with a CM and move to a competing firm, expect astonishingly bad build quality on the final order.&lt;/P&gt;



&lt;BR CLEAR=RIGHT&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:140%;"&gt;Distributors&lt;/SPAN&gt;
&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://3.bp.blogspot.com/_WibILqsOlLg/SLqoqYeZw7I/AAAAAAAAAOk/txTldcu1guI/s320/dollars.png" BORDER="0" WIDTH=142 HEIGHT=156 ALIGN=RIGHT ALT="Fistful of Dollars" TITLE="Yes, they're $20s. This is a low budget blog."/&gt;
&lt;P&gt;Like it or not, distributors are absolutely necessary in the embedded market. There are tens of thousands of customers buying millions of components. The component manufacturers simply cannot afford to maintain individual relationships with each customer when a distributor can represent multiple manufacturers with the same effort and expense.&lt;/P&gt;

&lt;P&gt;Many Distributors provide FAEs (Field Application Engineers) to assist their customers in selecting components. A good FAE is extremely valuable, due to the sheer number of product designs they have been involved in. They will often be able to suggest alternatives which you might not otherwise have found, and know which parts have been problematic in other designs.&lt;/P&gt;

&lt;SPAN STYLE="font-weight: bold;"&gt;Distributors and Registration&lt;/SPAN&gt;
&lt;P&gt;Distributors who perform a significant amount of technical support during the design of a product cannot allow themselves to be undercut by a cheaper alternative in production. Therefore the component manufacturers allow distributors to register as being responsible for the design win at a particular customer. Only that distributor will be allowed to offer advantageous pricing, other distributors will not be allowed to undercut them.&lt;/P&gt;

&lt;P&gt;Distributors do a lot more business with the big Contract Manufacturers than any individual customer, and CMs value their relationship with the distributor more than any individual customer. It is not unknown for a Distie to legitimately claim the design win for one particular component, then obtain the Bill of Materials from the CM and register themselves for every chip in the design. Choosing a Distributor is much like getting married: be very certain that the relationship will work in the long term before signing on the botted line.&lt;/P&gt;



&lt;BR CLEAR=RIGHT&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:140%;"&gt;Contract Engineering&lt;/SPAN&gt;
&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://4.bp.blogspot.com/_WibILqsOlLg/SLtsSCGWtlI/AAAAAAAAAPM/rkt1-aykm8s/s320/schematic.png" BORDER="0" WIDTH=145 HEIGHT=170 ALIGN=RIGHT ALT="Schematic" TITLE="Why would anyone need more than 20 MHz?"/&gt;
&lt;P&gt;For a niche market with well-defined needs, a product can sell for years with minimal changes. In these cases its better to contract out the design than recruit a team. The contract will include the functional spec to be designed, timelines for completion, etc. The engineering firm will supply the requisite hardware, software, and firmware expertise, and the resulting design will belong to the customer.&lt;/P&gt;

&lt;P&gt;Alternately, Contract Engineering firms can fill in gaps for an in-house design team. For example after components have been chosen and the design competed, a layout for the printed circuit board must be created. A good layout of a complex PCB requires an experienced designer and expensive CAD tools. It makes no sense to keep such a person on staff if only a few designs are done each year, so it is often contracted out. Mechanical design of the chassis and other sheet metal is also often done outside, for the same reason.&lt;/P&gt;

&lt;P&gt;Contracts in this area are essentially always on a time and materials basis. The upfront estimate of total cost is not guaranteed. Fixed price contracts are exceedingly rare, because they represent an enormous risk to the engineering firm if the design time goes over estimates. In most cases this will work out fine: the engineering firm will want to get the design done quickly in order to move on to the next customer. However if business slows, watch out for unjustified padding of billable hours.&lt;/P&gt;

&lt;BR CLEAR=RIGHT&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:140%;"&gt;Design Verification Test&lt;/SPAN&gt;
&lt;IMG STYLE="padding:1px; border:0px;" SRC="http://3.bp.blogspot.com/_WibILqsOlLg/SLt71hZjp-I/AAAAAAAAAPU/kr5Lho6D8W4/s320/cateye.png" BORDER="0" WIDTH=118 HEIGHT=76 ALIGN=RIGHT ALT="Eye pattern" TITLE="This is called an eye pattern. See the eye?"/&gt;
&lt;P&gt;Not every unit coming out of the factory will be identical. Each component in the design has a tolerance, an allowed deviation which is still considered within specification. Every system coming off the line will contain parts slightly above spec or below, and it is important to insure that the complete system will still function reliably even if it contains components at the extreme ends of the tolerance. Though the hardware design takes these tolerances into account, in the real world it is difficult to anticipate every possible interaction of components and PCBs.&lt;/P&gt;

&lt;P&gt;This is where DVT, for Design Verification test, comes in. During DVT the hardware engineers measure the system to ensure it not only does what it is supposed to do, but does so with sufficient margin to handle variances in its components. DVT is time-consuming work, and changes are often made in the production system based on what is found in the prototypes.&lt;/P&gt;

&lt;BR CLEAR=RIGHT&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:140%;"&gt;Compliance&lt;/SPAN&gt;
&lt;IMG STYLE="padding:2px; border:0px;" SRC="http://2.bp.blogspot.com/_WibILqsOlLg/SLt-_YvnbaI/AAAAAAAAAPc/AJnR4UGjNvs/s320/UL.png" BORDER="0" WIDTH=64 HEIGHT=64 ALIGN=RIGHT ALT="Underwriters Laboratories" TITLE="Full disclosure: this blog is not UL-listed."/&gt;
&lt;P&gt;The ubiquitous Underwriters Laboratories logo is likely the most widely known example of a compliance certification, but it is not the only one. There are a huge number of standards for product safety or performance, some recognized around the globe and others specific to each geographic market.&lt;/P&gt;

&lt;P&gt;Some industries place much more stringent requirements on their products than others. For example, the &lt;A HREF="http://en.wikipedia.org/wiki/Network_Equipment-Building_System/"&gt;NEBS Level 3&lt;/A&gt; guidelines for the telecommunications market specifies that there be no noxious chemicals released if the equipment catches fire. It gives new meaning to the old joke about a &lt;A HREF="http://en.wikipedia.org/wiki/Halt_and_Catch_Fire"&gt;&amp;quot;Halt and Catch Fire&amp;quot;&lt;/A&gt; instruction.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:140%;"&gt;Conclusions&lt;/SPAN&gt;
&lt;P&gt;One of the aims of this blog is to provide insight into fields of software development which don't get as much exposure as Ruby on Rails or web frameworks. I've tried to provide an overview of some of the gritty details of building products. Its really quite exciting when the first prototype of a new system comes back from the factory, and you try to boot the software for the first time. When it doesn't boot, reality hits.&lt;/P&gt;

&lt;P&gt;&lt;I&gt;I'd like to thank John Walsh for excellent feedback and suggestions of topics to cover in this posting.&lt;/I&gt;&lt;/P&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-7720085263068001701?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=jJZvpe6p"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=jJZvpe6p" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=vlaliXfV"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=vlaliXfV" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/CEIJ4_QRS6U" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=7720085263068001701" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/7720085263068001701?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/7720085263068001701?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/09/good-bad-and-ugly-of-gizmo-construction.html" title="The Good, Bad, and Ugly of Gizmo Construction" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/_WibILqsOlLg/SLqsxI1B13I/AAAAAAAAAPE/37nRhi_351c/s72-c/PCB.png" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;A0EFQngzfip7ImA9WxVbFko.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-5865964222094012788</id><published>2008-08-21T03:01:00.000-07:00</published><updated>2009-04-02T06:40:13.686-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-02T06:40:13.686-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><title>[0123456789abcdef]</title><content type="html">&lt;TABLE STYLE="padding: 4px; border-collapse: collapse; width: 99%;" COLUMNS=2 CELLSPACING=0&gt;
&lt;TR&gt;&lt;TD COLSPAN=2&gt;
&lt;P&gt;There are only 16 characters to work with, but programmers just love coming up with creative spellings using hex numbers. I suspect that &lt;A HREF="http://en.wikipedia.org/wiki/Leet"&gt;leetspeak&lt;/A&gt; evolved out of these spellings, though personally I prefer the original form.&lt;/P&gt;

&lt;P&gt;I assume that everyone working as a software developer in English will have seen 0xdeadbeef, and probably some other favorites as well.&lt;/P&gt;
&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;deadbeef&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;old standby #1&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;feedf00d&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;old standby #2&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;feedface&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;old standby #3&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;decafbad&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;developers love to complain about coffee&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;badcafe&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;developers really love to complain about coffee&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;badc0ffee&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;developers really, really love to complain about coffee&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;badc0c0a&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;&lt;A HREF="http://developer.apple.com/cocoa/"&gt;MacOS X&lt;/A&gt; developers might find more meaning in this one.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;c0c0abad&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;People who hate &lt;A HREF="http://developer.apple.com/cocoa/"&gt;MacOS X&lt;/A&gt; developers might find more meaning in this one.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD COLSPAN=2&gt;
&lt;P STYLE="padding-top: 1em;"&gt;A little sed scripting on /usr/share/dist/words can turn up a lot of interesting combinations. For the edification and bemusement of the Gentle Reader, allow me to present a few of them here. I rejected most of the results where '5' replaced an 'S' as being too ugly, but a few passed muster.&lt;/P&gt;

&lt;PRE STYLE="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;
cat /usr/share/dict/words | sed \
    -e "s/nine/9/g" -e "s/eight/8/g" -e "s/seven/7/g" -e "s/six/6/g"   \
    -e "s/five/5/g" -e "s/four/4/g" -e "s/three/3/g" -e "s/two/2/g"    \
    -e "s/one/1/g"  -e "s/zero/0/g"                                    \
    -e "s/ated/8ed/g"  -e "s/[oO]/0/g" -e "s/[lL]/1/g" -e "s/[sS]/5/g" \
    | egrep -v "[^0123456789aAbBcCdDeEfF]"
&lt;/PRE&gt;

&lt;P STYLE="padding-bottom: 1em;"&gt;The first few seem particularly suitable for memory fenceposts, either guard words before and after allocations or patterns to scribble over freed memory when looking for use-after-free bugs.&lt;/P&gt;
&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;a110c8ed&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;This memory is in use, buster!&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;5eef3712&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;This is ~(0xa110c8ed). No, it doesn't spell anything nifty.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;dea110c8&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Scribble over memory after free(), to catch dangling references.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;defec8ed&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;to crap all over memory&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;defaced&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;another bit pattern to scribble over memory to catch use-after-free errors&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD COLSPAN=2&gt;
&lt;P STYLE="padding-top: 1em;"&gt;To express one's true feelings about the quality of the code base there are really only two options:&lt;/P&gt;
&lt;OL&gt;
&lt;LI&gt;Profanity-laden comment blocks&lt;/LI&gt;
&lt;LI&gt;Clever use of constants&lt;/LI&gt;
&lt;/OL&gt;
&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;c0defaded&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;It is a well known fact that old code suffers bit rot. Refactor often!&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;badfacade&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;There are times when bad code can only be papered over. This is one of those times.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;efface&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Good code doesn't make a spectacle of itself.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;deface&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Bad code, on the other hand, gets drunk at its best friends wedding and hits on the bride.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;decade&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;This code base has been under development for a long time.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;baddeed&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;The EULA for this product specifies the precise amount of bad karma accumulated by using it.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;acceded&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;The software has finally given in.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;befa11&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;As in &amp;quot;what has befallen yon dead process?&amp;quot;&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;c0dedbad&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;self explanatory&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD COLSPAN=2&gt;
&lt;P STYLE="padding-top: 1em; padding-bottom: 0.5em;"&gt;Magic numbers are useful in all sorts of situations. Encoding one's birthday (0xMMDDYYYY) is clever, but obscure. Subtle jokes in hex also work well.&lt;/P&gt;
&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;abbacadabba&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Unfortunately 44 bits won't magically fit into a uint32_t.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;abadabba&lt;BR&gt;dabbadabba&lt;BR&gt;abbadabbadabba&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Said the monkey to the chimp. Real magic numbers are 128 bit.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;d00bee&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Debugging probably qualifies as &lt;A HREF="http://www.cdph.ca.gov/programs/MMP/Pages/default.aspx"&gt;&amp;quot;medicinal purposes.&amp;quot;&lt;/A&gt;&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;d0dec0de&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;How does one pronounce ioctl anyway? &amp;quot;eye oh cottle,&amp;quot; or &amp;quot;eye oct all ?&amp;quot;&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;babe2bed&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;The kid's bedtime is 7pm sharp.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;b0cceba11&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;You know, I only discovered Bocce Ball in my 30s.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;5ca1ab1e&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Ignore what you &lt;A HREF="http://www.codinghorror.com/blog/archives/000838.html"&gt;see elsewhere&lt;/A&gt;, the secret to &lt;A HREF="http://www.twitter.com/"&gt;scalability&lt;/A&gt; is in using good magic numbers.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;0x1de&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;With the leading 0x it sortof looks like &amp;quot;oxide&amp;quot; ... I admit it, this one sucks.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD COLSPAN=2&gt;
&lt;P STYLE="padding-top: 1em; padding-bottom: 0.5em;"&gt;Why should return codes be boring? {0, 1, 2, ...} is so dull. We can do better.&lt;/P&gt;
&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;cab0b&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;yummy&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;fa1afe1&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;even more yummy!&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;b1abbed&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;probably I/O related&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;bedded&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;The code went to sleep?&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;b0bbed&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;They call it &amp;quot;floating point&amp;quot; for a reason, bub.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;beaded&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Um, yeah. I can't think of anything funny about this one.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;bab00&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;My sweet baboo!&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;10aded&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;I bet it has an itchy trigger finger, too.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;ba11ad&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;I structure all my code to &lt;A HREF="http://en.wikipedia.org/wiki/Iambic_pentameter"&gt;iambic pentameter&lt;/A&gt;.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;a100f&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;My code doesn't like me.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;acc01ade&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Programmers rarely, if ever, hear praise of their work.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;affab1e&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Relatively approachable and friendly code, I guess.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;babb1e&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Why yes, my functions tend to be a bit on the longish side. Why?&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;baff1e&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Why yes, my functions tend to be a bit on the complex side. Why?&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;babe1&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;You can write FORTRAN in any language.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;ba1b0a&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Its the &lt;A HREF="http://www.imdb.com/title/tt0084602/"&gt;Eye of the Tiger&lt;/A&gt;, baby!&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;ed1f1ce&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;large, imposing blocks of code&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;5eceded&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;This module has declared its independence from the rest of the system.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;5c01ded&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;At times, it is necessary to be stern with the codebase. Give it a time out.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;5caff01d&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;This code was intended to be temporary. That was four years ago. Such is the way of things.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;ad0be&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;I bet they use this one in Photoshop.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;ab0de&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;my humble abode&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;d8edbabe&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;In college, yeah, sure you did.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;0ddba11&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;That is a strange one, alright.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD COLSPAN="2"&gt;
&lt;P STYLE="padding-top: 1em; padding-bottom: 0.5em;"&gt;Finally, here are some 16 bit numbers which are more interesting than &amp;quot;dead,&amp;quot; &amp;quot;f00d&amp;quot; and &amp;quot;beef&amp;quot;&lt;/P&gt;
&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;caca&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;A statement about software quality, I suppose.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;deaf&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;When programming, no-one can hear you scream.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;c0ed&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;It is the 21st century, after all.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;ba1d&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;If tires can go bald, why not programs?&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;a1fa&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;I couldn't find a reasonable approximation of beta.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;f01d&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Origami programming!&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;fa11&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;If code falls in the forest, does it make a sound?&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;c01d&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Software is a dish best served cold.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;ab1e&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;Or ! 0xab1e, as the case may be.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;SPAN STYLE="font-family: Courier New, Courier, fixed; font-weight: bold;"&gt;cede&lt;/SPAN&gt;&lt;/TD&gt;&lt;TD STYLE="border-style: dotted; border-color: #b3b3b3; border-width: thin; border-width:1px 1px 1px 1px; padding-left: 1em;"&gt;&lt;I&gt;I give up, I'm done.&lt;/I&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;TR&gt;&lt;TD COLSPAN=2&gt;&lt;P STYLE="padding-top: 1em; padding-bottom: 0.5em;"&gt;Do you have any additional hex numbers to share? The comments section is open for business.&lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;

&lt;/TABLE&gt;

&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; &lt;a href="http://www.lisaksimone.com/phoneonfire/about-the-author/"&gt;Lisa Simone&lt;/a&gt; wrote an article about teaching embedded systems and the use of hex words in an &lt;a href="http://www.lisaksimone.com/phoneonfire/2009/02/11/deadbeef-and-kids-these-days/"&gt;article on her site&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-5865964222094012788?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=dkJCzkoN"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=dkJCzkoN" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=JKH0zUSE"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=JKH0zUSE" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/APaQYPBDvFY" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=5865964222094012788" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/5865964222094012788?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/5865964222094012788?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/08/0123456789abcdef.html" title="[0123456789abcdef]" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;A0AFQHo6fyp7ImA9WxJRGEo.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-727595079196240735</id><published>2008-08-07T00:01:00.000-07:00</published><updated>2009-05-20T21:21:51.417-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-20T21:21:51.417-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="GPL" /><category scheme="http://www.blogger.com/atom/ns#" term="Product Development" /><title>opensource.mycompany.com</title><content type="html">&lt;p&gt;Using GPL software imposes the requirement to redistribute the source code, but this requirement is routinely ignored in commercial products. That is a shame: even if one doesn't care about the goals of the free software movement, simple pragmatism would still favor providing the source code. Violating the GPL can cause Bad Things&amp;#8482;&amp;nbsp; to happen, and compliance isn't very difficult. It is quite common for products to incorporate an almost unmodified &lt;a href="http://www.busybox.net/"&gt;busybox,&lt;/a&gt; &lt;a href="http://www.gnu.org/software/libc/"&gt;glibc,&lt;/a&gt; and &lt;a href="http://www.kernel.org/"&gt;Linux kernel&lt;/a&gt;. Providing the source code for these cases is straightforward, and doesn't risk inadvertently giving away intellectual property.&lt;/p&gt;

&lt;p&gt;Section 3 of version 2 of the &lt;a href="http://www.gnu.org/licenses/gpl.html"&gt;GNU Public License&lt;/a&gt; concerns the responsibility to distribute source code along with a binary:&lt;/p&gt;

&lt;div style="font-family: Courier New, Courier, fixed; font-size: 11px; line-height: 1.15em; margin-left: 5em; margin-right: 10em; text-align: justify;"&gt;
&amp;nbsp;&amp;nbsp;3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:

&lt;div style="margin-left: 3em; margin-right: 2em;"&gt;&lt;br&gt;a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,&lt;/div&gt;

&lt;div style="margin-left: 3em; margin-right: 2em;"&gt;&lt;br/&gt;b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,&lt;/div&gt;

&lt;div style="margin-left: 3em; margin-right: 2em;"&gt;&lt;br/&gt;c) Accompany it with the information you received as to the offer to distribute corresponding source code.  (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)&lt;/div&gt;

&lt;br/&gt;The source code for a work means the preferred form of the work for making modifications to it.  For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
&lt;/div&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;

&lt;img style="padding:3px; border:0px;" src="http://1.blogger.com/_WibILqsOlLg/SJRpj_NKBBI/AAAAAAAAAM8/ZAJMr7D_amg/s320/GNUlogo.png" width="153" height="150" align="right" border="0" alt="GNU logo" title="He has the Mona Lisa smile, doesn't he?"&gt;
&lt;p&gt;I am not a lawyer, though I think it might be fun to play one on TV. There is a lot of detail in the GPL about the requirements for distribution of source code, and maybe I'm dense but I don't understand what half of it means. However I would contend that if you get to the point of needing to argue over the precise definition of the terms in a legal context, you've already failed.&lt;/p&gt;

&lt;p&gt;The problem with violating the GPL is not that you'll get sued. Of course, it is quite possible you'll be sued for violating the GPL...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.softwarefreedom.org/news/2008/jul/21/busybox"&gt;Extreme Networks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.softwarefreedom.org/news/2008/jun/10/busybox/"&gt;Bell Microproducts and SuperMicro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.softwarefreedom.org/news/2007/dec/07/busybox/"&gt;Verizon (though the infringing product was made by Actiontec)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.softwarefreedom.org/news/2007/nov/20/busybox/"&gt;Xterasys and High-Gain Antennas, LLC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.softwarefreedom.org/news/2007/sep/20/busybox/"&gt;Monsoon Multimedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gpl-violations.org/news/20050414-fortinet-injunction.html"&gt;Fortinet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://gpl-violations.org/news/20060922-dlink-judgement_frankfurt.html"&gt;D-Link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;... but getting sued is not the real problem. The &lt;u&gt;real&lt;/u&gt; problem is when a posting about misappropriation of GPL software shows up on &lt;a href="http://slashdot.org/"&gt;Slashdot&lt;/a&gt; and &lt;a href="http://lwn.net/"&gt;LWN&lt;/a&gt;. The &lt;u&gt;real&lt;/u&gt; problem is when every public-facing phone number and email address for your company becomes swamped by legions of Linux fans demanding to know when you will provide the source code. The &lt;U&gt;real&lt;/U&gt; problem persists for years after the event, when Google searches for the name of your products turn up links about GPL violations coupled with ill-informed but damaging rants.&lt;/p&gt;

&lt;p&gt;So we want to avoid that outcome. If you read the legal complaints filed by the &lt;a href="http://www.softwarefreedom.org/news"&gt;Software Freedom Law Center&lt;/a&gt;, they follow a similar pattern:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Someone discovers a product which incorporates GPL code such as &lt;a href="http://www.busybox.net/"&gt;busybox,&lt;/a&gt; but cannot  find the source code on the company web site (probably because the company hasn't posted it).&lt;/li&gt;
&lt;li&gt;This person sends a request for the source code to an address they find on that website, possibly support@mycompany.com.&lt;/li&gt;
&lt;li&gt;This request is completely ignored or receives an unsatisfactory response.&lt;/li&gt;
&lt;li&gt;The person contacts SFLC, who sends a letter to the legal department of the infringing company demanding compliance with the license and that steps be taken to ensure no future infringements take place.&lt;/li&gt;
&lt;li&gt;SFLC also demands compensation for their legal expenses; thats how they fund their operation.&lt;/li&gt;
&lt;li&gt;The corporate legal team, misreading the complaint as a shakedown attempt, stonewalls the whole thing or offers some steps but refuses to pay legal costs.&lt;/li&gt;
&lt;li&gt;Lawsuit is filed, and the PR nightmare begins in earnest.&lt;/li&gt;
&lt;/ol&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;Keeping Bad Things From Happening&lt;/span&gt;
&lt;p&gt;There are two points in that progression where the bad result could be averted, in steps #2 and #4. Unfortunately it is not likely you can influence either one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In step #2 you have no idea where that initial request for the source code will go. They might send email to the sales department, or tech support. They might call the main corporate number and chat with the answering service. The request will very likely be filtered out before it makes it to someone who would realize its significance.&lt;/li&gt;
&lt;li&gt;By the time the lawyers get involved in step #4, you're already toast. Corporations, particularly medium to large corporations, are routinely targeted to extract money for licensing intellectual property, business partnerships, or any number of reasons. The GPL claim will look like all the rest, and be treated in the same way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a case where it is best to be proactive. One can't realistically wait until the first time someone requests the source code, too many things can go wrong and lead to the PR nightmare. Instead, Gentle Reader, it is best to post the GPL code somewhere that it can be found with little difficulty by someone looking for it, but otherwise draw little attention to itself.&lt;/p&gt;

&lt;img style="padding:3px; border:0px;" src="http://1.bp.blogspot.com/_WibILqsOlLg/SJpybuBC4sI/AAAAAAAAAN0/jx0bCgqrskA/s320/tapes.jpg" width="209" height="226" align="right" border="0" alt="Tapes" title="Yes, all of these are mine, and my old source code is probably still on them. I use the PDP-7 paper tape as garland on the Christmas tree."&gt;
&lt;p&gt;When the GPL was created software was delivered via some physical medium (magnetic tapes, later supplanted by floppy disks, CDs, DVDs, etc). One was expected to include the source code on the same medium, or at least be willing to provide another tape containing the source. Nowadays many embedded systems are delivered with the software pre-installed and updates delivered via the Internet, so adding a CD of source code would add to the &lt;a href="http://en.wikipedia.org/wiki/Cost_of_goods_sold"&gt;Cost of Goods Sold&lt;/a&gt;. Anything which adds COGS is probably a non-starter, so we'll move on.&lt;/p&gt;

&lt;p&gt;It is certainly an option to tar up all of the GPL packages from the source tree and try to get it linked from the corporate website, likely controlled by someone in the marketing department. That conversation may not go the way you want:&lt;/p&gt;

&lt;p&gt;&lt;div style="font-style: italic; margin-left: 3em; margin-right: 3em; line-height: 1.35em;"&gt;&amp;quot;Tell me again why we need to do this?&amp;quot;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;div style="font-style: italic; margin-left: 3em; margin-right: 3em; line-height: 1.35em;"&gt;&amp;quot;We're not an opensource company, we build widgets.&amp;quot;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;div style="font-style: italic; margin-left: 3em; margin-right: 3em; line-height: 1.35em;"&gt;&amp;quot;Isn't &lt;A HREF="http://www.mvista.com"&gt;Montavista&lt;/A&gt; supposed to take care of this for us?&amp;quot;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;div style="font-style: italic; margin-left: 3em; margin-right: 3em; line-height: 1.35em;"&gt;&amp;quot;Our market messaging revolves around the power of our brand and the strength of our secret sauce, not opensource code. End of discussion, you commie punk.&amp;quot;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;The (hypothetical) marketing person is not being unreasonable. Ok, the last one would be unreasonable, but I thought it would be funny. Nonetheless putting GPL source code right up on the corporate website implies it is a primary focus of the corporation, when in reality it probably is just one of many tools you use in building a product. Rather than find a place on the corporate website, I advise a separate site specifically for opensource code. It needs to be something which people can easily find &lt;i&gt;if they are motivated&lt;/i&gt; to look for it, but otherwise not draw much attention to itself. opensource.&lt;i&gt;&amp;lt;mycompany&amp;gt;&lt;/i&gt;.com or gpl.&amp;lt;mycompany&amp;gt;.com are reasonable conventions.&lt;/p&gt;

&lt;p&gt;Next you need a web server. Your company may already work with a web host, otherwise &lt;a href="http://sites.google.com"&gt;Google Sites&lt;/a&gt; is a reasonable (and free) choice. You'll need IT to set up a DNS CNAME directing opensource.&lt;i&gt;&amp;lt;mycompany&amp;gt;&lt;/i&gt;.com to point to the new web site. If you're using Google Sites there is a &lt;a href="http://sites.google.com/support/bin/topic.py?topic=15219"&gt;Help Topic&lt;/a&gt; on how to do this.&lt;/p&gt;

&lt;p&gt;The goal here is to avoid the bad result (GPL violation being posted to slashdot), not draw attention. You shouldn't spend time putting together a snazzy web site, a simple background with links to tarballs is fine. Ideally nobody will ever look at these pages.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span STYLE="font-weight: bold; font-size:115%;"&gt;Documentation&lt;/span&gt;
&lt;p&gt;Lets talk about documentation. There are a number of other open source software licenses, besides the GPL. Many of them carry an &amp;quot;advertising clause,&amp;quot; a requirement that &amp;quot;all advertising materials mentioning features or use of this software must display&amp;quot; an acknowledgement of the code used. The use of this clause derives from &lt;a href="http://en.wikipedia.org/wiki/BSD_license"&gt;Berkeley's original license&lt;/a&gt; for BSD Unix, and though Berkeley has disavowed the practice there is still a great deal of open source software out there which requires it.&lt;/p&gt;

&lt;p&gt;In practice the advertising clause results in a long appendix in the product documentation listing all of the various contributors. Honestly nobody will ever read that appendix, but nonetheless it is worth putting together. You can also include a notice that the GPL code is available for download from the following URL... so if despite your best efforts the company does get sued, you'll have something concrete to point to in defense.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;Now for the hard part&lt;/span&gt;
&lt;p&gt;The Gentle Reader may have noticed that we have not covered how to locate the GPL code used within a product. Really I'm hoping that the source tree is sufficiently organized to be able to browse the top few levels of directories and look for files named LICENSE and for copyright notices at the top of files. If it is difficult to determine whether the product contains any open source code, there is an article at &lt;a href="http://itmanagement.earthweb.com/osrc/article.php/3718176/Are+You+Violating+BusyBoxs+GPL+Code?.htm"&gt;Datamation&lt;/a&gt; which might be helpful. It discusses compliance tools, including tools which look for signatures from well-known codebases to track down more serious GPL violations.&lt;/p&gt;

&lt;p&gt;What about the difficult case, where GPL code is being used and has been extended with proprietary code which cannot simply be posted to a website? Even if one doesn't care about the free software ethos, pragmatically this is a ticking time bomb and one that should not be ignored. I'd recommend putting up an opensource website anyway to post what you can, and working as soon as possible to disentangle the rest. Development of new features in that area of the code can be used as the lever to refactor it in a GPL-compliant way.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt; 8/2008: The Software Freedom Law Center has published a &lt;a href="http://www.softwarefreedom.org/resources/2008/compliance-guide.html"&gt;GPL compliance guide.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;10/2008: Linux Hater's Redux holds up this blog post as an example of why &lt;a href="http://linux-haters-redux.blogspot.com/2008/10/open-sourcehakedown.html"&gt;Linux should be avoided&lt;/a&gt;. Okey dokey.&lt;/p&gt;

&lt;p&gt;12/2008: Add &lt;a href="http://www.cisco.com"&gt;Cisco&lt;/a&gt; to the list of companies sued by the &lt;a href="http://www.softwarefreedom.org/news/2008/dec/11/cisco-lawsuit/"&gt;SFLC&lt;/a&gt; over GPL issues. This time the suit was filed on behalf of the &lt;a href="http://www.gnu.org/"&gt;FSF&lt;/a&gt; for glibc, coreutils, and other core GNU components. Reactions to the news from &lt;a href="http://arstechnica.com/news.ars/post/20081211-free-software-foundation-lawsuit-against-cisco-a-first.html"&gt;Ars Technica&lt;/a&gt; and &lt;a href="http://blogs.zdnet.com/community/?p=139"&gt;Joe Brockmeier @ ZDNET&lt;/a&gt; have already appeared.&lt;/p&gt;

&lt;p&gt;5/2009: Cisco and the FSF have &lt;a href="http://www.fsf.org/news/2009-05-cisco-settlement.html"&gt;settled&lt;/a&gt; their lawsuit. Cisco will appoint a Free Software Director, make attempts to notify owners of Liksys products of their rights under the GPL, and will make a monetary contribution to the FSF.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-727595079196240735?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=64GnvUAx"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=64GnvUAx" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=6Tv8WUxP"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=6Tv8WUxP" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/FDe6Z3IE7Wo" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=727595079196240735" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/727595079196240735?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/727595079196240735?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/08/opensourcemycompanycom.html" title="opensource.mycompany.com" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://1.bp.blogspot.com/_WibILqsOlLg/SJpybuBC4sI/AAAAAAAAAN0/jx0bCgqrskA/s72-c/tapes.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;AkUMRHk6eCp7ImA9WxRTFEw.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-5729220941000224614</id><published>2008-07-23T06:50:00.000-07:00</published><updated>2008-09-02T22:04:45.710-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-02T22:04:45.710-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="control plane" /><category scheme="http://www.blogger.com/atom/ns#" term="embedded" /><title>The Control Plane is not an Aircraft</title><content type="html">&lt;P&gt;In my industry at least, the high end of the margin curve is dominated by modular systems: a chassis into which cards can be added to add features, increase capacity, etc. Products at the cheaper end of the spectrum tend to be a fixed configuration, a closed box which does stuff but is not terribly expandable (often called a pizza box). The fixed configuration products sell in much larger volumes, but margins are lower.&lt;/P&gt;

&lt;CENTER&gt;&lt;IMG STYLE="padding:0px; border: 0px;" SRC="http://bp0.blogger.com/_WibILqsOlLg/SIAfdFe-O-I/AAAAAAAAAKc/d-webeL2O28/s320/ChassisVsFixed.jpg" WIDTH=300 HEIGHT=240 BORDER=0 ALT="Chassis has high margins and low volume, fixed config has lower margins and high volumes"&gt;&lt;/CENTER&gt;

&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://bp2.blogger.com/_WibILqsOlLg/SIAfppKWTjI/AAAAAAAAAKk/sWFUcKpEMHU/s320/FixedConfig.png" WIDTH=121 HEIGHT=143 ALIGN=RIGHT BORDER=0 ALT="Block diagram of fixed configuration system, showing CPU attached to ASICs via a PCI bus" VSPACE=3&gt;
&lt;P&gt;In a pizza box system the control plane between the CPU and the chips it controls tends to be straightforward, as there is sufficient board space to route parallel busses everywhere. PCI is often used as the control interface, as most embedded CPUs contain a PCI controller and a lot of merchant silicon uses this interface.&lt;/P&gt;

&lt;P&gt;In a chassis product, there is typically a central supervisor card (or perhaps two, for redundancy) controlling a series of line cards. There are a huge number of options for how to handle the control plane over the backplane, but they mainly fall into two categories: memory mapped and message oriented. In todays article we'll examine the big picture of control plane software for a modular chassis, and then dive into some of the details.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Memory Mapped Control Plane&lt;/SPAN&gt;
&lt;P&gt;A memory-mapped chassis control plane extends a bus like PCI over traces on the backplane.&lt;/P&gt;

&lt;CENTER&gt;&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://bp3.blogger.com/_WibILqsOlLg/SIAgtdtI6hI/AAAAAAAAAKs/u7CWfFFj2Aw/s400/ChassisMemMapped.png" WIDTH=360 HEIGHT=132 BORDER=0 ALT="Memory mapped chassis system with PCI over the backplane"&gt;&lt;/CENTER&gt;

&lt;P&gt;To the software, all boards in the chassis appear as one enormous set of ASICs to manage.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Message Passing Control Plane&lt;/SPAN&gt;
&lt;P&gt;Alternately, the chassis may rely on a network running between the supervisor and line cards to handle control duties. There are PHY chips to run ethernet via PCB traces, and inexpensive switch products like the &lt;A HREF="http://www.broadcom.com/products/brands/RoboSwitch"&gt;Roboswitch&lt;/A&gt; to fan out from the supervisor CPU out to each slot in the chassis.&lt;/P&gt;

&lt;CENTER&gt;&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://bp2.blogger.com/_WibILqsOlLg/SIAg1PaICbI/AAAAAAAAAK0/-3y65G-Mmls/s400/ChassisMsgPassing.png" WIDTH=361 HEIGHT=157 BORDER=0 ALT="Message Passing chassis system with ethernet links"&gt;&lt;/CENTER&gt;

&lt;P&gt;This system requires more software, as each line card runs its own image in addition to the supervisor card. The line cards receive messages from the supervisor and control their local ASICs, while the supervisor has to handle any local ASICs directly and generate messages to control the remote cards.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Programming Model&lt;/SPAN&gt;
&lt;P&gt;As the Gentle Reader undoubtedly already knows, the programming model for these two different system arrangements will be radically different.

&lt;TABLE COLUMNS=2 STYLE="margin:1em 20px;" BORDER="1" CELLPADDING=5&gt;
&lt;TR&gt;&lt;TD&gt;&lt;B&gt;Memory mapped&lt;/B&gt;&lt;BR&gt;ASICs are mapped in as pointers&lt;/TD&gt;&lt;TD&gt;&lt;B&gt;Message Passing&lt;/B&gt;&lt;BR&gt;ASICs controlled using, erm, messages. Yeah.&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;&lt;TD VALIGN=TOP&gt;&lt;PRE STYLE="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;volatile myHWRegisters_t *p;

p = &lt;I&gt;&amp;lt;map in the hardware&amp;gt;&lt;/I&gt;
p-&gt;reset = 1;&lt;/PRE&gt;&lt;/TD&gt;
&lt;TD VALIGN=TOP&gt;&lt;PRE STYLE="font-family: Courier New,Courier,fixed; font-size: small; line-height: 1.1em;"&gt;myHWMessage_t msg;

msg.code = DO_RESET_ASIC;
send(socket, &amp;msg, sizeof(msg), 0);&lt;/PRE&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;/TABLE&gt;

Perhaps that was obvious. Moving on... At first glance the memory mapped model looks pretty compelling:
&lt;UL&gt;
&lt;LI&gt;it the same as a fixed configuration product, allowing easy code reuse
&lt;LI&gt;in the message passing model you still have to write memory map code to run out on the line card CPUs, and then you have to write the messaging layer
&lt;/UL&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Hot Swap Complicates Things&lt;/SPAN&gt;
&lt;P&gt;A big issue in the software support for a chassis is support for hot swap. Cards can be inserted or removed from the chassis at any time, while the system runs. The software needs to handle having cards come and go.&lt;/P&gt;

&lt;P&gt;With a message passing system hotswap is fairly straightforward to handle: the software will be notified of insertion or removal events, and starts sending messages. If there is a race condition where a message is sent to a card which has just been removed, nothing terrible happens. The software needs to handle timeouts and gracefully recover from an unresponsive card, but this isn't too difficult to manage.&lt;/P&gt;

&lt;P&gt;With a memory mapped chassis, hot &lt;U&gt;&lt;I&gt;insertion&lt;/I&gt;&lt;/U&gt; is relatively simple. The software is notified of an insertion event, and maps in the hardware registers. Removal is not so simple. If the software is given sufficient notice that the card is being removed, it can be cleanly unmapped and safely removed. If the card is yanked out of the chassis before the software is ready, havoc can ensue. Pointers will suddenly reference a non-existant device.&lt;/P&gt;


&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Blame the Hardware&lt;/SPAN&gt;
&lt;P&gt;So card removal is difficult to handle robustly in a chassis which memory-maps the line cards.&lt;/P&gt;

&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://bp1.blogger.com/_WibILqsOlLg/SIAg-Z5-23I/AAAAAAAAAK8/U8QeRmY688g/s320/CompactPCI.jpg" WIDTH=167 HEIGHT=220 ALIGN=RIGHT BORDER=0 ALT="CompactPCI card showing microswitch and hot-swap LED" TITLE="CompactPCI card"&gt;

&lt;P&gt;Ideally the hardware should help the software handle card removal. For example &lt;A HREF="http://www.picmg.org/test/compci.htm"&gt;CompactPCI&lt;/A&gt; cards include a microswitch on the ejector lever, which signals the software of an imminent removal. The user is supposed to flip the switch and wait for an LED to light up, an indication that the card is ready to be removed. Of course, people ignore the LED and pull the card out anyway if it takes longer than 1.26 seconds... we did studies, and stuff... ok I just made that number up. Card removal is often then made into a software problem: &lt;I&gt;&amp;quot;Just add a CLI command to deprovision the card, or something.&amp;quot;&lt;/I&gt;&lt;/P&gt;

&lt;P&gt;This makes for a reasonably nasty programming environment: to be robust you have to constantly double-check that the card is still present. Get a link up indication from one of the ports? Better check the card presence before trying to use that port, in case the linkup turns out to be the buswatcher's 0xdeadbeef pattern. Read an indication from one of the line cards that its waaaaay over temperature? Check that it is still there before you begin shutting the system down, it might just be a garbage reading.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Pragmatism is a Virtue&lt;/SPAN&gt;
&lt;P&gt;There is a maxim in the marketing strategy for a chassis product line: never let the customer remove the chassis from the rack - they might replace it with a competitors chassis. You can evolve the design of the line cards and other modules, but they must function in the chassis already present at the customer site. Backplane designs thus remain in production for a long time, often lasting through several generations of card designs before finally being retired. Though the Gentle Reader might have a firm preference for one control plane architecture or another, the harsh reality is that one likely has to accept whatever was designed in years ago.&lt;/P&gt; 

&lt;P&gt;So we'll spend a little time talking about the software support for the two alternatives.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Thoughts on Memory Mapped Designs&lt;/SPAN&gt;
&lt;P&gt;Software to control a hardware device does not automatically have to run in the kernel. There are only a few things which the kernel absolutely has to do:

&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://bp0.blogger.com/_WibILqsOlLg/SIFwzltbb0I/AAAAAAAAALE/yd0-SemugcM/s320/UserSpaceDriver.png" WIDTH=188 HEIGHT=192 ALIGN=RIGHT BORDER=0 ALT="Driver code can run in user space" TITLE="Userspace driver"&gt;

&lt;OL&gt;
&lt;LI&gt;map the physical address of the device in at a virtual address&lt;/LI&gt;
&lt;LI&gt;handle interrupts and mask the hardware IRQ&lt;/LI&gt;
&lt;LI&gt;deal with DMA, as this involves physical addressing of buffers&lt;/LI&gt;
&lt;/OL&gt;

&lt;P&gt;Everything else, all of the code to initialize the devices and all of the higher level handling in response to an interrupt, can go into a user space process where it will be easier to debug and maintain. The kernel driver needs to support an mmap() entry point, allowing the process to map in the hardware registers. Once mapped, the user process can program the hardware without ever calling into the kernel again.&lt;/P&gt;



&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Thoughts on Message Passing Designs&lt;/SPAN&gt;
&lt;P&gt;First, an assertion: RPC is a terrible way to implement a control plane. One of the advantages of having CPUs on each card is the ability to run operations in parallel, but using remote procedure calls means the CPUs will spend a lot of their time blocked. The control plane should be structured as a FIFO of operations in flight, without having to wait for each operation to complete. If information is needed from the remote card it should be structured as a callback, not a blocking operation.&lt;/P&gt;

&lt;P&gt;It is tempting to implement the control communications as a series of commands sent from the supervisor CPU to the line cards. Individual commands would most likely be a high level operation, requiring the line card CPU to implement a series of accesses to the hardware. The amount of CPU time it takes for the supervisor to send the command would be relatively small compared to the amount of time the line card will spend implementing the command, likely accentuated by a significantly faster CPU in the supervisor. Therefore the supervisor will be able to generate operations far faster than the line cards can handle them. In networking gear this is most visible when a link is flapping &lt;I&gt;[an ethernet link being established and lost very rapidly]&lt;/I&gt; where commands are sent each time to reconfigure the other links. If the flapping persists, you either cause a failure by overflowing buffers in the control plane or start making the supervisor block while waiting for the line card to drain its queue. Either way, its bad.&lt;/P&gt;

&lt;P&gt;One technique to avoid these overloads is to have the supervisor delay sending a message for a short time. If additional operations need to be done, the supervisor can discard the earlier updates and send only the most recent ones. The downside of delaying messages in this way is that it is a &lt;I&gt;delay,&lt;/I&gt; and responsiveness suffers.&lt;/P&gt;

&lt;P&gt;Another technique involves a somewhat more radical restructuring. The line card most likely contains various distinct bits of functionality which are mostly decoupled. Falling back to my usual example of networking gear, the configuration of each port is mostly independent of the other ports. Rather than send a message describing the changes to be made to the port, have the supervisor send a message containing the &lt;U&gt;complete&lt;/U&gt; port state. Because each message contains a complete snapshot of the desired state of the port, the line card can freely discard older messages so long as it implements the one most recently sent.&lt;/P&gt;

&lt;CENTER&gt;&lt;IMG STYLE="padding:3px; border:0px;" SRC="http://bp3.blogger.com/_WibILqsOlLg/SIcw31SzqTI/AAAAAAAAALU/RwtH9JMMnqs/s800/StateCoalescing.png" WIDTH=520 HEIGHT=544 BORDER=0 ALT="Control Plane State Coalescing" TITLE="State Coalescing"&gt;&lt;/CENTER&gt;

&lt;P&gt;By structuring the control messages to contain a desired state, you allow the remote card to degrade gracefully under load. Under light load it can probably handle every message the supervisor sends, while under heavier load it will be able to skip multiple updates to the same state.&lt;/P&gt;

&lt;P&gt;Note that the ordering of updates is lost, as events can be coalesced into a different order than that in which they were sent. This coalescing scheme can only work if the various bits of state really are independent, if one state depends on an earlier update to a different state then they are not independent.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size:115%;"&gt;Closing Thoughts&lt;/SPAN&gt;
&lt;P&gt;Gack, enough about control plane implementation already.&lt;/P&gt;

&lt;P&gt;The comments section recently converted over to &lt;A HREF="http://www.disqus.com/"&gt;Disqus&lt;/A&gt;, which allows threading so the Gentle Reader can reply to an earlier comment. Anonymous comments are enabled for now, though if comment spam becomes a problem that may change.&lt;/P&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-5729220941000224614?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=XE2TQcuD"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=XE2TQcuD" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=COinhAJU"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=COinhAJU" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/atMQKIHDU_g" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=5729220941000224614" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/5729220941000224614?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/5729220941000224614?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/07/control-plane-is-not-aircraft.html" title="The Control Plane is not an Aircraft" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_WibILqsOlLg/SIAfdFe-O-I/AAAAAAAAAKc/d-webeL2O28/s72-c/ChassisVsFixed.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;C0cGQXc5eip7ImA9WxVWEk4.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-3002804923669651434</id><published>2008-07-02T21:01:00.000-07:00</published><updated>2009-02-21T07:43:40.922-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-21T07:43:40.922-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="C Programming" /><title>gdb lies to you</title><content type="html">&lt;p&gt;Because I'm a terrible programmer, I spend a lot of time in gdb. gdb is a fantastic tool that has (so far) kept me from getting fired, but it has its limitations. Today, Gentle Reader, we will talk about one of the particularly vexing limitations.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;p&gt;&lt;span style="font-weight: bold; font-size:115%;"&gt;Not Always Correct, but Never Uncertain&lt;/span&gt;
&lt;br/&gt;Have you ever opened a core and immediately spotted the problem: &amp;quot;Oh, foo() was called with a bad argument. That isn't a valid widget ID.&amp;quot; So you dig around in the data structures which were used to call foo(), but you cannot see how it could possibly have been called with the argument gdb is showing?&lt;/p&gt;

&lt;p&gt;You're right, the program did not call foo with a bizarre argument. gdb is displaying incorrect information. Lets take a simple example:&lt;/p&gt;

&lt;p&gt;&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;
int testgdb()
{
    return foo(123);
}

int foo(int a)
{
    return bar(456);
}

int bar (int a)
{
    *((int *)NULL) = 0;  /* Deliberately crash */
    return a;
}&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;foo is called with a constant argument of 123. foo calls bar(456), and bar deliberately segfaults. Now lets examine the backtrace:&lt;/p&gt;

&lt;p&gt;&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;
(gdb) bt
#0  0x80045a1c in bar (a=456) at foo.c:4
#1  0x80045a30 in foo (a=456) at foo.c:10
#2  0x80045a4c in testgdb () at foo.c:15&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Weird, huh? How did foo get called with an argument of 456? The answer, of course, is that it didn't. It was called with 123 as its argument, and gdb is displaying incorrect information.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;p&gt;&lt;span style="font-weight: bold; font-size:115%;"&gt;More MIPS Assembly, Please&lt;/span&gt;
&lt;br/&gt;Because it is one of my &lt;a href="http://codingrelic.geekhold.com/2008/03/secret-life-of-volatile.html"&gt;favorite techniques,&lt;/a&gt; lets disassemble the object code to see why this happens. This was compiled for MIPS32 using gcc -O2, but with inlining disabled (because we need to have function calls if we're going to talk about function arguments).&lt;/p&gt;

&lt;p&gt;We'll start with testgdb():&lt;/p&gt;
&lt;p&gt;&lt;table style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.1em;" border="0"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td colspan="2"&gt;&amp;lt;testgdb&amp;gt;:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;addiu sp,sp,-24&lt;/td&gt;      &lt;td&gt;&amp;nbsp;&lt;i&gt;push a stack frame&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;sw    ra,16(sp)&lt;/td&gt;      &lt;td&gt;&amp;nbsp;&lt;i&gt;store the return address to the stack&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;jal   &amp;lt;foo&amp;gt;&lt;/td&gt;    &lt;td&gt;&amp;nbsp;&lt;i&gt;jump to foo()&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;li    a0,123&lt;/td&gt;         &lt;td&gt;&amp;nbsp;&lt;i&gt;pass 123 as the first argument to foo (in delay slot)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;lw    ra,16(sp)&lt;/td&gt;      &lt;td&gt;&amp;nbsp;&lt;i&gt;load return address back from the stack&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;jr    ra&lt;/td&gt;             &lt;td&gt;&amp;nbsp;&lt;i&gt;return to the caller&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;addiu sp,sp,24&lt;/td&gt;       &lt;td&gt;&amp;nbsp;&lt;i&gt;pop the stack frame&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt;

&lt;p&gt;MIPS passes arguments in CPU registers, and this is crucial to understanding why gdb sometimes displays the arguments incorrectly. Every CPU architecture has a set of &amp;quot;calling conventions&amp;quot; of how the compiler should pass arguments to functions. A long time ago in architectures like VAX and 680x0, arguments would be pushed on the stack. Since then CPUs have gotten dramatically faster while memory speed has improved less rapidly, so modern calling conventions pass arguments in registers whenever possible. The original calling conventions for x86 used the stack but the more recent &amp;quot;fastcall&amp;quot; passes two arguments in registers, while x86_64 uses registers for up to four arguments.&lt;/p&gt;

&lt;p&gt;In the assembly example above the argument to foo is loaded into register a0, using a &amp;quot;load immediate&amp;quot; instruction. This instructions happens to be in a delay slot: with MIPS the instruction immediately following a branch is always executed.&lt;/p&gt;

&lt;p&gt;&lt;table style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.1em;" border="0"&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td colspan="2"&gt;&amp;lt;foo&amp;gt;:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;addiu sp,sp,-24&lt;/td&gt;      &lt;td&gt;&amp;nbsp;&lt;i&gt;push a stack frame&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;sw    ra,16(sp)&lt;/td&gt;      &lt;td&gt;&amp;nbsp;&lt;i&gt;save return address to the stack&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;jal   &amp;lt;bar&amp;gt;&lt;/td&gt;    &lt;td&gt;&amp;nbsp;&lt;i&gt;jump to bar()&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;li    a0,456&lt;/td&gt;         &lt;td&gt;&amp;nbsp;&lt;i&gt;pass 456 as first argument to bar (in delay slot)&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;lw    ra,16(sp)&lt;/td&gt;      &lt;td&gt;&amp;nbsp;&lt;i&gt;after returning from bar(), load return address&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;jr    ra&lt;/td&gt;             &lt;td&gt;&amp;nbsp;&lt;i&gt;return to the caller&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&amp;nbsp;addiu sp,sp,24&lt;/td&gt;       &lt;td&gt;&amp;nbsp;&lt;i&gt;pop the stack frame&lt;/i&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt;

&lt;p&gt;foo() pushes a stack frame and loads 456 into a0 in order to call bar(). The crucial thing to notice is that the current value of a0 was not saved anywhere before overwriting it with 456. The compiler determined that the original value was no longer required for any further computation, and did not need to be saved. So the &amp;quot;123&amp;quot; value stored in a0 has been irrevocably lost. foo next calls bar, and crashes.&lt;/p&gt;

&lt;p&gt;In the gdb backtrace the function arguments will be displayed from the stack &lt;i&gt;if they are available.&lt;/i&gt; Otherwise, gdb displays the values of the argument registers and hopes for the best. The arguments in the backtrace are mostly correct because gdb can often pull them from the stack. When compiled -O0 functions always save their argument registers to the stack, so it is only optimized code which can make debugging difficult in this way.&lt;/p&gt;

&lt;p&gt;&amp;nbsp;&lt;p&gt;&lt;span style="font-weight: bold; font-size:115%;"&gt;Et tu, gdb?&lt;/span&gt;
&lt;p&gt;What can one do about this? Mainly be aware that what the debugger shows is not always correct. I believe this is good practice in general, always be a little bit suspicious of the tools.&lt;/p&gt;

&lt;p&gt;Personally I'd find it more helpful if gdb were to display &amp;quot;&amp;lt;argument unknown&amp;gt;&amp;quot; rather than an incorrect value, if it can determine that the argument register has been re-used. I've tried digging through the gdb sources to see if this could be improved, but got lost in mips-tdep.c (reference first paragraph about me being a terrible programmer). Information about register re-use is typically not included in the compiler's debugging information anyway, so gdb may not have sufficient information to know when a register still holds the correct argument value.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-3002804923669651434?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=bijtFena"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=bijtFena" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=jSTsuCrh"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=jSTsuCrh" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/ktnug3y7a2I" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=3002804923669651434" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/3002804923669651434?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/3002804923669651434?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/07/gdb-lies-to-you.html" title="gdb lies to you" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;A0cERH89eip7ImA9WxRTFEw.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-1479656817652338311</id><published>2008-06-25T02:03:00.000-07:00</published><updated>2008-09-02T22:16:45.162-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-09-02T22:16:45.162-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="filesystem" /><category scheme="http://www.blogger.com/atom/ns#" term="embedded" /><title>More Random Musings on Embedded Filesystems</title><content type="html">&lt;P&gt;The &lt;A HREF="http://codingrelic.geekhold.com/2008/05/random-musings-on-embedded-filesystems.html"&gt;first set of random musings&lt;/A&gt; ended up being far longer than planned, and rather a lot of material ended up on the cutting room floor. Some of it has been swept into this article.&lt;/P&gt;

&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size: 115%;"&gt;Incremental Patches&lt;/SPAN&gt;
&lt;P&gt;The Gentle Reader may have noticed that using a read-only file system like squashfs has an important repercussion: it is impossible to copy in a modified file as an incremental patch. No matter how small the change, delivering a bug fix means delivering a complete new filesystem. Realistically, this means a complete new software image.&lt;/P&gt;

&lt;P&gt;Personally I don't consider this to be a drawback for an embedded system. Microsoft, Apple, and most Linux distributions offer incremental patches because it suits their needs: when your software image is gigabytes in size and you have gazillions of users, it is important to deliver fixes in a bandwidth-efficient way. Embedded software is less affected by this problem, as it tends to be much smaller and have fewer downloads (either because the product sells in low volume, or has a relatively low fraction of users who will ever apply the updates).&lt;/P&gt;

&lt;P&gt;Additionally, incremental patches come with their own set of costs:
&lt;OL&gt;
&lt;LI&gt;perhaps obviously, you need management software to apply, remove, and list installed patches&lt;/LI&gt;
&lt;LI&gt;tech support has a larger number of variations to deal with&lt;/LI&gt;
&lt;LI&gt;test load increases rather dramatically: if you have 5 independent patches you may need to test the combinations: up to 2^5=32 variations to test, not just 5.&lt;/LI&gt;
&lt;/OL&gt;

&lt;P&gt;Again, for companies the size of Microsoft/Apple/RedHat these issues can be handled, but for a small company it can greatly add to the support and QA load for little real benefit.&lt;/P&gt;

&lt;P&gt;So personally, I favor not delivering incremental patches. For each major release of the product, maintain a patch branch containing all accumulated fixes to date. When a new customer issue comes in, the fix is added to the tip of that branch and a complete new build released with an incremented version number. In exceptional cases (an issue for one specific customer which may be deleterious for other customers) a new branch can be made and a private image delivered, but for your own sanity you want to make this an infrequent occurrence.&lt;/P&gt;

&lt;P&gt;General purpose OS vendors also use incremental patches to deliver fixes for specific problems without annoying their customers with superfluous changes or breakage. This is an issue for embedded vendors as well: there is no customer quite so pissed off as one where a software patch made things worse. However in the end I think this boils down to being disciplined about what changes are allowed in a maintenance release, and what needs to be pushed out to the next major release.&lt;/P&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size: 115%;"&gt;How to Implement Incremental Patches&lt;/SPAN&gt;
&lt;P&gt;If you really want to deliver incremental patches, it can still be done using the filesystem choices recommended here. The key observation is that even though there is a file in the filesystem, you don't have to use it. If a newer binary is present elsewhere, you can use it instead.&lt;/P&gt;

&lt;P&gt;To implement this scheme it is easiest to segregate the locally developed applications into their own directory, for example /mycompany/bin instead of directly in /bin. For this example I'll assume that incremental patch binaries will be installed in a flash filesystem in /flash/mycompany. When assembling the squashfs/cramfs/etc for a release, place the application binaries in /ro/mycompany/bin and make /mycompany be a softlink to the writeable ramdisk:&lt;/P&gt;

&lt;PRE STYLE="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;
# ls -l /
drwxr-xr-x    1 root     root          472 Jun  6 11:55 bin
drwxr-xr-x    1 root     root          836 Jun  6 11:55 dev
lrwxrwxrwx    1 root     root            7 Jun  6 11:55 etc -&gt; /rw/etc
drwxr-xr-x    1 root     root          555 Jun  6 11:55 lib
lrwxrwxrwx    1 root     root            9 Jun  6 11:55 mycompany -&gt; /rw/mycompany
drwxr-xr-x    1 root     root           12 Jun  6 11:55 ro
drwxrwxrwt    9 root     root          180 Jun  6 12:17 rw
lrwxrwxrwx    1 root     root            7 Jun  6 11:55 tmp -&gt; /rw/tmp
lrwxrwxrwx    1 root     root            7 Jun  6 11:55 var -&gt; /rw/var
&lt;/PRE&gt;

&lt;P&gt;At boot, you would use a script to populate softlinks in the /mycompany directory structure to point to each binary in /ro/mycompany. If an updated version of the application binary is available in /flash, the softlink will be updated to point to it instead.&lt;/P&gt;

&lt;PRE STYLE="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;
# First populate links to the release binaries
cd /ro
for dir in `find mycompany -type d -noleaf`; do
    mkdir -p /$dir
done
for file in `find mycompany -not -type d -noleaf`; do
    ln -s -f /ro/$file /$file
done
        
# Now replace the softlinks for binaries which we've incrementally patched
cd /flash
for dir in `find mycompany -type d -noleaf`; do
    mkdir -p /$dir
done
for file in `find mycompany -not -type d -noleaf`; do
    ln -s -f /flash/$file /$file
done&lt;/PRE&gt;

&lt;P&gt;What you'll end up with is a series of softlinks:&lt;/P&gt;

&lt;PRE STYLE="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;
# ls -l /mycompany/bin/
lrwxrwxrwx 1 root  root 22 Jun 24 16:49 appNum1 -&gt; /ro/mycompany/bin/appNum1
lrwxrwxrwx 1 root  root 22 Jun 24 16:49 appNum2 -&gt; /ro/mycompany/bin/appNum2
lrwxrwxrwx 1 root  root 25 Jun 24 16:49 appNum3 -&gt; /flash/mycompany/bin/appNum3
&lt;/PRE&gt;

&lt;BR&gt;&amp;nbsp;&lt;BR&gt;
&lt;SPAN STYLE="font-weight: bold; font-size: 115%;"&gt;Flash filesystems&lt;/SPAN&gt;
&lt;P&gt;These two articles have focussed on in-memory filesystems. If the total size of the libraries and binaries is reasonably small an embedded system can get by with just the in-memory filesystem, but at some point it makes more sense to store the application binaries in a flash filesystem to be paged into memory as needed. The Gentle Reader might be interested in an overview of flash filesystem technology at the IBM developerWorks site, written &lt;A HREF="http://www.ibm.com/developerworks/linux/library/l-flash-filesystems/index.html?S_TACT=105AGX03&amp;S_CMP=EDU"&gt;by M. Tim Jones&lt;/A&gt;.&lt;/P&gt;

&lt;P&gt;For now, I'll leave it at that.&lt;/P&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-1479656817652338311?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=FEAnmtm1"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=FEAnmtm1" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=MCXDYdKa"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=MCXDYdKa" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/fUzN5eYfYv0" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=1479656817652338311" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/1479656817652338311?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/1479656817652338311?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/06/more-random-musings-on-embedded.html" title="More Random Musings on Embedded Filesystems" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></entry><entry gd:etag="W/&quot;DUEDSXk8eSp7ImA9WxVQFEw.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-256731789305654284</id><published>2008-05-31T21:18:00.000-07:00</published><updated>2009-01-31T08:01:18.771-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-01-31T08:01:18.771-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="filesystem" /><category scheme="http://www.blogger.com/atom/ns#" term="embedded" /><title>Random Musings on Embedded Filesystems</title><content type="html">&lt;p&gt;Getting the kernel to successfully boot on a new board is a wonderful feeling, which lasts about 5 seconds before the application developers begin clamoring for time on the box. For the apps work to make progress the application binaries have to be accessible in a filesystem somewhere. During platform bringup it is common to use NFS because it will &amp;quot;just work&amp;quot; almost immediately after you get the platform to boot.&lt;/p&gt;

&lt;p&gt;I'd advise not relying on NFS for very long: using a server with gigabytes of disk means you're completely insulated from size constraints. You may find that the application footprint grows astronomically simply because nobody notices the pain. Adding library dependencies which pull in 20 Megs of DLLs is invisible on an NFS server, but a crisis when you discover very late in the process that it won't fit in flash.&lt;/p&gt;

&lt;p&gt;Lets start by listing a few assumptions about the target:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it does not have a hard disk (i.e. a spinning, magnetic disk)&lt;/li&gt;
&lt;li&gt;it has at least as much DRAM as flash, probably more.&lt;/li&gt;
&lt;li&gt;it uses a 32 or 64 bit processor with an MMU (PowerPC, MIPS, ARM, x86, etc)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your platform is significantly different from these assumptions, the recommendations in this article may not be appropriate. The biggest one is the hard disk: if your system has one, don't use these recommendations - your filesystem choices should revolve around what the disk can do for you. If your system does not have an MMU some of these recommendations may apply, but you need to look at using &lt;a href="http://www.uclinux.org"&gt;uCLinux&lt;/a&gt; and its associated best practices.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;A Note about the Buffer Cache&lt;/span&gt;
&lt;p&gt;The Linux I/O system is designed for a general purpose computer, with a disk connected to an I/O interface. Recently accessed files are stored in memory in the buffer cache; if the files are accessed again they can be provided from cache instead of hitting the disk a second time. You may also come across references to a page cache; in current kernel versions the page cache and buffer cache have been merged into a single pool of memory.&lt;/p&gt;

&lt;p&gt;Embedded systems generally don't have a disk, their filesystems reside in RAM. Nonetheless Linux still relies on the buffer cache. So though a file resides in a RAMdisk it will be read into the buffer cache, and then to the process which requested it. The buffer cache doesn't add as much value in this setup (re-reading the filesystem would be quick), but the buffer cache is essential to support Linux APIs like mmap(). The effect of the buffer cache will be described in a bit more detail later in the article.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;Option #1: Squashfs and tmpfs&lt;/span&gt;
&lt;p&gt;&lt;a href="http://squashfs.sourceforge.net/"&gt;SquashFS&lt;/a&gt; is a compressed, read-only filesystem. A squashfs filesystem is constructed offline, compressing a directory structure into a binary blob which can be loaded into memory and mounted. As blocks are read from the squashfs they are decompressed into the buffer cache; if memory becomes tight the cache can toss pages out and re-read them from the squashfs as needed. Thus the memory footprint is fairly well minimized, only files in active use will be decompressed.&lt;/p&gt;

&lt;p&gt;Squashfs is inherently read-only. It is not &lt;i&gt;mounted&lt;/i&gt; read-only, such that you could remount it read-write. The squashfs filesystem is assembled at compile time; once created it is immutable. This is generally acceptable for filesystems which shouldn't change anyway, like /bin or /lib, but is clearly not suitable for everything. There are configuration files like /etc/resolv.conf and /etc/timezone which probably need to be occasionally written, and numerous scratch files reside in /tmp. Therefore a second filesystem is needed, a writeable ramdisk.&lt;/p&gt;

&lt;p&gt;tmpfs is a ramdisk, and is a good way to provide the writeable portions of the filesystem. Daniel Robbins wrote an &lt;a href="http://www.ibm.com/developerworks/library/l-fs3.html"&gt;excellent article&lt;/a&gt; about tmpfs, published at IBM DeveloperWorks a few years ago. Squashfs would be used for the root filesystem mounted on /, while the tmpfs would be mounted on a subdirectory like /rw. /etc, /var and /tmp are most easily provided as soft links to the tmpfs. For example:&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;# ls  -l /
dr-xr-xr-x    1 root     472 May 16 10:10 bin
dr-xr-xr-x    1 root     836 May 16 10:10 dev
lrwxrwxrwx    1 root       7 May 16 10:10 etc -&gt; /rw/etc
dr-xr-xr-x    1 root     555 May 16 10:10 lib
dr-xr-xr-x   97 root       0 Dec 31  1969 proc
lrwxrwxrwx    1 root       8 May 16 10:10 root -&gt; /rw/root
drwxrwxrwt    9 root     180 May 16 10:12 rw
dr-xr-xr-x    1 root     151 May 16 10:10 sbin
lrwxrwxrwx    1 root       7 May 16 10:10 tmp -&gt; /rw/tmp
dr-xr-xr-x    1 root      23 May 16 10:09 usr
lrwxrwxrwx    1 root       7 May 16 10:10 var -&gt; /rw/var&lt;/pre&gt;

&lt;p&gt;At boot, the init script would mount the tmpfs filesystem and create the directories which the softlinks point to. You can specify a maximum size when mounting the tmpfs, 8 megabytes in this example:&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;if mount -t tmpfs -o size=8m tmpfs /rw &gt;/dev/null 2&gt;&amp;1; then
    mkdir /rw/tmp /rw/var /rw/etc /rw/root
else
    # Halt and catch fire
fi&lt;/pre&gt;

&lt;p&gt;Squashfs has not been accepted into the mainline kernel, so you will need to download patches from &lt;a href="http://squashfs.sourceforge.net/"&gt;the project website&lt;/a&gt;. There is also a &lt;a href="http://www.squashfs-lzma.org/"&gt;LZMA-enhanced&lt;/a&gt; version of Squashfs, though I have not personally used this version. LZMA appears to obtain better compression ratios than the zlib which Squashfs normally uses.&lt;/p&gt;

&lt;p&gt;For the root directory ("/"), the kernel has to be able to recognize and mount the filesystem without relying on any external mount program. This is a chicken and egg problem: there is no way to run a mount program until there is a filesystem mounted. To use squashfs for the root filesystem, linux/init/do_mounts.c must be modified to recognize its magic number. A &lt;a href="http://denton.gentry.googlepages.com/cramfsandsquashfsdiffs"&gt;patch&lt;/a&gt; is available, if your kernel source does not already have this handling. The patch also handles a cramfs root filesystem.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;Option #2: cramfs instead of squashfs&lt;/span&gt;
&lt;p&gt;&lt;a href="http://sourceforge.net/projects/cramfs/"&gt;cramfs&lt;/a&gt; is an older compressed, read-only filesystem for Linux, developed by Linus Torvalds. Cramfs works quite well and is included in the kernel sources. However squashfs typically achieves better compression, because it uses larger blocks in zlib. In one project I worked on a 20 Meg cramfs filesystem became about 18 Megs with squashfs.&lt;/p&gt;

&lt;p&gt;Like squashfs, cramfs must be paired with a second, writable ramdisk to be useful.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;Option #3: ext2 instead of tmpfs&lt;/span&gt;
&lt;p&gt;ext2 can be use to provide a writeable ramdisk, though there are relatively few reasons to use it for this purpose as opposed to tmpfs. Older tmpfs releases did not support certain filesystem features like sendfile(), but this is not an issue for most applications.&lt;/p&gt;

&lt;p&gt;Nonetheless if there is a reason to do so, an ext2 filesystem can be created in a /dev/ram# device and mounted as a ramdisk. ext2 has to be formatted before it can be used, which would normally mean bundling mke2fs into your target image. However there is another way to create an ext2 which is generally smaller than mke2fs. Empty, zero filled ext2 filesystems compress extremely well using bzip2. You can create a filesystem of the appropriate size while compiling the target image, by running commands on your build system:&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;dd if=/dev/zero of=/tmp/e2file bs=1024 count=8192
sudo losetup /dev/loop7 /tmp/e2file
sudo mke2fs -b 4096 -m 1 -i 16384 /dev/loop7
sudo tune2fs -c -1 /dev/loop7
sudo losetup -d /dev/loop7
bzip2 -9 /tmp/e2file&lt;/pre&gt;

&lt;p&gt;The bs and count arguments to dd specify the size of file to create, filled with zeros. We use a /dev/loop device to create a new ext2 filesystem in this file, and then compress it. The result should be a couple hundred bytes in size, far smaller than mke2fs would be. The e2file.bz2 is copied into the target filesystem, and mounted by the boot scripts like so:&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;bunzip2 -c /e2file.bz2 &gt;/dev/ram2 2&gt;/dev/null
if mount -t ext2 /dev/ram2 /rw &gt;/dev/null 2&gt;&amp;1; then
    mkdir /rw/tmp /rw/var /rw/etc /rw/root
else
    # Halt and catch fire
fi&lt;/pre&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;Option #4: JFFS2 instead of tmpfs&lt;/span&gt;
&lt;p&gt;In the discussion of the previous alternatives the read-only portion of the filesystem would be compressed in memory using squashfs or cramfs, but the writable portion would be stored uncompressed. If your system requires a large amount of ramdisk space but your memory is constrained, the natural solution is to look for a way to compress it.&lt;/p&gt;

&lt;p&gt;If the applications generating this data can be modified to use &lt;a href="http://www.zlib.net/"&gt;zlib&lt;/a&gt; and compress their own output, that is probably the best way to proceed. If you cannot modify the apps, there are a couple ways to get compression at the filesystem layer. The only natively compressed, writable filesystem for Linux I know of is &lt;a href="http://sourceware.org/jffs2/"&gt;JFFS2&lt;/a&gt;, which is not designed to be a ramdisk but can be pressed into service if necessary using the mtdram.o module (which exists to ease debugging of MTD applications). The vast majority of the complexity in JFFS2, for handling erase blocks and wear leveling and all of the other nuances of flash chips, is wasted when used as a ramdisk, but it does provide compression. The boot scripts would work like so:&lt;/p&gt;

&lt;pre style="font-family: Courier New,Courier,fixed; font-size: small; margin:1em 20px; line-height: 1.2em;"&gt;/sbin/insmod -q /lib/modules/mtdram.o total_size=8192 erase_size=16
if mount -n -t jffs2 -o umask=0666 /dev/mtdblocka /rw &gt;/dev/null 2&gt;&amp;1; then
    mkdir /rw/tmp /rw/var /rw/etc /rw/root
else
    # Halt and catch fire
fi&lt;/pre&gt;

&lt;p&gt;JFFS2 consumes a great deal more CPU time than other filesystems, due to the compression. The throughput to a compressed ramdisk will be relatively low. On one platform I worked on a tmpfs ramdisk could handle writes at about 40 MBytes/sec while the JFFS2 ramdisk managed only 1 MByte/sec.&lt;/p&gt;

&lt;p&gt;Note that this technique does not save as much memory as you might think. Whenever blocks are accessed they are decompressed by JFFS2 into the buffer cache, then copied up to the application. Likewise written blocks are held uncompressed in the buffer cache. If your system touches a large amount of data in the ramdisk, the footprint of the buffer cache will become more significant than the backing filesystem in JFFS2. This is another advantage of modifying applications to use zlib: the data remains compressed in the buffer cache, and is only decompressed within the application itself.&lt;/p&gt;

&lt;p&gt;There are other ways to implement a compressed, writable filesystem, but I have not used them myself and can't add anything pithy about them. Some links:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.knoppix.net/wiki/Cloop"&gt;cloop&lt;/a&gt; is a compressed block loopback device, allowing any filesystem to be mounted atop it&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sourceforge.net/projects/e2compr"&gt;e2compr&lt;/a&gt; patches compression support into ext2/ext3. Development was moribund for a long time, it is not clear to me how stable these patches are.&lt;/li&gt;
&lt;/ul&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:115%;"&gt;Option #5: ext2 for everything&lt;/span&gt;
&lt;p&gt;In large measure ext2 and the related ext3 are the &amp;quot;default&amp;quot; filesystem for Linux. When building a Linux desktop or server ext[23] is a reasonable solution, and the one generally chosen barring good reason to do otherwise. This notion of ext2 as the default often carries over into embedded systems as well, though there is far less advantage to doing so. It is certainly possible to create a large ext2 ramdisk (using the /dev/loop technique shown above) and use it to store both the application binaries as well as provide scratch space for /tmp et al. This does have the appeal of requiring only one filesystem, rather than the combinations recommended earlier.&lt;/p&gt;

&lt;p&gt;The memory footprint of an ext2 ramdisk is always going to be larger than the options described above. It is common practice to create the ext2 filesystem in a file, gzip it, and compile the gzipped binary into the target image. This reduces the image size, but at boot the kernel will decompress the &lt;i&gt;entire&lt;/i&gt; filesystem into memory. So if you have a 40 Meg ext2 which gzips down to 10 Megs, it will only add 10 Megs to the image size but expand to 40 Megs of RAM on the target. Compare this to squashfs, where a 10 Meg filesystem adds 10 Megs to the image size and also consumes 10 Megs of RAM on the target. The buffer cache does not perturb the result: when using either ext2 or squashfs, any files in active use will be present in the buffer cache and the footprint would be the same in both cases.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:120%;"&gt;Postscript&lt;/span&gt;
&lt;p&gt;This turned into an exceedingly long post. Early drafts were even &lt;i&gt;longer&lt;/i&gt;, and much material got left on the cutting room floor. I'll collect more random musings on embedded filesystems into a &lt;a href="http://codingrelic.geekhold.com/2008/06/more-random-musings-on-embedded.html"&gt;future post&lt;/a&gt;.&lt;/p&gt;

&lt;br/&gt;&amp;nbsp;&lt;br/&gt;
&lt;span style="font-weight: bold; font-size:120%;"&gt;Updates&lt;/span&gt;
&lt;p&gt;A comment from &lt;a href="http://www.blogger.com/profile/17062446164993461351"&gt;Lance&lt;/a&gt; pointed to a &lt;a href="http://lkml.org/lkml/2008/5/31/98"&gt;discussion on LKML&lt;/a&gt; about extending cramfs to be writable. Changes made to files would be stored in the page cache, and as far as I can tell would be stored in uncompressed form. The same technique could presumably be applied to squashfs. This would make things a bit simpler for those who need a small amount of writable ramdisk.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://disqus.com/people/78ea58bbde676c2b1c5ab50c3abad7e9/"&gt;Tom Ziomek&lt;/a&gt; notes that SquashFS has been &lt;a href="http://lwn.net/Articles/305083/"&gt;accepted&lt;/a&gt; into the mainline kernel in 2.6.29. Scroll down to see Tom's comment; since this article appeared this blog switched to using &lt;a href="http://disqus.com"&gt;Disqus&lt;/a&gt;, so you see the older comments first followed by the recent comments in a separate Disqus section.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-256731789305654284?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=p03wNE8w"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=p03wNE8w" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=h84M7X3j"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=h84M7X3j" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/KcnVWtD3xr4" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=256731789305654284" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/256731789305654284?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/256731789305654284?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/05/random-musings-on-embedded-filesystems.html" title="Random Musings on Embedded Filesystems" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry gd:etag="W/&quot;C0MMSXo8eyp7ImA9WxJSF0U.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-3684082777815369831</id><published>2008-05-08T21:27:00.000-07:00</published><updated>2009-05-08T04:18:08.473-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-05-08T04:18:08.473-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="CPU" /><title>The High-Level CPU Response</title><content type="html">&lt;p&gt;This time, Gentle Readers, we will explore something completely different. It is still related to embedded systems, for a suitably generous definition of &amp;quot;embedded.&amp;quot; Several weeks ago I read a posting by Yossi Kreinin regarding &lt;a href="http://www.yosefk.com/blog/the-high-level-cpu-challenge.html"&gt;CPU architectures&lt;/a&gt; optimized for very high level languages, plus a &lt;a href="http://www.yosefk.com/blog/high-level-cpu-follow-up.html"&gt;followup&lt;/a&gt; on the same topic.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;i&gt;Yossi said:&lt;/i&gt;&lt;br/&gt;
Do you think high-level languages would run fast if the stock hardware weren't &amp;quot;brain-damaged&amp;quot;/&amp;quot;built to run C&amp;quot;/&amp;quot;a von Neumann machine (instead of some other wonderful thing)&amp;quot;? You do think so? I have a challenge for you. I bet you'll be interested.&lt;br/&gt;
...&lt;br/&gt;
My challenge is this. If you think that you know how hardware and/or compilers should be designed to support HLLs, why don't you actually tell us about it, instead of briefly mentioning it?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;br/&gt;
&lt;p&gt;Really, the challenge would not normally appeal to me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I generally don't spend time on high-level languages. I make my living in the guts of the system writing assembly and C code, where Lisp/Haskell/Erlang/&amp;lt;ShinyNewLanguage&amp;gt;/etc do not play.&lt;/li&gt;
&lt;li&gt;I like C. Really, I do. I like assembly coding too. Really, I do.&lt;/li&gt;
&lt;li&gt;I think &lt;a href="http://en.wikipedia.org/wiki/Von_Neumann_architecture"&gt;von Neumann architectures&lt;/a&gt; are fine.&lt;/li&gt;
&lt;/ul&gt;

&lt;br/&gt;
&lt;p&gt;However in the past few years I've worked on three different ASICs involving custom embedded CPUs for the datapath (more accurately: two CPUs and one glorified state machine), and Yossi's challenge is interesting in a contemporary problem space. So I'll give it a whirl. Be warned, Gentle Readers, that it will probably suck. You have been warned.&lt;p/&gt;


&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;The instruction set&lt;/span&gt;
&lt;p&gt;&lt;img style="padding:6px; border:0px;" src="http://bp0.blogger.com/_WibILqsOlLg/SB_fTxORMlI/AAAAAAAAAG0/qK3TkKDJlJA/s320/IntelAtomSmall.jpg" border="0" width="106" height="256" align=right alt="Intel Atom CPU" title="It looks bigger than an Atom. I think they should have called it the Intel Molecule."/&gt;
In 2008, I believe the instruction set is the wrong place to look for these performance gains. The overwhelming effort put into optimizing the performance of current CPU architectures represents an enormous barrier to entry for a new instruction set. Intel itself has not made much headway with &lt;a href="http://search.theregister.co.uk/?q=itanium"&gt;Itanium&lt;/a&gt;, unable to successfully compete with its own x86 products. A decade ago RISC was hailed as the superior alternative, but x86 now stands over the dead bodies of several of its erstwhile RISC competitors: Alpha and PA-RISC, for a start. Modern CPU instruction sets are pragmatically extended with new opcodes as needed, witnessed by the sheer number of times Intel and AMD have added more vector instructions to their x86 implementations.&lt;/p&gt;

&lt;p&gt;Also (and crucially) CPU architectures so specialized as to only run code from a single programming language tend not to survive. There is a reason that &lt;a href="http://en.wikipedia.org/wiki/Lisp_machine"&gt;Lisp Machines&lt;/a&gt; and the &lt;a href="http://en.wikipedia.org/wiki/Pascal_MicroEngine"&gt;P-system CPUs&lt;/a&gt; no longer exist: they weren't terribly adaptable.&lt;/p&gt;

&lt;p&gt;So I won't be suggesting a radically new instruction set.&lt;/p&gt;


&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Exploring the problem space&lt;/span&gt;
&lt;p&gt;As I see it, there are three standout features of various high level languages which one might attempt to optimize for in the CPU design:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;dynamic typing: Type checks are performed at run time, not compile time like C or Java&lt;/li&gt;
&lt;li&gt;stackless languages: like &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt;, to support astonishing numbers of threads&lt;/li&gt;
&lt;li&gt;functional languages: functions have no side effects and there is no global state. The complete state a function operates on will be passed to it, by &lt;i&gt;value&lt;/i&gt; not by reference&lt;/li&gt;
&lt;/ol&gt;

&lt;br/&gt;
&lt;p&gt;For #1, Dynamic Typing: I don't think pushing type information down to be directly visible to the CPU is really productive. I'm quite likely wrong in this (there have certainly been a &lt;a href="http://compilers.iecc.com/comparch/article/91-04-082"&gt;large&lt;/a&gt; &lt;a href="http://pt.withy.org/publications/VLM.html"&gt;number&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/Burroughs_large_systems"&gt;of&lt;/a&gt; &lt;a href="http://fare.tunes.org/tmp/emergent/kmachine.htm"&gt;processors&lt;/a&gt; over the years with tagged arithmetic operations), but I'm also not really qualified to comment further. So I won't.&lt;/p&gt;

&lt;p&gt;For #2, Stacklessness: Many CPU designs have precious little hardware dedicated to &amp;quot;the stack.&amp;quot; For example, in MIPS32 by convention two registers are used to store sp and fp, but they are really just normal registers and the compiler uses regular ADD/MOV/etc instructions to adjust them. Other CPU families may have more hardware supporting the concept of a stack, but I just don't think there is much to be gained by removing it. There is also a whole lot to be lost, if it is no longer possible to run stack-based languages like C.&lt;/p&gt;

&lt;p&gt;For #3, Functional Languages: I believe this is the topic which has the most traction, and is where we'll focus for the rest of this writeup. Passing a large footprint of arguments by value implies a great deal of copying. Of course the runtime for these languages make efforts to avoid unnecessary copies, but perhaps modifications to the CPU can help the runtime perform this function more efficiently.&lt;/p&gt;


&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Avoiding unnecessary copies&lt;/span&gt;
&lt;p&gt;Though the programming model for a functional language may guarantee each function call receives its own independent copy of all arguments, in implementation the runtime will avoid copying large amounts of data unnecessarily. If a function only reads and never writes an argument, it can be allowed to reference the parent's copy of the argument. The crucial bit, of course, is ensuring that there is no code path which results in writing to the parent data.&lt;/p&gt;

&lt;p&gt;If the functional language compiles down to machine instructions, then any possible code path which results in modification of an argument makes it unsafe to reference the parent data. Even if the language is implemented by a VM, there is overhead in checking each store bytecode at runtime to ensure its target is allowed.&lt;/p&gt;

&lt;p&gt;In other problem spaces there is a well-understood technique for optimistically allowing a child a read-only reference to the parent's data: &lt;a href="http://en.wikipedia.org/wiki/Copy-on-write"&gt;Copy On Write&lt;/a&gt;. The relevant pages of memory will be mapped read-only. If a process attempts to write to this page it will trap into the operating system, which will make a private copy of the page for the process to modify. If Copy on Write were available, the functional language runtime would have another option available.&lt;/p&gt;

&lt;p&gt;The trouble with applying existing Copy on Write mechanisms to functional languages is the sheer frequency of mappings. The current uses of Copy on Write are infrequent: process creation, periodic (but rare) snapshots of data, etc. The overhead of calling into the Operating System to change page tables is acceptable for these uses. The overhead of calling into the OS to change page tables &lt;i&gt;for every function call&lt;/i&gt; would likely be prohibitive.&lt;/p&gt;


&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Overview of the proposal&lt;/span&gt;
&lt;p&gt;
&lt;blockquote&gt;&lt;i&gt;&amp;quot;There is no problem in computer programming which cannot be solved by an added level of indirection.&amp;quot;&lt;/i&gt; &lt;font size="-1"&gt;-- &lt;a href="http://en.wikipedia.org/wiki/Maurice_Vincent_Wilkes"&gt;Dr Maurice Wilkes&lt;/a&gt;&lt;/font&gt;&lt;/blockquote&gt;

64 bit CPUs are now the norm in the general purpose computing market. This is a truly staggering amount of virtual address space. If the application code could manage a section of its own address space with very low overhead, it could create and destroy mappings as needed. For cases where it makes sense, the language runtime could set up a read-only alias mapping of the parent data, and pass the alias address to the function. If the function attempts to modify its arguments there would be a trap, and a copy could be made.&lt;/p&gt;

&lt;p&gt;Before delving further lets examine when it makes sense to do this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;if the function cannot possibly write to its arguments, and the runtime can be certain of this, there is no reason to create an aliased mapping. Just pass in the parents data.&lt;/li&gt;
&lt;li&gt;if one or more of the arguments to the function are relatively small, just copy them. The brute force solution is cheaper than trying to finesse it.&lt;/li&gt;
&lt;li&gt;if the function &lt;i&gt;might&lt;/i&gt; modify its arguments and they are large, we have a candidate for an aliased argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;br/&gt;
&lt;img style="padding:3px; border:0px;" src="http://bp0.blogger.com/_WibILqsOlLg/SCKNeaRjdzI/AAAAAAAAAHw/09xnXX5OYdM/s320/TLB1.png" border="0" width="316" height="144" align=right alt="Aliasing TLB" title="I love OmniGraffle for technical illustrations!"/&gt;
&lt;p&gt;What would the hardware need to provide, the Gentle Reader might ask? Nothing much really, just a second MMU TLB. Simple. Ok, I kid, thats actually an enormous burden to place on the hardware, but I think it might be worthwhile. This is how I think it would work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I'll refer to the new TLB as the Aliasing TLB, which is looked up first. I'll refer to the existing MMU as the System TLB.&lt;/li&gt;
&lt;li&gt;The CPU has a pair of limit registers, giving an upper and lower bound of addresses to be looked up in the Aliasing TLB. Most processes would probably not use the Aliasing TLB at all, and the limit registers would be zero.&lt;/li&gt;
&lt;li&gt;Applications using this mechanism would be given a section of their address space to manage - an exabyte should do. Whenever there is a context switch, system code (i.e. the kernel) updates the limit registers for the new running process.&lt;/li&gt;
&lt;li&gt;Memory operations whose target is within the range of the limit registers would be looked up in the Aliasing TLB. We'll come back to the miss case later, for now assume an entry is found.&lt;/li&gt;
&lt;li&gt;The result of the Aliasing TLB lookup is a mapping to another &lt;i&gt;virtual&lt;/i&gt; address, plus permission bits.&lt;/li&gt;
&lt;/ol&gt;

&lt;br/&gt;
&lt;p&gt;If the permissions allow the operation, the CPU now has a new virtual address to use as the target of the memory operation. This new virtual address (labelled Va in the diagram) proceeds down the &amp;quot;traditional&amp;quot; CPU MMU path, searching the System TLB to find the physical address. If the original target address was not within the range of the limit registers, then the unmodified virtual address goes to the System TLB to be translated to a physical address.&lt;/p&gt;


&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Handling a miss&lt;/span&gt;
&lt;p&gt;Everything is fine and dandy if the CPU finds a matching entry in the Aliasing TLB, but what if it doesn't? I'm very fond of software-managed TLBs like MIPS and Sparc v9, and had initially written up a section on how the CPU could directly vector over to a TLB miss handler in the application's address space. The handler would need to be locked into memory so it could never be paged out, and immediately switch to a pre-allocated stack specifically for TLB handling, and ... yeah, it was really complicated.&lt;/p&gt;

&lt;p&gt;On further reflection, I don't think it needs to be that complicated. There are two characteristics of the Aliasing TLB which are different from the System TLB: entries are added to the Aliasing TLB immediately before they are used, and the entries only live for the span of one function call - generally speaking, a very short lifespan.&lt;/p&gt;

&lt;p&gt;When the OS adds a new entry to page tables the new entry is generally not immediately added to the System TLB, instead it will be added the first time the TLB faults on the new address. With the Aliasing TLB, when a new entry is added it should immediately be pushed into the hardware before resuming processing. We're expecting to immediately hit that address, and the number of Aliasing TLB misses would be dramatically reduced by doing so. Perhaps the misses which remain can take a more normal path of trapping into the OS, which would perform an upcall to the application to service the Aliasing TLB miss.&lt;/p&gt;


&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Page sizes&lt;/span&gt;
&lt;p&gt;Function arguments can be of any size and alignment, and may not be aligned to any particular boundary. An earlier draft of this posting proposed segmented memory support, to handle arbitrary argument sizes. On further reflection, I don't think it needs to be that complicated. The Aliasing TLB mechanism only makes sense for relatively large arguments, and I think it is a reasonable tradeoff to require the runtime to pad such arguments to be an even multiple of a page size.&lt;/p&gt;

&lt;p&gt;I do think it is important to support multiple page sizes, with the normal page size of the System TLB as a lower bound and truly enormous pages at the upper end. CPU MMUs have supported multiple page sizes for a long time, though Operating Systems generally only use one size for all memory pages. This is to avoid the complexity of fragmentation if one tried to actively page out memory with multiple page sizes in use. Some OSes do use large pages to map in things which can never be paged out, like the framebuffer.&lt;/p&gt;

&lt;p&gt;Page replacement is not a concern for the Aliasing TLB: it is mapping one virtual address to another, there is no concept of removing a page from memory. If we have a 128 Megabyte argument to map, we might as well use a 128 Megabyte page to map it. Assembling a bunch of small mappings to handle a huge region is only beneficial in very specific cases. The software will take advantage of multiple page sizes in the Aliasing TLB.&lt;/p&gt;


&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;Miscellaneous notes and drawbacks&lt;/span&gt;
&lt;ul&gt;
&lt;li&gt;The most serious downside of the Aliasing TLB is the latency it adds to instruction processing. To hide that latency the CPU will need additional pipeline stages, possibly many additional stages. Nobody wants a repeat of the SuperPipelineing of the Pentium IV, the additional pipelining may be a fatal drawback of this idea.&lt;/li&gt;
&lt;li&gt;We'll need Address Space Identifiers (ASID) for the Aliasing TLB. The OS must populate the ASID of the currently running process in a CPU register, and any mappings created by the user code &lt;i&gt;must&lt;/i&gt; inherit this ASID. If we allow application code to supply the ASID we'll destroy the security of the system: it could set up nearly arbitrary mappings in another process.&lt;/li&gt;
&lt;li&gt;The map addresses in the Aliasing TLB are always looked up in the System TLB, never recursively looked up in the Aliasing TLB even if they fall within the specified range of addresses. I do not believe multiple levels of indirection add any capability to the mechanism, and the hardware complexity in the pipelining would be prohibitive.&lt;/li&gt;
&lt;li&gt;I don't think it is reasonable to expect application code to deal with page coloring, so a CPU implementing an Aliasing TLB should use physically indexed caches (or some other solution which does not burden the software with coloring). This is probably not an issue, I believe page coloring has not been employed by any common processor for about ten years. A long time ago I worked on software for a Sparc processor which required it, and still bear the scars.&lt;/li&gt;
&lt;/ul&gt;


&lt;br/&gt;
&lt;span style="font-weight: bold; font-size: 115%;"&gt;The disclaimer&lt;/span&gt;
&lt;p&gt;Would an Aliasing TLB really boost the performance of a functional language runtime, and by how much? I've no idea. That is the sort of question best answered by an analysis of the behavior of existing functional language runtimes. Analyzing instruction and memory traces is the seventh most boring task in all of computer science, which means it is very, very boring. Since I'm writing this for a blog post, I choose to skip the boring and tedious stuff in favor of emphatic handwaving.&lt;/p&gt;

&lt;p&gt;I'm not sure I've actually addressed the challenge Yossi issued. He was pretty clearly seeking CPU instruction set ideas, and I haven't provided any. Also the challenge was issued in late January and subsequent comments indicate it is pretty much wrapped up, so I'm late to the party. Nonetheless it was interesting to write this up, so to me the time was well spent.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-3684082777815369831?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=wonDQ764"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=wonDQ764" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=La7Ir074"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=La7Ir074" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/T9HURyD7Ctc" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=3684082777815369831" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/3684082777815369831?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/3684082777815369831?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/05/high-level-cpu-response.html" title="The High-Level CPU Response" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://bp0.blogger.com/_WibILqsOlLg/SB_fTxORMlI/AAAAAAAAAG0/qK3TkKDJlJA/s72-c/IntelAtomSmall.jpg" height="72" width="72" /><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry><entry gd:etag="W/&quot;DUIEQ3wzfCp7ImA9WxZaE0g.&quot;"><id>tag:blogger.com,1999:blog-9151880169490356401.post-7164266109207964271</id><published>2008-04-27T21:36:00.000-07:00</published><updated>2008-04-27T21:38:22.284-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2008-04-27T21:38:22.284-07:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Product Development" /><title>Four Circles of Product Management Hell</title><content type="html">I typically work on software for hardware platforms, where the design cycle is considerably longer than that of a desktop or web application. 18 months for a new platform is not uncommon, and it is important to nail down the main feature set early enough in the process for the hardware capabilities to be designed in.&lt;br /&gt;&lt;br /&gt;  Nailing down these requirements is the job of the &lt;a href="http://en.wikipedia.org/wiki/Product_management"&gt;product manager&lt;/a&gt;, and at this point in my career I've worked closely with about a dozen of them. They generally approach the requirements definitions using a similar set of techniques:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;visit current customers to find out what they like and dislike about the product, and what they need the product to do&lt;/li&gt;&lt;li&gt;visit prospective customers to get first impressions and feedback&lt;/li&gt;&lt;li&gt;work with the sales teams to break down barriers to a big sale (which can mean one-off feature development if the deal is big enough)&lt;/li&gt;&lt;li&gt;keep track of what the competition is doing&lt;/li&gt;&lt;li&gt;generally stay abreast of where the market is going&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;  Over time my opinion of what constitutes &amp;quot;good product management&amp;quot; have evolved considerably. I'll give a few examples that portray what the younger, naive me thought of a particular style of product management versus what the older, cantankerous me thinks.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. All requirements generated directly from customer requests&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What I used to think:&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&amp;quot;Wow, this is great. These are all features which obviously appeal to customers. We won't waste time on stuff that nobody will ever use, everything we develop will help sell the product.&amp;quot;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What I now realize:&lt;br /&gt;This is &amp;quot;Building Yesterdays Technology, Tomorrow!&amp;quot; Customers will ask for improvements and extensions to the existing functionality of your product, and that is fine. When a customer asks for something completely new, it is generally because they can &lt;u&gt;already buy it&lt;/u&gt; from a competitor. In fact if the input is via an RFQ they are probably trying to steer the deal to that particular vendor by putting in a set of criteria which only that one vendor can meet. The competitor likely helped the customer put the RFQ together, as any good salescritter can supply a list of specifications which only their product can meet.&lt;br /&gt;&lt;br /&gt;Certainly, there will always be a healthy amount of catch-up requirements in any ongoing product development. Competitors are not sitting idle, and they're probably not incompetent, so they will come up with things which your product will need to match. Any product manager will end up listing requirements which simply match what competitors already have.&lt;br /&gt;&lt;br /&gt;However there must also be a significant amount of forward-looking development as well, building the product to meet anticipated features which the customer does not yet realize they need. Constantly playing catchup only works for companies in truly commanding (i.e. monopolistic) market positions.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2. Requirements list both externally visible behavior and internal implementation details&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What I used to think:&lt;br /&gt;&amp;quot;Umm, ok. Its a little weird that the product requirements are so detailed.&amp;quot;&lt;br /&gt;&lt;br /&gt;What I now realize:&lt;br /&gt;The product manager believes themself to be a systems architect trapped in a product managers body. They almost certainly used to be an engineering individual contributor (many product managers come from a hardware or software engineering background). They likely feel they have such a firm grasp of the tradeoffs in developing the product that they can easily guide the engineers in how to proceed.&lt;br /&gt;&lt;br /&gt;Also, and I am as guilty of this as anyone: engineers often have little respect for the product manager... and the reverse is true as well. The product manager may be trying to provide a top-level design for the product because they consider the engineering team to be incapable of doing it correctly.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3. Anticipates market needs with uncanny and unfailing accuracy, down to the smallest detail&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What I used to think:&lt;br /&gt;&amp;quot;Wow, this Product Manager really knows their stuff. This is great!&amp;quot;&lt;br /&gt;&lt;br /&gt;What I now realize:&lt;br /&gt;The company is doomed, because we're in a market which is too easy. We'll have five hundred competitors, who will all build exactly the same product and end up racing to the bottom of the price curve so nobody will make any money. Polish the resume.&lt;br /&gt;&lt;br /&gt;The Gentle Reader might ask, &amp;quot;What about the product manager who can analyze a difficult and complex market to generate the perfect product requirements with 100% accuracy?&amp;quot; The answer is quite simple really: such a person &lt;i&gt;does not exist&lt;/i&gt;. We developers very much want the paragon of product management to exist, someone who can predict the unpredictable and know the unknowable, in order to satisfy some deep-seated need in our world view. However there is no such thing as omniscience, the best product manager is one who can generate a market analysis which is good enough to get some initial traction and be refined in subsequent updates.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4. Product manager is often not in the office, and only revs the requirements document sporadically&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What I thought then:&lt;br /&gt;&amp;quot;Slacker.&amp;quot;&lt;br /&gt;&lt;br /&gt;What I now realize:&lt;br /&gt;Actually, these are some of the signs of a really good product manager. When kicking off a new product they will spend a lot of time visiting customers and potential customers to gather inputs.&lt;br /&gt;&lt;br /&gt;They apply a certain amount of skepticism, filtering and summarization to the raw inputs coming from outside the development environment, but without introducing too much delay. A product manager who passes on new inputs every time they meet with a customer or return from a trade show is not a good product manager, they're just a note taker. A product manager who suffers from &lt;a href="http://en.wikipedia.org/wiki/Analysis_paralysis"&gt;analysis paralysis&lt;/a&gt;, not wanting to make a call until the correct course of action is absolutely clear, is also not a good product manager. There is a finite window of market opportunity, miss it and you'll find out from your competitors what the right thing to do was.&lt;br /&gt;&lt;br /&gt;The best product managers know not to change the requirements too often, or the development team will never finish anything. So they will not be constantly releasing new versions of the requirements spec, and any changes which are made will have gotten enough validation to be credible without taking so long in validation as to be stale.&lt;br /&gt;&lt;br /&gt;Unfortunately it is also possible that the reason they are not in the office and don't produce much is because they really are a slacker. Caveat emptor.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; font-size: 105%;"&gt;Afterthoughts&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In fairness, I should acknowledge that the product manager job entails more than just defining the product. As they fit in a role equidistant between engineering, marketing and sales, the product manager is frequently pulled in to handle crises in any of the three areas.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;as an expert user of the product, they do a lot of sales demos&lt;/li&gt;&lt;li&gt;they frequently end up training resellers (and customers) on the use of the product&lt;/li&gt;&lt;li&gt;they invariably get sent to all of the &lt;a href="http://www.interop.com/"&gt;boring&lt;/a&gt; &lt;a href="http://www.rsaconference.com/"&gt;trade&lt;/a&gt; &lt;a href="http://www.cesweb.org/"&gt;shows&lt;/a&gt;&lt;/li&gt;&lt;li&gt;they assist in preparing white papers, sales guides for particular vertical markets, etc&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Finally, for a perspective from the other side of the fence I highly recommend &lt;a href="http://www.crankypm.com/"&gt;The Cranky Product Manager&lt;/a&gt;. Entertaining, with a dash of truthiness.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9151880169490356401-7164266109207964271?l=codingrelic.geekhold.com'/&gt;&lt;/div&gt;&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=JHf4O7Zn"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=JHf4O7Zn" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~f/CodingRelic?a=3AJWvSKz"&gt;&lt;img src="http://feeds.feedburner.com/~f/CodingRelic?i=3AJWvSKz" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/CodingRelic/~4/1g05zfSwQNk" height="1" width="1"/&gt;</content><link rel="replies" type="text/html" href="https://www.blogger.com/comment.g?blogID=9151880169490356401&amp;postID=7164266109207964271" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/7164266109207964271?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/9151880169490356401/posts/default/7164266109207964271?v=2" /><link rel="alternate" type="text/html" href="http://codingrelic.geekhold.com/2008/04/four-circles-of-product-management-hell.html" title="Four Circles of Product Management Hell" /><author><name>Denton Gentry</name><uri>http://www.blogger.com/profile/11782508603268183191</uri><email>noreply@blogger.com</email><gd:extendedProperty name="OpenSocialUserId" value="05410638791985846968" /></author><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">2</thr:total></entry></feed>
