代码质量优化之静态代码扫描

1 篇文章 0 订阅

背景

        质量是保证产品和服务满足顾客需求的关键,真正的好产品,是能以用户为中心,和用户做朋友的。作为开发人员,除了保证产品基础功能正常外,还要保证高标准的代码质量。代码质量可以从代码的严谨性、可读性、可维护性、健壮性、性能和效率、代码覆盖度、易测性、可移植性几个方面的评价。

  1. 严谨性:指的是逻辑严谨,代码逻辑要符合运行预期。在这一环节,要避免内存泄漏、句柄泄漏、使用恰到的同步\异步机制等。好的代码质量,在某段代码指令执行后,会产生什么样的动作、内存功耗占用多少都是要符合预期的。
  2. 可读性:代码应当易于理解和阅读,具有良好的注释和命名规范。变量、函数和类的命名应当具有描述性,能够清晰地表达其功能和作用。
  3. 可维护性:建立在前两步的基础上,使得代码易于修改和扩展,结构清晰、模块化。合理的代码组织和设计可以降低修改代码的难度和风险。
  4. 健壮性:代码应当具有错误处理和容错机制,或者是友好正确的异常提示。有过产品量产经验的同学应该知道,代码运行环境多样,甚至会有一些不可避免的硬件故障,当这些不可避免的故障出现后,我们要有恰到的容错机制或者是对用户有一个友好的提醒/引导。
  5. 性能和效率:代码应当具有良好的性能和效率。避免不必要的计算和内存消耗,合理使用算法和数据结构,能够尽可能地优化代码的执行速度和资源利用率。例如在一些对功耗要求较高的嵌入式产品中,某个算法运算时使用高性能的数学函数,避免高开销的函数调用,减少内存\IO操作来缩短代码执行时间,进而也是能提升功耗的。
  6. 代码覆盖度:指的高内聚低耦合。代码应当具有可重用性,避免重复编写相同或相似的代码。合理的代码组织和设计可以提高代码的复用性,减少代码的冗余和重复。
  7. 易测性:这里不仅仅是覆盖完整的测试路径,还要对一些正常用户难以复现的Bug,想出更容易复现的操作办法,如可以采取模拟用户场景、模拟用户数据参数等。
  8. 可移植性:嵌入式平台中,经常会有需要移植的代码段,尽量避免与系统/CPU高度耦合的代码(除去一些必须依赖汇编语言的锁操作)。

 总之,代码质量是一个综合评价,需要考虑多个方面的因素。一个高质量的代码能够提高开发效率、减少维护成本,并且能够更好地适应需求的变化。为了提高代码质量,准备从静态扫描、内存检测、CodeSize优化和性能优化四个方向来总结常用的方法,如有不足之处,欢迎大家留言指正。

工具介绍

fanalyzer

        代码扫描是针对源代码在编译阶段进行分析的工具,它通过对代码进行静态分析,对代码的语法、结构、逻辑错误等进行检查,提示代码中潜在的语法错误、内存泄漏、数组越界等潜在的代码风险。

GCC-Static Analysis

        这里指的gcc 10或者10以上或者clang版本的-fanalyzer选项。具体用法是gcc -fanalyzer -Wxxxx。 先看一下官方链接https://gcc.gnu.org/onlinedocs/gcc/Static-Analyzer-Options.html

        随着编译器不断的更新迭代,完善了一些开发工具,其中包括代码扫描工具。在GCC10和以上的版本可以使用。基于LLVM的C/C++编译器Clang早在2009年就内置了基于符号执行的静态源代码分析功能。该功能在近年来取得了巨大的发展。为此,在红帽的推动下,GCC也开始开发专门的静态分析功能。该功能最早在GCC 10出现,在GCC 12实现了较为完整的功能。建议在GCC10以上的版本使用。

This option is only available if GCC was configured with analyzer support enabled.
需要注意的是,使用该选项的前提需要gcc配置打开fanalyzer 选项。

使用举例:

lingx@lingx-virtual-machine:~$ cat a.c
#include<stdio.h>
#include<stdlib.h>

int main(int argc,char *argv[])
{
	int *p=(int *)malloc(sizeof(int));
	*p=1;
	printf("p @%p\n",p);
	return 0;
}
lingx@lingx-virtual-machine:~$ gcc -fanalyzer -Wanalyzer-malloc-leak  a.c -oa
a.c: In function ‘main’:
a.c:9:11: warning: dereference of possibly-NULL ‘p’ [CWE-690] [-Wanalyzer-possible-null-dereference]
    9 |         *p=1;
      |         ~~^~
  ‘main’: events 1-2
    |
    |    8 |         int *p=(int *)malloc(sizeof(int));
    |      |                       ^~~~~~~~~~~~~~~~~~~
    |      |                       |
    |      |                       (1) this call could return NULL
    |    9 |         *p=1;
    |      |         ~~~~           
    |      |           |
    |      |           (2) ‘p’ could be NULL: unchecked value from (1)
    |

其他更完整的选项可以参考官方文档介绍

fanalyzer
This option enables an static analysis of program flow which looks for “interesting” interprocedural paths through the code, and issues warnings for problems found on them.

This analysis is much more expensive than other GCC warnings.

In technical terms, it performs coverage-guided symbolic execution of the code being compiled. It is neither sound nor complete: it can have false positives and false negatives. It is a bug-finding tool, rather than a tool for proving program correctness.

The analyzer is only suitable for use on C code in this release.

Enabling this option effectively enables the following warnings:

-Wanalyzer-allocation-size
-Wanalyzer-deref-before-check
-Wanalyzer-double-fclose
-Wanalyzer-double-free
-Wanalyzer-exposure-through-output-file
-Wanalyzer-exposure-through-uninit-copy
-Wanalyzer-fd-access-mode-mismatch
-Wanalyzer-fd-double-close
-Wanalyzer-fd-leak
-Wanalyzer-fd-phase-mismatch
-Wanalyzer-fd-type-mismatch
-Wanalyzer-fd-use-after-close
-Wanalyzer-fd-use-without-check
-Wanalyzer-file-leak
-Wanalyzer-free-of-non-heap
-Wanalyzer-imprecise-fp-arithmetic
-Wanalyzer-infinite-loop
-Wanalyzer-infinite-recursion
-Wanalyzer-jump-through-null
-Wanalyzer-malloc-leak
-Wanalyzer-mismatching-deallocation
-Wanalyzer-null-argument
-Wanalyzer-null-dereference
-Wanalyzer-out-of-bounds
-Wanalyzer-overlapping-buffers
-Wanalyzer-possible-null-argument
-Wanalyzer-possible-null-dereference
-Wanalyzer-putenv-of-auto-var
-Wanalyzer-shift-count-negative
-Wanalyzer-shift-count-overflow
-Wanalyzer-stale-setjmp-buffer
-Wanalyzer-tainted-allocation-size
-Wanalyzer-tainted-array-index
-Wanalyzer-tainted-assertion
-Wanalyzer-tainted-divisor
-Wanalyzer-tainted-offset
-Wanalyzer-tainted-size
-Wanalyzer-undefined-behavior-strtok
-Wanalyzer-unsafe-call-within-signal-handler
-Wanalyzer-use-after-free
-Wanalyzer-use-of-pointer-in-stale-stack-frame
-Wanalyzer-use-of-uninitialized-value
-Wanalyzer-va-arg-type-mismatch
-Wanalyzer-va-list-exhausted
-Wanalyzer-va-list-leak
-Wanalyzer-va-list-use-after-va-end
-Wanalyzer-write-to-const
-Wanalyzer-write-to-string-literal

This option is only available if GCC was configured with analyzer support enabled.

除此之外还可以使用 -Wall -Werror来配合诊断代码质量。

Codechecker

官方链接:

https://codechecker.readthedocs.io/en/latest/

https://github.com/Ericsson/codechecker/tree/master

CodeChecker是一个建立在LLVM/Clang静态分析器工具链上的静态分析基础设施,它取代了Linux或macOS(OS X)开发环境中的Scan-build。

在Ubuntu2204可以利用python3-pip插件来安装这个功能。

sudo apt install python3-pip
sudo pip3 install codechecker
# Install mandatory dependencies for a development and analysis environment.
# NOTE: clang or clang-tidy can be any sufficiently fresh version, and need not
#       come from package manager!
#       In case of Cppcheck, the minimal supported version is 1.80.
#       In case of gcc, the minimal supported version is 13.0.0.
sudo apt-get install clang clang-tidy cppcheck g++ build-essential curl
      gcc-multilib git python3-dev python3-venv python3-setuptools

# Install nodejs dependency for web. In case of Debian/Ubuntu you can use the
# following commands. For more information see the official docs:
# https://nodejs.org/en/download/package-manager/
curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install -y nodejs

# Check out CodeChecker source code.
git clone https://github.com/Ericsson/CodeChecker.git --depth 1 ~/codechecker
cd ~/codechecker

# Create a Python virtualenv and set it as your environment.
# NOTE: if you want to develop CodeChecker, use the `venv_dev` target instead
# of `venv`.
make venv
source $PWD/venv/bin/activate

# [Optional] If you want to use external authentication methods (LDAP / PAM)
# follow the instructions in
# docs/web/authentication.md#external-authentication-methods

# Build and install a CodeChecker package.
make package

# For ease of access, add the build directory to PATH.
export PATH="$PWD/build/CodeChecker/bin:$PATH"

cd ..

//安装完成以后看一下help
lingx@lingx-virtual-machine:~$ CodeChecker -h
usage: CodeChecker [-h] {analyze,analyzer-version,analyzers,check,checkers,cmd,fixit,log,parse,server,store,version,web-version} ...

Run the CodeChecker sourcecode analyzer framework.
Please specify a subcommand to access individual features.

positional arguments:
  {analyze,analyzer-version,analyzers,check,checkers,cmd,fixit,log,parse,server,store,version,web-version}
                        commands
    analyze             Execute the supported code analyzers for the files recorded in a JSON Compilation Database.
    analyzer-version    Print the version of CodeChecker analyzer package that is being used.
    analyzers           List supported and available analyzers.
    check               Perform analysis on a project and print results to standard output.
    checkers            List the checkers available for code analysis.
    cmd                 View analysis results on a running server from the command line.
    fixit               Apply automatic fixes based on the suggestions of the analyzers
    log                 Run a build command and collect the executed compilation commands, storing them in a JSON file.
    parse               Print analysis summary and results in a human-readable format.
    server              Start and manage the CodeChecker Web server.
    store               Save analysis results to a database.
    version             Print the version of CodeChecker package that is being used.
    web-version         Print the version of CodeChecker server package that is being used.

下面举例说明一下使用方法。

1.先写一段比较糟糕的代码。

#include<stdio.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
	int *p=(int *)malloc(sizeof(int));
	*p=1/0;
	printf("p @%p\n",p);
	return 0;
}

这段代码很明显不合理的地方内存泄漏、除数为0。看看Codechecker是否能帮忙检测出来。

  • 1. 首先使用log选项,来运行一个build指令并生成一个json文件。关于log选项的说明如下

Run a build command and collect the executed compilation commands, storing them in a JSON file.

lingx@lingx-virtual-machine:~$ CodeChecker log --build "make" --output ./compile_commands.json
[INFO 2024-04-19 23:02] - Starting build...
[INFO 2024-04-19 23:02] - Using CodeChecker ld-logger.
gcc   a.c -oa
a.c: In function ‘main’:
a.c:9:13: warning: division by zero [-Wdiv-by-zero]
    9 |         *p=1/0;
      |             ^
[INFO 2024-04-19 23:02] - Build finished successfully.
  •  2.使用analyze来解析上面log生成的json,并输出报告。

    analyze             Execute the supported code analyzers for the files recorded in a JSON Compilation Database.

lingx@lingx-virtual-machine:~$ CodeChecker analyze ./compile_commands.json --enable sensitive --output ./reports
[INFO 2024-04-19 23:03] - Enabled checkers:
clang-tidy: bugprone-suspicious-memory-comparison, clang-diagnostic-array-bounds-pointer-arithmetic, clang-diagnostic-bitwise-conditional-parentheses, clang-diagnostic-bitwise-instead-of-logical, clang-diagnostic-bitwise-op-parentheses, clang-diagnostic-bool-operation, clang-diagnostic-cast-of-sel-type, clang-diagnostic-char-subscripts, clang-diagnostic-comment, clang-diagnostic-comments, clang-diagnostic-dangling-else, clang-diagnostic-delete-abstract-non-virtual-dtor, clang-diagnostic-delete-non-abstract-non-virtual-dtor, clang-diagnostic-delete-non-virtual-dtor, clang-diagnostic-deprecated-copy, clang-diagnostic-deprecated-copy-with-dtor, clang-diagnostic-deprecated-copy-with-user-provided-dtor, clang-diagnostic-deprecated-copy-dtor, clang-diagnostic-deprecated-copy-with-user-provided-copy, clang-diagnostic-division-by-zero, clang-diagnostic-double-promotion, clang-diagnostic-embedded-directive, clang-diagnostic-empty-init-stmt, clang-diagnostic-extern-c-compat, clang-diagnostic-float-conversion, clang-diagnostic-for-loop-analysis, clang-diagnostic-format, clang-diagnostic-format-non-iso, clang-diagnostic-format-pedantic, clang-diagnostic-format-type-confusion, clang-diagnostic-format=2, clang-diagnostic-format-extra-args, clang-diagnostic-format-insufficient-args, clang-diagnostic-format-invalid-specifier, clang-diagnostic-format-nonliteral, clang-diagnostic-format-security, clang-diagnostic-format-y2k, clang-diagnostic-format-zero-length, clang-diagnostic-frame-address, clang-diagnostic-fuse-ld-path, clang-diagnostic-ignored-qualifiers, clang-diagnostic-ignored-reference-qualifiers, clang-diagnostic-implicit, clang-diagnostic-implicit-atomic-properties, clang-diagnostic-implicit-conversion-floating-point-to-bool, clang-diagnostic-implicit-exception-spec-mismatch, clang-diagnostic-implicit-fallthrough, clang-diagnostic-implicit-fallthrough-per-function, clang-diagnostic-implicit-fixed-point-conversion, clang-diagnostic-implicit-retain-self, clang-diagnostic-implicitly-unsigned-literal, clang-diagnostic-implicit-float-conversion, clang-diagnostic-implicit-const-int-float-conversion, clang-diagnostic-implicit-function-declaration, clang-diagnostic-implicit-int, clang-diagnostic-implicit-int-conversion, clang-diagnostic-implicit-int-float-conversion, clang-diagnostic-infinite-recursion, clang-diagnostic-initializer-overrides, clang-diagnostic-int-in-bool-context, clang-diagnostic-logical-not-parentheses, clang-diagnostic-logical-op-parentheses, clang-diagnostic-misleading-indentation, clang-diagnostic-mismatched-tags, clang-diagnostic-missing-braces, clang-diagnostic-missing-field-initializers, clang-diagnostic-missing-method-return-type, clang-diagnostic-most, clang-diagnostic-move, clang-diagnostic-multichar, clang-diagnostic-non-virtual-dtor, clang-diagnostic-nonnull, clang-diagnostic-null-pointer-arithmetic, clang-diagnostic-null-pointer-subtraction, clang-diagnostic-objc-designated-initializers, clang-diagnostic-objc-flexible-array, clang-diagnostic-objc-missing-super-calls, clang-diagnostic-overloaded-shift-op-parentheses, clang-diagnostic-overloaded-virtual, clang-diagnostic-parentheses, clang-diagnostic-parentheses-equality, clang-diagnostic-pessimizing-move, clang-diagnostic-potentially-evaluated-expression, clang-diagnostic-private-extern, clang-diagnostic-range-loop-construct, clang-diagnostic-redundant-move, clang-diagnostic-reorder, clang-diagnostic-reorder-ctor, clang-diagnostic-reorder-init-list, clang-diagnostic-return-std-move, clang-diagnostic-return-type, clang-diagnostic-return-type-c-linkage, clang-diagnostic-self-assign, clang-diagnostic-self-assign-field, clang-diagnostic-self-assign-overloaded, clang-diagnostic-self-move, clang-diagnostic-semicolon-before-method-body, clang-diagnostic-shift-op-parentheses, clang-diagnostic-sign-compare, clang-diagnostic-sizeof-array-argument, clang-diagnostic-sizeof-array-decay, clang-diagnostic-sometimes-uninitialized, clang-diagnostic-static-in-inline, clang-diagnostic-static-self-init, clang-diagnostic-string-concatenation, clang-diagnostic-string-plus-int, clang-diagnostic-switch, clang-diagnostic-switch-default, clang-diagnostic-switch-enum, clang-diagnostic-switch-bool, clang-diagnostic-tautological-bitwise-compare, clang-diagnostic-tautological-compare, clang-diagnostic-tautological-constant-compare, clang-diagnostic-tautological-constant-out-of-range-compare, clang-diagnostic-tautological-objc-bool-compare, clang-diagnostic-tautological-overlap-compare, clang-diagnostic-tautological-pointer-compare, clang-diagnostic-tautological-undefined-compare, clang-diagnostic-trigraphs, clang-diagnostic-unevaluated-expression, clang-diagnostic-uninitialized, clang-diagnostic-uninitialized-const-reference, clang-diagnostic-unknown-pragmas, clang-diagnostic-unneeded-internal-declaration, clang-diagnostic-unused-argument, clang-diagnostic-unused-but-set-parameter, clang-diagnostic-unused-but-set-variable, clang-diagnostic-unused-comparison, clang-diagnostic-unused-const-variable, clang-diagnostic-unused-function, clang-diagnostic-unused-label, clang-diagnostic-unused-lambda-capture, clang-diagnostic-unused-local-typedef, clang-diagnostic-unused-local-typedefs, clang-diagnostic-unused-parameter, clang-diagnostic-unused-private-field, clang-diagnostic-unused-property-ivar, clang-diagnostic-unused-result, clang-diagnostic-unused-value, clang-diagnostic-unused-variable, clang-diagnostic-user-defined-warnings, clang-diagnostic-volatile-register-var, readability-suspicious-call-argument, boost-use-to-string, bugprone-argument-comment, bugprone-assert-side-effect, bugprone-bad-signal-to-kill-thread, bugprone-bool-pointer-implicit-conversion, bugprone-branch-clone, bugprone-copy-constructor-init, bugprone-dangling-handle, bugprone-dynamic-static-initializers, bugprone-exception-escape, bugprone-fold-init-type, bugprone-forward-declaration-namespace, bugprone-forwarding-reference-overload, bugprone-implicit-widening-of-multiplication-result, bugprone-inaccurate-erase, bugprone-incorrect-roundings, bugprone-infinite-loop, bugprone-integer-division, bugprone-lambda-function-name, bugprone-macro-repeated-side-effects, bugprone-misplaced-operator-in-strlen-in-alloc, bugprone-misplaced-pointer-arithmetic-in-alloc, bugprone-misplaced-widening-cast, bugprone-move-forwarding-reference, bugprone-multiple-statement-macro, bugprone-narrowing-conversions, bugprone-no-escape, bugprone-not-null-terminated-result, bugprone-parent-virtual-call, bugprone-posix-return, bugprone-redundant-branch-condition, bugprone-signal-handler, bugprone-signed-char-misuse, bugprone-sizeof-container, bugprone-sizeof-expression, bugprone-spuriously-wake-up-functions, bugprone-string-constructor, bugprone-stringview-nullptr, bugprone-string-integer-assignment, bugprone-string-literal-with-embedded-nul, bugprone-suspicious-enum-usage, bugprone-suspicious-include, bugprone-suspicious-memset-usage, bugprone-suspicious-missing-comma, bugprone-suspicious-semicolon, bugprone-suspicious-string-compare, bugprone-swapped-arguments, bugprone-terminating-continue, bugprone-throw-keyword-missing, bugprone-too-small-loop-variable, bugprone-undefined-memory-manipulation, bugprone-undelegated-constructor, bugprone-unhandled-self-assignment, bugprone-unhandled-exception-at-new, bugprone-unused-raii, bugprone-unused-return-value, bugprone-use-after-move, bugprone-virtual-near-miss, cert-con36-c, cert-con54-cpp, cert-dcl03-c, cert-dcl16-c, cert-dcl21-cpp, cert-dcl50-cpp, cert-dcl54-cpp, cert-dcl58-cpp, cert-dcl59-cpp, cert-env33-c, cert-err09-cpp, cert-err33-c, cert-err34-c, cert-err52-cpp, cert-err58-cpp, cert-err60-cpp, cert-err61-cpp, cert-fio38-c, cert-flp30-c, cert-mem57-cpp, cert-msc30-c, cert-msc32-c, cert-msc50-cpp, cert-msc51-cpp, cert-oop11-cpp, cert-oop54-cpp, cert-oop57-cpp, cert-oop58-cpp, cert-pos44-c, cert-pos47-c, cert-str34-c, concurrency-thread-canceltype-asynchronous, cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-type-static-cast-downcast, cppcoreguidelines-slicing, cppcoreguidelines-special-member-functions, cppcoreguidelines-virtual-class-destructor, google-build-explicit-make-pair, google-build-namespaces, google-build-using-namespace, google-explicit-constructor, google-global-names-in-headers, google-runtime-int, google-runtime-operator, misc-definitions-in-headers, misc-misleading-bidirectional, misc-misleading-identifier, misc-misplaced-const, misc-redundant-expression, misc-unconventional-assign-operator, misc-uniqueptr-reset-release, misc-unused-alias-decls, misc-unused-using-decls, modernize-deprecated-headers, modernize-replace-auto-ptr, modernize-replace-random-shuffle, mpi-buffer-deref, mpi-type-mismatch, performance-faster-string-find, performance-for-range-copy, performance-implicit-conversion-in-loop, performance-inefficient-algorithm, performance-inefficient-string-concatenation, performance-inefficient-vector-operation, performance-move-const-arg, performance-move-constructor-init, performance-no-automatic-move, performance-no-int-to-ptr, performance-noexcept-move-constructor, performance-trivially-destructible, performance-type-promotion-in-math-fn, performance-unnecessary-copy-initialization, performance-unnecessary-value-param, readability-container-contains, readability-container-data-pointer
clangsa: alpha.core.BoolAssignment, alpha.core.CastSize, alpha.core.Conversion, alpha.core.DynamicTypeChecker, alpha.core.SizeofPtr, alpha.core.TestAfterDivZero, alpha.cplusplus.DeleteWithNonVirtualDtor, alpha.cplusplus.EnumCastOutOfRange, alpha.cplusplus.InvalidatedIterator, alpha.cplusplus.IteratorRange, alpha.cplusplus.MismatchedIterator, alpha.cplusplus.STLAlgorithmModeling, alpha.cplusplus.SmartPtr, alpha.security.MmapWriteExec, alpha.security.ReturnPtrRange, alpha.security.cert.pos.34c, alpha.security.taint.TaintPropagation, alpha.unix.BlockInCriticalSection, alpha.unix.Chroot, alpha.unix.PthreadLock, alpha.unix.Stream, alpha.unix.cstring.NotNullTerminated, alpha.unix.cstring.OutOfBounds, core.CallAndMessage, core.DivideZero, core.NonNullParamChecker, core.NullDereference, core.StackAddressEscape, core.UndefinedBinaryOperatorResult, core.VLASize, core.uninitialized.ArraySubscript, core.uninitialized.Assign, core.uninitialized.Branch, core.uninitialized.CapturedBlockVariable, core.uninitialized.UndefReturn, cplusplus.InnerPointer, cplusplus.Move, cplusplus.NewDelete, cplusplus.NewDeleteLeaks, cplusplus.PlacementNew, cplusplus.PureVirtualCall, cplusplus.StringChecker, deadcode.DeadStores, nullability.NullPassedToNonnull, nullability.NullReturnedFromNonnull, nullability.NullableDereferenced, nullability.NullablePassedToNonnull, nullability.NullableReturnedFromNonnull, optin.cplusplus.UninitializedObject, optin.cplusplus.VirtualCall, optin.mpi.MPI-Checker, optin.portability.UnixAPI, alpha.security.cert.env.InvalidPtr, security.FloatLoopCounter, security.insecureAPI.UncheckedReturn, security.insecureAPI.getpw, security.insecureAPI.gets, security.insecureAPI.mkstemp, security.insecureAPI.mktemp, security.insecureAPI.rand, security.insecureAPI.vfork, unix.API, unix.Malloc, unix.MallocSizeof, unix.MismatchedDeallocator, unix.Vfork, unix.cstring.BadSizeArg, unix.cstring.NullArg, valist.CopyToSelf, valist.Uninitialized, valist.Unterminated
cppcheck: cppcheck-IOWithoutPositioning, cppcheck-StlMissingComparison, cppcheck-accessForwarded, cppcheck-accessMoved, cppcheck-argumentSize, cppcheck-arrayIndexOutOfBounds, cppcheck-arrayIndexOutOfBoundsCond, cppcheck-assertWithSideEffect, cppcheck-assignBoolToPointer, cppcheck-assignmentInAssert, cppcheck-autoVariables, cppcheck-autovarInvalidDeallocation, cppcheck-badBitmaskCheck, cppcheck-boostForeachError, cppcheck-bufferAccessOutOfBounds, cppcheck-charBitOp, cppcheck-charLiteralWithCharPtrCompare, cppcheck-checkCastIntToCharAndBack, cppcheck-clarifyStatement, cppcheck-compareBoolExpressionWithInt, cppcheck-comparePointers, cppcheck-comparisonFunctionIsAlwaysTrueOrFalse, cppcheck-comparisonOfBoolWithInvalidComparator, cppcheck-constStatement, cppcheck-containerOutOfBounds, cppcheck-copyCtorAndEqOperator, cppcheck-copyCtorPointerCopying, cppcheck-coutCerrMisusage, cppcheck-danglingLifetime, cppcheck-danglingReference, cppcheck-danglingTemporaryLifetime, cppcheck-deallocDealloc, cppcheck-deallocret, cppcheck-deallocuse, cppcheck-derefInvalidIterator, cppcheck-divideSizeof, cppcheck-doubleFree, cppcheck-duplInheritedMember, cppcheck-eraseDereference, cppcheck-exceptDeallocThrow, cppcheck-exceptThrowInDestructor, cppcheck-floatConversionOverflow, cppcheck-funcArgOrderDifferent, cppcheck-identicalConditionAfterEarlyExit, cppcheck-identicalInnerCondition, cppcheck-ignoredReturnValue, cppcheck-incompatibleFileOpen, cppcheck-incompleteArrayFill, cppcheck-incorrectCharBooleanError, cppcheck-incorrectLogicOperator, cppcheck-incorrectStringBooleanError, cppcheck-incorrectStringCompare, cppcheck-integerOverflow, cppcheck-invalidContainerLoop, cppcheck-invalidContainer, cppcheck-invalidFree, cppcheck-invalidFunctionArg, cppcheck-invalidFunctionArgBool, cppcheck-invalidFunctionArgStr, cppcheck-invalidIterator1, cppcheck-invalidLengthModifierError, cppcheck-invalidLifetime, cppcheck-invalidPrintfArgType_float, cppcheck-invalidPrintfArgType_n, cppcheck-invalidPrintfArgType_p, cppcheck-invalidPrintfArgType_s, cppcheck-invalidPrintfArgType_sint, cppcheck-invalidPrintfArgType_uint, cppcheck-invalidScanfArgType_float, cppcheck-invalidScanfArgType_int, cppcheck-invalidScanfArgType_s, cppcheck-invalidScanfFormatWidth, cppcheck-invalidScanfFormatWidth_smaller, cppcheck-invalidTestForOverflow, cppcheck-invalidscanf, cppcheck-iterators1, cppcheck-iterators2, cppcheck-iterators3, cppcheck-iteratorsCmp1, cppcheck-iteratorsCmp2, cppcheck-leakNoVarFunctionCall, cppcheck-leakReturnValNotUsed, cppcheck-leakUnsafeArgAlloc, cppcheck-literalWithCharPtrCompare, cppcheck-mallocOnClassError, cppcheck-mallocOnClassWarning, cppcheck-memleak, cppcheck-memleakOnRealloc, cppcheck-memsetClass, cppcheck-memsetClassFloat, cppcheck-memsetClassReference, cppcheck-memsetValueOutOfRange, cppcheck-memsetZeroBytes, cppcheck-mismatchAllocDealloc, cppcheck-mismatchSize, cppcheck-mismatchingContainerExpression, cppcheck-mismatchingContainers, cppcheck-missingMemberCopy, cppcheck-missingReturn, cppcheck-moduloAlwaysTrueFalse, cppcheck-multiplySizeof, cppcheck-negativeArraySize, cppcheck-negativeContainerIndex, cppcheck-negativeIndex, cppcheck-negativeMemoryAllocationSize, cppcheck-noCopyConstructor, cppcheck-noDestructor, cppcheck-noOperatorEq, cppcheck-nullPointer, cppcheck-nullPointerArithmetic, cppcheck-nullPointerArithmeticRedundantCheck, cppcheck-nullPointerDefaultArg, cppcheck-nullPointerRedundantCheck, cppcheck-objectIndex, cppcheck-operatorEqMissingReturnStatement, cppcheck-operatorEqToSelf, cppcheck-operatorEqVarError, cppcheck-oppositeInnerCondition, cppcheck-overlappingStrcmp, cppcheck-overlappingWriteFunction, cppcheck-overlappingWriteUnion, cppcheck-pointerAdditionResultNotNull, cppcheck-pointerArithBool, cppcheck-pointerSize, cppcheck-publicAllocationError, cppcheck-pureVirtualCall, cppcheck-raceAfterInterlockedDecrement, cppcheck-readWriteOnlyFile, cppcheck-redundantAssignInSwitch, cppcheck-redundantBitwiseOperationInSwitch, cppcheck-redundantCopyInSwitch, cppcheck-resourceLeak, cppcheck-rethrowNoCurrentException, cppcheck-returnAddressOfAutoVariable, cppcheck-returnAddressOfFunctionParameter, cppcheck-returnDanglingLifetime, cppcheck-returnLocalVariable, cppcheck-returnReference, cppcheck-returnTempReference, cppcheck-seekOnAppendedFile, cppcheck-selfAssignment, cppcheck-selfInitialization, cppcheck-shiftNegativeLHS, cppcheck-shiftNegative, cppcheck-shiftTooManyBits, cppcheck-shiftTooManyBitsSigned, cppcheck-signConversion, cppcheck-signedCharArrayIndex, cppcheck-sizeofCalculation, cppcheck-sizeofDivisionMemfunc, cppcheck-sizeofFunctionCall, cppcheck-sizeofsizeof, cppcheck-sizeofwithnumericparameter, cppcheck-sizeofwithsilentarraypointer, cppcheck-sprintfOverlappingData, cppcheck-staticStringCompare, cppcheck-stlBoundaries, cppcheck-stlIfFind, cppcheck-stlOutOfBounds, cppcheck-stlcstr, cppcheck-stlcstrReturn, cppcheck-stlcstrParam, cppcheck-stlcstrthrow, cppcheck-strPlusChar, cppcheck-stringCompare, cppcheck-stringLiteralWrite, cppcheck-suspiciousCase, cppcheck-suspiciousSemicolon, cppcheck-thisSubtraction, cppcheck-throwInNoexceptFunction, cppcheck-uninitDerivedMemberVar, cppcheck-uninitDerivedMemberVarPrivate, cppcheck-uninitMemberVar, cppcheck-uninitMemberVarPrivate, cppcheck-uninitStructMember, cppcheck-uninitdata, cppcheck-uninitstring, cppcheck-uninitvar, cppcheck-unknownEvaluationOrder, cppcheck-unsafeClassRefMember, cppcheck-unusedLabelSwitch, cppcheck-unusedLabelSwitchConfiguration, cppcheck-useClosedFile, cppcheck-uselessAssignmentPtrArg, cppcheck-uselessCallsCompare, cppcheck-uselessCallsEmpty, cppcheck-uselessCallsRemove, cppcheck-va_end_missing, cppcheck-va_list_usedBeforeStarted, cppcheck-va_start_referencePassed, cppcheck-va_start_subsequentCalls, cppcheck-va_start_wrongParameter, cppcheck-virtualCallInConstructor, cppcheck-virtualDestructor, cppcheck-writeReadOnlyFile, cppcheck-wrongPipeParameterSize, cppcheck-wrongPrintfScanfArgNum, cppcheck-wrongPrintfScanfParameterPositionError, cppcheck-wrongmathcall, cppcheck-zerodiv, cppcheck-zerodivcond
[INFO 2024-04-19 23:03] - Starting static analysis ...
[INFO 2024-04-19 23:03] - [1/3] clangsa analyzed a.c successfully.
[INFO 2024-04-19 23:03] - [2/3] cppcheck analyzed a.c successfully.
[INFO 2024-04-19 23:03] - [3/3] clang-tidy analyzed a.c successfully.
[INFO 2024-04-19 23:03] - ----==== Summary ====----
[INFO 2024-04-19 23:03] - Successfully analyzed
[INFO 2024-04-19 23:03] -   clang-tidy: 1
[INFO 2024-04-19 23:03] -   clangsa: 1
[INFO 2024-04-19 23:03] -   cppcheck: 1
[INFO 2024-04-19 23:03] - Reanalyzed compilation commands: 3
[INFO 2024-04-19 23:03] - Total analyzed compilation commands: 1
[INFO 2024-04-19 23:03] - ----=================----
[INFO 2024-04-19 23:03] - Analysis finished.
[INFO 2024-04-19 23:03] - To view results in the terminal use the "CodeChecker parse" command.
[INFO 2024-04-19 23:03] - To store results use the "CodeChecker store" command.
[INFO 2024-04-19 23:03] - See --help and the user guide for further options about parsing and storing the reports.
[INFO 2024-04-19 23:03] - ----=================----
[INFO 2024-04-19 23:03] - Analysis length: 0.33287763595581055 sec.

 此时当前目录下会多一个report文件夹。

  • 3 运行parse指令来解析report文件。
lingx@lingx-virtual-machine:~$ CodeChecker parse ./reports
[MEDIUM] /home/lingx/a.c:6:14: unused parameter 'argc' [clang-diagnostic-unused-parameter]
int main(int argc,char *argv[])
             ^

[MEDIUM] /home/lingx/a.c:6:25: unused parameter 'argv' [clang-diagnostic-unused-parameter]
int main(int argc,char *argv[])
                        ^

[MEDIUM] /home/lingx/a.c:9:6: division by zero is undefined [clang-diagnostic-division-by-zero]
  *p=1/0;
      ^

[MEDIUM] /home/lingx/a.c:10:19: format specifies type 'void *' but the argument has type 'int *' [clang-diagnostic-format-pedantic]
  printf("p @%p\n",p);
                   ^

Found 4 defect(s) in a.c

[HIGH] /home/lingx/a.c:9:6: Division by zero. [cppcheck-zerodiv]
  *p=1/0;
      ^

[HIGH] /home/lingx/a.c:11:2: Memory leak: p [cppcheck-memleak]
  return 0;
  ^

Found 2 defect(s) in a.c

[HIGH] /home/lingx/a.c:9:6: Division by zero [core.DivideZero]
  *p=1/0;
      ^

Found 1 defect(s) in a.c


----==== Severity Statistics ====----
----------------------------
Severity | Number of reports
----------------------------
MEDIUM   |                 4
HIGH     |                 3
----------------------------
----=================----

----==== Checker Statistics ====----
----------------------------------------------------------------
Checker name                      | Severity | Number of reports
----------------------------------------------------------------
clang-diagnostic-unused-parameter | MEDIUM   |                 2
clang-diagnostic-division-by-zero | MEDIUM   |                 1
clang-diagnostic-format-pedantic  | MEDIUM   |                 1
cppcheck-memleak                  | HIGH     |                 1
cppcheck-zerodiv                  | HIGH     |                 1
core.DivideZero                   | HIGH     |                 1
----------------------------------------------------------------
----=================----

----==== File Statistics ====----
-----------------------------
File name | Number of reports
-----------------------------
a.c       |                 7
-----------------------------
----=================----

----======== Summary ========----
---------------------------------------------
Number of processed analyzer result files | 3
Number of analyzer reports                | 7
---------------------------------------------
----=================----

也可以指定parse 后导出的文件格式,可以导出html格式:

CodeChecker parse --export html --output ./reports_html ./reports
 打开reports_html目录下的statistics.html

 cppCheck

官方手册:https://cppcheck.sourceforge.io/manual.html

Cppcheck 旨在分析您的 C/C++ 代码,即使它具有非标准语法,例如嵌入式项目中很常见,用法也很简单。

  • 1.安装 sudo apt-get install cppcheck
  • 2.使用:cppcheck可以对单个文件进行检测,也可以对指定目录进行检测,在实际开发项目中用起来更方面。
Example usage:
  # Recursively check the current folder. Print the progress on the screen and
  # write errors to a file:
  cppcheck . 2> err.txt

  # Recursively check ../myproject/ and don't print progress:
  cppcheck --quiet ../myproject/

  # Check test.cpp, enable all checks:
  cppcheck --enable=all --inconclusive --library=posix test.cpp

  # Check f.cpp and search include files from inc1/ and inc2/:
  cppcheck -I inc1/ -I inc2/ f.cpp

For more information:
    https://cppcheck.sourceforge.io/manual.pdf

Many thanks to the 3rd party libraries we use:
 * tinyxml2 -- loading project/library/ctu files.
 * picojson -- loading compile database.
 * pcre -- rules.
 * qt -- used in GUI
 * z3 -- theorem prover from Microsoft Research used in bug hunting.
  • 3.举例
lingx@lingx-virtual-machine:~$ cat a.c
#include<stdio.h>
#include<stdlib.h>
#ifdef DMALLOC
#include "dmalloc.h"
#endif
int main(int argc,char *argv[])
{
	int *p=(int *)malloc(sizeof(int));
	*p=1/0;
	printf("p @%p\n",p);
	return 0;
}
lingx@lingx-virtual-machine:~$ cppcheck a.c 
Checking a.c ...
a.c:11:2: error: Memory leak: p [memleak]
 return 0;
 ^
a.c:9:6: error: Division by zero. [zerodiv]
 *p=1/0;
     ^
Checking a.c: DMALLOC...
lingx@lingx-virtual-machine:~$ cppcheck --enable=all a.c 
Checking a.c ...
a.c:11:2: error: Memory leak: p [memleak]
 return 0;
 ^
a.c:9:6: error: Division by zero. [zerodiv]
 *p=1/0;
     ^
Checking a.c: DMALLOC...
nofile:0:0: information: Cppcheck cannot find all the include files (use --check-config for details) [missingInclude]

lingx@lingx-virtual-machine:~$ cppcheck --enable=all a.c --output-file=warn.log
Checking a.c ...
Checking a.c: DMALLOC...
lingx@lingx-virtual-machine:~$ cat warn.log 
a.c:11:2: error: Memory leak: p [memleak]
 return 0;
 ^
a.c:9:6: error: Division by zero. [zerodiv]
 *p=1/0;
     ^
nofile:0:0: information: Cppcheck cannot find all the include files (use --check-config for details) [missingInclude]

coverity工具

一款商用的检测软件,诊断全面且误报率低,很多团队使用Coverity来诊断代码质量。

最后

编译阶段扫描代码质量,能检测出很多不合规范的代码编写,但任何一款检测工具都有可能误报或漏报,不能盲目的相信检测工具,它只是做一个辅助工具,在实际项目中往往是提升团队代码,提升代码review效率的辅助工具,如果项目中DI值较高且Crash问题较多,此时或许有必要用代码扫描工具先筛查一遍,一些内存泄漏、越界等不规范的代码大部分都是能查出来的。

关于代码静态扫描的常用工具和使用方法先简单说到这里,下一章介绍内存检测工具。

  • 22
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值