http://review.sonyericsson.net
repo init -u git://review.sonyericsson.net/platform/manifest -b edream6.0-riogrande-plus-release
一、DMS01179806 (The illumination is still lit after auto power off by battery exhausted.)
1. 进入手机/sys/devices/platform/nmk-i2c.2/i2c-2/2-0040/leds/ 或 sys/class/leds/目录list如下
pwr-red,pwr-green,pwr-blue //控制充电led
l-key-red,l-key-green,l-key-blue //控制left button led
m-key-red,m-key-green,m-key-blue //控制middle button led
r-key-red,r-key-green,r-key-blue //控制right button led
lcd-backlight //控制lcd backlight led
设置其中brightness的值,可以调整各led的显示亮度
2. https://wiki.sonyericsson.net/androiki/Fuel_Gauge (SysFS interfaces)
Disable charging: adb shell "echo 0 > /sys/ab8500_chargalg/chargalg",用于得到没有充电的log
Enable AC dedicated charger: adb shell "echo 1 > /sys/ab8500_chargalg/chargalg"
Enable PC-USB and USB wall charger: adb shell "echo 2 > /sys/ab8500_chargalg/chargalg"
3. Kernel 有log输出
"SysRq : Emergency Remount R/O"
“Emergency Remount complete” 位于函数do_emergency_remount@kernel/fs/super.c
“Shutting down AS3676” 位于函数 as3676_shutdown@kernel/drivers/leds/leds-as3676.c
"CPU1: shutdown"
"Power down" 位于函数kernel_power_off@kernel/kernel/sys.c
Java 有log输出
“Performing low-level shutdown...” 位于函数 rebootOrShutdown@frameworks/base/core/java/com/android/internal/app/shutdownthread.java
4. 手机手动关机的过程
[JAVA framework]interceptPowerKeyDown和Runnable mPowerLongPress@frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java
[JAVA framework]shutdown dialog 在frameworks/policies/base/phone/com/android/internal/policy/impl/GlobalActions.java
[JAVA framework]Shutdown process 在frameworks/base/core/java/com/android/internal/app/ShutdownThread.java
Through beginShutdownSequence(), actual shutdown process is done in run().
Broadcasts ACTION_SHUTDOWN Intent
Calls shutdown of ActivityManager service
Calls disable of Bluetooth service
Calls Radio(false) of Phone service
Calls shutdown of Mount service
Vibrate for a while
Calls Power.shutdown(); //这个是关键
4.1 具体分析Power.shutdown如何从user spaced到kenrel space
[JAVA]其中Power.shutdown@frameworks/base/core/java/android/os/power.java
[JNI] android_os_Power_shutdown@frameworks/base/core/jni/andorid_os_power.cpp
[Library] android_reboot(ANDROID_RB_POWEROFF, 0, 0)@system/core/libcutils/android_reboot.c
[Library] reboot(RB_POWER_OFF)@kernel/bionic/libc/unistd/reboot.c
[Library] _reboot@kernel/bionic/libc/arch-arm/syscalls/__reboot.S 其中bionic/libc/include/sys/linux-syscalls.h中定义了#define __NR_reboot (__NR_SYSCALL_BASE + 88)这与kernel的定义就一致了,会产生系统中断88,调用kernel的sys_reboot
4.2 reboot系统调用:
[Kernel] kernel/arch/arm/include/asm/unistd.h 有#define __NR_reboot (__NR_SYSCALL_BASE+ 88);
[Kernel] kernel/arch/arm/kernel/calls.s 有CALL(sys_reboot) /*88*/
[Kernel] kernel/arch/arm/kernel/entry-common.s 会引用call.s 对sys_call_table(系统调用表)进行定义
[Kernel] kernel/kernel/sys.c中有定义SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),定义了reboot该系统调用
[Kernel] 如果是Power off 则会调用kernel_power_off和do_exit(0)@kernel/kernel/sys.c
[Kernel] kernel_power_off-->kernel_shutdown_prepare-->device_shutdown[关闭非系统设备 比如:AS3676 会调用as3676_shutdown@kernel/drivers/leds/leds-as3676.c]和 sysdev_shutdown[关闭所有系统设备]
[Kernel] 最后machine_power_off
5. LowBat interrupt trigger; 当电池电压低于某个值(在那设置?abx500_fg_parameters.lowbat_threshold的值@kernel/arch/arm/mach-ux500/board-mop500-bm.c)会触发如下中断函数
1). [KERNEL]ab8500_fg_lowbatf_handler@kernel/drivers/power/ab8500_fg.c 有log: "Battery voltage is below LOW threshold", 其中为了避免是电池电压偶尔的下降,需要多次检测(ab8500_fg_low_bat_work 有log: ”Battery voltage still LOW“),从而最终确认是否真的是电池电压不够了,
2). [KERNEL]最后会调用 ab8500_fg_check_capacity_limits->power_supply_changed@power_supply_core.c->power_supply_changed_work->kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);另外需要说明下,实际上任何电池温度和电量的变化都会触发 power_supply_changed.
3). [KERNEL]在kernel/drivers/power/power_supply_core.c中power_supply_changed_work->power_supply_update_leds@kernel/drivers/power/power_supply_leds.c会设置power leds的颜色
4). [KERNEL]谁发event通知java层电源低?????
在power_supply_class_init@kernel/drivers/power/power_supply_core.c中有
power_supply_class = class_create(THIS_MODULE, "power_supply"); [正好与Java层的mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply")一致]
还有power_supply_class->dev_uevent = power_supply_uevent;
power_supply_changed_work->kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);会调用到power_supply_uevent@kernel/drivers/power/power_supply_sysfs.c 但是如何调用过来的,没分析清楚??
//ab8500_fg_check_capacity_limits->sysfs_notify(&di->fg_kobject, NULL, "charge_now");
6. Java层的处理过程,[现在需要确认谁触发了ActivityMangagerService@frameworks/base/core/java/android/app/ActivityManager.java]
1). [JAVA]BatteryService.java@framework/base/services/java/com/android/server, 会在SystemServer.java的run函数中初始化,然后添加到ServiceManager
2). [JAVA]shutdownIfNoPower@BatteryService.java, 会发送intent {Intent.ACTION_REQUEST_SHUTDOWN}
3). [JAVA]ActivityMangagerService 会下发 intent{ act=android.intent.action.ACTION_REQUEST_SHUTDOWN}给com.android.server.ShutdownActivity
4). [JAVA]ShutdownActivity@frameworks/base/services/java/com/android/server/ShutdownActivity.java 接收到 intent 开始触发oncreate-> ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
5). [JAVA]shutdown@frameworks/base/core/java/com/android/internal/app/ShutdownThread.java 会调用 beginShutdownSequence(context);其中有
sInstance.mCpuWakeLock.setReferenceCounted(false); //sure never fall asleep
sInstance.mScreenWakeLock.setReferenceCounted(false); //sure screen stays on
6). [JAVA]sInstance.start();->run()
7. BatteryService.java具体分析,会接收kernel的uevent消息,并加以处理
a. framework/base/services/java/com/android/server/BatteryService.java, 会在SystemServer.java的run函数中初始化,然后添加到ServiceManager
b. update@BatteryService.java函数会被 mPowerSupplyObserver和mInvalidChargerObserver调用,两者都是UEventObserver.
mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply"); //在uevent监听事件中增加该匹配 "SUBSYSTEM=power_supply" 的UEventObserver,一旦有匹配的uevent收到,会调用mPowerSupplyObserver->onUEvent->update
c. update@BatteryService.java函数会得到最新的电池状态信息; update->native_update[JNI]->android_server_BatteryService_update@frameworks/base/services/jni/com_android_server_BatteryService.cpp
d. com_android_server_BatteryService.cpp中 实际是读取的 #define POWER_SUPPLY_PATH "/sys/class/power_supply" 的内容,
e. 顺便分析下C 向 JAVA如何传递数据,比如 private int mBatteryStatus@BatteryService.java, 在com_android_server_BatteryService.cpp中有
jfieldID mBatteryStatus;
gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I");
env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
f. JAVA的update->JNI的native_update,然后Batteryservice.java中的关于电池状态信息的变量会在 native_update中赋值,根据获取的电池状态,决定是否shutdownIfNoPower.
8. 可以直接修改ab8500的寄存器 https://wiki.sonyericsson.net/androiki/AB8500_Debug_registers_in_DebugFS
~ $ adb shell
# cd /data
# mkdir debug
# mount -t debugfs nodev debug
# cd debug/ab8500
bank: AB8500_SYS_CTRL2_BLOCK 0x02
address: AB8500_LOW_BAT_REG 0x03
value: 3.65v 0b110110 0x36[6:1] 0x1[0]enable lowbat function -> 0x6D 对应 3.65v
3.575v 0b110011 0x33[6:1] 0x1[0]enable lowbat function -> 0x67 对应 3.575v
3.55v 0b110010 0x32[6:1] 0x1[0]enable lowbat function -> 0x65 对应 3.55v //
3.35v 0b100010 0x2A[6:1] 0x1[0]enable lowbat function -> 0x45 对应 3.35v //bad
3.25v 0b100110 0x26[6:1] 0x1[0]enable lowbat function -> 0x4D 对应 3.25v //bad
// READ/WRITE OUT VALUE OF REGISTER 0x0203(LowBat : Low battery feature management.)
# echo 0x2 > register-bank
# echo 0x3 > register-address
# cat register-value
# echo 0x67 > register-value
9. 修改 ab8500_fg_parameters@/kernel/arch/arm/mach-ux500/board-mop500-bm.c中的 fg.lowbat_threshold = 3350, 检查手机电池状态sys/class/power_supply/ab8500_fg中可以查看voltage_now,etc
10. 只修改fg.lowbat_threshold = 3350,同时修改 cap_tbl_A_100mA数据使之对应, 会导致电池capacity减小,不是最佳solution
二、Power Manager的分析研究
1. Android Application 如何使用wakelock
a. PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);-->getSystemService@frameworks/base/core/java/android/view/contextthemewrapper.java-->getSystemService和getPowerManager@frameworks/base/core/java/android/app/contextimpl.java
b. mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "SoundRecorder");
PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
ACQUIRE_CAUSES_WAKEUP:一旦有请求锁时强制打开Screen和keyboard light
ON_AFTER_RELEASE: 在释放锁时reset activity timer
c. newWakeLock@frameworks/base/core/java/android/os/powermanager.java
d. mWakeLock.acquire();-->acquire@frameworks/base/core/java/android/os/powermanager.java-->acquireWakeLock和acquireWakeLockLocked@frameworks/base/services/java/com/android/server/powermanagerservice.java;
2. PowerManager@powermanager.java和PowerManagerService@powermanagerservice.java 两者如何关联上???
a. 在framework/base/services/java/com/android/server/systemserver.java中有如下代码
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);//把power service添加到servicemanager中管理
b. 在framework/base/core/java/android/app/contextimpl.java中有如下代码
IBinder b = ServiceManager.getService(POWER_SERVICE);
IPowerManager service = IPowerManager.Stub.asInterface(b);
sPowerManager = new PowerManager(service, mMainThread.getHandler());
所以 PowerManager.java中的mService就是PowerManagerService, 具体的实现原理还需要进一步分析java的语法
3. 在PowerManagerService.java中,mLocks保存的是所有WakeLock,该WakeLock不是以tag来区分的,也就是一个SCREEN_DIM_WAKE_LOCK tag,可以对应多个WakeLock;注意PowerManager.java中的WakeLock和PowerManagerService.java中的WakeLock不是一回事
mStayOnWhilePluggedInScreenDimLock@PowerManagerService.java
4. 如何go to sleep
5. frameworks/base/core/java/android/os/Power.java中的WakeLock只有PARTIAL_WAKE_LOCK和FULL_WAKE_LOCK两种类型,而powermanager.java中却有PARTIAL_WAKE_LOCK、FULL_WAKE_LOCK、等状态,它们如何对应上?
6. PowerManagerService.java 似乎之关心PARTIAL_WAKE_LOCK类型的WakeLock, 只有代码Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME) = Power.acquireWakeLock(2,"PowerManagerService");另外也说明没有延时wakelock被使用
7. [JNI]acquireWakeLock@frameworks/base/core/jni/android_os_power.cpp-->acquire_wake_lock@hardware/libhardware_legacy/power/power.c; write操作的是"/sys/power/wake_lock"文件
8. [Kernel] kernel/kernel/power/main.c中sysfs_create_group(power_kobj, &attr_group); 会创建sys文件接口[/sys/power/wake_lock],其中write操作对应wake_lock_store@kernel/kernel/power/userwakelock.c-->lookup_wake_lock_name[如果不存在该名称的wakelock,会先创建个在添加到user_wake_locks 队列中,并且添加的都是WAKE_LOCK_SUSPEND类型的wakelock]-->wake_lock和wake_lock_internal@kernel/kernel/power/wakelock.c,在wake_lock_internal函数中会把该wakelock添加到active_wake_locks[type=WAKE_LOCK_SUSPEND]中,WAKE_LOCK_SUSPEND表示不能让手机suspend,这个list链表只增不减??不是,在wake_unlock中有list_del(&lock->link);
8. [Kernel] 如果是有时间延时的wakelock,会启动一个timer,也就是通过一个timer来实现延时wakelock的操作,在wake_lock_internal@kernel/kernel/power/wakelock.c中有mod_timer(&expire_timer, jiffies + expire_in);
9. 系统suspend如何与wakelock关联起来的???
10. kernel/kernel/power/main.c中会创建pm_wq = create_freezeable_workqueue("pm");
11. 谁调用??suspend@kernel/kernel/power/wakelock.c --> has_wake_lock-->has_wake_lock_locked[active_wake_locks只要里面有wakelock就不能suspend],也就是说linux是通过一个list来管理wakelock,然后决定是否suspend.
12. User Space写入standby或者mem到"sys/power/state",会调用state_store@kernel/kernel/power/main.c-->request_suspend_state(state)@kernel/kernel/power/earlysuspend.c{其中state为PM_SUSPEND_STANDBY,PM_SUSPEND_MEM},如果需要suspend则会调用queue_work(suspend_work_queue, &early_suspend_work)-->early_suspend@kernel/kernel/power/earlysuspend.c 和 pos->suspend(pos); -->wake_unlock(&main_wake_lock)@kernel/kernel/power/wakelock.c 其中wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");如果可以进入suspend状态则会调用suspend(..) -->pm_suspend@kernel/kernel/power/suspend.c-->suspend_prepare和suspend_devices_and_enter-->suspend_console-> 继续分析??????
13. 其中pm_suspend(requested_suspend_state)中的requested_suspend_state 状态是如何确定的??? 在state_show和state_store@kernel/kernel/power/main.c中,查看手机#cat sys/power/state 为“standby mem”只支持这两种模式? {'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and'disk' (Suspend-to-Disk).}
14. 所有需要early suspend的设备需要预先用register_early_suspend@kernel/kernel/power/earlysuspend.c 注册到early_suspend_handlers中
三、如何使用debuggy
0. http://www.ibm.com/developerworks/cn/linux/l-cn-dumpanalyse
1. 如何安装debuggy, $sudo apt-get update; $sudo apt-get install debugy
1. 进入Crash文件目录
2. $debuggy --crash tlcore, 执行完后会生成 目录crash_workdir,文件isswcore,modemcore,mtracecore,smemcore, vmcore
3. $debuggy --crashpdf vmcore, 生成的vmcore.pdf在crash_workdir目录下
4. 进入crash 工作目录$crash -x vmcore vmlinux
四. Riogrande 平台上 charge only(电池电量不够,不能开机,只能进入开机充电画面)的程序在vendor/semc/hardware/power/chargemon 分析
1. charge only 如何触发运行? 在手机init.st-ericsson.rc中有
on early-boot
...
# Start the offline charging (This blocks booting further in some cases)
exec /system/bin/chargemon
2. 源代码中/device/semc/common/files/init.common.rc 通过/device/semc/common/SEMCBoard.mk 被合并到init.st-ericsson.rc,其中exec /system/bin/chargemon内容为init.common.rc 提供
3. 进入代码目录vendor/semc/hardware/power/chargemon, 修改代码后,再执行#mm ,编译
六、android 的init如何被kernrl启动
1. 手机上init 可执行文件位于/ 根目录下,源代码位于 /system/core/init/init.c, 查看其Android.mk里面有
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
其中 ./build/core/envsetup.mk 有 TARGET_ROOT_OUT := $(PRODUCT_OUT)/root; //编译生成的 executable file,最后保存到了root目录下,
PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)
七、dynamic debug log输出机制
0. 注意该机制只对 dev_dbg -> dynamic_dev_dbg 定义的debug log输出加以控制
1. 如何使用:(kernel/Documentation/dynamic-debug-howto.txt)
mkdir /data/debugfs
mount -t debugfs none /data/debugfs
echo -n 'file ab8500_fg.c +p' > /data/debugfs/dynamic_debug/control //增加该文件dynamic debug的输出
echo -n 'file ab8500_fg.c -p' > /data/debugfs/dynamic_debug/control //去掉该文件dynamic debug的输出
2. 如果想使用debugfs 必须,在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DEBUG_FS=y
3. 如果需要使用Dynamic debug机制,需要在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DYNAMIC_DEBUG=y
4. dev_dbg@kernel/include/linux/device.h ->dynamic_dev_dbg@kernel/include/linux/dynamic_debug.h
#define dynamic_dev_dbg(dev, fmt, ...) do { \
static struct _ddebug descriptor \
__used \
__attribute__((section("__verbose"), aligned(8))) = \
{ KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \
DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \
if (__dynamic_dbg_enabled(descriptor)) \
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
} while (0)
a. 该define 最终会展开在被调用dev_dbg函数的c文件中,也就是KBUILD_MODNAME, __func__, __FILE__, __LINE__ 会有对应的字符串
b. _DPRINTK_FLAGS_DEFAULT=0;
c. DEBUG_HASH和DEBUG_HASH2的定义在kernel/scripts/makefile.lib中
DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname)) //利用djb2 hash算法,计算modname的DEBUG_HASH value;
DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname)) //利用r5 hash算法,计算modname的DEBUG_HASH2 value;
d. 分析 kernel/scripts/basic/hash.c,会生成out/target/product/lotus/obj/kernel/scripts/basic/hash, shell可执行文件
e. 在编译连接完成后,该 descriptor 值会被保存到 data section的 __verbose区
5. dynamic_debug_init@kernel/lib/dynamic_debug.c
a. dir = debugfs_create_dir("dynamic_debug", NULL);
b. file = debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops); //在debugfs文件系统中创建dynamic_debug/control 文件
c. 通过__start___verbose和__stop___verbose@kernel/include/asm-generic/vmlinux.lds.h中,实际上是获取保存在__verbose区的 struct _ddebug 数据(就是前面编译后添加到data section的__verbose)
d. 如果是不同的modname,就添加到ddebug_tables 中,也就是所有dynamic_dev_dbg的模块(modname),文件(__FILE__),行(__LINE__),函数(__func__),是否输出的flag,对应的hash value都会逐条保存到ddebug_tables中
6. 分析 echo -n 'file ab8500_charger.c +p' > /data/debugfs/dynamic_debug/control 的实际操作
a. 通过system call,debugfs文件系统会调用到ddebug_proc_write,ddebug_parse_query和ddebug_parse_flags@kernel/lib/dynamic_debug.c分析传入的参数字符串
b. 在ddebug_change@kernel/lib/dynamic_debug.c中,会根据modname, __FILE__, __LINE__, __func__信息在ddebug_tables找到对应的item.
c. 然后根据输入的是 +p或-p ,来标志struct _ddebug中flag字段,还有根据struct _ddebug中的primary_hash和secondary_hash,来标志global value dynamic_debug_enabled和dynamic_debug_enabled2 对应的位,会在__dynamic_dbg_enabled@kernel/include/linux/dynamic_debug.h用到
7. 分析 #cat /data/debugfs/dynamic_debug/control的实际操作
a. 先ddebug_proc_open中有err = seq_open(file, &ddebug_proc_seqops);应用了seq file的读写机制
b. 然后seq_read,利用seq file机制逐个读出和显示ddebug_tables中的内容
8. long long dynamic_debug_enabled和dynamic_debug_enabled2@kernel/lib/dynamic_debugc,用于标志某个mod(可包含一个或多个文件,比如ab8500_fg mod,目前只包含ab8500_fg.c file)是否可以输出debug log的模块. 最多可以标志64*64=4096个dev_debug/dynamic_dev_dbg.
9. 是否输出dev_log/dynamic_dev_dbg的log, 关键是如下判断, @kernel/include/linux/dynamic_debug.h
#define __dynamic_dbg_enabled(dd) ({ \
int __ret = 0; \
if (unlikely((dynamic_debug_enabled & (1LL << DEBUG_HASH)) && \
(dynamic_debug_enabled2 & (1LL << DEBUG_HASH2)))) \
if (unlikely(dd.flags)) \
__ret = 1; \
__ret; })
a. dynamic_debug_enabled和dynamic_debug_enabled2就是前面分析的是否输出该modname的两个long long的组合标志位
b. DEBUG_HASH和DEBUG_HASH2 如前面所解释
c. dd.flag 默认为_DPRINTK_FLAGS_DEFAULT,但通过debugfs文件系统,最终操作ddebug_proc_write函数,会设置为_DPRINTK_FLAGS_PRIN或_DPRINTK_FLAGS_DEFAULT
10. debugfs 文件系统中的内容保存在那????? 内存中,类似proc
11. 小结:如果你需要用到dynamic debug info, 你需要在你的 .c 文件中用dev_log 输出log;CONFIG_DEBUG_FS=y;CONFIG_DYNAMIC_DEBUG=y
dynamic debug log输出机制
0. 注意该机制只对 dev_dbg -> dynamic_dev_dbg 定义的debug log输出加以控制
1. 如何使用:(kernel/Documentation/dynamic-debug-howto.txt)
mkdir /data/debugfs
mount -t debugfs none /data/debugfs
echo -n 'file ab8500_fg.c +p' > /data/debugfs/dynamic_debug/control //增加该文件dynamic debug的输出
echo -n 'file ab8500_fg.c -p' > /data/debugfs/dynamic_debug/control //去掉该文件dynamic debug的输出
2. 如果想使用debugfs 必须,在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DEBUG_FS=y
3. 如果需要使用Dynamic debug机制,需要在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DYNAMIC_DEBUG=y
4. dev_dbg@kernel/include/linux/device.h ->dynamic_dev_dbg@kernel/include/linux/dynamic_debug.h
#define dynamic_dev_dbg(dev, fmt, ...) do { \
static struct _ddebug descriptor \
__used \
__attribute__((section("__verbose"), aligned(8))) = \
{ KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \
DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \
if (__dynamic_dbg_enabled(descriptor)) \
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
} while (0)
a. 该define 最终会展开在被调用dev_dbg函数的c文件中,也就是KBUILD_MODNAME, __func__, __FILE__, __LINE__ 会有对应的字符串
b. _DPRINTK_FLAGS_DEFAULT=0;
c. DEBUG_HASH和DEBUG_HASH2的定义在kernel/scripts/makefile.lib中
DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname)) //利用djb2 hash算法,计算modname的DEBUG_HASH value;
DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname)) //利用r5 hash算法,计算modname的DEBUG_HASH2 value;
d. 分析 kernel/scripts/basic/hash.c,会生成out/target/product/lotus/obj/kernel/scripts/basic/hash, shell可执行文件
e. 在编译连接完成后,该 descriptor 值会被保存到 data section的 __verbose区
5. dynamic_debug_init@kernel/lib/dynamic_debug.c
a. dir = debugfs_create_dir("dynamic_debug", NULL);
b. file = debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops); //在debugfs文件系统中创建dynamic_debug/control 文件
c. 通过__start___verbose和__stop___verbose@kernel/include/asm-generic/vmlinux.lds.h中,实际上是获取保存在__verbose区的 struct _ddebug 数据(就是前面编译后添加到data section的__verbose)
d. 如果是不同的modname,就添加到ddebug_tables 中,也就是所有dynamic_dev_dbg的模块(modname),文件(__FILE__),行(__LINE__),函数(__func__),是否输出的flag,对应的hash value都会逐条保存到ddebug_tables中
6. 分析 echo -n 'file ab8500_charger.c +p' > /data/debugfs/dynamic_debug/control 的实际操作
a. 通过system call,debugfs文件系统会调用到ddebug_proc_write,ddebug_parse_query和ddebug_parse_flags@kernel/lib/dynamic_debug.c分析传入的参数字符串
b. 在ddebug_change@kernel/lib/dynamic_debug.c中,会根据modname, __FILE__, __LINE__, __func__信息在ddebug_tables找到对应的item.
c. 然后根据输入的是 +p或-p ,来标志struct _ddebug中flag字段,还有根据struct _ddebug中的primary_hash和secondary_hash,来标志global value dynamic_debug_enabled和dynamic_debug_enabled2 对应的位,会在__dynamic_dbg_enabled@kernel/include/linux/dynamic_debug.h用到
7. 分析 #cat /data/debugfs/dynamic_debug/control的实际操作
a. 先ddebug_proc_open中有err = seq_open(file, &ddebug_proc_seqops);应用了seq file的读写机制
b. 然后seq_read,利用seq file机制逐个读出和显示ddebug_tables中的内容
8. long long dynamic_debug_enabled和dynamic_debug_enabled2@kernel/lib/dynamic_debugc,用于标志某个mod(可包含一个或多个文件,比如ab8500_fg mod,目前只包含ab8500_fg.c file)是否可以输出debug log的模块. 最多可以标志64*64=4096个dev_debug/dynamic_dev_dbg.
9. 是否输出dev_log/dynamic_dev_dbg的log, 关键是如下判断, @kernel/include/linux/dynamic_debug.h
#define __dynamic_dbg_enabled(dd) ({ \
int __ret = 0; \
if (unlikely((dynamic_debug_enabled & (1LL << DEBUG_HASH)) && \
(dynamic_debug_enabled2 & (1LL << DEBUG_HASH2)))) \
if (unlikely(dd.flags)) \
__ret = 1; \
__ret; })
a. dynamic_debug_enabled和dynamic_debug_enabled2就是前面分析的是否输出该modname的两个long long的组合标志位
b. DEBUG_HASH和DEBUG_HASH2 如前面所解释
c. dd.flag 默认为_DPRINTK_FLAGS_DEFAULT,但通过debugfs文件系统,最终操作ddebug_proc_write函数,会设置为_DPRINTK_FLAGS_PRIN或_DPRINTK_FLAGS_DEFAULT
10. debugfs 文件系统中的内容保存在那????? 内存中,类似proc
11. 小结:如果你需要用到dynamic debug info, 你需要在你的 .c 文件中用dev_log 输出log;CONFIG_DEBUG_FS=y;CONFIG_DYNAMIC_DEBUG=y
八、Factory data reset流程
1. [JAVA] packages/apps/settings/src/com/android/settings/privacysetting.java和masterclear.java, 有如下代码
sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
2. [JAVA] frameworks/base/core/res/androidmanifest.xml中有
<receiver android:name="com.android.server.MasterClearReceiver"
...
<intent-filter>
<action android:name="android.intent.action.MASTER_CLEAR" />
<category android:name="android.intent.category.MASTER_CLEAR" />
</intent-filter>
</receiver>
3. [JAVA] frameworks/base/services/java/com/android/server/masterclearreceiver.java, 调用 RecoverySystem.rebootWipeUserData(context);
4. rebootWipeuserdata@frameworks/base/core/java/android/os/recoverysystem.java-->bootCommand--> 写入“--wipe_data”到手机的cache/recovery/command文件中,然后pm.reboot("recovery");
5. reboot@frameworks/base/core/java/android/os/powermanager.java-->reboot@frameworks/base/services/java/com/android/server/powermanagerservice.java
repo init -u git://review.sonyericsson.net/platform/manifest -b edream6.0-riogrande-plus-release
一、DMS01179806 (The illumination is still lit after auto power off by battery exhausted.)
1. 进入手机/sys/devices/platform/nmk-i2c.2/i2c-2/2-0040/leds/ 或 sys/class/leds/目录list如下
pwr-red,pwr-green,pwr-blue //控制充电led
l-key-red,l-key-green,l-key-blue //控制left button led
m-key-red,m-key-green,m-key-blue //控制middle button led
r-key-red,r-key-green,r-key-blue //控制right button led
lcd-backlight //控制lcd backlight led
设置其中brightness的值,可以调整各led的显示亮度
2. https://wiki.sonyericsson.net/androiki/Fuel_Gauge (SysFS interfaces)
Disable charging: adb shell "echo 0 > /sys/ab8500_chargalg/chargalg",用于得到没有充电的log
Enable AC dedicated charger: adb shell "echo 1 > /sys/ab8500_chargalg/chargalg"
Enable PC-USB and USB wall charger: adb shell "echo 2 > /sys/ab8500_chargalg/chargalg"
3. Kernel 有log输出
"SysRq : Emergency Remount R/O"
“Emergency Remount complete” 位于函数do_emergency_remount@kernel/fs/super.c
“Shutting down AS3676” 位于函数 as3676_shutdown@kernel/drivers/leds/leds-as3676.c
"CPU1: shutdown"
"Power down" 位于函数kernel_power_off@kernel/kernel/sys.c
Java 有log输出
“Performing low-level shutdown...” 位于函数 rebootOrShutdown@frameworks/base/core/java/com/android/internal/app/shutdownthread.java
4. 手机手动关机的过程
[JAVA framework]interceptPowerKeyDown和Runnable mPowerLongPress@frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindowManager.java
[JAVA framework]shutdown dialog 在frameworks/policies/base/phone/com/android/internal/policy/impl/GlobalActions.java
[JAVA framework]Shutdown process 在frameworks/base/core/java/com/android/internal/app/ShutdownThread.java
Through beginShutdownSequence(), actual shutdown process is done in run().
Broadcasts ACTION_SHUTDOWN Intent
Calls shutdown of ActivityManager service
Calls disable of Bluetooth service
Calls Radio(false) of Phone service
Calls shutdown of Mount service
Vibrate for a while
Calls Power.shutdown(); //这个是关键
4.1 具体分析Power.shutdown如何从user spaced到kenrel space
[JAVA]其中Power.shutdown@frameworks/base/core/java/android/os/power.java
[JNI] android_os_Power_shutdown@frameworks/base/core/jni/andorid_os_power.cpp
[Library] android_reboot(ANDROID_RB_POWEROFF, 0, 0)@system/core/libcutils/android_reboot.c
[Library] reboot(RB_POWER_OFF)@kernel/bionic/libc/unistd/reboot.c
[Library] _reboot@kernel/bionic/libc/arch-arm/syscalls/__reboot.S 其中bionic/libc/include/sys/linux-syscalls.h中定义了#define __NR_reboot (__NR_SYSCALL_BASE + 88)这与kernel的定义就一致了,会产生系统中断88,调用kernel的sys_reboot
4.2 reboot系统调用:
[Kernel] kernel/arch/arm/include/asm/unistd.h 有#define __NR_reboot (__NR_SYSCALL_BASE+ 88);
[Kernel] kernel/arch/arm/kernel/calls.s 有CALL(sys_reboot) /*88*/
[Kernel] kernel/arch/arm/kernel/entry-common.s 会引用call.s 对sys_call_table(系统调用表)进行定义
[Kernel] kernel/kernel/sys.c中有定义SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg),定义了reboot该系统调用
[Kernel] 如果是Power off 则会调用kernel_power_off和do_exit(0)@kernel/kernel/sys.c
[Kernel] kernel_power_off-->kernel_shutdown_prepare-->device_shutdown[关闭非系统设备 比如:AS3676 会调用as3676_shutdown@kernel/drivers/leds/leds-as3676.c]和 sysdev_shutdown[关闭所有系统设备]
[Kernel] 最后machine_power_off
5. LowBat interrupt trigger; 当电池电压低于某个值(在那设置?abx500_fg_parameters.lowbat_threshold的值@kernel/arch/arm/mach-ux500/board-mop500-bm.c)会触发如下中断函数
1). [KERNEL]ab8500_fg_lowbatf_handler@kernel/drivers/power/ab8500_fg.c 有log: "Battery voltage is below LOW threshold", 其中为了避免是电池电压偶尔的下降,需要多次检测(ab8500_fg_low_bat_work 有log: ”Battery voltage still LOW“),从而最终确认是否真的是电池电压不够了,
2). [KERNEL]最后会调用 ab8500_fg_check_capacity_limits->power_supply_changed@power_supply_core.c->power_supply_changed_work->kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);另外需要说明下,实际上任何电池温度和电量的变化都会触发 power_supply_changed.
3). [KERNEL]在kernel/drivers/power/power_supply_core.c中power_supply_changed_work->power_supply_update_leds@kernel/drivers/power/power_supply_leds.c会设置power leds的颜色
4). [KERNEL]谁发event通知java层电源低?????
在power_supply_class_init@kernel/drivers/power/power_supply_core.c中有
power_supply_class = class_create(THIS_MODULE, "power_supply"); [正好与Java层的mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply")一致]
还有power_supply_class->dev_uevent = power_supply_uevent;
power_supply_changed_work->kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);会调用到power_supply_uevent@kernel/drivers/power/power_supply_sysfs.c 但是如何调用过来的,没分析清楚??
//ab8500_fg_check_capacity_limits->sysfs_notify(&di->fg_kobject, NULL, "charge_now");
6. Java层的处理过程,[现在需要确认谁触发了ActivityMangagerService@frameworks/base/core/java/android/app/ActivityManager.java]
1). [JAVA]BatteryService.java@framework/base/services/java/com/android/server, 会在SystemServer.java的run函数中初始化,然后添加到ServiceManager
2). [JAVA]shutdownIfNoPower@BatteryService.java, 会发送intent {Intent.ACTION_REQUEST_SHUTDOWN}
3). [JAVA]ActivityMangagerService 会下发 intent{ act=android.intent.action.ACTION_REQUEST_SHUTDOWN}给com.android.server.ShutdownActivity
4). [JAVA]ShutdownActivity@frameworks/base/services/java/com/android/server/ShutdownActivity.java 接收到 intent 开始触发oncreate-> ShutdownThread.shutdown(ShutdownActivity.this, mConfirm);
5). [JAVA]shutdown@frameworks/base/core/java/com/android/internal/app/ShutdownThread.java 会调用 beginShutdownSequence(context);其中有
sInstance.mCpuWakeLock.setReferenceCounted(false); //sure never fall asleep
sInstance.mScreenWakeLock.setReferenceCounted(false); //sure screen stays on
6). [JAVA]sInstance.start();->run()
7. BatteryService.java具体分析,会接收kernel的uevent消息,并加以处理
a. framework/base/services/java/com/android/server/BatteryService.java, 会在SystemServer.java的run函数中初始化,然后添加到ServiceManager
b. update@BatteryService.java函数会被 mPowerSupplyObserver和mInvalidChargerObserver调用,两者都是UEventObserver.
mPowerSupplyObserver.startObserving("SUBSYSTEM=power_supply"); //在uevent监听事件中增加该匹配 "SUBSYSTEM=power_supply" 的UEventObserver,一旦有匹配的uevent收到,会调用mPowerSupplyObserver->onUEvent->update
c. update@BatteryService.java函数会得到最新的电池状态信息; update->native_update[JNI]->android_server_BatteryService_update@frameworks/base/services/jni/com_android_server_BatteryService.cpp
d. com_android_server_BatteryService.cpp中 实际是读取的 #define POWER_SUPPLY_PATH "/sys/class/power_supply" 的内容,
e. 顺便分析下C 向 JAVA如何传递数据,比如 private int mBatteryStatus@BatteryService.java, 在com_android_server_BatteryService.cpp中有
jfieldID mBatteryStatus;
gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I");
env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
f. JAVA的update->JNI的native_update,然后Batteryservice.java中的关于电池状态信息的变量会在 native_update中赋值,根据获取的电池状态,决定是否shutdownIfNoPower.
8. 可以直接修改ab8500的寄存器 https://wiki.sonyericsson.net/androiki/AB8500_Debug_registers_in_DebugFS
~ $ adb shell
# cd /data
# mkdir debug
# mount -t debugfs nodev debug
# cd debug/ab8500
bank: AB8500_SYS_CTRL2_BLOCK 0x02
address: AB8500_LOW_BAT_REG 0x03
value: 3.65v 0b110110 0x36[6:1] 0x1[0]enable lowbat function -> 0x6D 对应 3.65v
3.575v 0b110011 0x33[6:1] 0x1[0]enable lowbat function -> 0x67 对应 3.575v
3.55v 0b110010 0x32[6:1] 0x1[0]enable lowbat function -> 0x65 对应 3.55v //
3.35v 0b100010 0x2A[6:1] 0x1[0]enable lowbat function -> 0x45 对应 3.35v //bad
3.25v 0b100110 0x26[6:1] 0x1[0]enable lowbat function -> 0x4D 对应 3.25v //bad
// READ/WRITE OUT VALUE OF REGISTER 0x0203(LowBat : Low battery feature management.)
# echo 0x2 > register-bank
# echo 0x3 > register-address
# cat register-value
# echo 0x67 > register-value
9. 修改 ab8500_fg_parameters@/kernel/arch/arm/mach-ux500/board-mop500-bm.c中的 fg.lowbat_threshold = 3350, 检查手机电池状态sys/class/power_supply/ab8500_fg中可以查看voltage_now,etc
10. 只修改fg.lowbat_threshold = 3350,同时修改 cap_tbl_A_100mA数据使之对应, 会导致电池capacity减小,不是最佳solution
二、Power Manager的分析研究
1. Android Application 如何使用wakelock
a. PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);-->getSystemService@frameworks/base/core/java/android/view/contextthemewrapper.java-->getSystemService和getPowerManager@frameworks/base/core/java/android/app/contextimpl.java
b. mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "SoundRecorder");
PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
ACQUIRE_CAUSES_WAKEUP:一旦有请求锁时强制打开Screen和keyboard light
ON_AFTER_RELEASE: 在释放锁时reset activity timer
c. newWakeLock@frameworks/base/core/java/android/os/powermanager.java
d. mWakeLock.acquire();-->acquire@frameworks/base/core/java/android/os/powermanager.java-->acquireWakeLock和acquireWakeLockLocked@frameworks/base/services/java/com/android/server/powermanagerservice.java;
2. PowerManager@powermanager.java和PowerManagerService@powermanagerservice.java 两者如何关联上???
a. 在framework/base/services/java/com/android/server/systemserver.java中有如下代码
power = new PowerManagerService();
ServiceManager.addService(Context.POWER_SERVICE, power);//把power service添加到servicemanager中管理
b. 在framework/base/core/java/android/app/contextimpl.java中有如下代码
IBinder b = ServiceManager.getService(POWER_SERVICE);
IPowerManager service = IPowerManager.Stub.asInterface(b);
sPowerManager = new PowerManager(service, mMainThread.getHandler());
所以 PowerManager.java中的mService就是PowerManagerService, 具体的实现原理还需要进一步分析java的语法
3. 在PowerManagerService.java中,mLocks保存的是所有WakeLock,该WakeLock不是以tag来区分的,也就是一个SCREEN_DIM_WAKE_LOCK tag,可以对应多个WakeLock;注意PowerManager.java中的WakeLock和PowerManagerService.java中的WakeLock不是一回事
mStayOnWhilePluggedInScreenDimLock@PowerManagerService.java
4. 如何go to sleep
5. frameworks/base/core/java/android/os/Power.java中的WakeLock只有PARTIAL_WAKE_LOCK和FULL_WAKE_LOCK两种类型,而powermanager.java中却有PARTIAL_WAKE_LOCK、FULL_WAKE_LOCK、等状态,它们如何对应上?
6. PowerManagerService.java 似乎之关心PARTIAL_WAKE_LOCK类型的WakeLock, 只有代码Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME) = Power.acquireWakeLock(2,"PowerManagerService");另外也说明没有延时wakelock被使用
7. [JNI]acquireWakeLock@frameworks/base/core/jni/android_os_power.cpp-->acquire_wake_lock@hardware/libhardware_legacy/power/power.c; write操作的是"/sys/power/wake_lock"文件
8. [Kernel] kernel/kernel/power/main.c中sysfs_create_group(power_kobj, &attr_group); 会创建sys文件接口[/sys/power/wake_lock],其中write操作对应wake_lock_store@kernel/kernel/power/userwakelock.c-->lookup_wake_lock_name[如果不存在该名称的wakelock,会先创建个在添加到user_wake_locks 队列中,并且添加的都是WAKE_LOCK_SUSPEND类型的wakelock]-->wake_lock和wake_lock_internal@kernel/kernel/power/wakelock.c,在wake_lock_internal函数中会把该wakelock添加到active_wake_locks[type=WAKE_LOCK_SUSPEND]中,WAKE_LOCK_SUSPEND表示不能让手机suspend,这个list链表只增不减??不是,在wake_unlock中有list_del(&lock->link);
8. [Kernel] 如果是有时间延时的wakelock,会启动一个timer,也就是通过一个timer来实现延时wakelock的操作,在wake_lock_internal@kernel/kernel/power/wakelock.c中有mod_timer(&expire_timer, jiffies + expire_in);
9. 系统suspend如何与wakelock关联起来的???
10. kernel/kernel/power/main.c中会创建pm_wq = create_freezeable_workqueue("pm");
11. 谁调用??suspend@kernel/kernel/power/wakelock.c --> has_wake_lock-->has_wake_lock_locked[active_wake_locks只要里面有wakelock就不能suspend],也就是说linux是通过一个list来管理wakelock,然后决定是否suspend.
12. User Space写入standby或者mem到"sys/power/state",会调用state_store@kernel/kernel/power/main.c-->request_suspend_state(state)@kernel/kernel/power/earlysuspend.c{其中state为PM_SUSPEND_STANDBY,PM_SUSPEND_MEM},如果需要suspend则会调用queue_work(suspend_work_queue, &early_suspend_work)-->early_suspend@kernel/kernel/power/earlysuspend.c 和 pos->suspend(pos); -->wake_unlock(&main_wake_lock)@kernel/kernel/power/wakelock.c 其中wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");如果可以进入suspend状态则会调用suspend(..) -->pm_suspend@kernel/kernel/power/suspend.c-->suspend_prepare和suspend_devices_and_enter-->suspend_console-> 继续分析??????
13. 其中pm_suspend(requested_suspend_state)中的requested_suspend_state 状态是如何确定的??? 在state_show和state_store@kernel/kernel/power/main.c中,查看手机#cat sys/power/state 为“standby mem”只支持这两种模式? {'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and'disk' (Suspend-to-Disk).}
14. 所有需要early suspend的设备需要预先用register_early_suspend@kernel/kernel/power/earlysuspend.c 注册到early_suspend_handlers中
三、如何使用debuggy
0. http://www.ibm.com/developerworks/cn/linux/l-cn-dumpanalyse
1. 如何安装debuggy, $sudo apt-get update; $sudo apt-get install debugy
1. 进入Crash文件目录
2. $debuggy --crash tlcore, 执行完后会生成 目录crash_workdir,文件isswcore,modemcore,mtracecore,smemcore, vmcore
3. $debuggy --crashpdf vmcore, 生成的vmcore.pdf在crash_workdir目录下
4. 进入crash 工作目录$crash -x vmcore vmlinux
四. Riogrande 平台上 charge only(电池电量不够,不能开机,只能进入开机充电画面)的程序在vendor/semc/hardware/power/chargemon 分析
1. charge only 如何触发运行? 在手机init.st-ericsson.rc中有
on early-boot
...
# Start the offline charging (This blocks booting further in some cases)
exec /system/bin/chargemon
2. 源代码中/device/semc/common/files/init.common.rc 通过/device/semc/common/SEMCBoard.mk 被合并到init.st-ericsson.rc,其中exec /system/bin/chargemon内容为init.common.rc 提供
3. 进入代码目录vendor/semc/hardware/power/chargemon, 修改代码后,再执行#mm ,编译
六、android 的init如何被kernrl启动
1. 手机上init 可执行文件位于/ 根目录下,源代码位于 /system/core/init/init.c, 查看其Android.mk里面有
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
其中 ./build/core/envsetup.mk 有 TARGET_ROOT_OUT := $(PRODUCT_OUT)/root; //编译生成的 executable file,最后保存到了root目录下,
PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)
七、dynamic debug log输出机制
0. 注意该机制只对 dev_dbg -> dynamic_dev_dbg 定义的debug log输出加以控制
1. 如何使用:(kernel/Documentation/dynamic-debug-howto.txt)
mkdir /data/debugfs
mount -t debugfs none /data/debugfs
echo -n 'file ab8500_fg.c +p' > /data/debugfs/dynamic_debug/control //增加该文件dynamic debug的输出
echo -n 'file ab8500_fg.c -p' > /data/debugfs/dynamic_debug/control //去掉该文件dynamic debug的输出
2. 如果想使用debugfs 必须,在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DEBUG_FS=y
3. 如果需要使用Dynamic debug机制,需要在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DYNAMIC_DEBUG=y
4. dev_dbg@kernel/include/linux/device.h ->dynamic_dev_dbg@kernel/include/linux/dynamic_debug.h
#define dynamic_dev_dbg(dev, fmt, ...) do { \
static struct _ddebug descriptor \
__used \
__attribute__((section("__verbose"), aligned(8))) = \
{ KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \
DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \
if (__dynamic_dbg_enabled(descriptor)) \
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
} while (0)
a. 该define 最终会展开在被调用dev_dbg函数的c文件中,也就是KBUILD_MODNAME, __func__, __FILE__, __LINE__ 会有对应的字符串
b. _DPRINTK_FLAGS_DEFAULT=0;
c. DEBUG_HASH和DEBUG_HASH2的定义在kernel/scripts/makefile.lib中
DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname)) //利用djb2 hash算法,计算modname的DEBUG_HASH value;
DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname)) //利用r5 hash算法,计算modname的DEBUG_HASH2 value;
d. 分析 kernel/scripts/basic/hash.c,会生成out/target/product/lotus/obj/kernel/scripts/basic/hash, shell可执行文件
e. 在编译连接完成后,该 descriptor 值会被保存到 data section的 __verbose区
5. dynamic_debug_init@kernel/lib/dynamic_debug.c
a. dir = debugfs_create_dir("dynamic_debug", NULL);
b. file = debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops); //在debugfs文件系统中创建dynamic_debug/control 文件
c. 通过__start___verbose和__stop___verbose@kernel/include/asm-generic/vmlinux.lds.h中,实际上是获取保存在__verbose区的 struct _ddebug 数据(就是前面编译后添加到data section的__verbose)
d. 如果是不同的modname,就添加到ddebug_tables 中,也就是所有dynamic_dev_dbg的模块(modname),文件(__FILE__),行(__LINE__),函数(__func__),是否输出的flag,对应的hash value都会逐条保存到ddebug_tables中
6. 分析 echo -n 'file ab8500_charger.c +p' > /data/debugfs/dynamic_debug/control 的实际操作
a. 通过system call,debugfs文件系统会调用到ddebug_proc_write,ddebug_parse_query和ddebug_parse_flags@kernel/lib/dynamic_debug.c分析传入的参数字符串
b. 在ddebug_change@kernel/lib/dynamic_debug.c中,会根据modname, __FILE__, __LINE__, __func__信息在ddebug_tables找到对应的item.
c. 然后根据输入的是 +p或-p ,来标志struct _ddebug中flag字段,还有根据struct _ddebug中的primary_hash和secondary_hash,来标志global value dynamic_debug_enabled和dynamic_debug_enabled2 对应的位,会在__dynamic_dbg_enabled@kernel/include/linux/dynamic_debug.h用到
7. 分析 #cat /data/debugfs/dynamic_debug/control的实际操作
a. 先ddebug_proc_open中有err = seq_open(file, &ddebug_proc_seqops);应用了seq file的读写机制
b. 然后seq_read,利用seq file机制逐个读出和显示ddebug_tables中的内容
8. long long dynamic_debug_enabled和dynamic_debug_enabled2@kernel/lib/dynamic_debugc,用于标志某个mod(可包含一个或多个文件,比如ab8500_fg mod,目前只包含ab8500_fg.c file)是否可以输出debug log的模块. 最多可以标志64*64=4096个dev_debug/dynamic_dev_dbg.
9. 是否输出dev_log/dynamic_dev_dbg的log, 关键是如下判断, @kernel/include/linux/dynamic_debug.h
#define __dynamic_dbg_enabled(dd) ({ \
int __ret = 0; \
if (unlikely((dynamic_debug_enabled & (1LL << DEBUG_HASH)) && \
(dynamic_debug_enabled2 & (1LL << DEBUG_HASH2)))) \
if (unlikely(dd.flags)) \
__ret = 1; \
__ret; })
a. dynamic_debug_enabled和dynamic_debug_enabled2就是前面分析的是否输出该modname的两个long long的组合标志位
b. DEBUG_HASH和DEBUG_HASH2 如前面所解释
c. dd.flag 默认为_DPRINTK_FLAGS_DEFAULT,但通过debugfs文件系统,最终操作ddebug_proc_write函数,会设置为_DPRINTK_FLAGS_PRIN或_DPRINTK_FLAGS_DEFAULT
10. debugfs 文件系统中的内容保存在那????? 内存中,类似proc
11. 小结:如果你需要用到dynamic debug info, 你需要在你的 .c 文件中用dev_log 输出log;CONFIG_DEBUG_FS=y;CONFIG_DYNAMIC_DEBUG=y
dynamic debug log输出机制
0. 注意该机制只对 dev_dbg -> dynamic_dev_dbg 定义的debug log输出加以控制
1. 如何使用:(kernel/Documentation/dynamic-debug-howto.txt)
mkdir /data/debugfs
mount -t debugfs none /data/debugfs
echo -n 'file ab8500_fg.c +p' > /data/debugfs/dynamic_debug/control //增加该文件dynamic debug的输出
echo -n 'file ab8500_fg.c -p' > /data/debugfs/dynamic_debug/control //去掉该文件dynamic debug的输出
2. 如果想使用debugfs 必须,在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DEBUG_FS=y
3. 如果需要使用Dynamic debug机制,需要在kernel的config文件(kernel/arch/arm/configs/semc_lotus_deconfig)中有CONFIG_DYNAMIC_DEBUG=y
4. dev_dbg@kernel/include/linux/device.h ->dynamic_dev_dbg@kernel/include/linux/dynamic_debug.h
#define dynamic_dev_dbg(dev, fmt, ...) do { \
static struct _ddebug descriptor \
__used \
__attribute__((section("__verbose"), aligned(8))) = \
{ KBUILD_MODNAME, __func__, __FILE__, fmt, DEBUG_HASH, \
DEBUG_HASH2, __LINE__, _DPRINTK_FLAGS_DEFAULT }; \
if (__dynamic_dbg_enabled(descriptor)) \
dev_printk(KERN_DEBUG, dev, fmt, ##__VA_ARGS__); \
} while (0)
a. 该define 最终会展开在被调用dev_dbg函数的c文件中,也就是KBUILD_MODNAME, __func__, __FILE__, __LINE__ 会有对应的字符串
b. _DPRINTK_FLAGS_DEFAULT=0;
c. DEBUG_HASH和DEBUG_HASH2的定义在kernel/scripts/makefile.lib中
DEBUG_HASH=$(shell ./scripts/basic/hash djb2 $(@D)$(modname)) //利用djb2 hash算法,计算modname的DEBUG_HASH value;
DEBUG_HASH2=$(shell ./scripts/basic/hash r5 $(@D)$(modname)) //利用r5 hash算法,计算modname的DEBUG_HASH2 value;
d. 分析 kernel/scripts/basic/hash.c,会生成out/target/product/lotus/obj/kernel/scripts/basic/hash, shell可执行文件
e. 在编译连接完成后,该 descriptor 值会被保存到 data section的 __verbose区
5. dynamic_debug_init@kernel/lib/dynamic_debug.c
a. dir = debugfs_create_dir("dynamic_debug", NULL);
b. file = debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops); //在debugfs文件系统中创建dynamic_debug/control 文件
c. 通过__start___verbose和__stop___verbose@kernel/include/asm-generic/vmlinux.lds.h中,实际上是获取保存在__verbose区的 struct _ddebug 数据(就是前面编译后添加到data section的__verbose)
d. 如果是不同的modname,就添加到ddebug_tables 中,也就是所有dynamic_dev_dbg的模块(modname),文件(__FILE__),行(__LINE__),函数(__func__),是否输出的flag,对应的hash value都会逐条保存到ddebug_tables中
6. 分析 echo -n 'file ab8500_charger.c +p' > /data/debugfs/dynamic_debug/control 的实际操作
a. 通过system call,debugfs文件系统会调用到ddebug_proc_write,ddebug_parse_query和ddebug_parse_flags@kernel/lib/dynamic_debug.c分析传入的参数字符串
b. 在ddebug_change@kernel/lib/dynamic_debug.c中,会根据modname, __FILE__, __LINE__, __func__信息在ddebug_tables找到对应的item.
c. 然后根据输入的是 +p或-p ,来标志struct _ddebug中flag字段,还有根据struct _ddebug中的primary_hash和secondary_hash,来标志global value dynamic_debug_enabled和dynamic_debug_enabled2 对应的位,会在__dynamic_dbg_enabled@kernel/include/linux/dynamic_debug.h用到
7. 分析 #cat /data/debugfs/dynamic_debug/control的实际操作
a. 先ddebug_proc_open中有err = seq_open(file, &ddebug_proc_seqops);应用了seq file的读写机制
b. 然后seq_read,利用seq file机制逐个读出和显示ddebug_tables中的内容
8. long long dynamic_debug_enabled和dynamic_debug_enabled2@kernel/lib/dynamic_debugc,用于标志某个mod(可包含一个或多个文件,比如ab8500_fg mod,目前只包含ab8500_fg.c file)是否可以输出debug log的模块. 最多可以标志64*64=4096个dev_debug/dynamic_dev_dbg.
9. 是否输出dev_log/dynamic_dev_dbg的log, 关键是如下判断, @kernel/include/linux/dynamic_debug.h
#define __dynamic_dbg_enabled(dd) ({ \
int __ret = 0; \
if (unlikely((dynamic_debug_enabled & (1LL << DEBUG_HASH)) && \
(dynamic_debug_enabled2 & (1LL << DEBUG_HASH2)))) \
if (unlikely(dd.flags)) \
__ret = 1; \
__ret; })
a. dynamic_debug_enabled和dynamic_debug_enabled2就是前面分析的是否输出该modname的两个long long的组合标志位
b. DEBUG_HASH和DEBUG_HASH2 如前面所解释
c. dd.flag 默认为_DPRINTK_FLAGS_DEFAULT,但通过debugfs文件系统,最终操作ddebug_proc_write函数,会设置为_DPRINTK_FLAGS_PRIN或_DPRINTK_FLAGS_DEFAULT
10. debugfs 文件系统中的内容保存在那????? 内存中,类似proc
11. 小结:如果你需要用到dynamic debug info, 你需要在你的 .c 文件中用dev_log 输出log;CONFIG_DEBUG_FS=y;CONFIG_DYNAMIC_DEBUG=y
八、Factory data reset流程
1. [JAVA] packages/apps/settings/src/com/android/settings/privacysetting.java和masterclear.java, 有如下代码
sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
2. [JAVA] frameworks/base/core/res/androidmanifest.xml中有
<receiver android:name="com.android.server.MasterClearReceiver"
...
<intent-filter>
<action android:name="android.intent.action.MASTER_CLEAR" />
<category android:name="android.intent.category.MASTER_CLEAR" />
</intent-filter>
</receiver>
3. [JAVA] frameworks/base/services/java/com/android/server/masterclearreceiver.java, 调用 RecoverySystem.rebootWipeUserData(context);
4. rebootWipeuserdata@frameworks/base/core/java/android/os/recoverysystem.java-->bootCommand--> 写入“--wipe_data”到手机的cache/recovery/command文件中,然后pm.reboot("recovery");
5. reboot@frameworks/base/core/java/android/os/powermanager.java-->reboot@frameworks/base/services/java/com/android/server/powermanagerservice.java