1. 情景分析法必须确立一个情景,此情景满足以下条件:
# a.满足自己的学习目标
# b.该情景内的代码,应该只能被执行1次.譬如,分析usb进行数据传输的过程. 那么在执行该情景前,应该将
# 所以的USB设备拔出, 或者将它们对应的驱动取消执行.
# 再譬如, 要分析kmalloc()的执行过程, 那么我应该在该kmalloc第一次被执行处,加上一句while(1), 防止后续又
# 有kmalloc()被执行.
# c.若该情景需要用户应用, 譬如应用获取按键事件等. 所要执行的应用程序应尽量得到其源码, 或者自行编写或搜索相关例程.
# 拥有源码后, 就可以将不必要的代码删除, 减少关联驱动的阅读.
2. 情景分析法实践流程:
# a. 由上至下的代码简化. 由上至下,即由应用程序=>驱动框架=>硬件驱动.
# 应用程序的一句ioctl(), 可能涉及驱动的几百个函数的执行. 若是我们能先尽量地简化应用程序,或驱动框架,会大大地
# 减少代码阅读量.
# b. 批量添加函数名打印. 根据打印, 可以一一对被执行的函数进行简化. 具体添加打印方法,见
# https://blog.csdn.net/weixin_45154862/article/details/127192739
# c. 集合所有被简化的函数, 进行代码分析.
3. 细节说明之 函数简化:
# 去掉判断语句,循环语句, 宏定义的语句, 总之去掉所以需要我们进行分析判断, 耗费脑力的代码.
# 而最终简化的结果是, 我们可以无需依靠打印,开发板的运行输出, 便可以靠阅读代码,知道代码的执行流程.
# 函数简化的范围: 我们分析的是驱动代码, 其他非该驱动框架或驱动的代码无需简化分析. 譬如kmalloc.
# 一般,范围会限制在该驱动框架或驱动所在文件夹内.
# z. 直接删除函数
# 在对某个函数进行简化前, 我们可以试着将该函数删除, 会有30%的概率,情景依旧正常执行!
# 这样的结果是喜闻乐见的,因为一个函数,可能会嵌套几十个函数, 那么我们就可以少简化几十个函数,
# 少分析几十个函数. 大大地减少工作量.
# 因此简化前,请先执行此步骤.
# a. 保留副本
# 需要简化的函数, 应保留其副本(函数原文件处,复制一份,并重命令), 方便代码分析时回看.
# 而我们的简化工作就直接在原函数进行.
# b. 独立函数的执行.
# 若某函数被执行了多次, 且每次的执行, 执行流程的不同.可以将该函数的代码复制多份, 并重命名,
# 以独立每次函数的执行. 这样,简化工作就在其函数副本内执行, 这区别于步骤a. 每一个函数的副本,
# 若想它们被执行, 需要在头文件添加声明, 将会增加工作量. 这也就是步骤a直接修改原函数,而非副本函数的理由.
# 譬如:
# 处理前:
test();
test();
test();
# 处理后:
test1();
test2();
test3();
# c. 去掉所有的判断语句的分支, 仅保留被执行的代码,及其判断条件.
# 譬如:
# 简化前:
void test(int a,int b)
{
int c;
if(a>9) {
xxxx; /* 被执行 */
} else if (b<9) {
xxxx;
}
}
# 简化后
void test(int a,int b)
{
int c;
if(a>9) {
xxxx; /* 被执行 */
}
}
# d. 去掉循环
# 譬如:
# 简化前
i=0;
while(i<get_count()){
set_index(i);
i++;
}
# 简化后
/* get_count()的返回值为3 */
i=0;
set_index(i);
i=1;
set_index(i);
i=2;
set_index(i);
# 这时, set_index()函数被执行了多次.可以使用b步骤进行处理.
# e. 去掉宏定义
# 譬如:
# 简化前:
# xxx.h
#define TEST(a,b) a>b:3?4
xxx.c
TEST(5,6);
#简化后:
xxx.c
5>6:3?4
# f. 删除指针赋值语句
# 太多的指针变量, 使得结构体间的关系错综复杂.但其实许多的指针,在当前情景内,是不被使用的,
# 将它们删除, 可以减少代码分析时的工作量. 不仅是指针, 含有队列的入队,出队等语句, 也可删除的
# 都尽量删除.
4. 细节说明之 代码分析
# a. 集合所有简化后的,需要被执行的, 该驱动相关的, 函数到一个文件内. 依据函数的嵌套关系,将函数源码补充完全.
# 主要目的是方便分析, 和进行代码标注.
# 譬如:
int test(void)
{
get_index();
void get_index(void)
{
set_type(c);
void set_type(struct type a)
{
g_type = a;
}
}
}
# b. 结构体成员关系标注
# 使用<tagx> 指示 某一操作的对象,以及操作过程.
# 对象或成员处注有<tagx>, 操作处也注有 <tagx>, 可以实现跳转,通过手动搜索<tagx>.
# 譬如, 这里的<tagx> 可以记录该成员在何处被分配内存, 或被赋值.
struct machine {
struct tv b;
};
struct machine a;
-> struct tv *b; <tag3>
int machine_type_create(struct machine *mac)
{
mac->b = kmalloc(); <tag3>
}
# <point to objx> 意为此指针指向objx, <objx> 在objx 处有标注.
# 譬如一下, <point to obj6> 表明b成员指向sony_tv. 而<tag5> 标注此赋值是在何处进行的.
strct machine a;
->struc tv *b; <point to obj6> <tag5>
struct tv sony_tv; <obj6>
int get_tv(struct machine *mac)
{
mac->b = &sony_tv; <tag5>
}
# <add to listx> 意为将此list_head加入 listx, <listx> 在链表头处有标注.
#https://gitee.com/suiren/usb2hdmi/blob/master/drm_read.c
#drm_read.c一文,是完全使用本文的代码阅读方法, 而获得的驱动阅读总结, 也可作为参考.