main.sh 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. #!/bin/bash
  2. # 性能测试脚本,用于顺序执行多种性能分析工具
  3. # 颜色定义
  4. RED='\033[0;31m'
  5. GREEN='\033[0;32m'
  6. YELLOW='\033[1;33m'
  7. BLUE='\033[0;34m'
  8. NC='\033[0m' # No Color
  9. # 默认参数
  10. RESULT_DIR="results"
  11. TEST_SCRIPT="" # 待测试的脚本
  12. PSUTIL_SCRIPT="monitor_util.py" # 监测工具脚本
  13. INTEGRATED_SCRIPT="integrate_results.py" # 融合工具脚本
  14. CPROFILE_OUTPUT="$RESULT_DIR/cprofile_output.prof" # cProfile 结果文件
  15. MEMORY_PROFILE_OUTPUT="$RESULT_DIR/mprofile_output.dat" # memory_profiler输出文件
  16. MEMORY_PLOT_OUTPUT="$RESULT_DIR/mprofile_memory_plot.png" # memory_profiler 绘制的曲线图
  17. FLAMEGRAPH_OUTPUT="$RESULT_DIR/pyspy_output.svg" # py-spy 输出文件
  18. PSUTIL_OUTPUT="$RESULT_DIR/psutil_output.csv" # psutil 输出文件
  19. INTEGRATED_REPORT="$RESULT_DIR/performance_analysis_report.json" # 融合结果文件
  20. # 日志函数
  21. log_info() {
  22. echo -e "${BLUE}[INFO]${NC} $1"
  23. }
  24. log_success() {
  25. echo -e "${GREEN}[SUCCESS]${NC} $1"
  26. }
  27. log_warning() {
  28. echo -e "${YELLOW}[WARNING]${NC} $1"
  29. }
  30. log_error() {
  31. echo -e "${RED}[ERROR]${NC} $1"
  32. }
  33. # 显示标题
  34. show_header() {
  35. echo "========================================="
  36. echo "Python脚本性能分析工具链"
  37. echo "========================================="
  38. sleep 2
  39. }
  40. # 显示步骤标题
  41. show_step() {
  42. echo ""
  43. echo "-----------------------------------------"
  44. echo "$1"
  45. echo "-----------------------------------------"
  46. }
  47. # 检查脚本文件是否存在
  48. check_script_exists() {
  49. if [ -f "config.json" ]; then
  50. TEST_SCRIPT=$(python -c "
  51. import json
  52. try:
  53. with open('config.json', 'r') as f:
  54. config = json.load(f)
  55. print(config.get('test_script_dir', ''))
  56. except Exception as e:
  57. print('')
  58. " 2>/dev/null)
  59. if [ -n "$TEST_SCRIPT" ]; then
  60. log_info "从 config.json 读取测试脚本路径: $TEST_SCRIPT"
  61. else
  62. log_warning "config.json 中未找到有效的 test_script_dir 配置"
  63. return 1
  64. fi
  65. else
  66. log_warning "未找到 config.json 配置文件"
  67. return 1
  68. fi
  69. if [ ! -f "$TEST_SCRIPT" ]; then
  70. log_error "测试脚本 $TEST_SCRIPT 不存在"
  71. return 1
  72. fi
  73. sleep 2
  74. return 0
  75. }
  76. # 创建结果保存目录
  77. mk_dir(){
  78. if [ ! -d "$RESULT_DIR" ]; then
  79. mkdir -p "$RESULT_DIR"
  80. log_info "创建结果保存目录: $RESULT_DIR"
  81. else
  82. log_info "使用现有结果保存目录: $RESULT_DIR"
  83. fi
  84. }
  85. # 1. 使用cProfile进行性能分析
  86. run_cprofile() {
  87. show_step "步骤 1: 使用 cProfile 进行性能分析"
  88. log_info "准备对 $TEST_SCRIPT 进行性能分析..."
  89. # 确保使用绝对路径
  90. PROFILE_ABS_PATH="$(pwd)/$CPROFILE_OUTPUT"
  91. if python -m cProfile -o "$PROFILE_ABS_PATH" "$TEST_SCRIPT"; then
  92. log_success "cProfile 分析完成,结果保存至 $PROFILE_ABS_PATH"
  93. return 0
  94. else
  95. log_error "cProfile 分析失败"
  96. return 1
  97. fi
  98. }
  99. # 3. 使用memory_profiler分析内存使用情况
  100. run_memory_profiler() {
  101. show_step "步骤 3: 使用 memory_profiler 分析内存使用情况"
  102. # 检查是否安装了memory_profiler
  103. if ! command -v mprof &> /dev/null; then
  104. log_warning "未找到 memory_profiler (mprof),尝试安装..."
  105. if pip3 install memory-profiler -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  106. log_success "memory_profiler 安装成功"
  107. echo "适配安装matplotlib: "
  108. conda install matplotlib
  109. else
  110. log_error "memory_profiler 安装失败"
  111. return 1
  112. fi
  113. fi
  114. # 运行内存分析
  115. if command -v mprof &> /dev/null; then
  116. if mprof run --output "$MEMORY_PROFILE_OUTPUT" "$TEST_SCRIPT"; then
  117. log_success "memory_profiler 分析完成,结果保存至 $MEMORY_PROFILE_OUTPUT"
  118. # 绘制内存占用曲线
  119. log_info "绘制内存占用曲线"
  120. if MPLBACKEND=Agg mprof plot --flame "$MEMORY_PROFILE_OUTPUT" -o "$MEMORY_PLOT_OUTPUT"; then
  121. log_success "绘制完成,结果保存至 $MEMORY_PLOT_OUTPUT"
  122. else
  123. log_error "绘制失败"
  124. fi
  125. else
  126. log_error "memory_profiler 分析失败"
  127. fi
  128. else
  129. log_warning "无法安装 memory_profiler,跳过内存分析步骤"
  130. fi
  131. }
  132. # 2. 使用py-spy记录性能数据
  133. run_py_spy() {
  134. show_step "步骤 2: 使用 py-spy record 进行性能分析"
  135. # 检查是否安装了py-spy
  136. if ! command -v py-spy &> /dev/null; then
  137. log_warning "未找到 py-spy ,尝试安装..."
  138. if pip3 install py-spy -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  139. log_success "py-spy 安装成功"
  140. else
  141. log_error "py-spy 安装失败"
  142. return 1
  143. fi
  144. fi
  145. # 再次检查是否安装成功
  146. if ! command -v py-spy &> /dev/null; then
  147. log_error "py-spy 安装失败"
  148. return 1
  149. fi
  150. # 根据操作系统类型选择合适的py-spy参数,需要统计子线程和C拓展
  151. if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]]; then
  152. # Windows环境下使用--nonblocking选项
  153. log_info "Windows环境下使用--nonblocking选项"
  154. PYSPY_CMD="py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses --nonblocking -- python $TEST_SCRIPT"
  155. elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
  156. # Linux环境下使用sudo运行
  157. log_info "Linux环境下使用sudo运行py-spy"
  158. PYSPY_CMD="sudo env \"PATH=\$PATH\" py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses -- python $TEST_SCRIPT"
  159. else
  160. # 其他Unix系统使用默认选项
  161. PYSPY_CMD="py-spy record -o $FLAMEGRAPH_OUTPUT --duration 35 --native --subprocesses -- python $TEST_SCRIPT"
  162. fi
  163. log_info "执行命令: $PYSPY_CMD"
  164. eval "$PYSPY_CMD"
  165. PYSPY_PID=$!
  166. if wait $PYSPY_PID; then
  167. log_success "py-spy record 分析完成,火焰图保存至 $FLAMEGRAPH_OUTPUT"
  168. return 0
  169. else
  170. log_error "py-spy record 分析失败"
  171. if [[ "$OSTYPE" == "linux-gnu"* ]]; then
  172. log_warning "提示: 可能需要管理员权限,请使用 'sudo ./$0' 运行此脚本"
  173. fi
  174. return 1
  175. fi
  176. }
  177. # 4. 使用psutil获取进程信息
  178. run_psutil() {
  179. show_step "步骤 4: 使用 psutil 获取进程信息"
  180. # 检查是否安装了psutil
  181. if ! python -c "import psutil" &> /dev/null; then
  182. log_warning "未找到 psutil ,尝试安装..."
  183. if pip3 install psutil -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  184. log_success "psutil 安装成功"
  185. else
  186. log_error "psutil 安装失败"
  187. return 1
  188. fi
  189. fi
  190. # 在后台运行test_script.py并获取其PID
  191. log_info "在后台启动 $TEST_SCRIPT ..."
  192. python "$TEST_SCRIPT" &
  193. TEST_PID=$!
  194. # 等待一段时间确保进程已经完全启动
  195. sleep 2
  196. # 验证进程是否仍在运行
  197. if ! kill -0 $TEST_PID 2>/dev/null; then
  198. log_error "测试脚本启动失败"
  199. return 1
  200. fi
  201. log_info "测试脚本 PID: $TEST_PID"
  202. # 使用monitor.py监控该进程
  203. log_info "开始监控进程资源使用情况..."
  204. if [[ "$OSTYPE" == "linux-gnu"* ]]; then
  205. # Linux环境下使用sudo运行monitor.py
  206. sudo env "PATH=$PATH" python "$PSUTIL_SCRIPT" --pid $TEST_PID --output "$PSUTIL_OUTPUT" &
  207. else
  208. python "$PSUTIL_SCRIPT" --pid $TEST_PID --output "$PSUTIL_OUTPUT" &
  209. fi
  210. MONITOR_PID=$!
  211. # 等待test_script.py执行完成
  212. wait $TEST_PID
  213. sleep 4
  214. log_success "测试脚本执行完成"
  215. # 终止monitor.py
  216. kill $MONITOR_PID 2>/dev/null || true
  217. log_success "资源监控完成,结果保存至 $PSUTIL_OUTPUT"
  218. }
  219. # 自动打开SVG文件
  220. open_svg_file() {
  221. if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
  222. log_info "正在尝试在浏览器中打开火焰图..."
  223. # 检测是否在WSL环境中
  224. if grep -qE "(Microsoft|WSL)" /proc/version 2>/dev/null; then
  225. # WSL环境 - 使用PowerShell打开
  226. log_info "检测到WSL环境,使用PowerShell打开文件"
  227. if powershell.exe -Command "Start-Process '$(wslpath -w "$(pwd)/$FLAMEGRAPH_OUTPUT")'" 2>/dev/null; then
  228. log_success "SVG文件已在Windows默认应用中打开"
  229. return 0
  230. elif cmd.exe /c "start $(wslpath -w "$(pwd)/$FLAMEGRAPH_OUTPUT")" 2>/dev/null; then
  231. log_success "SVG文件已在Windows默认应用中打开"
  232. return 0
  233. else
  234. log_warning "无法在Windows主机上打开文件,请手动打开 $FLAMEGRAPH_OUTPUT"
  235. fi
  236. elif command -v xdg-open >/dev/null 2>&1; then
  237. # 标准Linux环境
  238. if xdg-open "$FLAMEGRAPH_OUTPUT"; then
  239. log_success "SVG文件已在默认应用中打开"
  240. return 0
  241. else
  242. log_warning "无法使用xdg-open打开文件"
  243. fi
  244. elif command -v gnome-open >/dev/null 2>&1; then
  245. if gnome-open "$FLAMEGRAPH_OUTPUT"; then
  246. log_success "SVG文件已在默认应用中打开"
  247. return 0
  248. else
  249. log_warning "无法使用gnome-open打开文件"
  250. fi
  251. else
  252. log_warning "找不到合适的打开命令,请手动在浏览器中打开 $FLAMEGRAPH_OUTPUT"
  253. fi
  254. fi
  255. return 1
  256. }
  257. # 自动打开prof文件
  258. open_prof_file() {
  259. # 检查snakeviz是否存在,不存在就尝试自动安装
  260. if [ -f "$CPROFILE_OUTPUT" ]; then
  261. log_info "自动打开cProfile分析结果"
  262. if ! command -v snakeviz &> /dev/null; then
  263. log_warning "未找到snakeviz,尝试安装..."
  264. if pip3 install snakeviz -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  265. log_success "snakeviz 安装成功"
  266. else
  267. log_error "snakeviz 安装失败"
  268. return 1
  269. fi
  270. fi
  271. if command -v snakeviz &> /dev/null; then
  272. log_info "正在尝试使用snakeviz打开prof文件..."
  273. if snakeviz "$CPROFILE_OUTPUT"; then
  274. log_success "prof文件浏览完成"
  275. return 0
  276. else
  277. log_warning "无法使用snakeviz打开prof文件"
  278. fi
  279. else
  280. log_warning "snakeviz未安装,请手动打开 $CPROFILE_OUTPUT"
  281. fi
  282. else
  283. log_error "$CPROFILE_OUTPUT 文件不存在"
  284. fi
  285. return 1
  286. }
  287. # 显示结果摘要
  288. show_summary() {
  289. show_step "性能分析结果摘要"
  290. echo "1. cProfile 输出: $CPROFILE_OUTPUT (可用于 pstats 分析)"
  291. echo "2. py-spy 火焰图: $FLAMEGRAPH_OUTPUT (可在浏览器中打开查看)"
  292. echo "3. memory_profiler 输出: $MEMORY_PROFILE_OUTPUT (内存使用情况)"
  293. echo "4. psutil 输出: $PSUTIL_OUTPUT (硬件资源使用情况)"
  294. log_success "性能分析工具链执行完毕!"
  295. }
  296. # 整合所有分析结果
  297. integrate_all_results(){
  298. show_step "步骤5 整合所有分析结果"
  299. # 检查python脚本是否存在
  300. if [ ! -f "$INTEGRATED_SCRIPT" ]; then
  301. log_warning "整合脚本 $INTEGRATED_SCRIPT 不存在"
  302. return 1
  303. else
  304. echo "结果整合中..."
  305. # 构建命令参数
  306. CMD="python $INTEGRATED_SCRIPT"
  307. if [ -f "$CPROFILE_OUTPUT" ]; then
  308. CMD="$CMD --cprofile $CPROFILE_OUTPUT"
  309. fi
  310. if [ -f "$MEMORY_PROFILE_OUTPUT" ]; then
  311. CMD="$CMD --memory $MEMORY_PROFILE_OUTPUT"
  312. fi
  313. if [ -f "$FLAMEGRAPH_OUTPUT" ]; then
  314. CMD="$CMD --flamegraph $FLAMEGRAPH_OUTPUT"
  315. fi
  316. if [ -f "$PSUTIL_OUTPUT" ]; then
  317. CMD="$CMD --psutil $PSUTIL_OUTPUT"
  318. fi
  319. CMD="$CMD --output $INTEGRATED_REPORT"
  320. # 执行整合脚本
  321. log_info "执行整合命令: $CMD"
  322. if eval "$CMD"; then
  323. log_success "所有分析结果已整合至 $INTEGRATED_REPORT"
  324. return 0
  325. else
  326. log_error "整合过程出错"
  327. # 检查pandas是否存在
  328. if ! python -c "import pandas" &> /dev/null; then
  329. log_warning "未找到pandas,尝试安装..."
  330. if pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple; then
  331. log_success "pandas 安装成功"
  332. else
  333. log_error "pandas 安装失败"
  334. return 1
  335. fi
  336. # 尝试再次执行命令
  337. if eval "$CMD"; then
  338. log_success "所有分析结果已整合至 $INTEGRATED_REPORT"
  339. return 0
  340. fi
  341. fi
  342. return 1
  343. fi
  344. fi
  345. }
  346. # 主函数
  347. main() {
  348. # 显示标题
  349. show_header
  350. # 检查脚本是否存在
  351. if ! check_script_exists; then
  352. log_error "请检查测试脚本是否存在."
  353. exit 1
  354. fi
  355. # 创建结果保存目录
  356. mk_dir
  357. # 执行各项分析
  358. run_cprofile # cProfile 算法性能分析,统计主线程的函数调用次数和运行时间
  359. run_py_spy # py-spy 火焰图
  360. run_memory_profiler # 内存使用情况分析
  361. run_psutil # 使用psutil监控进程资源使用情况
  362. show_summary # 显示结果摘要
  363. integrate_all_results # 整合所有结果
  364. # 自动打开结果文件
  365. open_svg_file
  366. open_prof_file
  367. }
  368. # 执行主函数
  369. main "$@"