藤
斎
茅再
彦
Sapidに よるソフ トウエア解析技法 工
― ソフ トウエア ・メ トリックスの作成 一
は じめに
Sapid(wwwe Sapid.org/)は 下流 レベルの ソフ トウェア解析 を効率 的 に実
現す るソフ トウエ アエ学 ツ
ール ・プラッ トフオームであ り,ソ フ トウエアの生
産性や品質の向上 を目的 としたプログラムを簡単 に作成す ることがで きる。本
論文では,ソ フ トウエア品質管理
・評価の手法のひとつであるソフ トウエア ・
メ トリックスを求めるプログラムを開発す る。
ソフ トウエア ・メ トリックスは,ソ フ トウエア品質管理,品 質評価の手法 と
して確立 され,開 発現場で も導入 されている。 しか し,実 際 にISOや」
IS等で
提案 されたメ トリックス に基づいたソフ トウエア評価 を行 うためには,構 文解
析等の手法 を用いて,メ トリックス評価 プログラムを作成す る必要がある。構
文解析系 も含 んだプログラムを作成することは容易ではな く,開 発 に多 くの時
間が必要 とされる。
本論文では,Sapidを 利用 して各種 のメ トリックス を求めるプログラムを作
成する。オープンソース として公 開されている,多 くのソフ トウエアのメ トリッ
クスを解析 し,結 果 を統計的に処理 して,メ トリックスに対する評価や比較の
指標 となるデー タを提供す る。オ
ープンソースの解析のために,オ ープンソー
ス ・ソフ トウエアの細粒度 リポジ トリ ・データベースを提供す るア
ーカイバ ・
サ イ トSappy(―
.aichi一
pu.ac.jp/sappy他
)の デー タを利用す る。
第 1章 で関数 とパ ラメー タの名前,型 を求めるプログラムを作成 し,第 2章
第 3章 で関数のメ トリックス情報 を求めるプログラムを作成する。第 4章 でそ
の他のメ トリックス情報 を求め,第 5章 でメ トリックスに関するデ
ー タの活用
彦根論叢 第 328号
I 関 数 とパ ラメータの名前,型 を求め る 一 オブジェク ト配列 の利用
「
Sapidに よる ソフ トウエ ア解析技 法 I」 体 は,関 数宣言 ,関 数 オプシ ョナ
ル宣言 と言 った ク ラス のID番 号 を求 め る方法 と して, ト ップ レベ ル (プログ
ラムID)か
ら関連 をた どる方法 (spdGetRe10明
関数群 ),オ ブ ジェク トの包含 関
係 を利用 す る方法 (spdCetlncludedOcc関
数群 )を 解説 した。
ここでは,オ ブジェク ト配列 (型 i SpdOttldArray)を
用 いて該 当す るオブジェ
ク トを求 め る方法 を用 い る。 関数 spdCetOttldArrayによ リオブ ジェ ク ト配列 を
求 め,繰 り返 し制御 (for文な ど)を 用 いて順番 にオブジェク トIDを 求 め る。i
番 目の オ ブ ジ ェ ク トIDは ,オ ブ ジ ェ ク ト配列 変数 の構 造体 メ ンバ変数id L]に
格納 され てい る (例 :血 nc_array.id□
)。
1口 例 題 1 関 数 宣言 オ ブジ ェク トー覧 をも とめ る
関数宣言 オブジェク トー覧 を,オ ブジェク ト配列 (クラス名 :dedaratOn,ソー
トDECL_FUNCDEF)と
して取得 す る。 関数 spdGetObildArrayに
求 め る ク ラス
名 (declaration)を
パ ラメー タ と して ォブ ジ ェ ク ト配列 を求め る。fOr文で順番
にオ ブ ジ ェ ク トIDを 取 り出 し, spdCetAttrV211nt関
数 でsortが関数宣言 (DECL
FUNCDEF)の
もの を取 り出す。
① オブジェク トIDを オブジェク ト配列から求める 一 spdGetObildArray
E C T ―A R R A Y n declaration ← DECL FUNCDEF
② 関数プロトタイプ
オブジェク ト配列←spdGetObildArray(ク
ラス名, 名
前1 条
件)
SpdObjldArray SpdStringi SpdStringi SpdCondObjldFunc
③ 関数プロトタイプ
func_array=spdGetOttldArray(‖ declaration‖
, NULL, NULL);
隅
ト
ー
ー
ー
ー
ー
ー
ー
ー
Sapldに よるソフ トウエ ア解析技法 コ
2.関
数の名前 と型 を求める
関数宣言 のオブジェク トIDか ら,順 番 に関数,パ ラメ
ー タの名前 と型 を求
める。関数の型の クラス typeは関数の宣言 declaration(属
性 :関数)か ら関連
deCI」
ype(declaration_type)を
た どって求める。 またクラス identiner(名
前)か ら
関連 問ent type(通
entiner_type)で
求めることもで きる (図I参 照)。
パ ラメー タは ク ラス optionaldecl(オ
プ シ ヨナ ル宣言 )か ら,関 連 func parm
(optionaldecl_declaration)を
た ど り,パ ラメー タの宣言 declaratio■
(属性 :変 数)
を順番 に求 め る。 そ こか ら名前 (クラス identiner,関
連 :decLident)と型 (ク
ラス :type,関 連 :deci」
ype)を 求 め る。
④ サ ンプルプログラムI 一 関数名と型を求める
↓オ ブ ジ ェク ト配 列 の 宣 言 S p d O b j l d A r r a y f u n c _ a r r a y ; i n t l ; ↓オ ブ ジ ェ ク ト配 列 の 取 得 func_array=spdGetObjldArray("declaration,,NULI,NULL); fOr(1=0;1く func_array.size;1++)r(3pdGetAttrValnt料
島拐ざ
鉢
ぷ望督あ
ずゲ予
:め
│千
]事
甚
督ぴ)
decr_id=8PaCetARe10bj(func_array.id【
11,"deCl_decl'',巾
anyⅢ
);
│
I一model
関数定義 main
↓有le_decl
↓func_body
パラメータ
パラメータ
argv彦根論叢 第 328号
サ ンプル プログラム I(続 き) 一 関数名 と型 を求める
ident_id = spdGetARe10bj(decr_id,・ decl_ident",Wanyり , func_name= spdGetName(ide nt_id);
type_id= spdGetARe10bj(ident_id,"ident_type",nany"); printf("FUNC_NAME=%‐ 308 FUNC_TYPE=",func_name); getTypeName(type_id);
3口 型の求め方
型の宣言には,int(整 数型),char(文 字型)と いった属性のほかに,const,
volatileと
いった修飾子,longな どのサイズ指定子, signed,unsignedと いっ
たsign才
旨定子 などがある。 また,ポ インター型,配 列,構 造体,共 用体,enum
型 とい った複雑 な型,ユ ーザ定義の型 もある。Sapidはすべての型の宣言 に関
す る情報 を保持 している。本 プログラムでは整数,文 字型 とポインター型,配
列型のみ を扱 う。
ク ラ ス 型 t y p e の 属″性 s o r t が T Y P E _ D E C L A R A T 1 0 N の と き , 関 数 g e t T y p e N a m e B a s i c によ り, 型 の 名 前 を 標 準 出 力 に 出 力 す る 。 関 数 g e t T y p e N a m e は , ク ラ ス 型 t y p e の 属 性 s o r t を 調 べ , T Y P E _ P O I N T E R ( ポ イ ン タ ー 型 ) の 場 合 は そ の 情 報を出力す る。関連 t y p e _ s t で
続 くt y p e オ
ブジェク トを求め, 関 数 g e t T y p e N a m e を
再 帰 的 に 呼 び 出 す 。
T Y P E t t R R A Y ( 配 列 型 ) の と き も同様 で あ る 。
T Y P E _ D E C L A R A T 1 0 N の
ときは関数 G e t T y p e N a m e B a s i c を
呼び出す。
関数 GetTypeNameBasic:型を求め る関数 (基本型)
char t getTypeNalneBasic(SpdObjld type
if(3pdGetAttrVallnt(type_id,"80rtり =TYPE_DECLARATION) switch(8PdGetAttrVallnt(type_id,巾 typeり)( cage TYPE_INT: return"INT";break; case TYPE_VOID: return"VOID巾 ;break, case TYPE_CHAR: return tCHAR";break; case TYPE_DOUBLE: return・ DOUBLE"ibreak; case TYPE_FLOAT: return"FLOAT";break; case TYPE_DOTSi return m.…Ⅲ;break; c a s e T Y P E _ U N K N O W N : r e t u r n ⅢU N K N O W N Ⅲ ; b r e a k ; return,1; default:
Sapldに よるソフ トウエ ア解析技法 Ⅱ
4 . パ ラメータ宣言の求 め方
パ ラメー タ部分 の宣 言 は optionaldecl(オ
プ シ ョナル宣言)か ら,関 連 func_
p a r m ( o p t i O n a l d e c l _ d e c i a r a t i o n ) を
た ど り,パ ラメー タの宣言 declaration(属
性 :
変 数 ) を s p d G e t R e 1 0 明
I n i t 関
数群 に よって順番 に求 め る。 そ こか ら名前 ( クラス
: i d e n t i n e r , 関
連 : d e c l _ i d e n t ) と
型 (クラス i type,関
連 : d e c l _ t y p e ) を求める。
関数 GetTypeNameBasic:型 を求める関数 (配列,ポ インタの場合)
v O i d g e t T y p e N a l l l e くS p d O b j i d t y p e _ S p d O b j l d t y p e _ i d l ;if((type_idl=spdGetARe10bj(type_id,巾 type_st山,Ⅲ allyり)!=SAPID_NON_ID) awitch(8PdGetAttrVallnt(type_id,"80rtり ){ case TYPE_POINTER: printf(“POINTER OF“ ); getTypeName(type_idl)ibreak, case TYPE_ARRAY: printf(“ARRAY OF“ ); getTypeName(type_idl);break; case TYPE_DECLARAT10N: printf(“%s“ ,getTypeNaIIleBasic(type_id l)); break; default: break; }
サ ンプル プログラム I(パ ラメー タ情報取得 まで)
int main(int argC,char targv□) { char ttarget_prOgralII; SpdObjld Prog_id,decr_id,opt_id,param_id,blk_id,ident_id; SpdObjldArray func_array; S p d O c c o p t _ o c c ; S p d C u r s O r t o p t _ c 8 r i i n t l ; t a r g e t _ p r o g r a I E = S m u G e t A P r o g r a m N a m e o ; i f ( ( p r O g _ i d = s p a T a r g e t ( ・I ‐m O d e l Ⅲ, t a r g e t _ p r o g r a m ) ) = = S A P I D _ N O N _ I D ) ( spdErrOrExit("PrOgram¥"%s¥"not found.",target_progra11); } f u n c _ a r r a y = s p d G e t O b j l d A r r a y ( " d e c i a r a t i o n ■, N U L L , N U L L ) , fOr(1=0,1く func_array.oize,1++) i f ( s p d G e t A l t r V a l l n t ( f u n c _ a r r a y . i d 【1 ] , " 8 0 r t " ) = = D E C L _ F U N C D E F ) decr_id=sPdGetARe10bj(func_array.id[1],"deCl_decl","any"); ident_id = 8pdoetARe10bj(decr_id,"decl_ident","anyり ; func_name= 8PdCetName(ident_id);
type_id= 8pdGetARe10bj(ident_id,"ident_typeⅢ ,"anyり, / ★漱 名 前 と型 を 出 力 す る岩 ★/ printf("FUNC_NAME=%-30s FUNC_TYPE=",func_name); getTypeName(type_id); oPt_id=spdGetARe10bj(decr_id,"decl_opt",“ anyり ; if(Opt_id!=SAPID_NON_ID) 【param_id=8pdCetARe10bj(opt_id,・ func_parm","anyり ; blk_id =spdGetARe10bj(opt_id,い func_body",Ⅲany町);
パラメータ
パラメータ
↓
funcparam
彦根論叢 第 328号
int main(int argc, char t argv[])
[解説]
オプシ ヨナル宣言 (optionaldecl)か
ら
↓funcparam:optionaldecl…
declaration
↓decl_decl:declaratiぃ
―
declarator
↓decl_ident:declarator―
identifier
↓ident_type:identifler―
type
と型 (type)を た どる。ポインター型,
配列型 は関連 type_stで
順番 にた どる。
ポイ ンター,配 列 は属性sortで指示 され
る。
図 Ⅱ 関 数mainのパ ラメー タの卜model
サ ンプル プログラム I(パ ラメー タ情報取得 まで 続 き)
H 関 数呼び 出 しを求 め る 一 基本 メ トリックス
ソース コー ドの手続 き的 メ トリックス を,プ ログラム ・メ トリックス と関数
メ トリックス に分 ける。 プログラム ・メ トリックスは関数 メ トリックスのデー
タを集計 して求め る。関数 メ トリックスの基本項 目は,関 数の引数の数,行 数,
コメ ン ト行 数 ,コ ールす る関数の情報 な どの項 目か らなる。関数が所属す るプ
ログラム,フ ァイル,関 数 とパ ラメー タの名前 ,型 は,「Sapidによるソフ トウ
1 )エア解析技法 I」 と第 1章の例題 Iか ら求めることができる。本章では関数呼
び出 しの回数,位 置 といつた情報を求めるプログラムを作成する。
param_id=spdGetARe10bj(opt_id,"func_parm・ ,"anyり, param_csr=spdGetRe10bjlnit(Param_id,"decI_decl","anyり ; w h i l e ( ( p a r a _ i d = s p d C e t R e 1 0 b j ( p a r a m _ C S r ) ) 1 = S A P I D _ N O N _ I D ) ( paraident_id=spdGetARe10bj(para_id,"decl_idellt",Ⅲany"); param_naIIle= 8paGetName(paraident_id), paratype_id=spdGetARe10bj(paraident_id,コ ident_type","any"); printf(・PARAM_NAME=%‐ 30s PARAM_TYPE=",param_name); getTypeNane(Paratype_id);S a p i d に
よるソフトウエア解析技法Ⅱ 7 5
1.例 題 2 関 数呼び 出 しの情 報 を求 め る
あ る関数 の中の関数呼 び出 し情報 を求め る手順 を考 える。関数呼 び出 しは,
関数 中の オブ ジ ェ ク トidentiner(名
前 )の 出現 と して求め る。関数定義部 の名
前 に ア クセ ス しない ため に,I―modelの 中で 関数 定 義 名 (ident血
er)よ り下位
に あ る 関 数 の オ プ シ ヨナ ル 宣 言 を基 点 と して , identier(名 前 )の 出 現
(occurrence)をspdGetlndidedOcc関
数群 で求 め る。順 番 にsortが関数 で あ る も
の を選 び,配 列 に格納 す る。 同 じ関数が複 数 回出現 した ときは,重 複 して処理
を行 なわない。
アル ゴ リズ ム
0 関 数名 を格納 す る配列的nc_idを用意す る (準備 )
1.ク ラス 田entier(名前)の オブジェク ト出現 (occurrence)の取得
2.属 性 i sort(種類 )が 関数 (DECL FUNCDEF)で
あ る もの を選ぶ
3.関 名 を格納 す る配列 を調べ ,新 規 の名前 か どうか を判断す る
4.新 規 の関数 であ った場合 は配列 に格納 し,情 報 を出力す る
関数 getFuncCall(関数呼び出 しの情報 を求める)
ne MAX_FUNC_SIZE 100
void getFuncCall(SpdObjld funcDecl_id) ( SpdOcc func_occ; spacursor tfunc_c8r; SpdObjld func_idIMAX_FUNC_SIZEl,funC_id l; int i=0,j=0, V i s i b l e な関 数 の C a l l を求 め る 手 順 Func_cer=spdGetlncludedOcclnit(funCDecl_id,Widentitter",funcDecl_id); 名 前 の 出 現 を 求 め る w h i l e ( ( f u n C _ 。C C = 8 P d G e t l n c l u d e d O c c ( f u n c _ c s r ) ) . r e l a t i o n l d t = S A P I D _ N O N _ I D ) i f ( s p d G e t A t t r V a l l n t ( f u n c _ o c c . o b j e C t l d , " o o r t り= = I D _ F U N C T 1 0 N ) 【 関 数 の 名 前 の 出 現 だ け 抜 き 出 す w h i l e G く= i ) i f ( f u n c _ i d D + 十] = = f u n C _ O C c . o b j e c t l d ) g O t O E X I T 2 , 重 複 す る 関 数 名 の 出 現 が あ っ た 場 合 は 取 り除 く func_id[il=func_occ.ObjeCtldi
printf("Calling Function Name%d:%s¥n・ ,1,spdCetName(func_id ti〕)),
1 + 十 ;
EXIT2: j=0; 〕 }
76 彦 根論叢 第 328号
サ ンプル プログラム E (関
数 メ トリックス を求め る)
illt main(int argc,char targv口) ( char ttarget_prograal; SpdObjld prog_id,decr_id,opt_id,paran_id,blk_id,ident_id; SpdObjldArray func_array, S p d O c c o p t _ o c c ; S p d C u r 8 0 r t O p t _ c s r i i l l t l ; t a r g e t _ P r o g r a m = s m u G e t A P r o g r a m N a m e o , i f ( ( p r o g _ i d = s p d T a r g e t ( “I ‐m O d e l " , t a r g e t _ p r o g r a m ) ) = = S A P I D _ N O N _ I D ) ( 8pdErrorExit("PrOgram¥"%8¥"■ Ot found.",target…program)テ } f u n c _ a r r a y = 8 p d G e t O b j l d A r r a y ( ・d e c l a r a t i o n " , N U L L , N U L L ) ; for(1=0,1く futtc_array.size,1++) i f ( s p d G e t A t t r V a l I I I t ( f u n c _ a r r a y . i d [ 1 ] , " 8 0 r t り= = D E C L _ F U N C D E F ) decr_id=spdCetARe10bj(func_array,id[11,田deCl_declⅢ,Ⅲany“);
o p t _ i d = s p d C e t A R e 1 0 b j ( d e c r _ i d , " d e c l _ o p t " , 田a n y ・) , │ 関 数 呼 び 出 し情 報 を 出 力 g e t F u n c C a l l ( f u n C _ a r r a y . i d [ 1 1 ) ; 関 数 被 呼 び 出 し情 報 を 出 力 g e t F u n c C a l l e d ( f u n c _ a r r a y . i d t 1 1 ) ; 変 数 メ ト リ ッ ク ス 情 報 を 出 力 getVarNun(prog_id)i
関数 getFuncCall(改良版 )
void getFuncCall(SpdObjld funcDecl_ia)( SpdOcc func_occ; SpdCuraor tFunc_csr, i n t j ; 構 造 体 の 定 義 s t r u c t F U N C { int Func_size; SpdObjld tfunc_idi };
struct FUNC Func; 構 造 体 の 初 期 化 f u n c . f u n c _ 3 i Z e = 0 , f u n c . f u n c _ i d = N U L L i func_csr=spdCetlncludedOcclnit(funcDecl_id,"identiner",funcDecl_id); while((funC_。CC=8pdCetlncludedOcc(funC_CSr)).relationld!=SAPID_NON_ID) if(apdCetAttrVallnt(func_occ,objeCtld,"80rtり==ID_FUNCT10N)( 関 数 の 名 前 の 出 現 だ け 抜 き 出 す 。 f O r G = 0 ; j くf u n c . f u n c _ s i z e ; j + + ) i f ( f u n c . f u n c _ i d L i ] = = f u n c _ o c c . o b j e C t l d ) g o t o E X I T 2 , 構 造 体 を 配 列 の よ う に 使 う方 法 ( 注参 照 ) f u n c . f u n c _ i d = 8 P d R e a l l o c ( f u n C . f u I I c _ i d , s i z e o f ( S p d O b j l d ) " ( f u n c f u l l c _ s i Z e + 1 ) ) ; f u n c . f u n c _ i d 〔f u n C . f u n c _ s i z e l = f u n c _ O c c . o b j e c t l d i 結 果 の 出 力 p r i n t f ( " C a l l i n g F u n c t i o n N a m e % 。¥ n " , spdCetNaEIC(funC,func_id[func.func_size])); func.func_size++; E X I T 2 : ) } } 注 fu nc.func_id=(SpdObjld)malloc(sizeof(S,dObjld)t(func,func_size+1)): で も可
S a p l d に
よるソフトウエア解析技法Ⅱ 77
関数 getFuncCallは
,関 数の呼 び出 し数 を最大100として配列 を宣言 してお り,
対象 とするソフ トウエアの関数呼び出 し数が100を超 えると処理で きな くなる。
そのため,構 造体 を用いて,サ イズに対応するようプログラムを改良する。
2.関 数呼び出 しの複雑 さ
呼び出 される関数 も他の関数 を呼ぶ。その関数呼び出 し構造 をツリー として
表現 し,関 数呼び出 しの複雑 さの情報 を求める。関数の複雑 さは,関 数呼び出
しの総数,呼 び出 しの最大 ネス ト数 といった情報である。
各 関数 ご とに関数呼 び出 しの情報 を配列 (クラス optionalded)に
格納 し,再
帰的にgetFuncCallを
適用する。ただ し同 じ名前の関数が出現 した場合の無限ルー
プの可能性 もあ り,実 用的なソフ トウエアとするためには,こ れを考慮する必
要がある。
関数 getFuncCall(関
数の複雑さを求める)
func_idti]=func_occ.ObjeCtttd;printf("Calling Functio■ NaIIle%di%s¥ュ ",i,spdGetName(func_idti])), オ☆キキヤ十 追 加 部 分 十☆十十☆キ オ プ シ ョ ナ ル 宣 言 オ ブ ジ ェ ク トー 覧 か ら 、 呼 び 出 した 関 数 と 同 じ も の を 選 ぶ f u n c _ a r r a y = 8 p d G e t O b j l d A r r a y ( " O p t i o I I a l d e c l " , N U L L , N U L L ) , f o r ( k = 0 ; k < f u n c _ a r r a y , s i z e ; k キ+ ) i f ( 8 P d G e t A t t r V a l l n t ( f u n c _ a r r a y . i d [ k ] , " 8 0 r t " ) = = D E C L _ F U N C D E F ) ( オ プ シ ョ ナ ル 宣 言 → 宣 言 子 → 名 前 と た ど る d e c r _ i d = s p d G e t A R e 1 0 b j ( f u n c _ a r r a y . i d 〔k l , " d e C l _ O p t " , " a I I y " ) ; i d e n t _ i d = 8 p d G e t A R e 1 0 b j ( d e c r _ i d , " d e c l _ i d e n t " , " a n y り, 関 数 と 同 じ 名 前 ( I D ) か ど う か を 判 断 i f ( f u n c . f u n c _ i d 【f u n c . f u n c _ s i z e ] = = i d e n t _ i d ) 再 帰 的 呼 び 出 し の 場 合 を 排 除 i f ( f u n c _ a r r a y o i d [ k 〕1 = f u n c D e c l _ i d ) ( 再 帰 的 に 関 数 呼 び 出 し情 報 を 出 力
plintf("The%S iS call the functioll¥n¥t",spdGetNane(ide nt_id)), getFuncCall(fu■lC_arrayid[k]);
printf(・%s func list end¥n″ ,spdGetName(ldent_id)); }
3.関 数の呼び出 し位置
関数の呼び出 し位置は,出 現 した関数名 mentinerか
ら,関 連 idenとref(名前一
式 :identiner一
expresslo■
)を求め, spdGetOttet関
数で,オ フセ ッ トを求める。
7 8 彦 根論叢 第 328号
終 的 にオ フセ ッ トを行 ,桁 数 に変換 して実際の位置 を求 め る必 要があ る。 これ
らの情報 はSapidのユ ーテ イリテ イSPIEに よ り取得 で きる。
関数 g e t F u n c C a l l ( 関
数の複雑 さを求める 続 き)
田 関 数の被呼び出 し
第 2章 では,あ る関数 における関数呼び出 し (call)に関す る様 々な情報 を
求めた。第 3章 では,関 数が他の関数で呼ばれる場合 (called)の情報 を求め
る。プログラム内における全 ての関数の中での出現 を調べ る必要があ り,関 数
(クラス optiondded)のオブジェク ト配列の中か ら当該関数の名前 (クラス :
週entiner属
性 i name)の occurrenceを
持 つ もの を選ぶ。同 じ関数が複数回出現
す る場合は重複 を取 り除 く。
アル ゴリズム
1.ク ラス optionaldedの
配列オブジェク トの取得
2.optionaldeclか
ら identinerの
名前が関数名 と等 しく属性 sortが関数であ
る ものを選ぶ
Sp dRel ide nt_rel;追 力田部 分 (宣 言 unc_idrI〕=func_。cc.ObjeCtld
/★者★★韓 追 加 普卜分 (手 順 nt_rel=spdGelARel(funC_id[I】 ,Wident_rer'
offset=spdGetOffset(ident_rel,relationld);
関 数 オ フ セ ッ ト値 ( グ ロ ー バ ル 変 数
ffse t=offset+func offee
p rintf("Calling Function Name%d:%g ofFset=¥ェ Ⅲ,i,spdGetName(func_idril),。 ffse
│
グローバル変数 を求める手順
e x t e m f u n c _ o f f s e t = 0 ; 追加 部 分 ( 宣言 ) int main(int argC,char targv[1)(
SpdRel ident_rel_func; 追力田部 分 (宣言 ) │ f u n c _ a r r a y = s p d G e t O b j l d A r r a y ( " d e c l a r a t i O n " , N U L L , N U L L ) , f O r ( 1 = 0 ; 1 く f u n c _ a r r a y . s i z e , 1 + + ) i f ( S p d C e t A t t r V a l l n t ( f u n c _ a r r a y . i d [ 1 1 , " 8 0 r t り= = D E C L _ F U N C D E F ) 減 ★キ去 追 加 部 分 十★球 ★/ ident_rel_func=spdGetARel(funC_array,id[1],"file_decl","anyⅢ); func_offset=spdGetOffset(ident_rel_func.relationld); │
S a p l d に
よるソフトウエア解析技法Ⅱ
3.当 該 関数 の identinerを
occurrenceとして持 つ もの を選 ぶ
4.新 規 の呼 び出 し元 関数 であ った場合 はエ ン トリ
ーす る
関数 getFuncCalled 関
数の被呼び出 し状況 を求める
IV 代 表的なソースコー ドメ トリックス
その他の ソースコ
ー ドのメ トリックス として,内 部変数 と変数の総数の比,
ループのネス ト数,Cyclomatic数といった情報 を求める。変数に関する情報は,
関数の ときと同 じく,ク ラス 組entinerを
用いて取得す る。プログラムの構造 に
関す る情報 はクラス b10Ckに
関す る情報 を利用す る。
1日 内部変数 に関するメ トリックス
変数の情報 は変数名,型 ,有 効範囲,出 現位置,含 まれる式 な どか らな
る。
変数名,型 は 1章 の関数のプログラムを改良 して求める。内部,外 部,静 的
と
いった変数の種類 はSapidの基本情報 (SDB)に は含 まれない。変数の宣言 を
予。
id print_Calling_FuncName(SpdObjld decl_id){
路嘉転岳
鑑驚
合
群盟
揺品掛
酷撤盤挽
it勁
}void getFuncCalled(SpdObjld called_func_id)( SpdOcc func_occi SpdCursor tfunc_csr; 艶 : 8 群 緊 A r r t t e 柱 ] 品「景話3 孟」d , 血n c 」d t t A X _ F U N C _ S I Z E l ; d e c r _ i d = 8 p d G e t A R e 1 0 b j ( d e c l _ i d , W d e c l _ d e c l " , " a n y り, i d e n t _ i d = S p d G e t A R e 1 0 b j ( d e c r _ i d , Ⅲd e c l _ i d e n t Ⅲ, H a n y Ⅲ) ; f u n c _ a r r a y = S p d G e t O b j l d A r r a y ( Ⅲd e c l a r a t i o n " , N U L L , N U L L ) ;
fOr Kl毬
品
推部:部i絲路
鮒
艦総朝予
&鞘錯棋誉
縦認鮒)
w h i l e ( ( f u n C _ O C C = S p d G e t l n c l u d e d C弧血nに
…的∝dd meぉωt」
引
if(func_array.id[1]1=Calle d_funl print_Calling_FuncName(func_array.id[1]); getFuncCalled(func_arrayid〔11), } } }8 0 彦
根論叢 第 3 2 8 号
参照 して,宣 言 に関する情報 として調べ る。変数の有効範囲や変数 を含む式 を
取得するために, expression,blockを
といったクラスを参照する。
関数 getvarNum i変
数のメ トリックス情報を求める
2 , C y c i o m a t i c 数
プ ログ ラムの複雑 さを計 測す る指標 と して,ブ ロ ックの ネス トの数,ブ ロ ッ
クの数 な どを用 い る。Cyclomatic complexity数はThomas McCabeに
よ り
1 9 7 6 年に提案 された,プ ログラムの健全性 (soundness),信頼性 (confidence)
を計 測 す る指標 であ る。Cyclomatic complexity数
は,プ ログ ラム ・モ ジュー
ルの複雑 さを,有 向 グラフ と して表現 した ときの線形独 立 なパ スの合計 であ ら
わ され る。
① Cycbmadcの 複雑数
C C = E 一 N t t p
E = グ
ラ フのエ ッジ数
N = グ
ラフの ノー ド数
eine MAX VAR SIZE 100
void getVarNum(prOg_id){ SpdObjldArray id_array; SpdObjld file_id,decl_id,decr_id,gvar_idtMAX_VAR_SIZEli SpdCur80r ★ decl_c8r; int count=0,coun12=0,i,j,gvar_num=0; グ ロ ー バ ル 変 数 の リ ス ト ( 配列 ) を 求 め る 生le_id=spdGetARe10bj(prog→ id,"prog_■le","any巾); decl_car=spdGetRe10bjlnit(■ le_id,"nle_decl","any"); while((decl_id i spdCetRe10bj(decl_CSr))!=SAPID_NON_ID) 【deCr_id=8pdGetARe10bj(decl_id,Ⅲ decl_decl","any"); if(spdGetAttrVallnt(decl_id,Ⅲ 9ortⅢ)==DECL_VARIABLE) gvar_idigvar_num++]; } 変 数 の 出 現 を 求 め 、 ロ ー カ ル 、 グ ロ ー バ ル を 判 断 す る i d _ a r r a y = s p d G e t O b j l d A r r a y ( " i d e n t i f i e r " , N U L L , N U L L ) ; fOr(i=0;iく id_array,size;i++) if (spdGetAttrVallnt(id_array.id[i],・30rtⅢ)==ID_VARIABLE){ count++; forG=0;jく gvar_num;j++) if(id_arrayoid[i]==gVar_idB])【 count2++;goto EXIT, } EXIT: } p r i n ば( ⅢN u m b e r o f L o c a l V a r i a b l e s : % d L O c a l ′T o t a l : % ‐5 . 2 f % % ¥ n " , c o u n t , ( ■o a t ) ( C O u n t _ c o u n t 2 ) ±1 0 0 ′C O u n t ) ;
S a p i d に
よるソフ トウエア解析技法 Ⅱ 8 1
p = 要
素 数
② Cyclomadcの 複雑さの基準 (Thomas McCabeによる)
1-10
リス クの ない平坦 なプログラム
11-20
少 し複雑,危 険度小
21-50
複雑 ,危 険度大
50以上
非常 に危 険
3口 最大ネス トと各 ブロック
条件,繰 り返 しといつた制御文や,中 括弧 │,│で囲まれたブロックに関する
複雑 さの情報 を求め る。Sapidでは,ブ ロ ックの種類 を,内 部 にブロ ックを含
むhierachicalブ
ロックとbasicブロックに分ける。basicブロツクは内部にブロッ
クを含 まないブロックの最小単位である。
関数 geと
depthと
サンプルプログラム
│ ト ッ プ レ ベ ル の b 1 0 c k のI D を 求 め るblk_id =spdGetARe10bj(opt_id,"func_body“,“any"); 関 数 の ネ ス ト の 深 さ を 求 め る
max_depth=get_depth(blk_id), │
get_depth(SpdObjld block,int depth) SpdObjld blk_id,block_id; SpdCursor オ blk_Csr;
extern int max;
if (spdGetAttrVallnt(bloCk,"eortり ==BLOCK_BASIC) return depth;
斌玉
8&試
!ぷ
呈
1糾8灘
乱裕品
構&盟ふ
丁
1巣ズ
3部
路。
NFD(
弧spdG:拾
皆
:濫
::持
豊
路諾だf∬礼孟母理鑑 8長
と
FORII
鍵襴難
鞘鞘垂
難撒且
cれ
) depth十 +; depth=get_depth(blk_id,depth); if(m aXく depth)max=depth; }
8 2 彦 根論叢 第 328号
分岐 を含 むhierachicalブロックは,分 岐部分のbranchブ ロックと分岐以外
のブロ ックcOmpoundブ ロ ックに分け られる。branchブ ロックは分岐の種類
によってif文,while文 などに分 け られる。関数 geと
depthは,あ るブロックのネ
ス トの深 さを返す。再帰的に呼び出 され,呼 び出されたブロックIDと ,そ の
ブロ ックの トップ レベルか らのネス トの深 さをパ ラメータ (depth)に もつ。
関数 (クラス option』
ded)か ら,関 連 血nc―
paramをた ど リトップレベルのブ
ロ ックのIDを 求めて, get_depth関
数 にパ ラメー タとして渡す。各分岐ブロ ッ
クを調べ る ときの情報 を,適 当な構造体 を定義 し,格 納することで,ifゃ fOr
といったブロックごとの情報 を得 る。
V ソ ースコー ド ・メ トリックスの取得 と利用
メ トリックス情報 をCSVや XMLと いったデー タ形式で出力 し,情 報 を活用
す る方法 を考察す る。CSVデ ータ形式 はEXCELで 処理可能であ り,関 数 ごと
の各種のメ トリックスを集計 して,プ ログラム ・メ トリックスを取得 し,最 大,
最小,平 均,標 準偏差 といた統計的なデータ処理 を行い,メ トリックス関連の
資料 を作成する。
多 くのオープ ンソース ・ソフ トウエアに対 して同様の処理 を行い,デ ータを
集計 ・解析す ることも可能である。オープンソースをSapidアプリケーシ ョン
で利用す るため には,細 粒度 ソフ トウエ ア リポジ トリ ・データベースSDBが
必要であるが,本 論文では,オ ープンソースのSDBを アーカイブ し,イ ンター
ネ ッ ト上で提供 しているインターネ ッ トサイ トsappyを利用する。
1 . C S V デ ータの処理
4 章 までに求めたメ トリックスに,ソ ースコー ドの行数 (SLOC)と コメン
ト行数 (COM)と いった基本 メ トリックスを加 え集計 し,最 大,最 小,平 均,
標準偏差 を求める。図回は文末の参考資料 回のプログラムAladdiflこ
関す るメ
トリックスデータの解析結果である。
F I I I I I I I
S a p l d によるソフ トウエア解析技法 Ⅱ 8 3
①その他の指標
MVG i Cyclomatic数
Depth i関 数callッリーの最大パ ス
Fanin:関 数callの数 ,(v)関
数callの複雑 さ
Fanout i関 数calledの数,(v)関
数calledの複雑 さ
番 号 LOC MVG COMI LOC/COWI MVG/COM Depth FaninV Fanln FanoutV Fanout 途 中 省 略 0 9 0 0 1 1 2 1 0。069 5 1 1 422 0 9 0 0 0 0 合 計 28605 2937 6853 3290.2 492.591 10312 1874 4335 平 均 67.8 6.96 7.7967 1.16728 24.44 4.44 最 大 1170 最 小 3 0 0 0 0 0 0 0 偏 差 10.565 10.6954 24.2 82.87 28.9
図 田 EXCELに
よるデー タ処理結果
2 日 X M L デ ー タの処理
X M L は デー タ交換 の標 準 フ ォーマ ッ トで あ り,適 当 なス タイル シー トを用
いて,ブ ラウザ か ら参照す る こ ともで きる。サ ンプルプログラム 皿はCSVデ ー
タをX M L デ ー タに変換 す るプログラムであ る。
84 彦 根論叢 第 328号
サ ンプル プログラム 田 XMLデ
ー タ変換
キincludeくstdio.h> キdefine MaxTextSize 10000 m a i n O 【 F I L E t t f p , t f p l ; i n t l i n e = 0 , … 以 下 、 各 項 目 の 宣 言 c h a r f n a I I l e [ 1 0 0 】; char tmp[MaXTextSize],tmpl[2]; int c,ュ=0; 入 力 と 出 力 の フ ァ イ ル を 開 く f p = f o p e n ( " O u t p u t . d a t " , " r " ) ; fp l=fopen(・Outp ut.xlnl",Ⅲwり ;X M L の バ ー ジ ョ ン や s ス タ イ ル シ ー トを指 定 す る
otrcpy(tmp,"<?Xml versiOn=¥"1.0¥"encOding=¥Ⅲ Shift_」IS¥"?> <?xmi‐stylesheet type=¥“text′x81¥Ⅲ href=¥"Outputl.xsl¥"?>
くIEletrics‐report>¥n"); fprintf(fp l,田%8",tmp), D T D の タ グ の 生 成 を 開 始 * while((c=fgetc(fp))!=EOF)【 strcpy(t】■P,"¥tくfunctiOn>¥nり ; fPrintf(fp l,"%8",tmp); etrcpy(tmp,"¥t¥tく name>り , sprintf(tmpl,"%c",(char)c)i s t r c a t ( t m p , t l n p l ) ; w h i l e ( ( c = f g e t c ( f p ) ) ! = ( i n t ) ' , り { S p r i n t f 〈t m p l , " % c Ⅲ, ( C h a r ) C ) ; 8trCat(tmp,tmpl); } fprintf(fp l,"%8く′name>¥n",tmp); 以 下 、 各 項 目 ご と に 、 タ グ を 生 成 す る 。 ・DTDの 定義は省略
出力結果 XMLデ
ー タ形式
〈?xmi verslon="1.0" encoding="Shift_JIS"?〉
く?xml―stylesheel lype="tex1/xsl" href=〕 Outpu↓1.xsl"?〉 く!DOCTYPE doc SYSTEM "filet//cl¥melrics.did"〉
くmetrics―report〉 くf ullc t i on〉 〈name〉1:Exiract_Eigenvector〈 /name〉 〈line〉28 く/1ine〉 〈MVG〉6 く/MVG〉 〈comment〉o〈 /comment〉 〈line_com〉0 く/1ine_c O lll〉 〈MVG_com〉0〈 /MVG_com〉 〈depth〉2〈/depth〉 〈FANIN〉5〈/FANIN〉 〈FANIN_visible〉5 く/FANIN_visible〉 くFANOUT〉1〈/FANOUT〉 くFANOUT_visible〉1 く/FANOUT_visible〉 〈/function〉
Sapidによるソフ トウエア解析技法 Ⅱ 85
お わ りに
Sapidのソフ トウエア解析技法の例題 として,ソ Tス コ
ー ド・メ トリックス
取得プログラムを詳細に解説 した。オ
ープンソースのソフトウエアに関するソー
スコー ド・メ トリックス情報を利用 して,ソ フ トウエアの品質解析の指標を開
発する手法を提案 し,オ ープンソ
ースの新たな利用方法を提起 した。
今後の課題 として, ドキュメントに関するメ トリックスを求める手法を考え
る。Sapidのドキュメント管理バージヨンDapidを利用 してコメン トや ドキュ
メント,仕 様書などを資源 として利用することが可能である。
[参考資料 I]SAPID関
数の名前 について
① SpdGetARe10晰 , SpdGetRelObilnit
SAPID関 数 取 得 関数 [一 つ取得]
参照 オブジエク ト
[カー ソル初期化]
Spd Get [A]
Re10tt ink
② spdGetlndudedOcclnit
SAPID関 数 取 得 関数 [一 つ取得 ]
Spd Get [A]
③ SpdGetObildArray:
SAPID関 数 取 得 関数
Spd Get
出現 を対象 [ カ
ー ソル初期化]
includedOcc init
オブジェク トI D 配列
O朗 ldArray
1 )[参考資料 工]HTML形
式に出力 1 対 象プログラムmain l)
プログラムmttnは 「
Sapidによるソフ トウエア解析技法 I」 で用いた,30行
ほどの簡単なサ ンプルである。V章 までで求めた各種メ トリックスを,HTML
の表形式で出力する。
彦根論叢 第 328号
メ トリックス 1:(REB00TESPRITプ
ロジェク トよ り参照P
番 号 値 メ トリ ックス 要 素 基 準 Wi l Developing ドキュメントのベージ数/ソ ースコー ドの行数 理解性 ドキ ュ メ ンテ ー シ ョン M12 ドキュメントのアクセス可能性のチェックリスト理解性 ドキ ュメ ンテ ー シ ョン W13 コメン ト行/ソ ースコー ドの行数 理解性 自己記述性 M 4 モジュールの 自己記述性のチェックリス ト理解性 自己記述性 脱15 9.25 ソースコー ドの行数/モ ジュール数 可搬性 ・乗軟性モ ジュ ラ リテ イ lv1 6 Developing マシン依存コー ド行数/実 行部コー ドの行数 可搬性 環境独立性 lv1 7 Developing システム依存 コー ド行数/実 行部コー ド行数可搬 性 環境独立性M 8 Fan―in,Fan―outの複雑さ (cyClomatic number)理解性 ・信頼性モジュラリテイー・要素複雑性
M 9 複雑 さ (cyclomadc number) 理解性 ・信頼性コー ド複雑性 M10 モジュールの 自己記述性のチェックリス ト柔軟性 汎用性 Mll 不良耐久性のチェックリス ト 信頼性 不良耐久性 M12 テス ト総数/検 出エラー数 M13 分離 された要求数/全 要求数 M14 データ無矛盾性チェックリス ト A/115 手続 き無矛盾性チェ ックリス ト い7116 0.135 複雑度/ソ ース コー ドの行数 理解性 ・信頼性要素複雑性 lv117 2 最大 ネス ト数 理解性 ・信頼性要素複雑性 巾118 5 内部変数の数/変 数の総数 卜7119 0.139 変数の総数/実 行部 コー ドの行数
[ 参考資料 田] H T M L 形
式 出力( 2 ) 対象 プログラムA l a d d i 辞
A l a d d i n は,linuxで利用 で きる行列処理 ソフ トウエ アであ る。Maryland大学
S y s t e m s R e s e a r c h ( U S A ) で
開発 され た オー プ ンソース で あ り,関 数 の数 が
4 2 2 個の 中規模 の プログラムであ る。 プログラム ・メ トリックス と関数 メ トリッ
クス につ い ての デー タをH T M L の 表形式 で出力す る。 メ トリックスの タグ記号
につ い て は,第 5章 を参照 の こ と。各 関数名 の リンク (参照)先 は,SPIEで 生
成 した情報 の ソース コー ド部分 (当該 関数 の先頭行 )で あ る。
F
ト
ー
ー
ー
ー
ー
ー
︲
S a p i d によるソフ トウエア解析技法 工 8 7
3 )
メ ト リ ックス 2 :Procedual Mに
trics
MIetric Tag Overall Per Function Line of Source Code SLOC 6149 14.6 M5 Cyclomatic number
l Number of if statement 2 Nuttber of for statement 3 Nuttber of while statement 4 Nuttber of switch statement 5 Nuttber of do statement 6 Nuttber of pain statement WIax Block Depth
MVG N if N―for N―while N―switch N―do N―plain W I B D 2.94e十 o3悦 19 1473 1240 23 200 1 0 18酌 117 6.96 3.49 2.94 0.0545 0.474 0.00237 0 N/A Fanin Fanout Fin Fout MIax Depth 5444 3921 13 M M 1 2 . 9 9 。2 9 N/A Lines of comment 30M 1 . 9 1 SLOC/COM L C 7.61958M13
N/A
MVG/COMI M C 3.63941 M116N/A
FunctionsFuction Name MVG COM L C M C FUN IN FUN OUT ]IO(VI
1:Extract_Eigenvector matrix.i:6121 0 0 0 2 10(V=5) 1(V=1) 7.11 2:Extract_Eigenvalue matrix.i:6092 4 0 0 2 10(V=5) 1(V=1) 7.11 3:Solve_Eigen matrix.i:5994 1 1 4 2.75 2 460(V=30) 1(V=1) 21.2 4:帥latrixSolveEigen matrix.i:5845 1 1 0.73 3 403(V=29) 2(V=1) 20.5 5:A/1atrixPut matrix.1:5737 3 16(V=8) 1(V=1) 11.3 5,7 途 中省略 7∼417 416:dktq06 elIIlt_shell_4n_q.1:6812 1 0.032 1 0(V=0) 18(V=2) 12.7 417:dktb06 elmt_shell_4n_q.16710 0.032 1 56(V=28) 16(V=1) 41.2 418:elmt_shell_4nodes_q elrnt_shell_4n_q.15915 789 1.06 0.117 5 212(V=106) 15(V=15) 75.7 419:Lalnina_Sys ellnt_larnina_systi:5773 1.05 0.030 2 50(V=25) 0(V=0) 17.7 420:sld02 elIIlt fiber.i:5776 1 . 1 1 0 0 2(V=1) 15(V=15) 42■SetUpFiberRespondBuff ellnt fiber.15744 2 1.03 0.069 2 10(V=5) 1(V=1) 7.11 422:ellnt ttber ellnt fiber.15724 1 . 1 1 0 0 0(V=0) 15(V=15)