1 Overview
To be continued.
2 Consepts
2.1 Common ARM Exceptions
Data abort: initiated when trying to access bad memory locations/Limited on access permission.
Data Aborts (common case)
Prefetch (instruction) abort: initiated when trying to read the next instruction from a bad memory location.
- Undefined instruction abort: initiated when trying to execute bad instruction code.
Prefetch Aborts/Undefined instructions (Cases that usually indicate a bad device or memory errors/corruption)
2.2 Virtual kernel memory layout:
vector : 0xffff0000 - 0xffff1000 ( 4 kB) fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) vmalloc : 0xdd000000 - 0xff000000 ( 544 MB) lowmem : 0xc0000000 - 0xdcb00000 ( 459 MB) pkmap : 0xbfe00000 - 0xc0000000 ( 2 MB) modules : 0xbf000000 - 0xbfe00000 ( 14 MB) .text : 0xc0008000 - 0xc0860d98 (8548 kB) .init : 0xc0861000 - 0xc0898c40 ( 224 kB) .data : 0xc089a000 - 0xc0957b20 ( 759 kB) .bss : 0xc0b00024 - 0xc0ce1820 (1926 kB)
kernel/Documentation/arm/memory.txt
2.3 Exceptions
Events that alters the normal sequence of execution and force the processor to execute special instructions in a privileged state
2.3.1 Classify
2.3.1.1 Synchronous Exceptions
- FAULT
Program allowed to restart after condition is corrected. No loss of continuity
E.g. Page FAULT
- TRAPS
Triggered when there is no need to re-execute the terminated instruction
- ABORT
Serious Error, Program terminated
Ex. divide by zero
2.3.1.2 Asynchronous
- Interrupts
An interrupt or an exception handler is not a process. It is a kernel control path
2.3.2 Exception Handling
ARMv7 has 8 Different Exceptions
1.reset(svc), 0x00
2.undefined instruction(Und), 0x04
3.supervisor call (svc), 0x08
4.pre-fetch abort(adt), 0x0C
5.data abort(adt), 0x10
6.not used, 0x14
7.irq(irq) 0x18
8.fiq(fiq) 0x1C
Two step Process in Linux
Exception Stub – Stack Maintenance and Switch to SVC Mode
Exception Specific Op (ASM) -> Exception Handler (C Code)
Data Abort:
- Data Access Transaction Failed
- Load/Store Instructions
Precise: Internal Aborts from the core – MMU Protection Fault
Imprecise: typically External Aborts – Unrecoverable
Pre-fetch Abort:
- Unable to fetch instruction from Memory
Architecture returns information in
FSR (Fault Status Register) of MMU
FAR (Fault Address Register)
3 Debuging Method& featues
3.1 Linux Kernel Debug Configs
CONFIG_DEBUG_SPINLOCK
With this option enabled, the kernel catches operations on uninitialized spinlocks and various other errors (such as unlocking a lock twice).
CONFIG_DEBUG_SPINLOCK_SLEEP
This option enables a check for attempts to sleep while holding a spinlock. In fact, it complains if you call a function that could potentially sleep, even if the call in question would not sleep.
CONFIG_DEBUG_STACKOVERFLOW
Additional Kernel Debug Features: slub debug, spinlock etc
3.2 Watchdog Bark
If the Linux kernel cannot schedule the watchdog kicking work queue because either the processor is spinning around in an int-locked state or the processor is hanging due to a bus freeze, watchdog bark FIQ is invoked.
Watchdog bark FIQ is handled in TrustZone (TZ) code.
The TZ FIQ handler checks to see if watchdog bark has happened and saves the register values, such as Program Counters (PC), link registers (LR), R13, etc., for all CPUs.
To debug show:
- CPU registers at the time of watchdog bark
- Call stack for all Linux processes at the time of failure
- Items in global work queue at the time of failure
For Crash dumps
- CPU Register Dump
- Process Call Stack
- Kernel dmesg
- Interrupt Status
- Kernel Global Work Queue
Checking
- Check list of process stacks, generally safe to ignore those with schedule() as their most recent function call.
- Focus on stacks with preempt_schedule – this means something had to be forcefully pre-empted – didn’t call a sleeping API.
- Focus on mutex_lock calls
- Focus on softirq threads & threaded ISRs
3.4 Kernel – Low Memory Killer
Description
Low memory killer is part of the kernel that monitors the remaining available memory.
If the available memory goes under the determined threshold, it chooses a victim process and kills it.
It calculates available memory by summing up active + inactive pages.
If available memory size goes down to a threshold, e.g., 6144*4 Kbytes, it picks the process that has a 15 adj value and kills it until it gets enough memory back.
It sends SIGKILL to the process to kill it. More debug messages could be printed by changing lowmem_debug_level.
This is an important performance tuning point.
3.5 Kernel – sys/proc fs List
3.6 Kernel – Apps Hardware Watchdog
3.7 Memory corruption
Some driver stomping on to memory
How to detect
CONFIG_SLUB_DEBUG_ON – Kernel debug
Look for poison/redzone overwritten warning in kernel log
Write after free symptom
Code review
Who allocates and releases memory
4 Kernel Error machanism
- bug
- oops
- panic
Occurs when the system has hit some fatal condition
Generally the system could not access memory
4.1 WARN_ON
System has detected something bad but not fatal
Should not be ignored!
Generally give the location where the warning occurred
WARNING: at
4.2 BUG() or BUG_ON()
bug属于轻微错误,比如在spin_lock期间调用了sleep,导致潜在的死锁问题,等等
Variation on kernel panic
Programmer put some checks in to detect bad state
If bad state occurs, kernel intentionally dereferences NULL pointer to crash System
Will see __bug in the backtrace
General way to check the sanity of the variable status in kernel
CONFIG_BUG needs to be set
If CONFIG_DEBUG_BUGVERBOSE is set, it is printing the filename and line number showing where the BUG line is and causes a kernel panic; otherwise, it is just hanging.
Explicit kernel BUGs (will use undefined instruction panic handler, but will print out a “kernel BUG at” message)
- Scheduling while atomic
When handling and IRQ or interrupts are disabled can’t call schedule
IRQs and interrupts are supposed to be quick, scheduling is long
4.3 Oops message
oops代表某一用户进程出现错误,需要杀死用户进程。这时如果用户进程占用了某些信号锁,所以这些信号锁将永远不会得到释放,这会导致系统潜在的不稳定性。
arch/arm/kernel/trap.c
Oops message is generated from:
Oops – Undefined instruction
Oops – Bad mode
Oops – Bad syscall
4.4 panic
panic是严重错误,代表整个系统崩溃
kernel/panic.c
panic_on_oops
Kernel panic - not syncing: Fatal exception
in_interrupt
Kernel panic - not syncing: Fatal exception in interrupt
- Page fault
ll
arch/arm/mm/fault.c
1 Alignment exception
2 Unhandled prefetch abort
3 Unable to handle kernel address (kernel page fault)
4 Unable to handle page fault (user page fault)
Debuging datas
1 Dmesg Buffer
1.1 Buffer define
/kernel/kernel/printk.c
char __log_buf[__LOG_BUF_LEN];
static char *log_buf = __log_buf;
static int log_buf_len = __LOG_BUF_LEN;
#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
1.2 Buffer size define
/kernel/init/Kconfig
config LOG_BUF_SHIFT
int "Kernel log buffer size (16 => 64KB, 17 => 128KB)"
range 12 21
default 17
help
Select kernel log buffer size as a power of 2.
Examples:
17 => 128 KB
16 => 64 KB
15 => 32 KB
14 => 16 KB
13 => 8 KB
12 => 4 KB
1.3 Buffer size define example
CONFIG_LOG_BUF_SHIFT=17
128KB
2 Locat Buffer
2.1 驱动层
定义LOG名称
drivers/staging/android/logger.h
#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
#define LOGGER_LOG_SYSTEM "log_system" /* system/framework messages */
#define LOGGER_LOG_MAIN "log_main" /* everything else */
drivers/staging/android/logger.c
#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \
static unsigned char _buf_ ## VAR[SIZE]; \
static struct logger_log VAR = { \
.buffer = _buf_ ## VAR, \
.misc = { \
.minor = MISC_DYNAMIC_MINOR, \
.name = NAME, \
.fops = &logger_fops, \
.parent = NULL, \
}, \
.wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \
.readers = LIST_HEAD_INIT(VAR .readers), \
.mutex = __MUTEX_INITIALIZER(VAR .mutex), \
.w_off = 0, \
.head = 0, \
.size = SIZE, \
};
DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 256*1024)
DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS, 256*1024)
DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO, 256*1024)
DEFINE_LOGGER_DEVICE(log_system, LOGGER_LOG_SYSTEM, 256*1024)
符号表
_buf_log_main[256*1024];
_buf_log_events[256*1024];
_buf_log_radio[256*1024];
_buf_log_system[256*1024];
注册设备
logger_init
ret = init_log(&log_main);
ret = init_log(&log_events);
ret = init_log(&log_radio);
ret = init_log(&log_system);
init_log
ret = misc_register(&log->misc);
对应设备节点
/dev/misc/log_main
/dev/misc/log_events
/dev/misc/log_radio
/dev/misc/log_system
发送创建/dev/misc/log_XXX节点的event,设备节点的创建将由system/core/init/devices进行创建。
2.2 中间层init
system/core/init/devices.c
对应处理设备event
handle_device_fd
创建设备/dev/log目录
名字格式化成main、radio、events和system
else if(!strncmp(uevent->subsystem, "misc", 4) &&
!strncmp(name, "log_", 4)) {
base = "/dev/log/";
mkdir(base, 0755);
name += 4;
}
然后创建设备真正的节点
/dev/log/main
/dev/log/radio
/dev/log/events
/dev/log/system
if (!devpath_ready)
snprintf(devpath, sizeof(devpath), "%s%s", base, name);
if(!strcmp(uevent->action, "add")) {
make_device(devpath, uevent->path, block, uevent->major, uevent->minor);
if (links) {
for (i = 0; links[i]; i++)
make_link(devpath, links[i]);
}
}
2.3 应用层logcat
system/core/logcat/logcat.cpp
main
devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm');
dev->fd = open(dev->device, mode);
android::readLogLines(devices);