其他分享
首页 > 其他分享> > missing semester - Debugging and Profiling

missing semester - Debugging and Profiling

作者:互联网

Debugging-调试

打印调试法与日志

“The most effective debugging tool is still careful thought, coupled with judiciously placed print statements” — Brian Kernighan, Unix for Beginners.

调试代码的第一种方法往往是在发现问题的地方添加一些打印语句,然后不断重复此过程直到获取了足够的信息并找到问题的根本原因。

另外一个方法是使用日志,而不是临时添加打印语句。日志较普通的打印语句有如下的一些优势:

调试器

当通过打印已经不能满足您的调试需求时,您应该使用调试器。

调试器是一种可以允许我们和正在执行的程序进行交互的程序,它可以做到:

很多编程语言都有自己的调试器。Python 的调试器是pdb。下面对pdb 支持的命令进行简单的介绍:

对于更底层的编程语言,您可能需要了解一下 gdb ( 以及它的改进版 pwndbg) 和 lldb

特殊工具

系统调用

即使需要调试的程序是一个二进制的黑盒程序,仍然有一些工具可以帮助到我们。

当程序需要执行一些只有操作系统内核才能完成的操作时,它需要使用 系统调用。有一些命令可以追踪您的程序执行的系统调用。在 Linux 中可以使用strace ,在 macOS 和 BSD 中可以使用 dtracedtrace 用起来可能有些别扭,因为它使用的是它自有的 D 语言,但是我们可以使用一个叫做 dtruss 的封装使其具有和 strace (更多信息参考 这里)类似的接口

# On Linux
sudo strace -e lstat ls -l > /dev/null
4
# On macOS
sudo dtruss -t lstat64_extended ls -l > /dev/null

网络抓包

有些情况下,我们需要查看网络数据包才能定位问题。像 tcpdumpWireshark 这样的网络数据包分析工具可以帮助您获取网络数据包的内容并基于不同的条件进行过滤。

前端开发

对于 web 开发, Chrome/Firefox 的开发者工具非常方便,功能也很强大:

静态分析

有些问题是不需要执行代码就能发现的。静态分析会将程序的源码作为输入然后基于编码规则对其进行分析并对代码的正确性进行推理。

大多数的编辑器和 IDE 都支持在编辑界面显示这些工具的分析结果、高亮有警告和错误的位置。 这个过程通常称为 code linting 。风格检查或安全检查的结果同样也可以进行相应的显示。

在 vim 中,有 alesyntastic 可以做同样的事情。 在 Python 中, pylintpep8 是两种用于进行风格检查的工具,而 bandit 工具则用于检查安全相关的问题。

对于风格检查和代码格式化,还有以下一些工具可以作为补充:用于 Python 的 black、用于 Go 语言的 gofmt、用于 Rust 的 rustfmt 或是用于 JavaScript, HTML 和 CSS 的 prettier 。这些工具可以自动格式化代码,这样代码风格就可以与常见的风格保持一致。

Profiling-性能分析

代码能跑起来不一定是成功的,可能在时间和内存上存在一定问题。

计时

大多数情况下只需要打印两处代码之间的时间即可发现问题。

import time, random
n = random.randint(1, 10) * 100

# 获取当前时间 
start = time.time()

# 执行一些操作
print("Sleeping for {} ms".format(n))
time.sleep(n/1000)

# 比较当前时间和起始时间
print(time.time() - start)

# Output
# Sleeping for 500 ms
# 0.5713930130004883

执行时间(wall clock time)不一定是该程序实际在CPU上运行的时间,(分时系统,时间片到期后需等待调度)

对于工具来说,需要区分真实时间、用户时间和系统时间。通常来说,用户时间+系统时间代表了您的进程所消耗的实际 CPU (更详细的解释可以参照这篇文章)。

$ time curl https://missing.csail.mit.edu &> /dev/null`
real    0m2.561s
user    0m0.015s
sys     0m0.012s

性能分析工具(profilers)

CPU

大多数情况下,当人们提及性能分析工具的时候,通常指的是 CPU 性能分析工具。

CPU 性能分析工具有两种: 追踪分析器(tracing)及采样分析器(sampling)。

追踪分析器会记录程序的每一次函数调用,而采样分析器则只会周期性的监测(通常为每毫秒)您的程序并记录程序堆栈。

在 Python 中,使用 cProfile 模块来分析每次函数调用所消耗的时间。

$ python -m cProfile -s tottime grep.py 1000 '^(import|\s*def)[^,]*$' *.py

[omitted program output]

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
   8000    0.266    0.000    0.292    0.000 {built-in method io.open}
   8000    0.153    0.000    0.894    0.000 grep.py:5(grep)
  17000    0.101    0.000    0.101    0.000 {built-in method builtins.print}
   8000    0.100    0.000    0.129    0.000 {method 'readlines' of '_io._IOBase' objects}
  93000    0.097    0.000    0.111    0.000 re.py:286(_compile)
  93000    0.069    0.000    0.069    0.000 {method 'search' of '_sre.SRE_Pattern' objects}
  93000    0.030    0.000    0.141    0.000 re.py:231(compile)
  17000    0.019    0.000    0.029    0.000 codecs.py:318(decode)
      1    0.017    0.017    0.911    0.911 grep.py:3(<module>)

[omitted lines]

关于 Python 的 cProfile 分析器(以及其他一些类似的分析器),需要注意的是它显示的是每次函数调用的时间。

更加符合直觉的显示分析信息的方式是包括每行代码的执行时间,这也是行分析器的工作。使用 line_profiler,它会基于行来显示时间:

$ kernprof -l -v a.py
Wrote profile results to urls.py.lprof
Timer unit: 1e-06 s

Total time: 0.636188 s
File: a.py
Function: get_urls at line 5

Line #  Hits         Time  Per Hit   % Time  Line Contents
==============================================================
 5                                           @profile
 6                                           def get_urls():
 7         1     613909.0 613909.0     96.5      response = requests.get('https://missing.csail.mit.edu')
 8         1      21559.0  21559.0      3.4      s = BeautifulSoup(response.content, 'lxml')
 9         1          2.0      2.0      0.0      urls = []
10        25        685.0     27.4      0.1      for url in s.find_all('a'):
11        24         33.0      1.4      0.0          urls.append(url['href'])

内存

像 C 或者 C++ 这样的语言,内存泄漏会导致您的程序在使用完内存后不去释放它。为了应对内存类的 Bug,我们可以使用类似 Valgrind 这样的工具来检查内存泄漏问题。

时间分析

在使用strace调试代码时,可能会希望忽略一些特殊的代码并希望在分析时将其当作黑盒处理。perf 命令将 CPU 的区别进行了抽象,它不会报告时间和内存的消耗,而是报告与您的程序相关的系统事件。

例如,perf 可以报告不佳的缓存局部性(poor cache locality)、大量的页错误(page faults)或活锁(livelocks)。下面是关于常见命令的简介:

可视化

使用分析器来分析真实的程序时,由于软件的复杂性,其输出结果中将包含大量的信息。人类是一种视觉动物,非常不善于阅读大量的文字。因此很多工具都提供了可视化分析器输出结果的功能。

FlameGraph

Call Graph

资源监控

有时候,分析程序性能的第一步是搞清楚它所消耗的资源。程序变慢通常是因为它所需要的资源不够了。例如,没有足够的内存或者网络连接变慢的时候。

有很多很多的工具可以被用来显示不同的系统资源,例如 CPU 占用、内存使用、网络、磁盘使用等。

如果希望测试一下这些工具,可以使用 stress 命令来为系统人为地增加负载。

特殊工具

有时候,只需要对黑盒程序进行基准测试,并依此对软件选择进行评估。

hyperfine 这样的命令行可以进行基准测试。例如,下面的例子中,我们可以看到fdfind 要快20倍。

$ hyperfine --warmup 3 'fd -e jpg' 'find . -iname "*.jpg"'
Benchmark #1: fd -e jpg
  Time (mean ± σ):      51.4 ms ±   2.9 ms    [User: 121.0 ms, System: 160.5 ms]
  Range (min … max):    44.2 ms …  60.1 ms    56 runs

Benchmark #2: find . -iname "*.jpg"
  Time (mean ± σ):      1.126 s ±  0.101 s    [User: 141.1 ms, System: 956.1 ms]
  Range (min … max):    0.975 s …  1.287 s    10 runs

Summary
  'fd -e jpg' ran
   21.89 ± 2.33 times faster than 'find . -iname "*.jpg"'

标签:Debugging,Profiling,可以,程序,semester,内存,工具,CPU,0.000
来源: https://www.cnblogs.com/cxl-/p/15359942.html