使用 Serial Port 來除錯的時候請小心

一般來說,程式設計師在除錯的時候常常會用的一種方式就是 print 大法,在程式適當的地方插入一些顯示資訊的指令,提供開發者更多資料來除錯。而在使用這個方法來除錯的時候,有很高的機會是利用 serial port 來顯示信息,特別是在嵌入式系統的開發上。然而當透過 serial port 來印信息的時候有著潛在的風險,因為在工作上遇到了,所以就特別紀錄在下面。首先先來看看下面這一段程式。

1
2
3
4
5
6
7
#!/usr/bin/python

import sys
import time

for item in range( 0, 10000, 1 ):
    print str( item ) + " ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"


這段程式相當的簡單,就只是單純的把一個字串印個 10000 次罷了。然後我們分別透過 SSH 以及 Serial port 連到測試設備上執行這個程式,並利用 time 這個指令來量測時間。結果記錄在下面(省略印字串的輸出)。

SSH:
root@OpenWrt:~# time ./test_print_01.py
real    0m 0.68s
user    0m 0.18s
sys     0m 0.08s


Serial Port (115200):
root@OpenWrt:~# time ./test_print_01.py
real    1m 8.18s
user    0m 0.17s
sys     0m 0.07s


可以發現時間差距非常的多,理由很簡單,因為 serial port 的輸出速度只有 115200 bps(本篇的實驗設備),所以慢也很正常的。問題是,如果今天要除錯的程式是一隻 time critical 的程式,那這就會遇到很大的麻煩。先把程式列在下面:

test_print.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/python

import sys
import time
import threading
from socket import *

BUFSIZE = 1024

class myThread (threading.Thread):
    def __init__( self, threadID, name, port ):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.port = port
    def run(self):
        print "Start UDP server: " + self.name + " (" + str( self.port ) + ")"
        s = socket( AF_INET, SOCK_DGRAM )
        s.bind( ( '', self.port ) )
        while 1:
            data, addr = s.recvfrom( BUFSIZE )
            print 'server received %r from %r' % (data, addr)
            s.sendto( data, addr )
        print "Exiting " + self.name

thread1 = myThread( 1, "Thread-1", 20000 )

thread1.start()

for item in range( 0, 500000, 1 ):
    print str( item ) + " ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"

test_print_client.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/python

import sys
import time
import datetime
from socket import *

BUFSIZE = 1024

addr = "127.0.0.1", 20000
s = socket(AF_INET, SOCK_DGRAM)
s.bind( ( '', 0 ) )

for item in range( 0, 100000, 1 ):
    time1 = datetime.datetime.now()
    print "[", time1, "]: Send"
    s.sendto( "++++++++++", addr )
    data, fromaddr = s.recvfrom( BUFSIZE )
    time2 = datetime.datetime.now()
    print "[", time2, "]: Recv %r from %r" % ( data, fromaddr )
    elapsedTime = time2 - time1
    print "Time Diff:", elapsedTime
    time.sleep(2)
    

上面這隻程式很簡單,說穿了就是開了一個 thread 來做 echo server,同時在 main thread 中拼命的印東西。另外一隻程式則是簡單的 echo client。接下來我們讓 test_print.py 分別在 SSH 以及 Serial Port 上面執行,client 的部分則統一在 SSH 上面執行,來看看會有甚麼差別。

SSH:
root@OpenWrt:~# ./test_print_client.py
[ 2017-04-17 13:55:14.600182 ]: Send
[ 2017-04-17 13:55:14.796277 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.196095
[ 2017-04-17 13:55:16.798956 ]: Send
[ 2017-04-17 13:55:16.842231 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.043275
[ 2017-04-17 13:55:18.844467 ]: Send
[ 2017-04-17 13:55:18.913060 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.068593
[ 2017-04-17 13:55:20.915303 ]: Send
[ 2017-04-17 13:55:20.922704 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.007401
[ 2017-04-17 13:55:22.924940 ]: Send
[ 2017-04-17 13:55:22.951737 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.026797


Serial Port:
root@OpenWrt:~# ./test_print_client.py
[ 2017-04-17 13:57:37.278359 ]: Send
[ 2017-04-17 13:57:39.991233 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:02.712874
[ 2017-04-17 13:57:41.993506 ]: Send
[ 2017-04-17 13:57:45.668543 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:03.675037
[ 2017-04-17 13:57:47.670792 ]: Send
[ 2017-04-17 13:57:51.346018 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:03.675226
[ 2017-04-17 13:57:53.346490 ]: Send
[ 2017-04-17 13:57:57.023386 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:03.676896
[ 2017-04-17 13:57:59.025634 ]: Send
[ 2017-04-17 13:58:02.700888 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:03.675254


可以看到 Serial Port 所需要的時間是 SSH 的數十倍。明明是同樣的程式,只是因為輸出界面的差異就可以在效能上有如此大的區別,所以在使用 print 大法的時候實在是應該要小心。

當然,剛剛那隻程式的 print 實在是太暴力了,我們稍微改寫一下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/python

import sys
import time
import threading
from socket import *

BUFSIZE = 1024

class myThread (threading.Thread):
    def __init__( self, threadID, name, port ):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.port = port
    def run(self):
        print "Start UDP server: " + self.name + " (" + str( self.port ) + ")"
        s = socket( AF_INET, SOCK_DGRAM )
        s.bind( ( '', self.port ) )
        while 1:
            data, addr = s.recvfrom( BUFSIZE )
            print 'server received %r from %r' % (data, addr)
            s.sendto( data, addr )
        print "Exiting " + self.name

thread1 = myThread( 1, "Thread-1", 20000 )

thread1.start()

for item in range( 0, 500000, 1 ):
    print str( item ) + " ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
    time.sleep(0.005)

其實只是加上了 sleep( 0.005 )。為什麼是 0.005 ... 因為在工作上列印了太多的 log ,平均起來一秒印了 200 行,所以就先設定為 0.005 囉。結果如下:

SSH:
root@OpenWrt:~# ./test_print_client.py
[ 2017-04-17 14:10:51.235181 ]: Send
[ 2017-04-17 14:10:51.236046 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000865
[ 2017-04-17 14:10:53.236485 ]: Send
[ 2017-04-17 14:10:53.237083 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000598
[ 2017-04-17 14:10:55.239284 ]: Send
[ 2017-04-17 14:10:55.239859 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000575
[ 2017-04-17 14:10:57.242061 ]: Send
[ 2017-04-17 14:10:57.242655 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000594
[ 2017-04-17 14:10:59.244856 ]: Send
[ 2017-04-17 14:10:59.245456 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000600
[ 2017-04-17 14:11:01.246487 ]: Send
[ 2017-04-17 14:11:01.247073 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000586
[ 2017-04-17 14:11:03.249279 ]: Send
[ 2017-04-17 14:11:03.249872 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000593
[ 2017-04-17 14:11:05.252072 ]: Send
[ 2017-04-17 14:11:05.252653 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000581
[ 2017-04-17 14:11:07.254854 ]: Send
[ 2017-04-17 14:11:07.255434 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000580
[ 2017-04-17 14:11:09.256489 ]: Send
[ 2017-04-17 14:11:09.257069 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000580
[ 2017-04-17 14:11:11.259268 ]: Send
[ 2017-04-17 14:11:11.259870 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000602
[ 2017-04-17 14:11:13.262075 ]: Send
[ 2017-04-17 14:11:13.262657 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000582
[ 2017-04-17 14:11:15.264855 ]: Send
[ 2017-04-17 14:11:15.265425 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000570
[ 2017-04-17 14:11:17.266485 ]: Send
[ 2017-04-17 14:11:17.267076 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000591
[ 2017-04-17 14:11:19.269279 ]: Send
[ 2017-04-17 14:11:19.269852 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000573
[ 2017-04-17 14:11:21.272052 ]: Send
[ 2017-04-17 14:11:21.272616 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000564


Serial Port:
root@OpenWrt:~# ./test_print_client.py
[ 2017-04-17 14:08:34.943589 ]: Send
[ 2017-04-17 14:08:34.944421 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000832
[ 2017-04-17 14:08:36.946486 ]: Send
[ 2017-04-17 14:08:36.947092 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000606
[ 2017-04-17 14:08:38.949301 ]: Send
[ 2017-04-17 14:08:38.949905 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000604
[ 2017-04-17 14:08:40.952110 ]: Send
[ 2017-04-17 14:08:40.952706 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000596
[ 2017-04-17 14:08:42.954913 ]: Send
[ 2017-04-17 14:08:42.955494 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000581
[ 2017-04-17 14:08:44.956492 ]: Send
[ 2017-04-17 14:08:50.240876 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:05.284384
[ 2017-04-17 14:08:52.243117 ]: Send
[ 2017-04-17 14:08:52.243708 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000591
[ 2017-04-17 14:08:54.245913 ]: Send
[ 2017-04-17 14:08:54.246531 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000618
[ 2017-04-17 14:08:56.248738 ]: Send
[ 2017-04-17 14:08:56.249326 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000588
[ 2017-04-17 14:08:58.251531 ]: Send
[ 2017-04-17 14:08:58.252116 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000585
[ 2017-04-17 14:09:00.254326 ]: Send
[ 2017-04-17 14:09:00.254899 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000573
[ 2017-04-17 14:09:02.256485 ]: Send
[ 2017-04-17 14:09:02.257076 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000591
[ 2017-04-17 14:09:04.259290 ]: Send
[ 2017-04-17 14:09:08.467511 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:04.208221
[ 2017-04-17 14:09:10.468712 ]: Send
[ 2017-04-17 14:09:10.469497 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000785
[ 2017-04-17 14:09:12.471345 ]: Send
[ 2017-04-17 14:09:12.472161 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000816
[ 2017-04-17 14:09:14.473987 ]: Send
[ 2017-04-17 14:09:14.474775 ]: Recv '++++++++++' from ('127.0.0.1', 20000)
Time Diff: 0:00:00.000788


看起來結果差不多,不過 serial port 不定時會出現飆高的狀況。因此,在透過 Serial Port 使用 print 大法的時候,要記得這是會影響效能的。

PS. 在 python 上使用 threading 的話,使用了 Ctrl-C 來中止程式, thread 還是會繼續執行唷。 

留言

這個網誌中的熱門文章

如何將Linux打造成OpenFlow Switch:Openvswitch

我弟家的新居感恩禮拜分享:善頌善禱

Linux Virtual Interface: TUN/TAP