GDB
GDB(GNU Debugger)是UNIX及UNIX-like下的强大调试工具,可以调试ada, c, c++, asm, minimal, d, fortran, objective-c, go, java,pascal等语言。
GDB常用命令
基本
| 命令 | 描述 |
|---|---|
| gdb | 打开调试器 |
| file FILE | 装载指定可执行文件 |
| r | 代表run,从头开始运行程序直到断点 |
| q | 退出debug |
| bt | 列出调用堆栈 |
显示被调试文件信息
| 命令 | 描述 |
|---|---|
| info files | 显示被调试文件的详细信息 |
| info func | 显示所有函数名称 |
| info local | 显示当前函数中的局部变量信息 |
| info prog | 显示被调试程序的执行状态 |
| info var | 显示所有的全局和静态变量名称 |
查看/修改内存
| 命令 | 描述 |
|---|---|
| p x | 相当于“print x”。显示当前变量 x 的值 |
| display x | 和print的区别是,x不是只显示一次就消失,而是一直显示,每次继续运行程序都会刷新。相当于VS的“watch”功能 |
| undisplay x | 停止对变量x的display |
| x address | 查看指针所指位置的值 |
| set x = 12 set x = y | 修改变量x的值 |
| call function() | 调用某函数。这个函数可以是你程序里定义的函数,甚至是标准库函数,我的理解是只要在当前位置可访问到的函数都可以调用。 |
断点
| 命令 | 描述 |
|---|---|
| b | b即break。在当前行设置断点。 |
| b 45 | 在某行设置断点。 |
| b functionName | 在某函数开始处设置断点。常用:b main 在程序开始设置断点。 |
| watch x == 3 | 设置条件断点。这个比VS的条件断点更方便,因为它不需要设置在哪一行!时刻监控! |
| info break | 查看当前存在的所有断点。每个断点都有自己的编号。 |
| delete N | 删除编号为N的那个断点。 |
调试运行
| 命令 | 描述 |
|---|---|
| n | “next”,运行一行代码, 相当于VS的step over。 |
| s | “step”,运行一个指令,相当于VS的step in。n和s都可以一次运行多行,比如n 5 |
| c | “continue”,继续运行直到下一个断点。 |
| f | “finish”,运行完当前程序,相当于VS的 step out。 |
vscode
通过配置launch.json实现gdb调用
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/bin/eigen_test",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerPath": "/usr/bin/gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
基本参数
program要调试的程序名,${workspaceFolder}为当前工作目录,即.vscode同级目录cwd调试程序的路径miDebuggerPathgdb 的路径
高级参数
"preLaunchTask": "build"生成子 task,可在里面编辑 gcc 命令等。具体可参考 VSCode GDB 调试配置"miDebuggerServerAddress"配置服务器的地址和端口
ROS
run
rosrun --prefix 'gdb -ex run --args' [package_name] [node_name]
launch
需安装xterm
通过在launch中设置launch-prefix启动调试,参考
launch-prefix="xterm -e gdb --args": run your node in a gdb in a separate xterm window, manually typerunto start itlaunch-prefix="gdb -ex run --args": run your node in gdb in the same xterm as your launch without having to typerunto start itlaunch-prefix="stterm -g 200x60 -e gdb -ex run --args": run your node in gdb in a new stterm window without having to typerunto start itlaunch-prefix="valgrind": run your node in valgrind valgrind工具可以用于检测内存泄露,并执行性能分析launch-prefix="xterm -e": run your node in a separate xterm windowlaunch-prefix="nice": nice your process to lower its CPU usagelaunch-prefix="screen -d -m gdb --args": useful if the node is being run on another machine; you can then ssh to that machine and doscreen -D -Rto see the gdb sessionlaunch-prefix="xterm -e python -m pdb": run your python node a separate xterm window in pdb for debugging; manually typerunto start itlaunch-prefix="yappi -b -f pstat -o <filename>": run your rospy node in a multi-thread profiler such asyappi.launch-prefix="/path/to/run_tmux": run your node in a new tmux window; you’ll need to create/path/to/run_tmuxwith the contents:
C++程序可增加launch-prefix="xterm -e gdb -ex run --args "
python程序可增加launch-prefix="xterm -e python -m pdb "
参考
信号处理
linux下sig类型,可以通过kill -${sig_num} ${PID}给对应进程发送信号。
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM # 发送给程序的终止请求
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
- SIGTERM 发送给程序的终止请求
- SIGSEGV 非法内存访问(分段错误)
- SIGINT 外部中断,通常为用户所起始
- SIGILL 非法程序映像,例如非法指令
- SIGABRT 异常终止条件,例如为 std::abort() 所起始
- SIGFPE 错误算术运算,例如除以零
- SIGPIPE PIPE破裂
在代码运行过程中,可以设置对应的信号处理函数,记录程序异常时的堆栈信息以分析问题。
#include <signal.h>
void sig_handler(int sig) {
LOGE("signal: {}", sig);
void *array[10];
// 获取堆栈内容,Debug模式下可用
auto size = backtrace(array, 10);
char **strings = backtrace_symbols(array, size);
for (auto i = 0; i < size; i++) {
LOGE("Backtrace: {}", strings[i]); // 写堆栈日志
}
free(strings);
// 重新发出信号以触发默认崩溃行为
signal(sig, SIG_DFL);
raise(sig);
}
int main(int argc, char **argv){
signal(SIGTERM, sig_handler); // 发送给程序的终止请求
signal(SIGSEGV, sig_handler); // 非法内存访问(分段错误)
signal(SIGPIPE, sig_handler); // PIPE破裂
signal(SIGINT, sig_handler); // 外部中断,通常为用户所起始
signal(SIGILL, sig_handler); // 非法程序映像,例如非法指令
signal(SIGABRT, sig_handler); // 异常终止条件,例如为 std::abort() 所起始
signal(SIGFPE, sig_handler); // 错误算术运算,例如除以零
}
代码检测
静态分析是在代码编译前发现潜在问题,覆盖所有代码路径(无论是否被执行),但可能存在误报,且对复杂运行时问题(如动态内存管理和并发)的检测能力有限。
动态分析(尤其是ASan和Valgrind)是在程序运行时发现实际发生的问题,准确率极高,尤其擅长检测内存错误和并发问题。但它们需要程序被执行,且会对性能造成一定影响。
| 特性 | Cppcheck | Clang Static Analyzer (scan-build/clang-tidy) | AddressSanitizer (ASan) | Valgrind (Memcheck等) | Flawfinder |
|---|---|---|---|---|---|
| 分析类型 | 静态分析 | 静态分析 | 动态分析 (运行时插桩) | 动态分析 (运行时模拟) | 静态分析 |
| 主要关注 | 编译器不检测的缺陷,低误报率,可移植性 | 深度缺陷(包括复杂逻辑/并发),代码风格,最佳实践 | 运行时内存错误(越界、UAF、UAR、UAS),内存泄漏 | 运行时内存错误,线程竞争,性能 | 潜在安全漏洞(基于已知危险函数) |
| 分析深度 | 中等 | 高(基于AST和数据流/路径分析) | 极高(运行时精确检测) | 极高(运行时行为,精确度高) | 浅(基于模式匹配) |
| 使用方式 | 独立工具,直接分析源代码 | 拦截编译命令 (scan-build),或集成到构建系统 (clang-tidy) | 需要用特殊编译选项编译 (-fsanitize=address),然后运行程序 | 运行时工具,需要编译并运行程序 | 独立工具,直接分析源代码 |
| 误报率 | 较低 | 较低到中等(clang-tidy规则多可能报告更多“建议”) | 极低(几乎无误报) | 极低(运行时发现的问题通常是真实存在的) | 中高(基于模式匹配可能误报) |
| 集成能力 | 易于集成到CI/CD,IDE插件可用 | 与LLVM/Clang生态系统紧密集成,易于集成到CI/CD,IDE支持 | 与Clang/GCC编译器紧密集成,在CI/CD中常用作测试环节 | 需要单独运行,结果报告通常为文本格式,可集成到CI/CD | 易于集成到CI/CD,结果通常为文本格式 |
| 性能开销 | 否(分析时间) | 否(分析时间) | 是(运行时增加CPU和内存开销,约2x) | 较高(运行时模拟,约5-20x性能下降) | 否(分析时间) |
| 典型缺陷 | 空指针,内存泄漏,越界,未初始化变量,资源泄漏 | 更复杂的内存/资源管理,并发问题,未定义行为,风格问题 | 堆/栈/全局越界,Use-after-free/return/scope,Double-free,内存泄漏 | 精确的内存泄漏,堆栈溢出,非法读写,竞争条件 | 缓冲区溢出,格式字符串漏洞,TOCTOU竞争条件 |
| 内存泄漏检测能力 | 有限(仅限于简单模式,可能漏报) | 有限(同Cppcheck,更复杂模式仍难捕捉) | 强(通过LSan模块,高准确率检测运行时泄漏) | 强(通过Memcheck,高准确率检测运行时泄漏) | 无 |
静态检测
这里使用cppcheck
.
├── build # 编译输出,静态检查报告输出
├── cmake # 存放.cmake文件
├── CMakeLists.txt
├── src # 代码
└── xslt # 存放转换文件
可选参数
1. 检查范围和类型控制
--enable=<id>:启用额外的检查,一般使用all。--suppress=<spec>:抑制匹配<spec>的警告--inline-suppr:允许在代码中使用内联抑制。--std=<id>:设置 C/C++ 标准。--language=<language>:强制 Cppcheck 将所有文件作为给定语言检查,有效值为c或c++。--inconclusive:即使分析结果不确定,也允许报告缺陷。
2. 预处理器和包含路径
-I <dir>:指定包含文件的搜索路径。可以提供多个-I参数来指定多个路径。
3. 报告和输出
-j <jobs>:启动<jobs>个线程同时进行检查。-q, --quiet:只在有错误时打印信息。-rp, --relative-paths=<paths>:在输出中使用相对路径。当给定<paths>时,会将其用作基准路径。多个路径可以用;分隔。--xml:以 XML 格式输出结果。常用于与其他工具集成。--xml-version=<version>:指定 XML 输出的版本。--template=<format>:自定义报告的输出格式。
4. 配置和高级选项
--check-config:检查 Cppcheck 配置,使用此标志时,会禁用正常的代码分析。--check-library:当库文件信息不完整时显示信息性消息。-f, --force:强制检查包含大量配置的文件。如果找到此类文件,会打印错误,如果与--max-configs=一起使用,则最后一个选项有效。--max-configs=<num>:限制每个符号的最大配置数。--addon=<addon>:执行 Cppcheck 插件(Addon)。
构建CMAKE
filter_compile_commands.py
#!/usr/bin/env python3
"""
过滤compile_commands.json,只保留项目源代码的编译单元,排除第三方库
"""
import json
import sys
import os
def filter_compile_commands(input_file, output_file, source_dir):
"""Filter compile_commands.json to only include project source files"""
with open(input_file, 'r', encoding='utf-8') as f:
commands = json.load(f)
# 只保留src目录和app目录下的文件
# 排除thirdparty、build/_deps等第三方库目录
filtered = []
src_dir = os.path.join(source_dir, 'src')
app_dir = os.path.join(source_dir, 'app')
exclude_patterns = ['thirdparty', '_deps', 'external', 'third_party', 'build']
for cmd in commands:
file_path = cmd.get('file', '')
# 规范化路径以进行比较
file_path_normalized = os.path.normpath(file_path)
# 检查是否在src或app目录中
in_src = file_path_normalized.startswith(os.path.normpath(src_dir))
in_app = file_path_normalized.startswith(os.path.normpath(app_dir))
# 检查是否包含排除的模式
exclude = any(pattern in file_path_normalized for pattern in exclude_patterns)
if (in_src or in_app) and not exclude:
filtered.append(cmd)
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(filtered, f, indent=2)
print(f'Filtered {len(commands)} -> {len(filtered)} compilation units')
return len(filtered)
if __name__ == '__main__':
if len(sys.argv) != 4:
print('Usage: filter_compile_commands.py <input> <output> <source_dir>')
sys.exit(1)
count = filter_compile_commands(sys.argv[1], sys.argv[2], sys.argv[3])
sys.exit(0 if count > 0 else 1)
static_check.cmake
# ================================
# C++ 静态检查配置
# ================================
# 功能:对项目源代码进行静态分析,排除第三方库
# 使用:make run_cppcheck 或 make all_cppcheck_reports
# ================================
# 生成编译数据库
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# ============ 查找工具 ============
find_program(CPPCHECK_EXECUTABLE cppcheck)
if(NOT CPPCHECK_EXECUTABLE)
message(FATAL_ERROR "Cppcheck executable not found. Please install Cppcheck or set its path.")
endif()
find_package(Python3 COMPONENTS Interpreter)
if(NOT Python3_FOUND)
message(WARNING "Python3 not found. Will use unfiltered compile_commands.json")
endif()
find_program(XSLTPROC_EXECUTABLE xsltproc)
if(NOT XSLTPROC_EXECUTABLE)
message(FATAL_ERROR "xsltproc executable not found. Please install xsltproc.")
endif()
# ============ 文件路径配置 ============
set(FILTER_SCRIPT ${CMAKE_SOURCE_DIR}/cmake/modules/filter_compile_commands.py)
set(FILTERED_COMPILE_COMMANDS ${CMAKE_BINARY_DIR}/compile_commands_filtered.json)
set(CPPCHECK_SUPPRESSIONS ${CMAKE_BINARY_DIR}/cppcheck_suppressions.txt)
set(CPPCHECK_XML_REPORT ${CMAKE_BINARY_DIR}/cppcheck_report.xml)
set(CPPCHECK_HTML_REPORT ${CMAKE_BINARY_DIR}/cppcheck_report.html)
set(CPPCHECK_XSLT_STYLESHEET ${CMAKE_SOURCE_DIR}/xslt/cppcheck_report.xslt)
# 确保 XSLT 样式表存在
if(NOT EXISTS ${CPPCHECK_XSLT_STYLESHEET})
message(FATAL_ERROR "Cppcheck XSLT stylesheet not found: ${CPPCHECK_XSLT_STYLESHEET}")
endif()
# ============ 生成抑制规则文件 ============
# 排除常见第三方库的头文件检查(ROS、Eigen3、PCL、Boost等)
file(WRITE ${CPPCHECK_SUPPRESSIONS} "# Suppress all checks in third-party libraries
# ROS libraries
*:/opt/ros/*
*:*/ros/*
*:*/catkin/*
*:*/roscpp/*
*:*/rosbag/*
*:*/std_msgs/*
*:*/sensor_msgs/*
*:*/geometry_msgs/*
*:*/nav_msgs/*
*:*/tf/*
*:*/tf2/*
# Eigen library
*:*/eigen3/*
*:*/Eigen/*
# PCL library
*:*/pcl/*
*:*/pcl-*/include/*
# TBB library
*:*/tbb/*
*:*/oneapi/*
# Boost library
*:*/boost/*
# OpenCV library
*:*/opencv/*
*:*/opencv2/*
*:*/opencv4/*
# System libraries
*:/usr/include/*
*:/usr/local/include/*
# Project third-party directories
*:*/thirdparty/*
*:*/_deps/*
*:*/external/*
*:*/third_party/*
*:*/vendor/*
")
# ============ 过滤编译数据库 ============
if(Python3_FOUND)
add_custom_target(filter_compile_commands
COMMAND ${Python3_EXECUTABLE} ${FILTER_SCRIPT}
${CMAKE_BINARY_DIR}/compile_commands.json
${FILTERED_COMPILE_COMMANDS}
${CMAKE_SOURCE_DIR}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Filtering compile_commands.json to exclude third-party libraries..."
VERBATIM
)
set(CPPCHECK_COMPILE_DB ${FILTERED_COMPILE_COMMANDS})
else()
set(CPPCHECK_COMPILE_DB ${CMAKE_BINARY_DIR}/compile_commands.json)
endif()
# ============ 运行 Cppcheck ============
add_custom_target(run_cppcheck
COMMAND ${CPPCHECK_EXECUTABLE}
--inline-suppr
--std=c++17
--enable=all
--quiet
--force
--inconclusive
--language=c++
--project=${CPPCHECK_COMPILE_DB}
--suppressions-list=${CPPCHECK_SUPPRESSIONS}
--suppress=noValidConfiguration
--suppress=missingIncludeSystem
--max-configs=1
-i${CMAKE_SOURCE_DIR}/thirdparty
-i${CMAKE_BINARY_DIR}/_deps
--xml
--xml-version=2
--output-file=${CPPCHECK_XML_REPORT}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Running Cppcheck static analysis..."
VERBATIM
)
if(Python3_FOUND)
add_dependencies(run_cppcheck filter_compile_commands)
endif()
# ============ 生成 HTML 报告 ============
add_custom_target(generate_cppcheck_html
COMMAND ${XSLTPROC_EXECUTABLE} ${CPPCHECK_XSLT_STYLESHEET} ${CPPCHECK_XML_REPORT} > ${CPPCHECK_HTML_REPORT}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMENT "Generating HTML report from Cppcheck XML..."
VERBATIM
)
add_dependencies(generate_cppcheck_html run_cppcheck)
# ============ 组合目标 ============
add_custom_target(all_cppcheck_reports)
add_dependencies(all_cppcheck_reports generate_cppcheck_html)
cppcheck_report.xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:template match="/">
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Cppcheck 报告</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 20px;
color: #333;
background-color: #f8f9fa;
}
.header {
background: linear-gradient(135deg, #1a237e, #0d47a1); /* 加深蓝色梯度 */
color: white;
text-align: center;
padding: 25px 30px;
border-radius: 8px;
margin-bottom: 25px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); /* 加深阴影 */
}
.header h1 {
font-weight: 700; /* 加粗字体 */
margin-bottom: 8px;
font-size: 2.4rem; /* 增大字号 */
text-shadow: 0 2px 4px rgba(0,0,0,0.3); /* 添加文字阴影 */
letter-spacing: 0.5px; /* 增加字间距 */
}
.header p {
opacity: 0.9;
font-size: 1.1rem;
text-shadow: 0 1px 2px rgba(0,0,0,0.2); /* 副标题也添加阴影 */
}
@media (max-width: 768px) {
.header h1 {
font-size: 1.8rem;
}
}
table {
border-collapse: collapse;
width: 100%;
margin-top: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
background-color: white;
border-radius: 8px;
overflow: hidden;
}
th, td {
border: 1px solid #f0f0f0;
padding: 14px 20px;
text-align: left;
vertical-align: top;
}
th {
background-color: #f5f7ff;
color: #1a237e;
font-weight: 600;
border-bottom: 2px solid #e0e0e0;
}
tr:hover {
background-color: #f8fbff;
}
.severity-tag {
display: inline-block;
padding: 5px 12px;
border-radius: 14px;
font-size: 0.85rem;
font-weight: 600;
text-align: center;
min-width: 80px;
}
.error .severity-tag {
background-color: #ffcdd2;
color: #d32f2f;
}
.warning .severity-tag {
background-color: #ffe0b2;
color: #f57c00;
}
.style .severity-tag {
background-color: #bbdefb;
color: #1976d2;
}
.information .severity-tag {
background-color: #e1bee7;
color: #7b1fa2;
}
.location {
font-family: monospace;
font-size: 0.9em;
color: #555;
}
.summary {
background-color: white;
padding: 25px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
margin: 20px 0;
}
.summary-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 18px;
margin-top: 10px;
}
.summary-item {
background: white;
padding: 20px 15px;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.07);
text-align: center;
border: 1px solid #e0e0e0;
transition: all 0.3s ease;
}
.summary-item:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.stats-label {
font-size: 0.95rem;
color: #616161;
margin-bottom: 8px;
font-weight: 500;
}
.stats-value {
font-weight: 700;
font-size: 2.2rem;
}
.error-count { color: #d32f2f; }
.warning-count { color: #f57c00; }
.style-count { color: #1976d2; }
.information-count { color: #7b1fa2; }
.footer {
text-align: center;
margin-top: 30px;
color: #757575;
font-size: 0.9rem;
}
</style>
</head>
<body>
<div class="header">
<h1>Cppcheck 静态分析报告</h1>
<p>版本 <xsl:value-of select="results/cppcheck/@version"/> | 报告格式版本 <xsl:value-of select="results/@version"/></p>
</div>
<!-- 汇总统计 -->
<div class="summary">
<h2>📊 问题概览</h2>
<div class="summary-grid">
<xsl:variable name="errors" select="count(results/errors/error[@severity='error'])"/>
<xsl:variable name="warnings" select="count(results/errors/error[@severity='warning'])"/>
<xsl:variable name="styles" select="count(results/errors/error[@severity='style'])"/>
<xsl:variable name="infos" select="count(results/errors/error[@severity='information'])"/>
<xsl:variable name="total" select="count(results/errors/error)"/>
<div class="summary-item">
<div class="stats-label">问题总数</div>
<div class="stats-value"><xsl:value-of select="$total"/></div>
</div>
<div class="summary-item">
<div class="stats-label">错误</div>
<div class="stats-value error-count"><xsl:value-of select="$errors"/></div>
</div>
<div class="summary-item">
<div class="stats-label">警告</div>
<div class="stats-value warning-count"><xsl:value-of select="$warnings"/></div>
</div>
<div class="summary-item">
<div class="stats-label">代码风格</div>
<div class="stats-value style-count"><xsl:value-of select="$styles"/></div>
</div>
<div class="summary-item">
<div class="stats-label">信息</div>
<div class="stats-value information-count"><xsl:value-of select="$infos"/></div>
</div>
</div>
</div>
<!-- 问题详情表格 -->
<h2>🔍 详细问题列表</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>严重程度</th>
<th>消息</th>
<th>位置</th>
<th>CWE</th>
</tr>
</thead>
<tbody>
<xsl:choose>
<xsl:when test="count(results/errors/error) = 0">
<tr>
<td colspan="5" style="text-align:center; padding:30px; background-color:#f9f9f9;">
🎉 恭喜!未发现任何静态分析问题
</td>
</tr>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="results/errors/error"/>
</xsl:otherwise>
</xsl:choose>
</tbody>
</table>
<div class="footer">
<p>报告生成时间: <script>document.write(new Date().toLocaleString());</script></p>
<p>问题反馈: jassimxiong@gmail.com</p>
</div>
</body>
</html>
</xsl:template>
<xsl:template match="error">
<tr class="{@severity}">
<td><xsl:value-of select="@id"/></td>
<td>
<span class="severity-tag"><xsl:value-of select="@severity"/></span>
</td>
<td>
<strong><xsl:value-of select="@msg"/></strong>
<div style="margin-top:5px; font-size:0.9em; color:#666;">
<xsl:value-of select="@verbose"/>
</div>
</td>
<td class="location">
<xsl:choose>
<xsl:when test="location">
<div><strong>文件:</strong> <xsl:value-of select="location/@file"/></div>
<div><strong>行:</strong> <xsl:value-of select="location/@line"/></div>
<xsl:if test="location/@column">
<div><strong>列:</strong> <xsl:value-of select="location/@column"/></div>
</xsl:if>
<xsl:if test="location/symbol">
<div><strong>符号:</strong> <xsl:value-of select="location/symbol"/></div>
</xsl:if>
</xsl:when>
<xsl:otherwise>全局问题</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:choose>
<xsl:when test="@cwe">
<a href="https://cwe.mitre.org/data/definitions/{@cwe}.html" target="_blank" style="color:#1a237e; text-decoration:none;">
CWE-<xsl:value-of select="@cwe"/>
</a>
</xsl:when>
<xsl:otherwise>-</xsl:otherwise>
</xsl:choose>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
使用
使用 Cppcheck 对项目C++代码进行静态分析,自动排除第三方库(ROS、Eigen3、PCL、Boost等)的检查。
依赖
sudo apt install cppcheck xsltproc python3使用
cd build cmake .. # 方式1:生成完整报告(推荐) make all_cppcheck_reports # 方式2:只运行静态检查 make run_cppcheck # 方式3:只生成HTML报告(需先运行cppcheck) make generate_cppcheck_html # 或者 cmake --build . --target run_cppcheck --parallel # 进行静态检查 cmake --build . --target all_cppcheck_reports --parallel # 进行静态检查并生成html报告查看结果
XML报告:
build/cppcheck_report.xmlHTML报告:
build/cppcheck_report.html(推荐,可在浏览器中打开)过滤后的编译数据库:
build/compile_commands_filtered.json
配置说明
静态检查配置位于
cmake/static_check.cmake,已配置为:✅ 只检查
src/和app/目录下的项目代码✅ 排除所有第三方库(ROS、Eigen3、PCL、TBB、Boost、OpenCV等)
✅ 抑制配置相关的信息级别警告(noValidConfiguration、missingIncludeSystem)
✅ 使用C++17标准进行检查
如需修改检查范围或规则,编辑 cmake/static_check.cmake 和 cmake/filter_compile_commands.py。
动态检测
Sanitizer是由Google发起的开源工具集,用于检测内存泄露等问题。
- AddressSanitizer,检测内存访问问题
- MemorySanitizer,检测未初始化内存问题
- ThreadSanitizer,检测线程竞态和死锁问题
- LeakSanitizer,检测内存泄露问题
参考sanitizers-cmake实现调用
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(ioctree_demo)
add_compile_options(-std=c++14 )
set(CMAKE_CXX_FLAGS "-std=c++14 -pthread") # 注意不能开启代码优化,否则部分内存泄漏会被优化
add_definitions(-w)
find_package(PCL 1.8 REQUIRED)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH})
find_package(Sanitizers REQUIRED)
include_directories(
${PCL_INCLUDE_DIRS}
ikd-Tree
octree2
)
add_executable(time_compare examples/time_compare.cpp ikd-Tree/ikd_Tree.cpp)
target_link_libraries(time_compare ${PCL_LIBRARIES})
add_sanitizers(time_compare)
编译时加入编译选项启用功能,SANITIZE_ADDRESS, SANITIZE_MEMORY, SANITIZE_THREAD or SANITIZE_UNDEFINED
cmake .. -DSANITIZE_ADDRESS=On
AddressSanitizer
问题记录
报错找不到libasan.so.5库,设置-static-libasan,链接静态库。
https://blog.csdn.net/register_k/article/details/120319834
参考
资源分析
pidstat是sysstat工具的一个命令,用于监控全部或指定进程的cpu、内存、线程、设备IO等系统资源的占用情况。
pidstat [ 选项 ] [ <时间间隔> ] [ <次数> ]
常用的参数:
- -u:默认的参数,显示各个进程的cpu使用统计
- -r:显示各个进程的内存使用统计
- -d:显示各个进程的IO使用情况
- -p:指定进程号
- -w:显示每个进程的上下文切换情况
- -t:显示选择任务的线程的统计信息外的额外信息
- -T { TASK | CHILD | ALL } 这个选项指定了pidstat监控的。TASK表示报告独立的task,CHILD关键字表示报告进程下所有线程统计信息。ALL表示报告独立的task和task下面的所有线程。 注意:task和子线程的全局的统计信息和pidstat选项无关。这些统计信息不会对应到当前的统计间隔,这些统计信息只有在子线程kill或者完成的时候才会被收集。
- -V:版本号
- -h:在一行上显示了所有活动,这样其他程序可以容易解析。
- -I:在SMP环境,表示任务的CPU使用率/内核数量
- -l:显示命令名和所有参数
例子按2s频率监听进程的CPU和内存占用,并写入到日志里
pidstat -u -r -p 15249 2 > run_mapping.log
使用python解析日志并输出分析
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PIDSTAT Log Analysis Tool
Features:
1. Read pidstat log file with CPU and memory usage data
2. Calculate average and maximum resource usage statistics
3. Generate statistical report
4. Plot CPU and memory usage vs time charts with English labels
Usage:
python pidstat_analyzer.py <log_file_path>
Example:
python pidstat_analyzer.py pidstat.log
Output files:
- resource_report.txt: Resource usage statistics report
- cpu_usage.png: CPU usage chart
- memory_usage.png: Memory usage chart
"""
import re
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime, timedelta
import matplotlib.dates as mdates
import sys
import os
import matplotlib
# Configure matplotlib for better English text rendering
matplotlib.use('Agg') # Use non-interactive backend
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'Liberation Sans', 'sans-serif']
plt.rcParams['axes.unicode_minus'] = False # Fix negative sign display
def parse_pidstat_log(file_path):
"""
Parse pidstat log file to extract CPU and memory data
Args:
file_path (str): Path to the log file
Returns:
tuple: (cpu_data_list, mem_data_list)
cpu_data_list: List of CPU data dictionaries
mem_data_list: List of memory data dictionaries
"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
except FileNotFoundError:
print(f"Error: File '{file_path}' not found")
sys.exit(1)
except Exception as e:
print(f"Error: Failed to read file - {e}")
sys.exit(1)
cpu_data = []
mem_data = []
current_section = None
header_pattern = re.compile(r'\s*UID\s+PID\s+')
for line_num, line in enumerate(lines, 1):
line = line.strip()
if not line:
continue
# Skip system info line
if line.startswith('Linux'):
continue
# Detect CPU data header
if re.search(r'%usr\s+%system\s+%guest\s+%wait\s+%CPU\s+CPU\s+Command', line):
current_section = 'cpu'
continue
# Detect memory data header
if re.search(r'minflt/s\s+majflt/s\s+VSZ\s+RSS\s+%MEM\s+Command', line):
current_section = 'mem'
continue
# Skip header lines that contain "UID PID"
if header_pattern.match(line):
continue
# Parse time - handle both Chinese and numeric formats
time_match = re.match(r'(\d+)[时:](\d+)[分:](\d+)秒?', line)
if not time_match:
continue
hour, minute, second = map(int, time_match.groups())
time_str = f"{hour:02d}:{minute:02d}:{second:02d}"
# Extract data part after time
time_prefix = f"{hour}时{minute}分{second}秒"
if time_prefix in line:
data_part = line[len(time_prefix):].strip()
else:
# Try alternative time format
time_prefix_alt = f"{hour:02d}:{minute:02d}:{second:02d}"
if time_prefix_alt in line:
data_part = line[len(time_prefix_alt):].strip()
else:
data_part = line[time_match.end():].strip()
if not data_part:
continue
# Split data by whitespace, but handle command name properly
parts = re.split(r'\s+', data_part)
try:
if current_section == 'cpu':
# Expected format: UID PID %usr %system %guest %wait %CPU CPU Command
if len(parts) < 9:
print(f"Warning: Line {line_num} has insufficient CPU data fields ({len(parts)} vs expected 9+)")
continue
uid = int(parts[0])
pid = int(parts[1])
usr = float(parts[2])
system = float(parts[3])
guest = float(parts[4])
wait = float(parts[5])
cpu_percent = float(parts[6])
cpu_core = int(float(parts[7])) # Handle potential float values
command = ' '.join(parts[8:])
cpu_data.append({
'time': time_str,
'pid': pid,
'usr': usr,
'system': system,
'guest': guest,
'wait': wait,
'cpu_percent': cpu_percent,
'cpu_core': cpu_core,
'command': command.strip()
})
elif current_section == 'mem':
# Expected format: UID PID minflt/s majflt/s VSZ RSS %MEM Command
if len(parts) < 8:
print(f"Warning: Line {line_num} has insufficient memory data fields ({len(parts)} vs expected 8+)")
continue
uid = int(parts[0])
pid = int(parts[1])
minflt = float(parts[2])
majflt = float(parts[3])
vsz = int(float(parts[4])) # Handle potential float values
rss = int(float(parts[5])) # Handle potential float values
mem_percent = float(parts[6])
command = ' '.join(parts[7:])
mem_data.append({
'time': time_str,
'pid': pid,
'minflt/s': minflt,
'majflt/s': majflt,
'VSZ': vsz,
'RSS': rss,
'mem_percent': mem_percent,
'command': command.strip()
})
except (ValueError, IndexError) as e:
print(f"Warning: Failed to parse line {line_num} - {e}")
print(f" Line content: {line}")
print(f" Parts: {parts}")
continue
return cpu_data, mem_data
def calculate_statistics(cpu_df, mem_df):
"""
Calculate resource usage statistics
Args:
cpu_df (DataFrame): CPU data DataFrame
mem_df (DataFrame): Memory data DataFrame
Returns:
dict: Statistics dictionary
"""
stats = {'cpu': {}, 'mem': {}}
if not cpu_df.empty:
stats['cpu'] = {
'avg_cpu_percent': cpu_df['cpu_percent'].mean(),
'max_cpu_percent': cpu_df['cpu_percent'].max(),
'avg_usr': cpu_df['usr'].mean(),
'max_usr': cpu_df['usr'].max(),
'avg_system': cpu_df['system'].mean(),
'max_system': cpu_df['system'].max(),
'avg_wait': cpu_df['wait'].mean(),
'max_wait': cpu_df['wait'].max(),
'avg_guest': cpu_df['guest'].mean(),
'max_guest': cpu_df['guest'].max(),
'max_cpu_time': cpu_df.loc[cpu_df['cpu_percent'].idxmax(), 'time'] if not cpu_df.empty else '',
'max_core_usage': cpu_df['cpu_core'].value_counts().idxmax() if not cpu_df.empty else 0
}
if not mem_df.empty:
stats['mem'] = {
'avg_mem_percent': mem_df['mem_percent'].mean(),
'max_mem_percent': mem_df['mem_percent'].max(),
'avg_rss': mem_df['RSS'].mean(),
'max_rss': mem_df['RSS'].max(),
'avg_vsz': mem_df['VSZ'].mean(),
'max_vsz': mem_df['VSZ'].max(),
'avg_minflt': mem_df['minflt/s'].mean(),
'max_minflt': mem_df['minflt/s'].max(),
'avg_majflt': mem_df['majflt/s'].mean(),
'max_majflt': mem_df['majflt/s'].max(),
'max_mem_time': mem_df.loc[mem_df['mem_percent'].idxmax(), 'time'] if not mem_df.empty else '',
'max_rss_time': mem_df.loc[mem_df['RSS'].idxmax(), 'time'] if not mem_df.empty else ''
}
return stats
def plot_cpu_usage(cpu_df, output_prefix='cpu_usage'):
"""
Plot CPU usage chart
Args:
cpu_df (DataFrame): CPU data DataFrame
output_prefix (str): Output file prefix
"""
if cpu_df.empty:
print("Warning: No CPU data available, skipping plot")
return
# Convert time to datetime objects
base_date = datetime(2025, 11, 14) # Use date from log
cpu_df['datetime'] = cpu_df['time'].apply(
lambda x: base_date + timedelta(
hours=int(x.split(':')[0]),
minutes=int(x.split(':')[1]),
seconds=int(x.split(':')[2])
)
)
plt.figure(figsize=(14, 10))
# CPU Usage Overview
ax1 = plt.subplot(2, 1, 1)
ax1.plot(cpu_df['datetime'], cpu_df['cpu_percent'], 'b-', linewidth=2.5,
label=f'Total CPU Usage (Avg: {cpu_df["cpu_percent"].mean():.1f}%)')
ax1.plot(cpu_df['datetime'], cpu_df['usr'], 'g--', linewidth=2,
label=f'User Mode (Avg: {cpu_df["usr"].mean():.1f}%)')
ax1.plot(cpu_df['datetime'], cpu_df['system'], 'r--', linewidth=2,
label=f'System Mode (Avg: {cpu_df["system"].mean():.1f}%)')
ax1.plot(cpu_df['datetime'], cpu_df['wait'], 'y--', linewidth=2,
label=f'I/O Wait (Avg: {cpu_df["wait"].mean():.1f}%)')
ax1.set_title('CPU Usage Monitoring', fontsize=16, fontweight='bold', pad=20)
ax1.set_ylabel('CPU Usage (%)', fontsize=12)
ax1.grid(True, alpha=0.3, linestyle='--')
ax1.legend(loc='upper right', fontsize=10)
# CPU Core Allocation
ax2 = plt.subplot(2, 1, 2)
ax2.plot(cpu_df['datetime'], cpu_df['cpu_core'], 'o-', color='purple',
linewidth=2, markersize=8, label=f'CPU Core (Most used: {cpu_df["cpu_core"].value_counts().idxmax()})')
ax2.fill_between(cpu_df['datetime'], cpu_df['cpu_core'], alpha=0.2, color='purple')
ax2.set_title('CPU Core Allocation', fontsize=16, fontweight='bold', pad=20)
ax2.set_ylabel('CPU Core Number', fontsize=12)
ax2.set_xlabel('Time', fontsize=12)
ax2.grid(True, alpha=0.3, linestyle='--')
ax2.legend(loc='upper right', fontsize=10)
ax2.set_ylim(-0.5, cpu_df['cpu_core'].max() + 1)
# Format x-axis
for ax in [ax1, ax2]:
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
ax.xaxis.set_major_locator(mdates.AutoDateLocator())
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
plt.tight_layout()
output_file = f'{output_prefix}.png'
plt.savefig(output_file, dpi=300, bbox_inches='tight')
print(f"✓ CPU chart generated: {output_file}")
plt.close()
def plot_memory_usage(mem_df, output_prefix='memory_usage'):
"""
Plot memory usage chart
Args:
mem_df (DataFrame): Memory data DataFrame
output_prefix (str): Output file prefix
"""
if mem_df.empty:
print("Warning: No memory data available, skipping plot")
return
# Convert time to datetime objects
base_date = datetime(2025, 11, 14) # Use date from log
mem_df['datetime'] = mem_df['time'].apply(
lambda x: base_date + timedelta(
hours=int(x.split(':')[0]),
minutes=int(x.split(':')[1]),
seconds=int(x.split(':')[2])
)
)
plt.figure(figsize=(14, 12))
# Memory Usage Percentage
ax1 = plt.subplot(2, 1, 1)
ax1.plot(mem_df['datetime'], mem_df['mem_percent'], 'b-', linewidth=3,
label=f'Memory Usage (Avg: {mem_df["mem_percent"].mean():.2f}%)')
ax1.set_title('Memory Usage Monitoring', fontsize=16, fontweight='bold', pad=20)
ax1.set_ylabel('Memory Usage (%)', fontsize=12)
ax1.grid(True, alpha=0.3, linestyle='--')
ax1.legend(loc='upper right', fontsize=10)
# Memory Size (RSS vs VSZ)
ax2 = plt.subplot(2, 1, 2)
rss_mb = mem_df['RSS'] / 1024 # Convert to MB
vsz_mb = mem_df['VSZ'] / 1024 # Convert to MB
ax2.plot(mem_df['datetime'], rss_mb, 'g-', linewidth=2.5,
label=f'RSS Physical Memory (Avg: {rss_mb.mean():.1f} MB)')
ax2.plot(mem_df['datetime'], vsz_mb, 'r-', linewidth=2.5,
label=f'VSZ Virtual Memory (Avg: {vsz_mb.mean():.1f} MB)')
ax2.set_title('Memory Size Monitoring', fontsize=16, fontweight='bold', pad=20)
ax2.set_ylabel('Memory Size (MB)', fontsize=12)
ax2.set_xlabel('Time', fontsize=12)
ax2.grid(True, alpha=0.3, linestyle='--')
ax2.legend(loc='upper right', fontsize=10)
# Format x-axis
for ax in [ax1, ax2]:
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S'))
ax.xaxis.set_major_locator(mdates.AutoDateLocator())
plt.setp(ax.xaxis.get_majorticklabels(), rotation=45, ha='right')
plt.tight_layout()
output_file = f'{output_prefix}.png'
plt.savefig(output_file, dpi=300, bbox_inches='tight')
print(f"✓ Memory chart generated: {output_file}")
plt.close()
def generate_report(stats, cpu_df, mem_df, output_file='resource_report.txt'):
"""
Generate resource usage statistics report
Args:
stats (dict): Statistics dictionary
cpu_df (DataFrame): CPU data DataFrame
mem_df (DataFrame): Memory data DataFrame
output_file (str): Output report filename
"""
with open(output_file, 'w', encoding='utf-8') as f:
f.write("=" * 60 + "\n")
f.write(" Resource Usage Statistics Report\n")
f.write("=" * 60 + "\n\n")
f.write(f"Report Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"Data Points Analyzed: CPU: {len(cpu_df)}, Memory: {len(mem_df)}\n")
if not cpu_df.empty and not mem_df.empty:
f.write(f"Process Analyzed: {cpu_df['command'].iloc[0]}\n")
f.write("\n")
if 'cpu' in stats and stats['cpu']:
f.write("📈 CPU USAGE STATISTICS:\n")
f.write("-" * 60 + "\n")
f.write(f" Average CPU Usage: {stats['cpu']['avg_cpu_percent']:6.2f}%\n")
f.write(f" Maximum CPU Usage: {stats['cpu']['max_cpu_percent']:6.2f}%\n")
f.write(f" Average User Mode: {stats['cpu']['avg_usr']:6.2f}%\n")
f.write(f" Maximum User Mode: {stats['cpu']['max_usr']:6.2f}%\n")
f.write(f" Average System Mode: {stats['cpu']['avg_system']:6.2f}%\n")
f.write(f" Maximum System Mode: {stats['cpu']['max_system']:6.2f}%\n")
f.write(f" Average I/O Wait: {stats['cpu']['avg_wait']:6.2f}%\n")
f.write(f" Maximum I/O Wait: {stats['cpu']['max_wait']:6.2f}%\n")
f.write(f" Average Guest Mode: {stats['cpu']['avg_guest']:6.2f}%\n")
f.write(f" Maximum Guest Mode: {stats['cpu']['max_guest']:6.2f}%\n")
f.write(f" Most Used CPU Core: {stats['cpu']['max_core_usage']}\n")
f.write(f" Peak CPU Usage Time: {stats['cpu'].get('max_cpu_time', 'N/A')}\n\n")
if 'mem' in stats and stats['mem']:
f.write("📊 MEMORY USAGE STATISTICS:\n")
f.write("-" * 60 + "\n")
f.write(f" Average Memory Usage: {stats['mem']['avg_mem_percent']:6.2f}%\n")
f.write(f" Maximum Memory Usage: {stats['mem']['max_mem_percent']:6.2f}%\n")
f.write(f" Average RSS Memory: {stats['mem']['avg_rss']/1024:6.2f} MB\n")
f.write(f" Maximum RSS Memory: {stats['mem']['max_rss']/1024:6.2f} MB\n")
f.write(f" Average VSZ Memory: {stats['mem']['avg_vsz']/1024:6.2f} MB\n")
f.write(f" Maximum VSZ Memory: {stats['mem']['max_vsz']/1024:6.2f} MB\n")
f.write(f" Average Minor Page Faults:{stats['mem']['avg_minflt']:6.1f}/sec\n")
f.write(f" Maximum Minor Page Faults:{stats['mem']['max_minflt']:6.1f}/sec\n")
f.write(f" Average Major Page Faults:{stats['mem']['avg_majflt']:6.1f}/sec\n")
f.write(f" Maximum Major Page Faults:{stats['mem']['max_majflt']:6.1f}/sec\n")
f.write(f" Peak Memory Time: {stats['mem'].get('max_mem_time', 'N/A')}\n")
f.write(f" Peak RSS Memory Time: {stats['mem'].get('max_rss_time', 'N/A')}\n\n")
f.write("=" * 60 + "\n")
f.write("ANALYSIS NOTES:\n")
f.write("-" * 60 + "\n")
f.write("• CPU Usage = User Mode + System Mode + I/O Wait + Guest Mode\n")
f.write("• RSS (Resident Set Size): Actual physical memory used by the process\n")
f.write("• VSZ (Virtual Memory Size): Total virtual memory allocated to the process\n")
f.write("• %MEM: Percentage of total physical memory used by the process\n")
f.write("• Minor Page Faults: Page faults that didn't require disk I/O\n")
f.write("• Major Page Faults: Page faults that required disk I/O\n")
f.write("=" * 60 + "\n")
print(f"✓ Statistics report generated: {output_file}")
def main():
"""
Main function
"""
if len(sys.argv) != 2:
print("\n" + "=" * 60)
print(" PIDSTAT LOG ANALYSIS TOOL")
print("=" * 60)
print(f"\nUsage: {os.path.basename(sys.argv[0])} <pidstat_log_file_path>")
print("\nExamples:")
print(f" {os.path.basename(sys.argv[0])} pidstat.log")
print(f" {os.path.basename(sys.argv[0])} /path/to/your/pidstat_output.log")
print("\n" + "=" * 60)
print("OUTPUT FILES:")
print(" resource_report.txt - Detailed statistics report")
print(" cpu_usage.png - CPU usage visualization")
print(" memory_usage.png - Memory usage visualization")
print("=" * 60 + "\n")
sys.exit(1)
log_file = sys.argv[1]
print("\n" + "=" * 60)
print("STARTING PIDSTAT LOG ANALYSIS")
print("=" * 60)
print(f"Log file: {log_file}")
print(f"Start time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("-" * 60 + "\n")
# Parse log
cpu_data, mem_data = parse_pidstat_log(log_file)
if not cpu_data and not mem_data:
print("Error: No valid CPU or memory data parsed. Please check log file format")
sys.exit(1)
# Convert to DataFrames
cpu_df = pd.DataFrame(cpu_data) if cpu_data else pd.DataFrame()
mem_df = pd.DataFrame(mem_data) if mem_data else pd.DataFrame()
print(f"✅ PARSING COMPLETED SUCCESSFULLY")
print(f" • CPU data points: {len(cpu_df)}")
print(f" • Memory data points: {len(mem_df)}")
if not cpu_df.empty:
print(f" • Process name (CPU): {cpu_df['command'].iloc[0]}")
if not mem_df.empty:
print(f" • Process name (Memory): {mem_df['command'].iloc[0]}")
print()
# Calculate statistics
stats = calculate_statistics(cpu_df, mem_df)
# Generate report
generate_report(stats, cpu_df, mem_df)
# Plot charts
if not cpu_df.empty:
plot_cpu_usage(cpu_df)
if not mem_df.empty:
plot_memory_usage(mem_df)
# Print summary
print("\n" + "=" * 60)
print("📊 ANALYSIS SUMMARY")
print("=" * 60)
if not cpu_df.empty:
print(f"CPU USAGE SUMMARY:")
print(f" • Average Total CPU: {stats['cpu']['avg_cpu_percent']:.2f}%")
print(f" • Maximum Total CPU: {stats['cpu']['max_cpu_percent']:.2f}%")
print(f" • Peak CPU Time: {stats['cpu'].get('max_cpu_time', 'N/A')}")
print(f" • Most Used Core: #{stats['cpu']['max_core_usage']}")
if not mem_df.empty:
print(f"\nMEMORY USAGE SUMMARY:")
print(f" • Average Memory Usage: {stats['mem']['avg_mem_percent']:.2f}%")
print(f" • Maximum Memory Usage: {stats['mem']['max_mem_percent']:.2f}%")
print(f" • Average RSS Memory: {stats['mem']['avg_rss']/1024:.2f} MB")
print(f" • Maximum RSS Memory: {stats['mem']['max_rss']/1024:.2f} MB")
print(f" • Peak Memory Time: {stats['mem'].get('max_mem_time', 'N/A')}")
print(f"\nFILES GENERATED:")
print(f" • resource_report.txt")
print(f" • cpu_usage.png")
print(f" • memory_usage.png")
print("\n" + "=" * 60)
print("ANALYSIS COMPLETED SUCCESSFULLY!")
print(f"End time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("=" * 60)
if __name__ == "__main__":
main()