顯示具有 ubuntu 標籤的文章。 顯示所有文章
顯示具有 ubuntu 標籤的文章。 顯示所有文章

2017年3月7日 星期二

除錯技巧:在 Ubuntu 上找出第三方函式庫的程式碼

這篇藉由一個小例子說明如何使用 gdb, locate, apt-file, apt-get 找出問題原因。藉由取得第三方函式庫的程式碼,可以減少瞎猜的時間。

問題

我發現某個程式會不預期的結束。但它不是 crash,沒有 core dump 可看。

用 gdb 找出怎麼結束的

先用 gdb attach 程式,繼續操作。程式結束時 gdb 顯示是呼叫 exit() 結束的。

於是再執行一次,這次用 gdb 在 exit 設中斷點再繼續執行。

取得的 backtrace 如下:

#0  __GI_exit (...) at exit.c:104
#1  0x00007fdd27f60408 in _XDefaultError (...) at ../../src/XlibInt.c:1414
#2  0x00007fdd27f6054b in _XError (...) at ../../src/XlibInt.c:1463
#3  0x00007fdd27f5d5e7 in handle_error ...) at ../../src/xcb_io.c:213
#4  0x00007fdd27f5e687 in _XReply (...) at ../../src/xcb_io.c:699
#5  0x00007fdd27f45346 in XGetWindowProperty (...) at ../../src/GetProp.c:69
#6  0x00007fdd2825db30 in XmuClientWindow () from /usr/lib/x86_64-linux-gnu/libXmu.so.6
...

看 backtrace 沒什麼頭緒,都是第三方函式庫的程式。照 XmuClientWindow() 的說明,它可能會失敗,但它不該直接呼叫 exit()。先找 XmuClientWindow 的原始碼,看看有什麼線索。

用 Ubuntu 的 package 系統找出原始碼

首先用 apt-file 找出 libXmu.so.6 在那個套件 (第一次執行需先跑 apt-file update 更新索引):

$ apt-file search /usr/lib/x86_64-linux-gnu/libXmu.so.6
libxmu6: /usr/lib/x86_64-linux-gnu/libXmu.so.6
libxmu6: /usr/lib/x86_64-linux-gnu/libXmu.so.6.2.0
libxmu6-dbg: /usr/lib/debug/usr/lib/x86_64-linux-gnu/libXmu.so.6.2.0

然後用 apt-get source 取得原始碼:

$ apt-get source libxmu6

在 src/ClientWin.c 裡找到 XmuClientWindow [*1]:

Window
XmuClientWindow(Display *dpy, Window win)
{
    ...
    XGetWindowProperty(dpy, win, WM_STATE, 0, 0, False, AnyPropertyType,
               &type, &format, &nitems, &after, &data);
    ....
}

沒什麼有用資訊,繼續找 XGetWindowProperty 的程式碼。從 backtrace 裡知道它在 ../../src/GetProp.c 裡,但我不知道 GetProp.c 是什麼套件的程式。用 Google 搜尋 GetProp.c 也許會有線索,不過這裡我用另一個作法。

從 header 找出原始碼

src/ClientWin.c 引入的 header 不多,只有這些:

#include <X11/Xlib.h>
#include <X11/Xatom.h>

#include <X11/Xmu/WinUtil.h>

先用 locate 查出完整路徑:

$ locate X11/Xlib.h
/usr/include/X11/Xlib.h

再用 apt-file 找套件名稱:

$ apt-file search /usr/include/X11/Xlib.h
libx11-dev: /usr/include/X11/Xlib.h

取得原始碼:

$ apt-get source libx11-dev

附帶一提,可透過 gcc -E 展開 #include 驗證 XGetWindowProperty 確實出自 X11/Xlib.h:

$ echo "#include " | gcc - -E | grep XGetWindowProperty
extern int XGetWindowProperty(

header 太多的話,直接編譯原始檔比較快:

$ gcc -E src/ClientWin.c > t
得到的資訊如下:
1674 # 1303 "/usr/include/X11/Xlib.h" 3 4
...
3060 extern int XGetWindowProperty(

找到目標後,往回找第一個 header 路徑就是來源了。如果找不到的話,可能是少指定 include header 的來源目錄。可以從 make 產生的結果得知完整的編譯訊息:

$ ./configure
$ make V=1 | grep ClientWin.c
/bin/bash ../libtool  --tag=CC   --mode=compile gcc -std=gnu99 -DHAVE_CONFIG_H \
-I. -I..  -I../include -I../include/X11/Xmu < ... 略 ...> -c -o ClientWin.lo ClientWin.c
...

找出 -I 的部份,和 -E 一樣加在 gcc 的參數上展開 include。

關於如何讓 make 顯示編譯時的參數 (cflags),參見這裡。

繼續追踪程式碼

在 src/GetProp.c 裡發現:

 69     if (!_XReply (dpy, (xReply *) &reply, 0, xFalse)) {

這和前面的 backtrace 一致:

#0  __GI_exit (...) at exit.c:104
#1  0x00007fdd27f60408 in _XDefaultError (...) at ../../src/XlibInt.c:1414
#2  0x00007fdd27f6054b in _XError (...) at ../../src/XlibInt.c:1463
#3  0x00007fdd27f5d5e7 in handle_error ...) at ../../src/xcb_io.c:213
#4  0x00007fdd27f5e687 in _XReply (...) at ../../src/xcb_io.c:699
#5  0x00007fdd27f45346 in XGetWindowProperty (...) at ../../src/GetProp.c:69
#6  0x00007fdd2825db30 in XmuClientWindow () from /usr/lib/x86_64-linux-gnu/libXmu.so.6

表示尋找的方向正確。再繼續看 src/xcb_io.c 和 src/XlibInt.c 內相關函式,得知:

// src/XlibInt.c
1463     rtn_val = (*_XErrorFunction)(dpy, (XErrorEvent *)&event); /* upcall */

對照 backtrace 得知執行 _XErrorFunction 後,是呼叫 _XDefaultError。_XDefaultError 程式如下:

1409 int _XDefaultError(
1410     Display *dpy,
1411     XErrorEvent *event)
1412 {
1413     if (_XPrintDefaultError (dpy, event, stderr) == 0) return 0;
1414     exit(1);
1415     /*NOTREACHED*/
1416 }

確認是這裡呼叫 exit()。所以換掉 _XErrorFunction 可以解決問題。

在程式碼裡搜尋 _XErrorFunction 發現可用 XSetErrorHandler() 設值。這樣至少不會造成程式結束了。

備註

1. 可用 grep 尋找,我是用 gj,找起來更有效率。

2015年9月17日 星期四

用 Ubuntu 14.04 作為 Hotspot

最近需要模擬被擋封包的網路環境, 想想用筆電作為 hotspot 再設 firewall, 應該滿容易的。然後用手機連上 hotspot, 就可以模擬出各種擋封包的情況 (如立即回傳 connection error, 或只是 drop 封包)。

實際操作後的確很容易, 裝好 Ubuntu 14.04 後, 需要的東西都齊了, 內建的 Network Manager 有圖形介面, 點一點就可以分享 Wi-Fi。iPad/iPhone 可以連得上, 但是 Android 不行, 原因是 Ubuntu 預設 hotspot 用 Wi-Fi ad hoc mode, 但是 Android 不支援, 要改用 Wi-Fi infrastructure mode 才行。作法見 How do I create hotspots in Ubuntu 14.04?。

其它

  • Android 4.0 後支援 Wi-Fi P2P, 可以讓 Android 裝置互連, 不過還是不支援 Wi-Fi ad hoc mode。
  • 用 Wi-Fi ad hoc mode 的時候, iPad/iPhone 會在 Wi-Fi 列表的「裝置」看到筆電用的 SSID; 若改用 infrastructure mode 的時候, 會在 AP 列表看到筆電用的 SSID。
  • 我有試過用 Mac OS X 作一樣的事, 但是新版 (我用 10.10.4) 內建只能用 pf 作 firewall, 和這東西不熟, 看起來沒 iptables 直覺。另外開啟 Internet Sharing 後 pf 就失效了, 就沒再深入研究。想想以後還是專心用 Linux 就好, 時間有限, 搞熟兩套 OS 的成本太高了。

2013年5月30日 星期四

和 Ubuntu 安裝套件相關的知識

先散亂地記在這邊, 慢慢補完:

1. 用 apt-get cache FILE 從檔案反查套件名稱

2. http://packages.ubuntu.com/ 找詢是否有這套件, 那一版開始有這套件 (Distribution 選 "any")

3. 套件分類 ( 參考資料 )

  • main: Free software, officially supported by Canonical
  • universe: Free software, NOT supported by Canonical
  • restricted: Non-free software officially supported by Canonical
  • multiverse: Non-free software NOT supported by Canonical

X 版本 (如 X = precise) 的 X-updates 表示 stable release update, 會有比較新一點的套件。預設 /etc/apt/sources.list 裡應該已有 X/X-updates 和 main/universe/restricted/multiverse 組合的八種來源。

4. 用 apt-cache policy PKG 了解安裝的優先順序

例如:

$ apt-cache policy sqlite3
sqlite3:
  Installed: 3.7.9-2ubuntu1.1
  Candidate: 3.7.9-2ubuntu1.1
  Version table:
 *** 3.7.9-2ubuntu1.1 0
        500 http://free.nchc.org.tw/ubuntu/ precise-updates/main amd64 Packages
        100 /var/lib/dpkg/status
     3.7.9-2ubuntu1 0
        500 http://free.nchc.org.tw/ubuntu/ precise/main amd64 Packages

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...