Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

#if !defined(__LOG_H__)
#define __LOG_H__

#if defined(X86_BOOT_CODE) && defined(CONFIG_ESS_LOCK_ANALYSIS)
#undef CONFIG_ESS_LOCK_ANALYSIS
#endif

#ifdef CONFIG_ESS_LOCK_ANALYSIS
#include <linux/kernel.h>
#include <linux/irqflags.h>
#include <linux/spinlock.h>
#include <linux/seqlock_types.h>
#include <linux/semaphore.h>
#include <linux/log_event.h>

#define DELIMITER	"#"
#define DELIMITER_CHAR	'#'
#define PING_CHAR	'p'
#define MK_STRING(x)	#x
#define IO_PORT_LOG	(0x00e9)

#define log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,_irqsync)	{			\
	spinlock_t *__spinlock;										\
	raw_spinlock_t *__rawspinlock;									\
	rwlock_t *__rwlock;										\
	struct mutex *__mutex;										\
	seqlock_t *__seqlock;										\
	const seqlock_t *__constseqlock;										\
	seqcount_t *__seqcount;									\
	const seqcount_t *__constseqcount;									\
	struct semaphore *__semaphore;									\
	struct rw_semaphore *__rwsemaphore;								\
	unsigned long *__bitspinlock;									\
	int status = log_preempt_count();								\
	if (_ptr == (void*)PSEUDOLOCK_ADDR_RCU) {									\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,PSEUDOLOCK_NAME_RCU,status,_irqsync);	\
	} else if (_ptr == (void*)PSEUDOLOCK_ADDR_HARDIRQ) {									\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,PSEUDOLOCK_NAME_HARDIRQ,status,_irqsync);	\
	} else if (_ptr == (void*)PSEUDOLOCK_ADDR_SOFTIRQ) {									\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,PSEUDOLOCK_NAME_SOFTIRQ,status,_irqsync);	\
	} else if (__same_type(_ptr,__spinlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,"spinlock_t",status,_irqsync);	\
	} else if (__same_type(_ptr,__rwlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,"rwlock_t",status,_irqsync);	\
	} else if (__same_type(_ptr,__mutex)) {								\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,"mutex",status,_irqsync);	\
	} else if (__same_type(_ptr,__seqlock) || __same_type(_ptr,__constseqlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "seqlock_t",status,_irqsync);	\
	} else if (__same_type(_ptr,__seqcount) || __same_type(_ptr,__constseqcount)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "seqcount_t",status,_irqsync);	\
	} else if (__same_type(_ptr,__rawspinlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "raw_spinlock_t",status,_irqsync);	\
	} else if (__same_type(_ptr,__semaphore)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "semaphore",status,_irqsync);	\
	} else if (__same_type(_ptr,__rwsemaphore)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "rw_semaphore",status,_irqsync);	\
	} else if (__same_type(_ptr,__bitspinlock)) {							\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "bit_spin_lock",status,_irqsync);	\
	} else{												\
		__log_lock(_acquire,_lockmember,_ptr,_file,_line,_function, "unknown",status,_irqsync);	\
	}												\
}

extern struct log_action la_buffer;
int log_preempt_count(void);

/* Basic port I/O */
static inline void outb_(u8 v, u16 port)
{
	asm volatile("outb %0,%1" : : "a" (v), "dN" (port));
}

static inline void log_memory(int alloc, const char *datatype, const void *ptr, size_t size) {
	unsigned long flags;
	
	raw_local_irq_save(flags);

	memset(&la_buffer,0,sizeof(la_buffer));

	la_buffer.action = (alloc == 1 ? 'a' : 'f');
	la_buffer.ptr = (unsigned long)ptr;
	la_buffer.size = size;
	/*
	 * One could use a more safe string function, e.g., strlcpy. 
	 * However, we want these *log* functions to be fast.
	 * We therefore skip all sanity checks, and all that stuff.
	 * To ensure any string buffer contains a valid string, we
	 * always write a NULL byte at its end.
	 */
	strncpy(la_buffer.type,datatype,LOG_CHAR_BUFFER_LEN);
	la_buffer.type[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	outb_(PING_CHAR,IO_PORT_LOG);

	raw_local_irq_restore(flags);
}

/*
 * The parameter irqsync should actually be an enum IRQ_SYNC. To avoid circular dependencies in include/linux/{irqflags,bottom_half}.h which contains
 * a declaration of this function, this parameter has to be an int.
 */
static inline void __log_lock(int lock_op, const char *lock_member, const void *ptr, const char *file, int line, const char *function, const char *lock_type, int preempt_count, int irq_sync) {
	unsigned long flags;

	raw_local_irq_save(flags);

	memset(&la_buffer,0,sizeof(la_buffer));

	la_buffer.action = 'l';
	la_buffer.lock_op = lock_op;
	la_buffer.ptr = (unsigned long)ptr;
	strncpy(la_buffer.type,lock_type,LOG_CHAR_BUFFER_LEN);
	la_buffer.type[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	strncpy(la_buffer.lock_member,lock_member,LOG_CHAR_BUFFER_LEN);
	la_buffer.lock_member[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	strncpy(la_buffer.file,file,LOG_CHAR_BUFFER_LEN);
	la_buffer.file[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	strncpy(la_buffer.function,function,LOG_CHAR_BUFFER_LEN);
	la_buffer.line = line;
	la_buffer.function[LOG_CHAR_BUFFER_LEN - 1] = '\0';
	la_buffer.preempt_count = preempt_count;
	la_buffer.irq_sync = irq_sync;
	outb_(PING_CHAR,IO_PORT_LOG);

	raw_local_irq_restore(flags);
}

#else
#define MK_STRING(x)	#x
#define log_lock(_acquire,_lockmember,_ptr,_file,_line,_function,_logFn)
#define log_memory(alloc, datatype, ptr, size)
#endif /* !CONFIG_ESS_LOCK_ANALYSIS */

#endif // __LOG_H__