歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux資訊 >> Linux文化 >> 開發工具設計初稿(一)

開發工具設計初稿(一)

日期:2017/2/27 12:14:01   编辑:Linux文化

一、總體構想:
1)分布式跨平台的開發工具
2)在同一界面下能進行混合語言編程
3)擁有一個的無語言差別的基本類庫,並包含一個無系統差異的基本系統類庫
4)在源碼調試基礎上實現各種
5)實現基本的自動測試
6)一定的變形繁衍能力

跨平台是指為一般用戶提供一個無系統差別統一界面的開發平台,同時又能為高級用戶提供具體系統的接口

分布式特征是指允許同一個開發小組的開發人員能同時針對同一個項目編碼和調試,使得對分布式系統的調試更加有效

混合編程的含義是,在同一個項目中使用多種語言編碼,語言的差別由編譯器來處理,這樣能讓開發者使用自己最拿手的語言開發,而不必為

統一開發語言而費時費力

現今隨著編程語言的不斷進化,主流開發語言正趨向一致的特征,尤其在面向對象的類結構出現後,多數語言除了關鍵字的名稱不同外,基本

是一致的,而結構分支語句在長期發展中基本統一了名稱,就IF/ELSE、WHILE、與FOR的多種變形,而現在所存在的不同語言開發工具中的接口

不統一問題是一種商業行為導致的,和語言本身無關,現今計算機語言總共有三個特點
1)多數語言設計時的差別是一種歷史差別,本質是並無差別(即使象LISP等非結構化語言,差別是問題描述不同)
2)主流語言發展的趨勢是相互吸收統一,看看C#和JAVA、DELPHI等等就知道,從語言角度是一致的
3)不同語言產生的結果是一樣的,都是一種目標文件(即使是臨時的和可執行文件的目標文件也是一樣的性質),目標文件描述上有差別與系

統有關,和語言本身無關
而就是第三條是導致語言最終無差別的基礎

(具體分析將在編譯器模塊分析中展開)

這為構造無語言差別的類庫奠定了基礎,而且類庫本身的特征是一種工具庫,使用者本質上只需要知道類名、屬性名、功能名和它們的用法就

能編程了,無需知道其內部細節,當然在調試中是允許進入類庫源碼

系統無差異的基本類庫,是根據現有操作系統理論中中相對成熟的那些部分,在較高的層次上(如多數通用的系統API名稱)建立一個基本的形

式類庫,然後針對不同系統,予以接口映射

在通用類庫基礎上,將編譯器框架化(比如引入COM形式),變可構造無語言差別的編譯器了

為此我們從語言角度討論一下現有開發工具
如Borland的產品WINDOWS下的DELPHI與LINUX下的Kylix基本一致,體現了跨平台的趨勢(我想老外的目標也應該是這個),Delphi與C++
Builder的比較可以看出它們的類庫是等價的,體現了一種無差別性,只是,它還是以不同語言來表達了幾遍
而微軟的C#就是以一種統一平台的姿勢露臉的,只是一種新的語言形式(雖然和JAVA很象),成為一種好的商業模式的同時,卻增加了開發人

員的負擔,花精力去學習新的形式,會使其推廣要經歷長時間,不過它的應用結構和我想的很類似,值得學習
LINUX下GCC以開放源碼的形式,發展挺快,但本身沒有一個良好的集成開發環境,限制了普通用戶的使用欲望,而一個開發工具發展到現在,

操作界面已經成為發展的重點,畢竟內部的操作對用戶來說無錯就行了,而操作是直接面對的問題,對效率的影響很大
總的來講,現有開發工具的調試功能和以前DOS和UNIX下的字符界面時代並沒有多大變化,沒有更多的去挖掘,象UNIX及其類似系統下有很多的

輔助調試工具,可惜都停留在字符狀態,而且綜合程度不夠,很是遺憾,就象我遇到的不少UNIX開發者,他們就是喜歡用VI編程,只是一種長

時間形成的習慣,從另一種角度也說明類UNIX系統上相關產品對用戶的需求調查的不夠,最終導致用它們的都是高手,一般人望而生畏,好在

LINUX正在改變這種狀態

本文的一個重點就是分析調試功能,以調試操作系統為基礎,力求分析出多數情況下的調試需求
以調試操作系統為基礎,是因為一般的系統是建立在操作系統上的,調試中有了操作系統的幫助,難度上大大降低,而調試操作系統時,困難

就很明顯了,現在唯一能依靠的只有硬件本身了,而且在調試一般軟件時,由操作系統屏蔽了很多的硬件特征,如單進程和多進程、單處理器

和多處理器、單機系統和分布式系統等等,這些差異在調試操作系統中是不能避免的
後面將會針對這些情況詳細討論

自動測試功能是一個難度不低的課題,相關的文章很多,只是好的實用工具還不多,在此僅涉及與編碼相關的白盒測試,討論以源代碼本身的

結構為基礎的一種自動測試方案,有一定的局限性,需要使用者進行一定量的先期描述,如消息接受的正確順序、操作過程描述、狀態變化的

描述,而對非對象編程而言還要描述消息定義,就是描述在正常狀態情況下程序的走向,然後由測試器結合一定的隨機操作對代碼(源碼或者

可執行代碼都可)進行自動測試,用戶也可以指定測試過程

變形和繁衍
變形是指用戶定制,比如EMACS就是很不錯的方式,它後面有一個LISP支持,那是復雜了點,關鍵是如何在設計中留出各種操作接口,供二次開

發使用

繁衍是指一種自動移植功能,當有一種新的操作系統生成了,最好是在此開發環境下生成的,要麼就是提供了足夠的接口描述腳本,然後此開

發工具按一定的描述規則為新系統生成一個配套的開發工具,最基本的是生成命令行方式的如GCC,圖形界面是屬於操作系統的一種應用軟件,

可以為第三方供應,由此自動生成圖形界面的開發工具的難度比較大


二、開發工具的基本結構分析(寫的簡單了點)


開發工具的基本功能就是編譯功能,連調試功能都不能算是最基本的模塊

發展至今,開發工具從原始的匯編工具到非集成方式的開發環境,到字符狀態的集成環境,最後是現在的可視化集成環境

主要模塊劃分成
編譯模塊、調試模塊、庫(包括類庫和基本函數庫)、編輯器

分析中大部分以編譯和調試為主,將庫分析作為一個獨立部分,而編輯器將在最後分析,因為必須等前面所有模塊的需求分析完後,才能得到

一個基本的接口表,這個表是編輯器和各模塊交互的接口,如此對編輯器分析將能更全面

而所有接口細節將在按視圖角度分析各模塊後得到


第一部分 基本功能分析

一、編譯器分析

編譯器 ——將某種指定語言的源代碼翻譯成某種指定的目標代碼,同時檢查源碼中的靜態邏輯是否符合語言定義

基本包含三大塊內容:語言選擇、錯誤檢查、翻譯

編譯器從流程看是一個單純的數據流處理過程,分析源碼,查找語法錯誤,判斷正確後,將源碼轉化成中間代碼,進行優化,最後按硬件的要

求轉化成目標代碼,這就是編譯器的基本運轉圖

基本結構如下:


源碼 —— 詞法分析 (詞定義規則)
|
|—— 語法分析 (語法定義規則)
|
|—— 語義分析 (語義描述規則)
|
|—— 代碼優化 (優化規則)
|
|—— 中間代碼生成 (轉換規則)
|
|—— 目標代碼生成 (轉換規則)
|
|—— 可執行文件 (代碼連接規則)


1、計算機語言分析


每種語言產生時,都說知己是最好的,至少在某方面,實際情況呢,個人認為,在計算機語言發展到現在,還是強調哪個語言適合哪個方面,

本質上是一種商業操作,而非語言自身的問題

主流開發語言,如:

Ada —— 美國政府強推的號語言,但不能被廣泛使用

C —— 被廣泛使用,但定義不嚴格

Pascal —— 定義很嚴格,長期被用於教學,但幾乎被淘汰,幸好有了DELPHI才重新恢復了生機

C++ —— 源自C被廣泛使用,但和C一樣有許多不嚴格之處

VB —— 門檻較低的語言,在視窗初期,由於開發工具的種類單調,使其獲得了廣泛的使用

Java —— 產生於商業競爭,做為網絡開發工具前景不錯,還不適合底層開發

C# —— 新的品種,存在和JAVA等語言嚴重等同之處,其應用結構不錯,發展如何尚待時間來證明一切


1)從發展趨勢看語言的特征

語言的發展趨勢是差異逐步縮小,各種語言之間的交流接口是人為制造的商業障礙,正在被標准化漸漸消除,語言使用的一些早期約定,如內

存分配方式、參數排列順序、對象垃圾回收等等,都是於語言本質上無關的內容,是操作系統和編譯器之間的約定

語言 —— 是一種表達規則,讓使用者方便的按照一定格式描述自己需要表達的邏輯

而現在經過長期的商業宣傳後,使多數人產生了如下的誤解,如C++就是VC或C++Builder,Pascal就是DELPHI,把語言和一個完整的開發工具等

同了,使得用某種語言必然是用某種開發工具,是一種商品僵化開發人員思想的體現,屬於一種比較成功的商業行為,從一定程度上迫使開發

人員跟著商業產品打轉,總是忙於各種各樣的語言學習中,而主要是學習各種開發工具的使用

語言發展在面向對象分析推廣後,除了原本的關鍵字差異外,在類上使用的符號基本一致,由於開發中類庫的大量使用,使得語言側重點從基

本語句的表達,轉向類庫設計中,而類庫本身是面向對象的產物,無傳統語言的痕跡,所以各開發語言工具的類庫相似就不足為奇了
而最近出現的C#,第一眼看上去就是JAVA或DELPHI,和C/C++的形象相差很大了,以明顯的方式告訴世人語言的未來是一致的

2)對語言的一些分工考察

早期的語言誕生是為了一些特定的目的,如BASIC是一種解釋語言,但語言發展至今,還要說語言分工的話,就是一種商業行為了
BASIC至今仍舊是一種解釋型語言,而它一度被廣泛使用,原因就是VC較難用或者是使用方式不友好,而當時能競爭一下的BC沒能跟上時代,而

被淘汰了,使得VC那種格式保留至今而未有改變,個人認為那是一種嚴重限制自由的風格

就象當年說的,VB做界面和數據庫、VC做底層,說白了是微軟的一種商業策略,而和語言自身無關

其實對一般用戶來說,是解釋還是編譯的本質差別根本就不用考慮,他們考慮的是問題的結果,就是是否能得到一個速度滿意的可執行文件,

而解釋型語言現在也形成了可執行文件了,還不斷提高效率,從結果上說和編譯型沒差別,中間的實現過程和語言無關的,因為並沒有法律規

定BASIC就必須是解釋型的,同樣的代碼是以解釋方式還是編譯方式對語言本身是沒有區別的,純粹是一種商業運作

畢竟市場是壟斷的,開發者這樣規定了,使用者就只能只能這樣認為了

正因為如此,和VB的特點很象的DELPHI和C++builder側重於可視化編程的工具一出現,就獲得很好的口碑,不過奇怪的是C++Builder在市場上

若隱若現,一直來推廣不利,這與DELPHI的流行形成強對比,因為兩者本質上是一個界面和一個類庫及函數庫,差別僅在於關鍵字的叫法不同

,或許是不願意和微軟VC過於正面沖突,采取了低姿態

3)近來語言的發展
從DELPHI、C++Builder和Kylix可以看出Borland正是充分地利用了語言無差別性,輕松的擴展其產品線,而能保證他們地統一界面,基本一致

的類庫,和一樣的調試方式,可以看出它的良苦用心,以傳統語言方式和微軟抗衡

而微軟的以C#核心的.NET計劃,同樣是試圖統一開發平台的計劃,只是它建立在新語言上,這本身是一種風險,幸好微軟的實力大,有強制推

廣新語言的能力,而且它是以一種語言為根基,從中間代碼上下功夫,以的中間代碼轉化方式來適應各種硬件平台,實質和GCC能適應各種平台

的本質是一樣的(是否算抄襲呢)

Java經歷了跨世紀的發展,到現在已經成為一種標准,面對微軟C#的挑戰,加上編譯形式限制了速度,可以說今後的路難走,畢竟模仿它的新

語言還會出現,而Java由於兼容性,必然會越來越顯老化,看其自身變化是否能跟上時代了
這也是傳統語言共同面對的問題,不進則退

總結:
1)語言趨向一致
2)開發新語言成本與風險高,周期太長
結論:

1)不開發新的語言種類
2)改變編譯器的結構,使其不再受限制於特定語言

采用統一界面,以界面不變微基礎,吸收各種語言形式,做到以不變應萬變,再好的語言都能直接使用,省去推廣新語言的風險,比較適合國

內的形式,而單純的算法突破不足以改變現有編譯結構,除非編譯理論有重大突破,即使真的這樣了,也只需要改變編譯器的前半部分的功能

,畢竟所產生的目標代碼還是機器代碼,而後半部分的大改變是要計算機結構有本質變化時才可能,而那也只是改變編譯器的後一半,與語言

本身是無關的

所以重點放在編譯器的結構上

2、目標代碼和中間代碼(寫的簡單了點)

目標代碼 —— 針對特定系統執行所需的格式,是源代碼最終被編譯器翻譯成的結果

不一定和硬件有關,比如一個解釋型操作系統,它用解釋方式來屏蔽硬件的細節,則目標代碼就可以和硬件無關了,實質是一種中間代碼
一般來講目標代碼使用硬件提供的功能越多,效率也越好,關鍵是看所在軟件平台對硬件細節做多大程度的屏蔽

中間代碼 —— 一種處於源碼語言和目標代碼之間的表達形式,可以為任何形式,與任何硬件都無關,是一種純粹的語言邏輯表達方式
中間代碼具有很高的靈活性,是唯一可以有編譯器開發者完全支配的語言形式,象編程語言一般都有相應的國際標准,而目標代碼更是受制於

應用平台,因此它的表達方式很多,可以是某種匯編語言,還有教材中常見的三元式、四元式、DAG等,在各種編譯理論分析中,都屬於很成熟

的部分

而在類UNIX系統中,以PDP-11匯編語言來統一平台,代碼中涉及硬件的也可由

目標代碼的趨同性:(針對編譯器的范疇)
雖然不同的硬件,使用的機器指令也不同,但作為基本結構是馮氏的,基本指令本質上是雷同的,如寄存器操作、內存讀取、邏輯操作和跳轉

指令,而這些操作是目標代碼的主體,剩余的特殊指令、I/O指令、中斷和非中斷方式等操作,可以通過庫函數一級來統一
由此可以看出,目標代碼也存在無差別性,為編譯器組件化提供了依據
在GCC中已經能兼容多種機型,為我們提供了現實基礎,當然難度還是很不小的


總的來講,問題是一個:還沒有業界認可的統一表達形式,體現了現今形式表達的不完善

幸好這點對於編譯器的組件化構造不是致命的,而組件化本身就是一種體現多表達式相結合的形式

3、編譯器的組件化

從編譯器處理過程看:
前面討論的語言無差別問題僅涉及了編譯器模塊的前半功能,中間代碼轉化是編譯器的中間部件,目標代碼轉化和連接是最終部件

三者之間基本是獨立的,有相應的轉化規則來定義處理過程,正是這種良好的分工方式,為編譯器的組件化提供了自由的設計空間

1)基本組件接口分析
而現今成熟的編譯理論使得結構基本很穩定了,而語言本身還是不斷變化的,因為存在市場爭奪的需要,國內暫時還不具備開發新語言並推廣

的實力和權威性,而語言的正則描述也很成熟了,一時不存在語言突破正則表達的范圍,這是語言現在的不變性

這為構造無差別的編譯器提供了理論基礎,即使有變化,也時對編譯器的前半部分產生影響

下面的各種接口是一定理想化的描述,實際情況中,很多是一體的,如語法分析、語義分析和中間代碼轉化經常是同一個步驟

接口一:

源碼處理接口 —— 詞法描述
功能 : 檢查詞法錯誤
輸出 : 有效部分的詞流

接口二:

語法接口 —— 語言形式文法描述
功能 :供編譯器進行詞法分析、語法分析
輸出 :語法樹

接口三:

中間代碼轉化接口 —— 中間代碼描述
功能 : 將語法樹轉化為中間代碼形式
輸出 : 中間代碼流(以樹型結構比較好)

接口四:

優化接口 —— 優化行為描述
功能 : 對中間代碼進行優化
輸出 : 優化後的中間代碼流

接口五:

目標代碼轉化接口 —— 目標代碼描述
功能 : 生成具有相對地址的目標代碼
輸出 : 相對獨立的目標代碼模塊

接口六:

連接接口 —— 連接規則描述
功能 : 將各獨立的目標代碼連接成一個完整的可執行文件
輸出 : 可執行文件

如此劃分的出發點是盡可能的劃分各模塊的界限,而且理論上是可行的,只是現在都趨向於一次編譯解決問題,使得認為的模糊了界限
現在劃分界限的結果肯定會使得編譯器的執行速度比以前慢些,但從組件化的優勢考慮是值得的

2)關於組件化本身的探討

組件化是一個公認的好趨勢,但存在如下缺點(個人意見)
A)必須有操作系統支持才能實現組件化
B)一旦系統的注冊表崩潰,則所有的組件都將不識別,即使它還存在於硬盤,從龐大的硬盤中手工找出它們然後注冊似乎不現實
C)組件是一種趨勢,但並非是操作系統必須的形式,作為編譯器應該考慮不支持組件的系統
另外有一個重要的想法是:
如果能實現編譯器內部自身的組件形式會更方便,即不依賴操作系統的組件方式,從現有的組件化的發展歷史就能看出內部組件方式很早就

有了,而不是新的內容
匯編語言設計中最體現內部組件的方式是它的指令指針的自由性,可以將指令指針指向任何的區域,所以,很多模塊可以按數據的方式讀入

內存,然後指定好代碼段、數據段、和棧位置,就可以直接將指令指針跳轉至該數據區,使它們以代碼方式執行
在DOS階段,就存在一種高級語言的實現這種匯編級功能的格式,給程序段添加一個額外的代碼管理模塊,允許程序執行中自由載入和卸載模

塊,這就是最早的DLL形式,只是不由操作系統管理
到了WINDOWS階段,這些形式就開始向DLL變化,但DLL是由系統管理的,因為在進入圖形化操作階段,一般的開發群體對操作系統的核心了解

沒有DOS那麼深入了,而且系統過於龐大,已經很難使用以前的技巧
然後就是組件概念的出現,使得模塊開發成為一種可統一重用的方式,本質上依舊是將以前用戶自己管理改變成系統管理

這種趨勢總體是好的,但並不應該是全部,以前使用的內部組件方式有它的優勢,就是穩定,而且無須平台要求,這個優勢是現在的組件無

法替代的

而作為通用結構開發的編譯器結構,如直接使用組件方式,必然使得很多操作系統不能支持,但組件化是主流,不支持是結構上的落伍
有如下方案供參考:
基礎:編譯器的特殊性,它自身存在一個連接器,使得編譯器本身的代碼結構可以更好的根據開發者的需求自由組合
編譯器自身以內部組件的方式構成,對外提供一個外部組件定義接口,在有組件功能的操作系統中,允許外部組件一定義的接口來替換原有

的內部組件
內部組件,本身和一般組件的定義可以是一樣的,不同的是由編譯器自己管理,而不是由操作系統管理,這樣在提供組件功能的操作系統中

,可以將內部組件直接以外部組件的形式出現
這種折中方式的實現可以有很多種,可建立在不同級別上,如:源碼級、中間代碼級、目標代碼級
由於編譯器本身就是生成最終代碼的工具,因此可以完成對自身代碼的動態修改,只要將足夠的內部信息獨立保留即可

3)編譯器組件化結構

組件結構的基礎是擁有一個整體框架,以這個框架為中心將各種組件連接在一起完成工作目標
對於編譯器來講,其數據流是最好的框架分析線索,前面敘述的那種理想描述可以作為一種結構
框架本身與應用是無關的,甚至連各模塊的接口細節都不一定要知道,只要知道知道一個連接過程即可,因為在編譯器結構中是一種一條線到底的

流水作業,出口和入口都是唯一的,中間最復雜的是所有模塊共享的數據結構的處理,如符號表、錯誤輸出、關聯表等等很多數據

在此引入數據庫存儲方式,要求程序內部以數據庫形式存儲,為和傳統形式兼容,可以按文件方式將工程由數據庫中導出,但最好以數據庫中

內容為主,然後提供一個更新工具,為那些習慣於文件編輯的人將在外編輯的代碼更新到相應數據庫位置
這裡有一點很重要,開發者不必知道實際存儲方式,在編輯界面中看到的還是一個個文件,而外部同樣存在一個工程目錄,把所有代碼以文件

方式展現(是從兼容角度考慮),所有的更新工作有開發工具自動完成
內部以數據庫方式存儲有如下優點:
A)為編譯器各模塊提供一個統一的輸入輸出接口
B)實現編程期間的同步前期編譯,如詞法和語法分析,直接在數據庫中保存分析結果(代碼獨立保存),提高實際編譯速度
C)由同步分析得到的數據,可以使得代碼視圖能同步實現(這點在視圖分析中詳細討論)
D)為同步文檔設計留下了自由發揮的空間
E)對於分布式整體設計,提供統一的接口
F)能夠系統地保存開發期間各種版本
G)可添加級別保護,便於進行開發人員的管理

關於數據庫的設計:
A)按標准的方式設計當然最好,或者使用現成的數據庫(局限性太大),只是工程量比較大,標准數據庫設計可是另一個大項目
B)簡化方式,先以最簡單的方式實現,即使是文件方式也可,重點是分析出一個和開發工具的標准接口,即按組件的方式定義出標准接口

,這樣可以逐步升級數據庫的設計
C)接口分為內部和外部兩種,外部接口用於直接和商業數據庫連接,將存儲外包,內部接口是一種編碼上的自定義,用於和自己設計的數據

庫連接,這樣能保證沒有數據庫的操作系統也能使用,有好的數據庫的操作系統可以使用高性能產品

數據庫的析和開發工具的設計是兩個完全獨立的范疇,只是一種使用關系,不是重點,准備放在全文末尾部分
而數據庫和開發工具的接口要等到所有開發工具模塊的細節分析結束,總結出一個相對完善接口定義,每個模塊相對獨立,均有一個相對獨立

的接口定義,准備在視圖分析中總結各模塊接口

4、預處理(簡單了點)
在面向對象編程中,由於類結構的引入和消息機制的使用,使得編程中很多操作細節被屏蔽了,而在編譯前,必須恢復其真實面目,由此出

現了程序的預處理,將面向對象代碼轉化為過程方式的代碼,硬件體制還是只認識過程方式
這一步是在進行正式編譯前完成的(也可和詞法分析一同進行,個人認為是的),相對獨立,且屬於面向對象范疇,將在類庫設計中討論

二、調試器分析


調試器 ——屬於白盒測試的范疇,是一種有目的的測試,即:知道了錯誤現象後,基於源代碼級別的動態測試過程

調試器的目的是解決已知的錯誤,而不是測試那樣為了找錯誤,因此與白盒測試略有不同

和編譯器不同,調試器沒有強大的理論背景,只有一些單純的技巧組合,本身功能與硬件結合密切

在此僅從個人思考的角度分析各種調試情況,在後面視圖分析中,將引進圖論中的概念進行深入分析,也算是一個小的理論基礎吧

調試器是按一定的規則向源代碼中添加一定調試代碼(調試框架代碼),並結合使用者描述的調試內容(單步、斷點或條件)生成實際調試代

碼並嵌入源碼中,經編譯器生成可執行代碼,在最終執行中由內嵌的調試代碼向調試器匯報各種數據,由調試器展現給調試者判斷
這就是現有開發工具中使用的調試方式,基本上本文也是按此法進行分析

1、調試器實現方案
基本有如下五種方案:仿真器、純軟件式調試器、純硬件調試器、結合硬件的軟調試器、結合軟件的硬件調試器

1)仿真器
仿真就是用一個系統的功能模仿另一個系統功能

調試功能僅是仿真器的一個簡單的功能,屬於舉手之勞,一般我們常用的開發工具不屬於這種范疇

仿真器可分為兩種:軟件式仿真器和硬件式仿真器

軟件仿真器 —— 以軟件方式描述硬件設備,包含機器代碼的解釋器

硬件仿真器 ——以功能強大的硬件設備來模擬功能較弱的硬件設備,如單片機仿真器
大型機中虛擬主機的概念是一種結合特定硬件的軟仿真器

2)純軟件式調試器
不使用硬件功能,通過純軟件方式調試可分為兩種:解釋器方式和單純代碼嵌入式調試技術

解釋器 —— 用於開發語言編寫的代碼,采用解釋方式執行代碼,速度會低很多,但有很多控制上的優點

單純代碼嵌入技術 —— 直接以源碼的編寫語言將調試指令內嵌,但不使用硬件系統提供的調試功能,采取通訊方式和調試器連接

3)純硬件式調試器
依靠獨立的附加硬件設備從總線中提取運行數據進行分析
缺點是:當系統使用高速緩存時,無法從總線獲取真實信息,因為CPU直接從緩存獲取數據,外部設備無法正確判斷
4)結合硬件的軟調試器
常見的開發工具如VC、VB、DELPHI等等,都屬於這種調試類型即結合主機芯片中提供的調試功能,實現高效率的調試
如單步方式、斷點設置等都是硬件級功能,由CPU的來實現

5)結合軟件的硬件調試器
如CODETEST,從《2001嵌入式系統及單片機國際學術交流會論文集》中發現的,是一篇深圳華唐公司的員工寫的論文,讓我了解到很多,差

點想去仔細分析這種工具了,不過對於硬件還是入門水平,以後有機會再說了
這種調試方式,是吸取了純硬件調試受到高速緩存的影響的缺點,要求在源代碼中內嵌很簡單的指令,而這些指令的執行和調試硬件直接相

關,使得硬件調試繞過緩存障礙,真實的從總線中獲得數據,同時具有硬件調試的高速性能

本文的主題是開發工具的設計,因此只討論軟仿真器、純軟件式調試器和結合硬件的軟調試器

2、簡述軟仿真器、純軟件式調試器和結合硬件的軟調試器


軟仿真器和純軟件式調試器有一個交集,即源碼級的仿真器和純軟件式中解釋器是同一概念

1)
軟仿真器可以按對硬件細節描述的深淺程度來分出多種層次

如高級語言解釋級、匯編語言解釋級——只需要簡單模擬操作系統的API結果即可

深入模擬級——模擬中斷機制、I/O調用模仿、BIOS模仿(直接對機器代碼進行解釋)

更深入可以是芯片級模擬——芯片接口、總線仿真等等

可以通過逐步求精的方式一步步仿真出一個完整的硬件系統

2) 純軟件式調試中的解釋器方式同上描述

其單純代碼嵌入技術——其實就是每個開發者慣用的方法,如多寫幾個打印語句或者是顯示語句,就是一種
本質是由開發者自行添加調試代碼

此方法中的調試代碼屬於源碼的一部分,本身也是被調試的范疇,一般因為其使用語法比較簡單(以打印語句為主),較少成為故障點

當然,我就碰到過這樣的事,是在UNIX下C的調試經歷,采用UNIX中的字符屏幕顯示技術,將需要的調試信息直接顯示在屏幕中的固定位置,但

在消息比較長時,超過4K時,系統屏幕顯示的緩沖區溢出,導致COREDUMP,這個小錯誤開始總被忽略,因為多數信息都很短,以至出錯時,一

時還沒注意它的問題

所以單純的代碼嵌入調試技術的缺點就是——本身也是被調試的范圍,也是故障點,完全由開發者自己控制,容易使用不當

其優點是直接性,如果加以系統控制,就能形成好的調試工具(但犧牲效率)
以軟件形式的模擬硬件調試功能,如實現軟件單步方式、軟件斷點方式,而可以由調試器從整體出發生成對開發者不可見的代碼,加入源碼中

,一起編譯執行,細節在後面的“基本調試功能分析”中討論
存在的缺陷是:自動生成的調試代碼可能和源碼一樣多,1:1甚至更多,使得調試中軟件運行的速度降低很多

3)結合硬件的軟調試器
硬件提供的最基本的調試功能是單步方式和斷點設置,通過產生軟中斷由軟件執行態切換到調試狀態,但容易被軟件中一些特殊設計干擾
如基於INTEL的芯片的操作系統中的軟件如果修改了相應的調試軟中斷INT03等,就會使這些調試手段失效,在DOS時代這是一種比較好的反跟蹤

手段

更好的硬調試功能是提供硬中斷,如INTEL386以上級別的芯片中的調試寄存器DR0~DR7,將需跟蹤的地址存入這些寄存器,軟件運行到該地址

時,由系統激活硬中斷,這種方式是無法反跟蹤的


4)一些特殊話題

三種軟方式均有自己的特點,假如有三個調試器A、B和C分別代表軟仿真器、純軟件式調試器和結合硬件的軟調試器

如何實現A、B和C之間的互調?

這個問題的實質是如何實現解釋器與其他調試器的的互調

本文重點討論的是結合硬件的軟調試器

3、基本調試功能分析

因為還沒有分析過真正的調試器源碼,如GDB,所以基本是根據個人的推理得出的結果,有待考證


調試的基本技術有四種:單步、斷點、條件、斷言

除斷言外均為從硬件角度引出的調試技術

單步——顧名思義,就是讓程序按照源代碼中的語句順序,一句一句執行,在匯編中則幾乎和一條一條機器代碼執行相近

斷點——即當程序運行到斷點設置處就停止運行,將權利轉交給調試器,是最常用的方式,通過編輯器在某源碼行上打上標記,由調試器來安

排運行中進入斷點

條件斷點——是一種比較復雜的斷點方式,由調試者給出一個表達式,當程序運行至表達式成立時,激活斷點,切換至調試器,難度在於事先無

法判斷斷點位置,只能根據條件表達式判斷出斷點的分布位置


斷點從設置方式分兩種:靜態斷點和動態斷點

靜態斷點設置的代表是VC,在VC中斷點是在編譯前設置的,調試中不能修改和添加,所以稱為靜態斷點

動態斷點正好相對,可以在調試過程中自由添加和刪除斷點,代表是DELPHI等工具,象VB那樣的工具甚至能動態修改運行的“代碼指針”,畢

竟是在解釋狀態中,自由度比編譯型的大了很多
兩種斷點方式在實現上明顯有難度差異

靜態斷點的實現很方便,再多的斷點,都能通過在斷點設置位置前直接添加相應的斷點中斷指令,就能完成(好象有點濫用中斷的嫌疑,希望

實際代碼中能找到更方便的實現方法),然後經過編譯便可完事,

當然通過函數入口激活也比較可行,比如調試器在有設置的斷點前,添加一段照硬件斷點設置方式設置斷點的代碼,這樣在一些中斷使用受限

制的系統中比較方便,如在類UNIX系統中比較可行

動態斷點的實現難度很大,需要從調試結構來考慮

類似的條件斷點也可分為:靜態條件斷點和動態條件斷點
條件斷點,首先得由調試器分析,分解成基本元素,一般現有的調試器中描述的條件斷點都比較簡單,或者是多個復合,而非一個條件中有多種元素

組成,因為過多元素會導致為判斷一個條件而設置太多的斷點

而斷言本身就是一種純軟件的調試方式,典型代表是VC的ASSERT的使用,是一種函數庫提供的調試函數
關於斷言的理論在《面向對象系統的測試》(人民郵電出版社)一書中由專門的一章來討論,在此就簡單描述了,因為是純軟件方式,在實現技

術上就沒有硬件的限制,不存在技術難度

對於這些調試技術分三種方式討論:純軟件方式、單步陷阱與INT3方式和調試寄存器方式,均以INTEL芯片為討論對象,其它類型的芯片沒有資

料,准備在GDB分析中仔細考察

無論是那種方式,都是建立一種被調試程序和調試器的一種通訊方式,以此通訊形式來完成調試過程

1)純軟件方式

此方式使用在如下情況中:對硬件系統未提供調試功能或開發者對此技術細節步清楚的時候,屬於純軟件方式的內嵌調試技術
分兩種表現形式:顯示表達和隱含表達
顯示表達——編程者可以看到調試器內嵌的調試語句,但應當示不可修改的
隱含表達——不顯示,直接在內部嵌入並完成編譯,對開發者而言是不必了解的細節

設計的前提是增加的代碼能做到不干涉源代碼的的數據和整體結構
缺點是:內嵌的代碼量極大,至少和源代碼一樣多

首先是建立一種進程通訊方式,可以為任意一種操作系統提供的進程間通訊方式,提供一個切換函數,一旦被調試程序運行了此函數,就被掛

起,將權利移交給調試器,直到調試器發出進一步指令才繼續工作
由此需為源碼添加一段通訊用的代碼,往往量比較大,可以使用獨立文件單獨列出,而切換函數就是它為調試提供的主調用語句
由於添加新代碼,必須在編譯之前完成,運行中是無法添加的,但並不說明這種調試方式是靜態方式,它可以成為動態,但是建立在軟件單步

上的動態,這樣內嵌的代碼量是最大的

軟件方式的單步:
在源代碼中的每一行前嵌入一條切換語句(即調用切換函數)
軟件方式的斷點:
只在斷點那行前嵌入一條切換語句
軟件方式的條件斷點:
首先由調試器分析軟件斷點的表達式,提取相關的基本元素(如變量名,函數名等等),在源碼中搜索出所有相關元素的位置,然後在這個

位置的前後都添加入條件斷點的表達式和一條切換語句,僅當條件成立時才使用切換語句
因此,須對表達式有一定的約束,必須為自包含的,不能影響表達式之外的代碼語句

2)單步陷阱與INT3方式
單步陷阱是由CPU提供的一種調試手段,設置單步陷阱後,CPU執行一條指令後就產生一個單步中斷,它是指每條機器指令一次單步,而非編程

中的一句源碼的含義
INT3方式是指在INTEL芯片中指定由第三號中斷來實現斷點調試,而INT3的中斷向量中具體處理方法由用戶自行定義,而且INT3指令是一條僅占

一個字節的指令,使用很方便,所以INT3是使用非常普遍的調試技術

單步:
由於單步陷阱是按機器指令來計算的,不能直接使用在程序單步上,要修改的是單步激活的中斷處理,使其能按指令地址判斷是否到了程

序單步的位置,由這樣一個判斷方法,每次單步前,中斷中記錄的是下一步可能去的所有分支地址,然後在每次機器指令單步激活後和指令地

址比較,就能完成判斷了
所以單步的實現是比較簡單的,而且在匯編調試中就更方便了,基本是一對一的,對中斷的修改很少

INT3斷點:
使用INT3方式的斷點,是一種動態斷點方式,當然任何靜態斷點的方式都能以動態方式實現
對靜態方式的斷點:首先在編譯中記錄斷點在源碼中的位置和在目標代碼中的起始代碼地址,將起始代碼直接換成INT3指令,將被替換位

置的數據和位置地址獨立保存,由調試器將這些數據通知給INT3中斷處理程序,在運行中,執行到斷點位置則激活了INT3中斷,比較地址即可

判斷出中斷位置,並將原指令寫回,並切換到調試器,等後下以步指令,當下以步指令為繼續執行時,得先設置單步標志,這樣執行完第一個

後繼指令後,就進入了單步中斷,這裡由一步很重要,就是將剛才修改的INT3位置恢復,保證下一次中斷正常完成
這種是靜態方式,也可由動態方式完成
動態斷點方式:記錄下編譯前設置的斷點位置,但不做任何代碼改動,只是將這些設置和後來的動態斷點地址看成同樣的斷點,在被調試

程序載入內存,准備執行時,才進行修改,同樣將地址中的原內容保留,替換成INT3指令,後面的操作和靜態方式下的操作一樣(同樣要單步中

斷的幫助),對於運行中設置的斷點,由調試器通知INT3處理程序,因為設置時被調試程序處於等待狀態的
可能出現的問題:
A)由於目標代碼一般是相對地址,要在執行前重新定位的,這大概就是,現有調試軟件,都由一個載入命令,以控制方式執行被調試

程序,獲得高的控制級別,便於讀取實際地址,以和源代碼位置相對應
B)在進入匯編級調試中,INT3方式是通過修改指令完成的,因此對進入BIOS代碼的調試是無法完成的,這點在調試寄存器出現後得以解


C)INT3是指令方式的斷點,所以無法實現對數據區的斷點設置,調試寄存器可以實現
D)容易產生沖突,比如那些反跟蹤的軟件就可利用INT3中斷的可重定義特點,在代碼中加入了修改INT3中斷處理程序的功能,這樣使用

INT3跟蹤會導致失敗

條件斷點:
以INT3方式可以實現一些簡單的條件斷點,比如簡單的變量比較,但實現過程很繁雜
首先是條件分析,在編譯中為調試器保留語法樹和與目標代碼的對照表,運行中,用戶描述了一個條件斷點,根據對條件的分析,得出條件的基

本量,根據編譯時留下的圖表,搜索出所有和條件相關的位置
然後在這些位置設置INT3斷點,根據前面的方法即可,只是在中斷處理中判斷條件是否成立,以識別是否進行切換
顯然是一個非常復雜的實現方式


3)調試寄存器方式
在INTEL386之後的芯片中都增加了調試寄存器,使得調試能力大大的增強,因為這是硬件調試手段,不存在那樣被反跟蹤破壞的可能,而且能直

接將斷點定位於數據區或不可寫的ROM區
最近查遍了書店,發現相關書比較少,有一本清華大學出版的《80X86匯編語言設計》中有很詳細的介紹,還有相關匯編代碼的簡單例子,而

在INUX下的書是《LINUX2.4版源碼分析大全》(機械工業出版社)中有較詳細的討論
先簡要的介紹一個這些調試器:
名稱是DR0~DR7,其中DR0~DR3可用於保存斷點地址,DR4、DR5為INTEL保留,DR6是調試狀態寄存器,DR7是調試控制寄存器(具體資料以後

會在文中補上)
可實現的功能:
A)DR0~DR3中能保存的地址是任何有效地址,可以是全局地址或局部地址(由TSS任務模式產生的差異)
B)激活條件是相應地址被讀、讀寫或執行,產生INT1,由此可以將斷點設置在數據區和ROM區
C)調試器修改受兩種方式的保護,訪問設置位GD,GD=0時只能在實方式或特權級0時由訪問調試寄存器,GD=1時任何訪問都產生異常,激活INT1

中斷

應用:

由於可設置地址的只有四個寄存器,大規模使用存在數量沖突,因此在SOFTICE中都限制為四次
不過對於非匯編級的高級語言調試中,可以較好的使用它,在一定程度上突破數量限制,原因在於,直接的機器代碼中難以精確判斷出函數的

界限,而這點在高級語言編程中是明了的事
通過編程中函數設計而產生的天然界限,可以靈活的在全局變量和局部變量之間分配四個寄存器
設計數據區的斷點(包括條件斷點)最好的方法是使用調試寄存器,而數據區直接和代碼中的變量相對應
當要設置斷點的全局變量為四個時,顯然沒有任何回旋的余地了,同樣,想在同一個函數中設置同范疇內的四個局部變量為斷點,也時沒有回

旋余地的
能有發揮空間的是數據斷點(也可以是代碼斷點)分別屬於不同的范疇,而且每個范疇中的斷點在同一段時間內出現不超過四個,所謂同一段

時間是指一個函數的范圍(或者函數中的模塊范疇,針對函數中小范圍中定義的局部變量而言)
在這種情況下,可以通過和單步與INT3的合作來靈活分配四個調試寄存器,顯然操作比較繁雜,但理論上並不復雜

例如:
在一個函數內有四個局部變量,想考慮其條件斷點,使用四個調試寄存器跟蹤這些變量的讀寫,可以很方便的考察條件是否成立,而在進

入函數前是沒有使用調試寄存器的必要,因此使用INT3斷點方式在函數入口處設置一個斷點,當該函數被調用時,就直接進入INT3中斷處理,此時

將條件斷點進行設置,使用調試寄存器,就比較合理了,如此可以在進入之前將調試寄存器用於其它需要的地方了,
有一點就是,在函數出口是否放置斷點?有必要,但存在一定困難的,函數出口是不唯一的尤其在有消息機制的系統中,如果不修改會導致

全局變量上的斷點設置出現問題,這裡存在一個變量生存范圍的概念,如何判斷是否出界,有一個笨的方法是,在編譯中,為每個函數的出入口都添

加INT3斷點,不是對現存指令進行修改,是添加,得包括對函數的調用前後的設置,因為可以調用庫函數和系統函數,那裡去加入INT3斷點不是很好

,之在必要的時候才這樣做,而函數運行范圍判斷中還不需要這樣,而消息機制在編譯中也是轉化成過程方式的,所以是同理

4)斷言
(基本內容摘自《面向對象系統的測試》)
斷言是一種內嵌的測試機制,供開發者將測試語句設置在需要的地方,
一個可執行的斷言包含三部分: 謂詞表達式、動作和允許(ENABLE/DISABLE)
格式一般為: ASSERT(~)
斷言的用途:
A)檢查特定於實現的假設
B)檢查在方法的入口必須為真的條件(前置條件)
C)檢查在方法的出口必須為真的條件(後置條件)
D)檢查在任何時候對於一個對象必須為真的條件(不變式)

(以下是個人的分析)
一般認為斷言應該是顯示表達的,為開發者主動使用,這樣就將斷言使用限制在測試范圍了,而它完全可以成為一種特殊的條件斷點,用於調試

中,由調試器統一安排
現在由開發者使用的斷言,均為人為維護,關鍵是在編譯中使用編譯開關,將斷言開啟或關閉,而且很多情況下斷言隨著正式產品一起發布了,

因為很多時候,關閉了斷言會導致一些錯誤在運行中被發現而無法定位了, 這樣做的情況使得斷言維護變得困難,編寫者常常會將一些自己寫的

斷言的用途都忘記了(可以使用注釋),但如果由系統統一管理顯然就方便了
在此將斷言分成顯示斷言和隱型斷言兩種
顯示斷言是由開發者自己嵌入的斷言,但有系統自動統計,對於這些斷言的注釋,應當在文檔中記載,而不僅僅是在程序中加注釋語句(這點將

在文檔部分詳細討論)
隱型斷言是指由調試器提供的斷言,可以是嵌入式,也可以是斷點方式的斷言,定義這種斷點的理由是前面寫的斷點的四個用途
從斷點的四個用途中可以明顯看出,後三種方式都有固定位置(全局也是一種固定位置),如此情況下,由調試器完成就很輕松了,而用戶所要做

的是描述斷言(條件),由此調試器進行分析,並生成相應的調試文檔,然後結合INT3和調試寄存器實現這些隱型斷言
同樣隱型斷言也可以是內嵌方式,在硬件受限制的條件下,就能這樣做,在編譯的時候添加隱型斷言(以ASSERT語句方式的斷言),由此,也可得

出這樣一個結論,隱型斷言也可以顯示出現,只是這些斷言不能讓用戶修改,但可以看見(修改也是可以的,只是調試器須跟蹤記錄用戶的相關修改

生成相應的文檔)
總的講就是將各種斷言接受系統管理,而不是以簡單的編譯開關來完成管理,從視圖角度考慮,斷言整體也是一張調試圖,可以從整體角度給予

新的分析,在視圖中進行分析


未完代續

4、基本調試器的框架
(與編譯器的約定、嵌入代碼的設計規則、調試器的結構)


5、一些額外的討論


[email protected]


Copyright © Linux教程網 All Rights Reserved