Search code examples
linuxdebugginggdbkernel

Ignoring "value has been optimized out" errors in gdb


I'm trying to track down some refcnt leaks in the Linux kernel (specifically in the ax.25 subsystem). I'm trying to see if calls to netdev_put balance with calls to netdev_hold, and to do so I'm using breakpoints like this:

break netdev_hold
  commands
    silent
    if $_regex(dev->name, "ax")
    printf "netdev_hold %s r:%d u:%d n:%d\n", dev->name, dev->dev_refcnt->refs->counter, dev->refcnt_tracker->untracked->refs->counter, dev->refcnt_tracker->no_tracker->refs->counter
    end
    continue
  end
break netdev_put
  commands
    silent
    if $_regex(dev->name, "ax")
    printf "netdev_put %s r:%d u:%d n:%d\n", dev->name, dev->dev_refcnt->refs->counter, dev->refcnt_tracker->untracked->refs->counter, dev->refcnt_tracker->no_tracker->refs->counter
    end
    continue
  end

The goal is to only print information when we're dealing with ax* interfaces. This works, mostly, except once in a while it results in:

value has been optimized out

And that causes gdb to drop back to the (gdb) prompt. Is there a way to ignore this error so that gdb will continue in any case? I wasn't sure how to write "if the named variable is available" in gdb.

I guess I should be clear that I'm not looking to disable optimizations -- the cases in which netdev_{hold,put} is called when dev is not available are not cases in which I am interested.


Solution

  • Using the linked solution as inspiration, I ended up writing a new "is_optimized_out" convenience function, like this:

    class IsOptimizedOutFunction(gdb.Function):
        def __init__ (self):
            super ().__init__ ("is_optimized_out")
    
        def invoke (self, arg):
          val = gdb.parse_and_eval(arg.string())
          return val.is_optimized_out
    
    IsOptimizedOutFunction()
    

    That allows me to write a gdb script like this:

    break netdev_hold
      commands
        silent
        if ! $is_optimized_out("dev")
          if $_regex(dev->name, "ax")
            printf "netdev_hold %s r:%d u:%d n:%d\n", dev->name, dev->dev_refcnt->refs->counter, dev->refcnt_tracker->untracked->refs->counter, dev->refcnt_tracker->no_tracker->refs->counter
          end
        end
        continue
      end
    

    And that does exactly what I want; the printf only executes when netdev_hold is called for ax* devices, and doesn't abort if the value of dev isn't available.


    NB: You might look at this and ask yourself (or me), "why not write it like this?":

    if ! $is_optimized_out("dev") && $_regex(dev->name, "ax")
      printf ...
    end
    

    It appears that gdb doesn't implement short-circuit behavior in boolean expressions, so even if ! $is_optimized_out() is false, the second expression also gets evaluated, causing the commands to abort with the value has been optimized out error.