历史上的今天
今天是:2025年08月05日(星期二)
2021年08月05日 | MC9S12G128模块化分层化软件架构之八_QAC静态代码分析
2021-08-05 来源:eefocus
1 overview
1.1 目的
本文档用于起点开发板的软件优化说明。
不局限于硬件功能的实现,着眼于实现高质量、优美的软件。

2 QAC基础知识
2.1 introduction
QA·C是用于C代码的深度静态分析器。 QA·C旨在帮助提高软件开发的质量。
QA·C在逐个文件和完整项目的基础上分析源代码,以识别C语言的危险用法。包含1300多个警告消息的库用于突出显示那些不可移植的、难以维护的、过于复杂或可能引起问题的方式编写的源码。
该工具还可以识别不符合ISO C90标准(ISO / IEC 9899:1990)或依赖于未指定、未定义的行为的语言用法。该工具会在使用ISO C99标准的特定功能时发出警告,但确实支持其中许多功能的语义。
整个项目的源代码的警告消息通过浏览器显示,该浏览器对所有源文件中出现的消息进行分类和分组。
该工具的其他功能包括:
•提供可量化衡量代码属性的代码度量:基于函数的33个,基于文件的30个和项目级别的4个;
•功能结构图,可深入了解控制流程;
•关系图,用于演示函数调用,全局引用和文件树;
•关系统计分析,根据行业基准对代码质量进行全面评估;
•跨模块分析(CMA)功能,分析功能递归和各种全局标识符问题;
•一个基准模块,可简化对遗留代码的修改。
2.2 message等级
QAC中将message分为10个等级。
2.2.1 Level0:information
information等级的警告重要性最低。

2.2.2 Level 1: Obsolete Messages
保留标记为过时的消息是为了向后兼容2个产品版本,并将随后将其删除。
2.2.3 Level 2: Minor
级别2信息识别那些可能会违反编码标准要求的问题,但这些问题不一定是严重的错误。级别2信息组与C语言的不同方面相关联。
2.2.4 Level 3: Major
Major信息是那些可能会识别出重大编码错误,异常或问题的信息。通常认为这些信息(如7、8和9级)太重要了,以至于没有充分的理由就无法忽略。 3级信息组与C语言的不同方面相关联。
2.2.5 Level 4: Local Standards
级别4信息识别那些不符合您的编程标准的功能。按照惯例,在安装QA·C模块时,会将其他信息组添加到级别4,其中将包含与编码标准中特定规则相对应的信息。
2.2.6 Level5: Dataflow Analysis
此级别的信息对运行时行为进行分析,确定问题,从严重的问题(如未定义的行为)到经常与编码和逻辑错误相关的条件。

overflow and wraparound:
当整数值增加到太大而无法存储在关联表示中时,就会发生整数溢出或环绕。发生这种情况时,该值可能会变成非常小的值或负数。当结果用于控制循环,做出安全性决定或确定行为的偏移量或大小(例如内存分配,复制,串联等)时,这对安全性至关重要。
2.2.7 Level 6: portability

2.2.8 Level 7: Undefined behavior

3 meesage help

4 QAC 报issue的点
4.1 LEVEL6: [C99]Trailing comma at the end of an enumerator-list.
枚举列表末尾的逗号。
ISO C99language features.
在枚举常量列表的末尾有逗号。ISO:C90无法识别此语法,但是ISO:C99允许使用此语法。
forexample:
enum ED { ZERO=0, TWO=2, FOUR=4, };
对应的MISARC 2004 rule:1.1。
4.1.1 修改的代码
4.1.1.1 drio.h
等。
4.1.1.2 mdio_cfg.h

等。
4.2 level 7: The identifier is reserved for use by the library.
标识符保留给库使用。
使用保留名称去定义一个标识符不可取,因为该名称可能与系统库的现有版本或将来版本中的标识符冲突。以下标识符保留:
a)以2个下划线开头;
b)以1个下划线紧跟着一个大写字母开头;
c) 以1个下划线和一个其他的标识符开头;
4.2.1 例子
int __glob = 0; /* 不符合 */
int _Glob = 1; /* 不符合 */
对应的MISAR C 2004 rule:20.2。
4.2.2 修改的代码
4.2.2.1 apl_key.h

等。
4.3 level 3: An expression of ‘essentially signed’ type is being used asthe operand of this bitwise operator.
原始有符号类型的表达式用作位运算的操作数。
4.3.1 例子
void foo(int a)
{
a & 1u; /* 不符合 */
a | 1u; /* 不符合 */
~a; /* 不符合 */
a >> 1u; /* 不符合 */
1u << a; /* 不符合 */
}
对应的MISARrule 12.7。
4.3.2 对应代码中的message
4.3.2.1 drint.c

4.3.3 修改的代码
4.3.3.1 drint.c

4.4 level3: An expression of ‘essentially Boolean’ type is being converted tounsigned type on assignment.
分配时,“基本布尔”类型的表达式将转换为无符号类型。
相关的MISARCrule 10.1。
例子:
void foo (unsigned b);
unsigned test (int a)
{
unsigned uia = (a != 0); /*不符合*/
foo(a < 0); /*不符合*/
uia = (a < 0); /*不符合*/
return (a == 0); /*不符合*/
}
对应的MISAR C 2004rule 10.1。
4.4.1 修改的代码
4.4.1.1 bitop.h

BYTEOP_BIT_READ原宏定义:
#defineBYTEOP_BIT_READ_AT(data, bitidx) (((uint8)(data) & BYTEOP_POWER_2(bitidx)) != 0U)
返回的值为bool型,true or false
改为:
#defineBYTEOP_BIT_READ_AT(data, bitidx) ((((uint8)(data) & BYTEOP_POWER_2(bitidx)) != 0U) ? (uint8)1U :(uint8)0U)
返回的值为1U或0U

4.5 level 2: Loop control variable in this ‘for’ statement is modifiedin the body of the loop.
for语句的循环控制变量在循环体中被修改了。
在此“ for”循环中,被识别为控制变量的变量在循环体内进行了修改。
QAC通过检查 “ for-loop”中的3个表达式的来识别控制变量。通常,它是第二个表达式中的变量,并且是关系运算符的操作数。
在常规的for循环中,仅在for语句的第三个表达式中修改循环控制变量。循环中其他位置的修改可能会引起混乱。
4.5.1 例子
extern void foo(void)
{
int j;
for (j = 0; j < 10; j++)
{
++j;
}
}
对应的MISARC2004rule: 13.6。
4.5.2 对应的代码中的message

4.5.3 修改的代码
4.5.3.1 drint.c

4.6 level 2: This statement has no side-effect – it can be removed.
语句没有side-effect,可以移除。
可以被优化掉的代码。
sideeffects: 更改执行环境的动作。访问易失性对象,修改对象,修改文件或调用执行任何这些操作的函数都是副作用。
4.6.1 例子
void foo( int x )
{
int n = 0;
n == x; /*报警告*/
}
对应的MISAR2004Rule 14.2。
4.6.2 对应的代码中的message

4.6.3 修改的代码
4.6.3.1 mdio.c

等。
4.7 level 2: function with internal linkage, is being defined without aprevious declaration.
一个被内部引用的函数在定义时没有提前的声明。
在文件中定义的所有的函数都应在文件顶部加原型声明,这很有帮助。这样可以避免在函数被声明之前被调用的可能性。在文件开始处的一个位置记录函数接口的方式也很有用。
例子:
static voidfoo(void) /*没有提前声明*/
{
}
对应的MISAR 2004rule 8.1
4.7.1 对应的代码中的message
4.7.1.1 mdio.c

4.7.2 修改的代码
4.7.2.1 mdio.c
在文件头部增加static uint8 mdio_find_in_input_cfg(uint8port, uint8 pin)函数声明。
4.8 level 2:A function counld probably be used instead of this function-like macro.
类似函数的宏应当用函数替换。
该宏是表达式的形式,因此可以用一个函数代替。用函数替换类似函数的宏并不总是可行或方便。但是函数通常比宏更安全,因为可以对参数执行类型检查。
下面这些类似函数的宏会导致该条信息的产生:
· 有至少一个参数;
· 不包含’#’ or ‘##’操作符;
· 不包含’ {}; ‘或任何关键字;
对应的MISARC2004rule: 19.7。
4.8.1 例子
#define M1(a, b, c) ( (a) + (b) + (c)) /* 不符合 */
#define M6(A, B) ((A)[1] +(B)[2]) /* 不符合 */
对应的MISAR C2004 rule 19.7。
需要写成do-while(0)格式。
为什么写成do-while(0)格式,请看MISARC2004rule: 19.4 和 19.10。
4.9 level 8:initializer pointer to a more heavily qualified type.
初始化程序指向更严格的类型。
指针已使用表达式进行了初始化,该表达式指向更严格的类型。
ISO: C要求被初始化对象的类型必须具有初始化者指向的类型的所有的qualifier。因此,不允许使用类型为“ const TYPE *”(指向const TYPE的指针)或“ volatile TYPE *”(指向volatile TYPE的指针)的表达式来初始化类型“ TYPE *”(指向TYPE的指针)的对象;这样做将违反类型资格限制。
另一方面,使用类型“ TYPE *”的表达式初始化“ const TYPE *”或“ volatile TYPE *”甚至“ const volatile TYPE *”类型的对象是完全合法的。
原文:
A pointer has been initialized with anexpression which points to a more heavily qualified type.
ISO:Crequires that the type pointed to by the initialised object must have all thequalifiers of the type pointed to by the initializer. It is therefore notpermissible to initialize an object of type "TYPE *" (pointer toTYPE) with an expression of type "const TYPE *" (pointer to constTYPE) or "volatile TYPE *" (pointer to volatile TYPE); to do so wouldpermit type qualification restrictions to be violated.
On theother hand, it is perfectly legitimate to initialize an object of type"const TYPE *" or "volatile TYPE *" or even "constvolatile TYPE *" with an expression of type "TYPE *".
4.9.1 例子
extern int *gpi;
extern const int *gpci;
extern volatile int *gpvi;
extern const volatile int * gpcvi;
void test (void)
{
int *xpia = gpi; /* no message */
int *xpb = gpci; /* 不符合 */
int *xpc = gpvi; /* 不符合 */
int *xpd = gpcvi; /* 不符合 */
const int *xpcia = gpi; /* no message */
const int *xpcib = gpci; /* no message */
constint *xpcic = gpvi; /* 不符合 */
constint *xpcid = gpcvi; /* 不符合 */
volatile int *xpvia = gpi; /* no message */
volatile int *xpvib = gpci; /* 不符合 */
volatile int *xpvic = gpvi; /* no message */
volatile int *xpvod = gpcvi; /* 不符合 */
const volatile int *xpcvia = gpi; /* no message */
const volatile int *xpcvib = gpci; /* no message */
const volatile int *xpcvic = gpvi; /* no message */
const volatile int *xpcvid = gpcvi; /* no message */
}
4.9.2 对应的代码中的message
4.9.2.1 drio_cfg.c

4.9.3 修改的代码
4.9.3.1 drio_cfg.h
typedef uint8 * DrRegType;
改为
typedef uint8 * DrRegType;

因为DDRE的定义和使用为:
4.10 level 3: A non-constantexpression of ‘essentially signed’ type is being converted to unsigned type onassignment.
“原始有符号的”类型的非常量的表达式转换为无符号类型。
void foo(unsigned b);
unsignedtest(short a)
{
unsigned uia = a; /* 不符合 */
foo( a + 1 ); /* 不符合 */
uia = (a – 1); /* 不符合 */
return a; /* 不符合 */
}
4.10.1 对应的代码中的message

4.10.2 修改的代码
4.10.2.1 drio.c

4.11 level2: Array declared with unknown size.
大小未知的数组声明。
数组声明时类型不完整。
仅当数组具有外部连接时才允许这样做。一些编码标准不赞成这种做法,因为它阻止工具检查数组下标操作的有效性。
4.11.1 例子
extern int xa[10];
extern int xb[]; /* 警告 */
extern void foo(void)
{
xa[10] = 0; /* 警告,越界 */
xb[10] = 0; /* 没有警告,数组大小未知 */
}
对应的MISAR C2004 rule: 8.12。
4.11.2 修改的代码
4.11.2.1 drio_cfg.h

4.12 Macroparameter not enclosed in ().
宏参数未包含在()中。
在某些宏中,将参数括在替换列表的括号中这一操作很重要。并非所有的宏或所有参数都是如此。这取决于宏中如何使用参数。在某些类型的表达式中,将宏参数用作操作数时就会出现问题。考虑以下示例:
#define SQUARE(A) (A * A) /* 报警告 */
史海拾趣
|
各位朋友,请问有做过s3c2442的么?我现在在调试s3c2442的开发板,uart和nand flash已经测试可用了,但是到sdram的时候卡住了 具体的问题就是:根据s3c2442的数据手册,初始化之前首先要precharge all,我看了precharge all的真值表,需要设置RA ...… 查看全部问答> |
|
1、以上有源低通滤波直流跟随电路,用TINA仿真V_IN和V_OUT可以跟随的很好,电压差几十uV左右,实际电路中V_IN在0.5V时V_OUT会高出13mV左右,V_IN在4.5V时V_OUT会高出2mV左右,测试电路中V_IN电压变化由R2、R5分压产生,请问是什么原因如何解决跟 ...… 查看全部问答> |






