1. 程式人生 > >除錯技巧(二):BUG_ON(), WARN_ON()和panic()

除錯技巧(二):BUG_ON(), WARN_ON()和panic()

核心中有許多地方呼叫類似BUG()的語句,它非常像一個核心執行時的斷言,意味著本來不該執行到BUG()這條語句,一旦執行即丟擲Oops。 BUG()的定義為:

#define BUG() do { \
	printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); \
	panic("BUG!"); \
} while (0)
#endif

其中的panic()定義在kernel/panic.c中,會導致核心崩潰,並列印Oops。比如arch/arm/kernel/dma.c中的enable_dma()函式:
 

void enable_dma (unsigned int chan)
{
	dma_t *dma = dma_channel(chan);

	if (!dma->lock)
		goto free_dma;

	if (dma->active == 0) {
		dma->active = 1;
		dma->d_ops->enable(chan, dma);
	}
	return;

free_dma:
	printk(KERN_ERR "dma%d: trying to enable free DMA\n", chan);
	BUG();
}

上述程式碼的含義是,如果在dma->lock不成立的情況下,驅動直接呼叫了enable_dma(),實際上意味著核心的一個bug。


BUG()還有一個變體叫BUG_ON(),它的內部會引用BUG(),形式為

#define BUG_ON(condition)           \
do {                                \
    if (unlikely(condition)) BUG(); \
} while(0)

對於BUG_ON()而言,只有當括號內的條件成立的時候,才丟擲Oops。比如drivers/char/random.c中的類似程式碼:
 

static void push_to_pool(struct work_struct *work)
{
    struct entropy_store *r = container_of(work, struct entropy_store
    push_work);
    BUG_ON(!r);
    _xfer_secondary_pool(r, random_read_wakeup_bits/8);
    trace_push_to_pool(r->name, r->entropy_count >> ENTROPY_SHIFT);
    r->pull->entropy_count >> ENTROPY_SHIFT);
}

除了BUG_ON()外,核心有個稍微弱一些WARN_ON(),在括號中的條件成立的時候,核心會丟擲棧回溯,但是不會panic(),這通常用於核心丟擲一個警告,暗示某種不太合理的事情發生了。

如在kernel/locking/mutexdebug.c中的debug_mutex_unlock()函式發現mutex_unlock()的呼叫者和mutex_lock()的呼叫者不是同
一個執行緒的時候或者mutex的owner為空的時候,會丟擲警告資訊。

void debug_mutex_unlock(struct mutex *lock)
{
    if (likely(debug_locks)) {
        DEBUG_LOCKS_WARN_ON(lock->magic != lock);
        if (!lock->owner)
            DEBUG_LOCKS_WARN_ON(!lock->owner);
        else
            DEBUG_LOCKS_WARN_ON(lock->owner != current);
        DEBUG_LOCKS_WARN_ON(!lock->wait_list.prev && !lock->wait_li
        mutex_clear_owner(lock);
    }
}

小技巧:

WARN_ON()可以作為一個除錯技巧。比如,我們進到核心某個函式後,不知道這個函式是怎麼一級一級被呼叫進來的,那可以在該函式中加入一個WARN_ON(1)。