geninfo 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657
  1. #!/usr/bin/perl -w
  2. #
  3. # Copyright (c) International Business Machines Corp., 2002,2012
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or (at
  8. # your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful, but
  11. # WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. # General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  18. #
  19. #
  20. # geninfo
  21. #
  22. # This script generates .info files from data files as created by code
  23. # instrumented with gcc's built-in profiling mechanism. Call it with
  24. # --help and refer to the geninfo man page to get information on usage
  25. # and available options.
  26. #
  27. #
  28. # Authors:
  29. # 2002-08-23 created by Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
  30. # IBM Lab Boeblingen
  31. # based on code by Manoj Iyer <manjo@mail.utexas.edu> and
  32. # Megan Bock <mbock@us.ibm.com>
  33. # IBM Austin
  34. # 2002-09-05 / Peter Oberparleiter: implemented option that allows file list
  35. # 2003-04-16 / Peter Oberparleiter: modified read_gcov so that it can also
  36. # parse the new gcov format which is to be introduced in gcc 3.3
  37. # 2003-04-30 / Peter Oberparleiter: made info write to STDERR, not STDOUT
  38. # 2003-07-03 / Peter Oberparleiter: added line checksum support, added
  39. # --no-checksum
  40. # 2003-09-18 / Nigel Hinds: capture branch coverage data from GCOV
  41. # 2003-12-11 / Laurent Deniel: added --follow option
  42. # workaround gcov (<= 3.2.x) bug with empty .da files
  43. # 2004-01-03 / Laurent Deniel: Ignore empty .bb files
  44. # 2004-02-16 / Andreas Krebbel: Added support for .gcno/.gcda files and
  45. # gcov versioning
  46. # 2004-08-09 / Peter Oberparleiter: added configuration file support
  47. # 2008-07-14 / Tom Zoerner: added --function-coverage command line option
  48. # 2008-08-13 / Peter Oberparleiter: modified function coverage
  49. # implementation (now enabled per default)
  50. #
  51. use strict;
  52. use File::Basename;
  53. use File::Spec::Functions qw /abs2rel catdir file_name_is_absolute splitdir
  54. splitpath catpath/;
  55. use Getopt::Long;
  56. use Digest::MD5 qw(md5_base64);
  57. if( $^O eq "msys" )
  58. {
  59. require File::Spec::Win32;
  60. }
  61. # Constants
  62. our $lcov_version = 'LCOV version 1.10';
  63. our $lcov_url = "http://ltp.sourceforge.net/coverage/lcov.php";
  64. our $gcov_tool = "gcov";
  65. our $tool_name = basename($0);
  66. our $GCOV_VERSION_4_7_0 = 0x40700;
  67. our $GCOV_VERSION_3_4_0 = 0x30400;
  68. our $GCOV_VERSION_3_3_0 = 0x30300;
  69. our $GCNO_FUNCTION_TAG = 0x01000000;
  70. our $GCNO_LINES_TAG = 0x01450000;
  71. our $GCNO_FILE_MAGIC = 0x67636e6f;
  72. our $BBG_FILE_MAGIC = 0x67626267;
  73. # Error classes which users may specify to ignore during processing
  74. our $ERROR_GCOV = 0;
  75. our $ERROR_SOURCE = 1;
  76. our $ERROR_GRAPH = 2;
  77. our %ERROR_ID = (
  78. "gcov" => $ERROR_GCOV,
  79. "source" => $ERROR_SOURCE,
  80. "graph" => $ERROR_GRAPH,
  81. );
  82. our $EXCL_START = "LCOV_EXCL_START";
  83. our $EXCL_STOP = "LCOV_EXCL_STOP";
  84. our $EXCL_LINE = "LCOV_EXCL_LINE";
  85. # Compatibility mode values
  86. our $COMPAT_VALUE_OFF = 0;
  87. our $COMPAT_VALUE_ON = 1;
  88. our $COMPAT_VALUE_AUTO = 2;
  89. # Compatibility mode value names
  90. our %COMPAT_NAME_TO_VALUE = (
  91. "off" => $COMPAT_VALUE_OFF,
  92. "on" => $COMPAT_VALUE_ON,
  93. "auto" => $COMPAT_VALUE_AUTO,
  94. );
  95. # Compatiblity modes
  96. our $COMPAT_MODE_LIBTOOL = 1 << 0;
  97. our $COMPAT_MODE_HAMMER = 1 << 1;
  98. our $COMPAT_MODE_SPLIT_CRC = 1 << 2;
  99. # Compatibility mode names
  100. our %COMPAT_NAME_TO_MODE = (
  101. "libtool" => $COMPAT_MODE_LIBTOOL,
  102. "hammer" => $COMPAT_MODE_HAMMER,
  103. "split_crc" => $COMPAT_MODE_SPLIT_CRC,
  104. "android_4_4_0" => $COMPAT_MODE_SPLIT_CRC,
  105. );
  106. # Map modes to names
  107. our %COMPAT_MODE_TO_NAME = (
  108. $COMPAT_MODE_LIBTOOL => "libtool",
  109. $COMPAT_MODE_HAMMER => "hammer",
  110. $COMPAT_MODE_SPLIT_CRC => "split_crc",
  111. );
  112. # Compatibility mode default values
  113. our %COMPAT_MODE_DEFAULTS = (
  114. $COMPAT_MODE_LIBTOOL => $COMPAT_VALUE_ON,
  115. $COMPAT_MODE_HAMMER => $COMPAT_VALUE_AUTO,
  116. $COMPAT_MODE_SPLIT_CRC => $COMPAT_VALUE_AUTO,
  117. );
  118. # Compatibility mode auto-detection routines
  119. sub compat_hammer_autodetect();
  120. our %COMPAT_MODE_AUTO = (
  121. $COMPAT_MODE_HAMMER => \&compat_hammer_autodetect,
  122. $COMPAT_MODE_SPLIT_CRC => 1, # will be done later
  123. );
  124. our $BR_LINE = 0;
  125. our $BR_BLOCK = 1;
  126. our $BR_BRANCH = 2;
  127. our $BR_TAKEN = 3;
  128. our $BR_VEC_ENTRIES = 4;
  129. our $BR_VEC_WIDTH = 32;
  130. our $UNNAMED_BLOCK = 9999;
  131. # Prototypes
  132. sub print_usage(*);
  133. sub gen_info($);
  134. sub process_dafile($$);
  135. sub match_filename($@);
  136. sub solve_ambiguous_match($$$);
  137. sub split_filename($);
  138. sub solve_relative_path($$);
  139. sub read_gcov_header($);
  140. sub read_gcov_file($);
  141. sub info(@);
  142. sub get_gcov_version();
  143. sub system_no_output($@);
  144. sub read_config($);
  145. sub apply_config($);
  146. sub get_exclusion_data($);
  147. sub apply_exclusion_data($$);
  148. sub process_graphfile($$);
  149. sub filter_fn_name($);
  150. sub warn_handler($);
  151. sub die_handler($);
  152. sub graph_error($$);
  153. sub graph_expect($);
  154. sub graph_read(*$;$$);
  155. sub graph_skip(*$;$);
  156. sub sort_uniq(@);
  157. sub sort_uniq_lex(@);
  158. sub graph_cleanup($);
  159. sub graph_find_base($);
  160. sub graph_from_bb($$$);
  161. sub graph_add_order($$$);
  162. sub read_bb_word(*;$);
  163. sub read_bb_value(*;$);
  164. sub read_bb_string(*$);
  165. sub read_bb($);
  166. sub read_bbg_word(*;$);
  167. sub read_bbg_value(*;$);
  168. sub read_bbg_string(*);
  169. sub read_bbg_lines_record(*$$$$$);
  170. sub read_bbg($);
  171. sub read_gcno_word(*;$$);
  172. sub read_gcno_value(*$;$$);
  173. sub read_gcno_string(*$);
  174. sub read_gcno_lines_record(*$$$$$$);
  175. sub determine_gcno_split_crc($$$);
  176. sub read_gcno_function_record(*$$$$);
  177. sub read_gcno($);
  178. sub get_gcov_capabilities();
  179. sub get_overall_line($$$$);
  180. sub print_overall_rate($$$$$$$$$);
  181. sub br_gvec_len($);
  182. sub br_gvec_get($$);
  183. sub debug($);
  184. sub int_handler();
  185. sub parse_ignore_errors(@);
  186. sub is_external($);
  187. sub compat_name($);
  188. sub parse_compat_modes($);
  189. sub is_compat($);
  190. sub is_compat_auto($);
  191. # Global variables
  192. our $gcov_version;
  193. our $gcov_version_string;
  194. our $graph_file_extension;
  195. our $data_file_extension;
  196. our @data_directory;
  197. our $test_name = "";
  198. our $quiet;
  199. our $help;
  200. our $output_filename;
  201. our $base_directory;
  202. our $version;
  203. our $follow;
  204. our $checksum;
  205. our $no_checksum;
  206. our $opt_compat_libtool;
  207. our $opt_no_compat_libtool;
  208. our $rc_adjust_src_path;# Regexp specifying parts to remove from source path
  209. our $adjust_src_pattern;
  210. our $adjust_src_replace;
  211. our $adjust_testname;
  212. our $config; # Configuration file contents
  213. our @ignore_errors; # List of errors to ignore (parameter)
  214. our @ignore; # List of errors to ignore (array)
  215. our $initial;
  216. our $no_recursion = 0;
  217. our $maxdepth;
  218. our $no_markers = 0;
  219. our $opt_derive_func_data = 0;
  220. our $opt_external = 1;
  221. our $opt_no_external;
  222. our $debug = 0;
  223. our $gcov_caps;
  224. our @gcov_options;
  225. our @internal_dirs;
  226. our $opt_config_file;
  227. our $opt_gcov_all_blocks = 1;
  228. our $opt_compat;
  229. our %opt_rc;
  230. our %compat_value;
  231. our $gcno_split_crc;
  232. our $func_coverage = 1;
  233. our $br_coverage = 0;
  234. our $rc_auto_base = 1;
  235. our $cwd = `pwd`;
  236. chomp($cwd);
  237. #
  238. # Code entry point
  239. #
  240. # Register handler routine to be called when interrupted
  241. $SIG{"INT"} = \&int_handler;
  242. $SIG{__WARN__} = \&warn_handler;
  243. $SIG{__DIE__} = \&die_handler;
  244. # Prettify version string
  245. $lcov_version =~ s/\$\s*Revision\s*:?\s*(\S+)\s*\$/$1/;
  246. # Set LANG so that gcov output will be in a unified format
  247. $ENV{"LANG"} = "C";
  248. # Check command line for a configuration file name
  249. Getopt::Long::Configure("pass_through", "no_auto_abbrev");
  250. GetOptions("config-file=s" => \$opt_config_file,
  251. "rc=s%" => \%opt_rc);
  252. Getopt::Long::Configure("default");
  253. # Read configuration file if available
  254. if (defined($opt_config_file)) {
  255. $config = read_config($opt_config_file);
  256. } elsif (defined($ENV{"HOME"}) && (-r $ENV{"HOME"}."/.lcovrc"))
  257. {
  258. $config = read_config($ENV{"HOME"}."/.lcovrc");
  259. }
  260. elsif (-r "/etc/lcovrc")
  261. {
  262. $config = read_config("/etc/lcovrc");
  263. }
  264. if ($config || %opt_rc)
  265. {
  266. # Copy configuration file and --rc values to variables
  267. apply_config({
  268. "geninfo_gcov_tool" => \$gcov_tool,
  269. "geninfo_adjust_testname" => \$adjust_testname,
  270. "geninfo_checksum" => \$checksum,
  271. "geninfo_no_checksum" => \$no_checksum, # deprecated
  272. "geninfo_compat_libtool" => \$opt_compat_libtool,
  273. "geninfo_external" => \$opt_external,
  274. "geninfo_gcov_all_blocks" => \$opt_gcov_all_blocks,
  275. "geninfo_compat" => \$opt_compat,
  276. "geninfo_adjust_src_path" => \$rc_adjust_src_path,
  277. "geninfo_auto_base" => \$rc_auto_base,
  278. "lcov_function_coverage" => \$func_coverage,
  279. "lcov_branch_coverage" => \$br_coverage,
  280. });
  281. # Merge options
  282. if (defined($no_checksum))
  283. {
  284. $checksum = ($no_checksum ? 0 : 1);
  285. $no_checksum = undef;
  286. }
  287. # Check regexp
  288. if (defined($rc_adjust_src_path)) {
  289. my ($pattern, $replace) = split(/\s*=>\s*/,
  290. $rc_adjust_src_path);
  291. local $SIG{__DIE__};
  292. eval '$adjust_src_pattern = qr>'.$pattern.'>;';
  293. if (!defined($adjust_src_pattern)) {
  294. my $msg = $@;
  295. chomp($msg);
  296. $msg =~ s/at \(eval.*$//;
  297. warn("WARNING: invalid pattern in ".
  298. "geninfo_adjust_src_path: $msg\n");
  299. } elsif (!defined($replace)) {
  300. # If no replacement is specified, simply remove pattern
  301. $adjust_src_replace = "";
  302. } else {
  303. $adjust_src_replace = $replace;
  304. }
  305. }
  306. }
  307. # Parse command line options
  308. if (!GetOptions("test-name|t=s" => \$test_name,
  309. "output-filename|o=s" => \$output_filename,
  310. "checksum" => \$checksum,
  311. "no-checksum" => \$no_checksum,
  312. "base-directory|b=s" => \$base_directory,
  313. "version|v" =>\$version,
  314. "quiet|q" => \$quiet,
  315. "help|h|?" => \$help,
  316. "follow|f" => \$follow,
  317. "compat-libtool" => \$opt_compat_libtool,
  318. "no-compat-libtool" => \$opt_no_compat_libtool,
  319. "gcov-tool=s" => \$gcov_tool,
  320. "ignore-errors=s" => \@ignore_errors,
  321. "initial|i" => \$initial,
  322. "no-recursion" => \$no_recursion,
  323. "no-markers" => \$no_markers,
  324. "derive-func-data" => \$opt_derive_func_data,
  325. "debug" => \$debug,
  326. "external" => \$opt_external,
  327. "no-external" => \$opt_no_external,
  328. "compat=s" => \$opt_compat,
  329. "config-file=s" => \$opt_config_file,
  330. "rc=s%" => \%opt_rc,
  331. ))
  332. {
  333. print(STDERR "Use $tool_name --help to get usage information\n");
  334. exit(1);
  335. }
  336. else
  337. {
  338. # Merge options
  339. if (defined($no_checksum))
  340. {
  341. $checksum = ($no_checksum ? 0 : 1);
  342. $no_checksum = undef;
  343. }
  344. if (defined($opt_no_compat_libtool))
  345. {
  346. $opt_compat_libtool = ($opt_no_compat_libtool ? 0 : 1);
  347. $opt_no_compat_libtool = undef;
  348. }
  349. if (defined($opt_no_external)) {
  350. $opt_external = 0;
  351. $opt_no_external = undef;
  352. }
  353. }
  354. @data_directory = @ARGV;
  355. # Check for help option
  356. if ($help)
  357. {
  358. print_usage(*STDOUT);
  359. exit(0);
  360. }
  361. # Check for version option
  362. if ($version)
  363. {
  364. print("$tool_name: $lcov_version\n");
  365. exit(0);
  366. }
  367. # Check gcov tool
  368. if (system_no_output(3, $gcov_tool, "--help") == -1)
  369. {
  370. die("ERROR: need tool $gcov_tool!\n");
  371. }
  372. ($gcov_version, $gcov_version_string) = get_gcov_version();
  373. # Determine gcov options
  374. $gcov_caps = get_gcov_capabilities();
  375. push(@gcov_options, "-b") if ($gcov_caps->{'branch-probabilities'} &&
  376. ($br_coverage || $func_coverage));
  377. push(@gcov_options, "-c") if ($gcov_caps->{'branch-counts'} &&
  378. $br_coverage);
  379. push(@gcov_options, "-a") if ($gcov_caps->{'all-blocks'} &&
  380. $opt_gcov_all_blocks && $br_coverage);
  381. push(@gcov_options, "-p") if ($gcov_caps->{'preserve-paths'});
  382. # Determine compatibility modes
  383. parse_compat_modes($opt_compat);
  384. # Determine which errors the user wants us to ignore
  385. parse_ignore_errors(@ignore_errors);
  386. # Make sure test names only contain valid characters
  387. if ($test_name =~ s/\W/_/g)
  388. {
  389. warn("WARNING: invalid characters removed from testname!\n");
  390. }
  391. # Adjust test name to include uname output if requested
  392. if ($adjust_testname)
  393. {
  394. $test_name .= "__".`uname -a`;
  395. $test_name =~ s/\W/_/g;
  396. }
  397. # Make sure base_directory contains an absolute path specification
  398. if ($base_directory)
  399. {
  400. $base_directory = solve_relative_path($cwd, $base_directory);
  401. }
  402. # Check for follow option
  403. if ($follow)
  404. {
  405. $follow = "-follow"
  406. }
  407. else
  408. {
  409. $follow = "";
  410. }
  411. # Determine checksum mode
  412. if (defined($checksum))
  413. {
  414. # Normalize to boolean
  415. $checksum = ($checksum ? 1 : 0);
  416. }
  417. else
  418. {
  419. # Default is off
  420. $checksum = 0;
  421. }
  422. # Determine max depth for recursion
  423. if ($no_recursion)
  424. {
  425. $maxdepth = "-maxdepth 1";
  426. }
  427. else
  428. {
  429. $maxdepth = "";
  430. }
  431. # Check for directory name
  432. if (!@data_directory)
  433. {
  434. die("No directory specified\n".
  435. "Use $tool_name --help to get usage information\n");
  436. }
  437. else
  438. {
  439. foreach (@data_directory)
  440. {
  441. stat($_);
  442. if (!-r _)
  443. {
  444. die("ERROR: cannot read $_!\n");
  445. }
  446. }
  447. }
  448. if ($gcov_version < $GCOV_VERSION_3_4_0)
  449. {
  450. if (is_compat($COMPAT_MODE_HAMMER))
  451. {
  452. $data_file_extension = ".da";
  453. $graph_file_extension = ".bbg";
  454. }
  455. else
  456. {
  457. $data_file_extension = ".da";
  458. $graph_file_extension = ".bb";
  459. }
  460. }
  461. else
  462. {
  463. $data_file_extension = ".gcda";
  464. $graph_file_extension = ".gcno";
  465. }
  466. # Check output filename
  467. if (defined($output_filename) && ($output_filename ne "-"))
  468. {
  469. # Initially create output filename, data is appended
  470. # for each data file processed
  471. local *DUMMY_HANDLE;
  472. open(DUMMY_HANDLE, ">", $output_filename)
  473. or die("ERROR: cannot create $output_filename!\n");
  474. close(DUMMY_HANDLE);
  475. # Make $output_filename an absolute path because we're going
  476. # to change directories while processing files
  477. if (!($output_filename =~ /^\/(.*)$/))
  478. {
  479. $output_filename = $cwd."/".$output_filename;
  480. }
  481. }
  482. # Build list of directories to identify external files
  483. foreach my $entry(@data_directory, $base_directory) {
  484. next if (!defined($entry));
  485. push(@internal_dirs, solve_relative_path($cwd, $entry));
  486. }
  487. # Do something
  488. foreach my $entry (@data_directory) {
  489. gen_info($entry);
  490. }
  491. if ($initial && $br_coverage) {
  492. warn("Note: --initial does not generate branch coverage ".
  493. "data\n");
  494. }
  495. info("Finished .info-file creation\n");
  496. exit(0);
  497. #
  498. # print_usage(handle)
  499. #
  500. # Print usage information.
  501. #
  502. sub print_usage(*)
  503. {
  504. local *HANDLE = $_[0];
  505. print(HANDLE <<END_OF_USAGE);
  506. Usage: $tool_name [OPTIONS] DIRECTORY
  507. Traverse DIRECTORY and create a .info file for each data file found. Note
  508. that you may specify more than one directory, all of which are then processed
  509. sequentially.
  510. -h, --help Print this help, then exit
  511. -v, --version Print version number, then exit
  512. -q, --quiet Do not print progress messages
  513. -i, --initial Capture initial zero coverage data
  514. -t, --test-name NAME Use test case name NAME for resulting data
  515. -o, --output-filename OUTFILE Write data only to OUTFILE
  516. -f, --follow Follow links when searching .da/.gcda files
  517. -b, --base-directory DIR Use DIR as base directory for relative paths
  518. --(no-)checksum Enable (disable) line checksumming
  519. --(no-)compat-libtool Enable (disable) libtool compatibility mode
  520. --gcov-tool TOOL Specify gcov tool location
  521. --ignore-errors ERROR Continue after ERROR (gcov, source, graph)
  522. --no-recursion Exclude subdirectories from processing
  523. --no-markers Ignore exclusion markers in source code
  524. --derive-func-data Generate function data from line data
  525. --(no-)external Include (ignore) data for external files
  526. --config-file FILENAME Specify configuration file location
  527. --rc SETTING=VALUE Override configuration file setting
  528. --compat MODE=on|off|auto Set compat MODE (libtool, hammer, split_crc)
  529. For more information see: $lcov_url
  530. END_OF_USAGE
  531. ;
  532. }
  533. #
  534. # get_common_prefix(min_dir, filenames)
  535. #
  536. # Return the longest path prefix shared by all filenames. MIN_DIR specifies
  537. # the minimum number of directories that a filename may have after removing
  538. # the prefix.
  539. #
  540. sub get_common_prefix($@)
  541. {
  542. my ($min_dir, @files) = @_;
  543. my $file;
  544. my @prefix;
  545. my $i;
  546. foreach $file (@files) {
  547. my ($v, $d, $f) = splitpath($file);
  548. my @comp = splitdir($d);
  549. if (!@prefix) {
  550. @prefix = @comp;
  551. next;
  552. }
  553. for ($i = 0; $i < scalar(@comp) && $i < scalar(@prefix); $i++) {
  554. if ($comp[$i] ne $prefix[$i] ||
  555. ((scalar(@comp) - ($i + 1)) <= $min_dir)) {
  556. delete(@prefix[$i..scalar(@prefix)]);
  557. last;
  558. }
  559. }
  560. }
  561. return catdir(@prefix);
  562. }
  563. #
  564. # gen_info(directory)
  565. #
  566. # Traverse DIRECTORY and create a .info file for each data file found.
  567. # The .info file contains TEST_NAME in the following format:
  568. #
  569. # TN:<test name>
  570. #
  571. # For each source file name referenced in the data file, there is a section
  572. # containing source code and coverage data:
  573. #
  574. # SF:<absolute path to the source file>
  575. # FN:<line number of function start>,<function name> for each function
  576. # DA:<line number>,<execution count> for each instrumented line
  577. # LH:<number of lines with an execution count> greater than 0
  578. # LF:<number of instrumented lines>
  579. #
  580. # Sections are separated by:
  581. #
  582. # end_of_record
  583. #
  584. # In addition to the main source code file there are sections for each
  585. # #included file containing executable code. Note that the absolute path
  586. # of a source file is generated by interpreting the contents of the respective
  587. # graph file. Relative filenames are prefixed with the directory in which the
  588. # graph file is found. Note also that symbolic links to the graph file will be
  589. # resolved so that the actual file path is used instead of the path to a link.
  590. # This approach is necessary for the mechanism to work with the /proc/gcov
  591. # files.
  592. #
  593. # Die on error.
  594. #
  595. sub gen_info($)
  596. {
  597. my $directory = $_[0];
  598. my @file_list;
  599. my $file;
  600. my $prefix;
  601. my $type;
  602. my $ext;
  603. if ($initial) {
  604. $type = "graph";
  605. $ext = $graph_file_extension;
  606. } else {
  607. $type = "data";
  608. $ext = $data_file_extension;
  609. }
  610. if (-d $directory)
  611. {
  612. info("Scanning $directory for $ext files ...\n");
  613. @file_list = `find "$directory" $maxdepth $follow -name \\*$ext -type f 2>/dev/null`;
  614. chomp(@file_list);
  615. @file_list or
  616. die("ERROR: no $ext files found in $directory!\n");
  617. $prefix = get_common_prefix(1, @file_list);
  618. info("Found %d %s files in %s\n", $#file_list+1, $type,
  619. $directory);
  620. }
  621. else
  622. {
  623. @file_list = ($directory);
  624. $prefix = "";
  625. }
  626. # Process all files in list
  627. foreach $file (@file_list) {
  628. # Process file
  629. if ($initial) {
  630. process_graphfile($file, $prefix);
  631. } else {
  632. process_dafile($file, $prefix);
  633. }
  634. }
  635. }
  636. #
  637. # derive_data(contentdata, funcdata, bbdata)
  638. #
  639. # Calculate function coverage data by combining line coverage data and the
  640. # list of lines belonging to a function.
  641. #
  642. # contentdata: [ instr1, count1, source1, instr2, count2, source2, ... ]
  643. # instr<n>: Instrumentation flag for line n
  644. # count<n>: Execution count for line n
  645. # source<n>: Source code for line n
  646. #
  647. # funcdata: [ count1, func1, count2, func2, ... ]
  648. # count<n>: Execution count for function number n
  649. # func<n>: Function name for function number n
  650. #
  651. # bbdata: function_name -> [ line1, line2, ... ]
  652. # line<n>: Line number belonging to the corresponding function
  653. #
  654. sub derive_data($$$)
  655. {
  656. my ($contentdata, $funcdata, $bbdata) = @_;
  657. my @gcov_content = @{$contentdata};
  658. my @gcov_functions = @{$funcdata};
  659. my %fn_count;
  660. my %ln_fn;
  661. my $line;
  662. my $maxline;
  663. my %fn_name;
  664. my $fn;
  665. my $count;
  666. if (!defined($bbdata)) {
  667. return @gcov_functions;
  668. }
  669. # First add existing function data
  670. while (@gcov_functions) {
  671. $count = shift(@gcov_functions);
  672. $fn = shift(@gcov_functions);
  673. $fn_count{$fn} = $count;
  674. }
  675. # Convert line coverage data to function data
  676. foreach $fn (keys(%{$bbdata})) {
  677. my $line_data = $bbdata->{$fn};
  678. my $line;
  679. my $fninstr = 0;
  680. if ($fn eq "") {
  681. next;
  682. }
  683. # Find the lowest line count for this function
  684. $count = 0;
  685. foreach $line (@$line_data) {
  686. my $linstr = $gcov_content[ ( $line - 1 ) * 3 + 0 ];
  687. my $lcount = $gcov_content[ ( $line - 1 ) * 3 + 1 ];
  688. next if (!$linstr);
  689. $fninstr = 1;
  690. if (($lcount > 0) &&
  691. (($count == 0) || ($lcount < $count))) {
  692. $count = $lcount;
  693. }
  694. }
  695. next if (!$fninstr);
  696. $fn_count{$fn} = $count;
  697. }
  698. # Check if we got data for all functions
  699. foreach $fn (keys(%fn_name)) {
  700. if ($fn eq "") {
  701. next;
  702. }
  703. if (defined($fn_count{$fn})) {
  704. next;
  705. }
  706. warn("WARNING: no derived data found for function $fn\n");
  707. }
  708. # Convert hash to list in @gcov_functions format
  709. foreach $fn (sort(keys(%fn_count))) {
  710. push(@gcov_functions, $fn_count{$fn}, $fn);
  711. }
  712. return @gcov_functions;
  713. }
  714. #
  715. # get_filenames(directory, pattern)
  716. #
  717. # Return a list of filenames found in directory which match the specified
  718. # pattern.
  719. #
  720. # Die on error.
  721. #
  722. sub get_filenames($$)
  723. {
  724. my ($dirname, $pattern) = @_;
  725. my @result;
  726. my $directory;
  727. local *DIR;
  728. opendir(DIR, $dirname) or
  729. die("ERROR: cannot read directory $dirname\n");
  730. while ($directory = readdir(DIR)) {
  731. push(@result, $directory) if ($directory =~ /$pattern/);
  732. }
  733. closedir(DIR);
  734. return @result;
  735. }
  736. #
  737. # process_dafile(da_filename, dir)
  738. #
  739. # Create a .info file for a single data file.
  740. #
  741. # Die on error.
  742. #
  743. sub process_dafile($$)
  744. {
  745. my ($file, $dir) = @_;
  746. my $da_filename; # Name of data file to process
  747. my $da_dir; # Directory of data file
  748. my $source_dir; # Directory of source file
  749. my $da_basename; # data filename without ".da/.gcda" extension
  750. my $bb_filename; # Name of respective graph file
  751. my $bb_basename; # Basename of the original graph file
  752. my $graph; # Contents of graph file
  753. my $instr; # Contents of graph file part 2
  754. my $gcov_error; # Error code of gcov tool
  755. my $object_dir; # Directory containing all object files
  756. my $source_filename; # Name of a source code file
  757. my $gcov_file; # Name of a .gcov file
  758. my @gcov_content; # Content of a .gcov file
  759. my $gcov_branches; # Branch content of a .gcov file
  760. my @gcov_functions; # Function calls of a .gcov file
  761. my @gcov_list; # List of generated .gcov files
  762. my $line_number; # Line number count
  763. my $lines_hit; # Number of instrumented lines hit
  764. my $lines_found; # Number of instrumented lines found
  765. my $funcs_hit; # Number of instrumented functions hit
  766. my $funcs_found; # Number of instrumented functions found
  767. my $br_hit;
  768. my $br_found;
  769. my $source; # gcov source header information
  770. my $object; # gcov object header information
  771. my @matches; # List of absolute paths matching filename
  772. my @unprocessed; # List of unprocessed source code files
  773. my $base_dir; # Base directory for current file
  774. my @tmp_links; # Temporary links to be cleaned up
  775. my @result;
  776. my $index;
  777. my $da_renamed; # If data file is to be renamed
  778. local *INFO_HANDLE;
  779. info("Processing %s\n", abs2rel($file, $dir));
  780. # Get path to data file in absolute and normalized form (begins with /,
  781. # contains no more ../ or ./)
  782. $da_filename = solve_relative_path($cwd, $file);
  783. # Get directory and basename of data file
  784. ($da_dir, $da_basename) = split_filename($da_filename);
  785. $source_dir = $da_dir;
  786. if (is_compat($COMPAT_MODE_LIBTOOL)) {
  787. # Avoid files from .libs dirs
  788. $source_dir =~ s/\.libs$//;
  789. }
  790. if (-z $da_filename)
  791. {
  792. $da_renamed = 1;
  793. }
  794. else
  795. {
  796. $da_renamed = 0;
  797. }
  798. # Construct base_dir for current file
  799. if ($base_directory)
  800. {
  801. $base_dir = $base_directory;
  802. }
  803. else
  804. {
  805. $base_dir = $source_dir;
  806. }
  807. # Check for writable $base_dir (gcov will try to write files there)
  808. stat($base_dir);
  809. if (!-w _)
  810. {
  811. die("ERROR: cannot write to directory $base_dir!\n");
  812. }
  813. # Construct name of graph file
  814. $bb_basename = $da_basename.$graph_file_extension;
  815. $bb_filename = "$da_dir/$bb_basename";
  816. # Find out the real location of graph file in case we're just looking at
  817. # a link
  818. while (readlink($bb_filename))
  819. {
  820. my $last_dir = dirname($bb_filename);
  821. $bb_filename = readlink($bb_filename);
  822. $bb_filename = solve_relative_path($last_dir, $bb_filename);
  823. }
  824. # Ignore empty graph file (e.g. source file with no statement)
  825. if (-z $bb_filename)
  826. {
  827. warn("WARNING: empty $bb_filename (skipped)\n");
  828. return;
  829. }
  830. # Read contents of graph file into hash. We need it later to find out
  831. # the absolute path to each .gcov file created as well as for
  832. # information about functions and their source code positions.
  833. if ($gcov_version < $GCOV_VERSION_3_4_0)
  834. {
  835. if (is_compat($COMPAT_MODE_HAMMER))
  836. {
  837. ($instr, $graph) = read_bbg($bb_filename);
  838. }
  839. else
  840. {
  841. ($instr, $graph) = read_bb($bb_filename);
  842. }
  843. }
  844. else
  845. {
  846. ($instr, $graph) = read_gcno($bb_filename);
  847. }
  848. # Try to find base directory automatically if requested by user
  849. if ($rc_auto_base) {
  850. $base_dir = find_base_from_graph($base_dir, $instr, $graph);
  851. }
  852. ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
  853. # Set $object_dir to real location of object files. This may differ
  854. # from $da_dir if the graph file is just a link to the "real" object
  855. # file location.
  856. $object_dir = dirname($bb_filename);
  857. # Is the data file in a different directory? (this happens e.g. with
  858. # the gcov-kernel patch)
  859. if ($object_dir ne $da_dir)
  860. {
  861. # Need to create link to data file in $object_dir
  862. system("ln", "-s", $da_filename,
  863. "$object_dir/$da_basename$data_file_extension")
  864. and die ("ERROR: cannot create link $object_dir/".
  865. "$da_basename$data_file_extension!\n");
  866. push(@tmp_links,
  867. "$object_dir/$da_basename$data_file_extension");
  868. # Need to create link to graph file if basename of link
  869. # and file are different (CONFIG_MODVERSION compat)
  870. if ((basename($bb_filename) ne $bb_basename) &&
  871. (! -e "$object_dir/$bb_basename")) {
  872. symlink($bb_filename, "$object_dir/$bb_basename") or
  873. warn("WARNING: cannot create link ".
  874. "$object_dir/$bb_basename\n");
  875. push(@tmp_links, "$object_dir/$bb_basename");
  876. }
  877. }
  878. # Change to directory containing data files and apply GCOV
  879. debug("chdir($base_dir)\n");
  880. chdir($base_dir);
  881. if ($da_renamed)
  882. {
  883. # Need to rename empty data file to workaround
  884. # gcov <= 3.2.x bug (Abort)
  885. system_no_output(3, "mv", "$da_filename", "$da_filename.ori")
  886. and die ("ERROR: cannot rename $da_filename\n");
  887. }
  888. # Execute gcov command and suppress standard output
  889. $gcov_error = system_no_output(1, $gcov_tool, $da_filename,
  890. "-o", $object_dir, @gcov_options);
  891. if ($da_renamed)
  892. {
  893. system_no_output(3, "mv", "$da_filename.ori", "$da_filename")
  894. and die ("ERROR: cannot rename $da_filename.ori");
  895. }
  896. # Clean up temporary links
  897. foreach (@tmp_links) {
  898. unlink($_);
  899. }
  900. if ($gcov_error)
  901. {
  902. if ($ignore[$ERROR_GCOV])
  903. {
  904. warn("WARNING: GCOV failed for $da_filename!\n");
  905. return;
  906. }
  907. die("ERROR: GCOV failed for $da_filename!\n");
  908. }
  909. # Collect data from resulting .gcov files and create .info file
  910. @gcov_list = get_filenames('.', '\.gcov$');
  911. # Check for files
  912. if (!@gcov_list)
  913. {
  914. warn("WARNING: gcov did not create any files for ".
  915. "$da_filename!\n");
  916. }
  917. # Check whether we're writing to a single file
  918. if ($output_filename)
  919. {
  920. if ($output_filename eq "-")
  921. {
  922. *INFO_HANDLE = *STDOUT;
  923. }
  924. else
  925. {
  926. # Append to output file
  927. open(INFO_HANDLE, ">>", $output_filename)
  928. or die("ERROR: cannot write to ".
  929. "$output_filename!\n");
  930. }
  931. }
  932. else
  933. {
  934. # Open .info file for output
  935. open(INFO_HANDLE, ">", "$da_filename.info")
  936. or die("ERROR: cannot create $da_filename.info!\n");
  937. }
  938. # Write test name
  939. printf(INFO_HANDLE "TN:%s\n", $test_name);
  940. # Traverse the list of generated .gcov files and combine them into a
  941. # single .info file
  942. @unprocessed = keys(%{$instr});
  943. foreach $gcov_file (sort(@gcov_list))
  944. {
  945. my $i;
  946. my $num;
  947. # Skip gcov file for gcc built-in code
  948. next if ($gcov_file eq "<built-in>.gcov");
  949. ($source, $object) = read_gcov_header($gcov_file);
  950. if (!defined($source)) {
  951. # Derive source file name from gcov file name if
  952. # header format could not be parsed
  953. $source = $gcov_file;
  954. $source =~ s/\.gcov$//;
  955. }
  956. $source = solve_relative_path($base_dir, $source);
  957. if (defined($adjust_src_pattern)) {
  958. # Apply transformation as specified by user
  959. $source =~ s/$adjust_src_pattern/$adjust_src_replace/g;
  960. }
  961. # gcov will happily create output even if there's no source code
  962. # available - this interferes with checksum creation so we need
  963. # to pull the emergency brake here.
  964. if (! -r $source && $checksum)
  965. {
  966. if ($ignore[$ERROR_SOURCE])
  967. {
  968. warn("WARNING: could not read source file ".
  969. "$source\n");
  970. next;
  971. }
  972. die("ERROR: could not read source file $source\n");
  973. }
  974. @matches = match_filename($source, keys(%{$instr}));
  975. # Skip files that are not mentioned in the graph file
  976. if (!@matches)
  977. {
  978. warn("WARNING: cannot find an entry for ".$gcov_file.
  979. " in $graph_file_extension file, skipping ".
  980. "file!\n");
  981. unlink($gcov_file);
  982. next;
  983. }
  984. # Read in contents of gcov file
  985. @result = read_gcov_file($gcov_file);
  986. if (!defined($result[0])) {
  987. warn("WARNING: skipping unreadable file ".
  988. $gcov_file."\n");
  989. unlink($gcov_file);
  990. next;
  991. }
  992. @gcov_content = @{$result[0]};
  993. $gcov_branches = $result[1];
  994. @gcov_functions = @{$result[2]};
  995. # Skip empty files
  996. if (!@gcov_content)
  997. {
  998. warn("WARNING: skipping empty file ".$gcov_file."\n");
  999. unlink($gcov_file);
  1000. next;
  1001. }
  1002. if (scalar(@matches) == 1)
  1003. {
  1004. # Just one match
  1005. $source_filename = $matches[0];
  1006. }
  1007. else
  1008. {
  1009. # Try to solve the ambiguity
  1010. $source_filename = solve_ambiguous_match($gcov_file,
  1011. \@matches, \@gcov_content);
  1012. }
  1013. # Remove processed file from list
  1014. for ($index = scalar(@unprocessed) - 1; $index >= 0; $index--)
  1015. {
  1016. if ($unprocessed[$index] eq $source_filename)
  1017. {
  1018. splice(@unprocessed, $index, 1);
  1019. last;
  1020. }
  1021. }
  1022. # Skip external files if requested
  1023. if (!$opt_external) {
  1024. if (is_external($source_filename)) {
  1025. info(" ignoring data for external file ".
  1026. "$source_filename\n");
  1027. unlink($gcov_file);
  1028. next;
  1029. }
  1030. }
  1031. # Write absolute path of source file
  1032. printf(INFO_HANDLE "SF:%s\n", $source_filename);
  1033. # If requested, derive function coverage data from
  1034. # line coverage data of the first line of a function
  1035. if ($opt_derive_func_data) {
  1036. @gcov_functions =
  1037. derive_data(\@gcov_content, \@gcov_functions,
  1038. $graph->{$source_filename});
  1039. }
  1040. # Write function-related information
  1041. if (defined($graph->{$source_filename}))
  1042. {
  1043. my $fn_data = $graph->{$source_filename};
  1044. my $fn;
  1045. foreach $fn (sort
  1046. {$fn_data->{$a}->[0] <=> $fn_data->{$b}->[0]}
  1047. keys(%{$fn_data})) {
  1048. my $ln_data = $fn_data->{$fn};
  1049. my $line = $ln_data->[0];
  1050. # Skip empty function
  1051. if ($fn eq "") {
  1052. next;
  1053. }
  1054. # Remove excluded functions
  1055. if (!$no_markers) {
  1056. my $gfn;
  1057. my $found = 0;
  1058. foreach $gfn (@gcov_functions) {
  1059. if ($gfn eq $fn) {
  1060. $found = 1;
  1061. last;
  1062. }
  1063. }
  1064. if (!$found) {
  1065. next;
  1066. }
  1067. }
  1068. # Normalize function name
  1069. $fn = filter_fn_name($fn);
  1070. print(INFO_HANDLE "FN:$line,$fn\n");
  1071. }
  1072. }
  1073. #--
  1074. #-- FNDA: <call-count>, <function-name>
  1075. #-- FNF: overall count of functions
  1076. #-- FNH: overall count of functions with non-zero call count
  1077. #--
  1078. $funcs_found = 0;
  1079. $funcs_hit = 0;
  1080. while (@gcov_functions)
  1081. {
  1082. my $count = shift(@gcov_functions);
  1083. my $fn = shift(@gcov_functions);
  1084. $fn = filter_fn_name($fn);
  1085. printf(INFO_HANDLE "FNDA:$count,$fn\n");
  1086. $funcs_found++;
  1087. $funcs_hit++ if ($count > 0);
  1088. }
  1089. if ($funcs_found > 0) {
  1090. printf(INFO_HANDLE "FNF:%s\n", $funcs_found);
  1091. printf(INFO_HANDLE "FNH:%s\n", $funcs_hit);
  1092. }
  1093. # Write coverage information for each instrumented branch:
  1094. #
  1095. # BRDA:<line number>,<block number>,<branch number>,<taken>
  1096. #
  1097. # where 'taken' is the number of times the branch was taken
  1098. # or '-' if the block to which the branch belongs was never
  1099. # executed
  1100. $br_found = 0;
  1101. $br_hit = 0;
  1102. $num = br_gvec_len($gcov_branches);
  1103. for ($i = 0; $i < $num; $i++) {
  1104. my ($line, $block, $branch, $taken) =
  1105. br_gvec_get($gcov_branches, $i);
  1106. print(INFO_HANDLE "BRDA:$line,$block,$branch,$taken\n");
  1107. $br_found++;
  1108. $br_hit++ if ($taken ne '-' && $taken > 0);
  1109. }
  1110. if ($br_found > 0) {
  1111. printf(INFO_HANDLE "BRF:%s\n", $br_found);
  1112. printf(INFO_HANDLE "BRH:%s\n", $br_hit);
  1113. }
  1114. # Reset line counters
  1115. $line_number = 0;
  1116. $lines_found = 0;
  1117. $lines_hit = 0;
  1118. # Write coverage information for each instrumented line
  1119. # Note: @gcov_content contains a list of (flag, count, source)
  1120. # tuple for each source code line
  1121. while (@gcov_content)
  1122. {
  1123. $line_number++;
  1124. # Check for instrumented line
  1125. if ($gcov_content[0])
  1126. {
  1127. $lines_found++;
  1128. printf(INFO_HANDLE "DA:".$line_number.",".
  1129. $gcov_content[1].($checksum ?
  1130. ",". md5_base64($gcov_content[2]) : "").
  1131. "\n");
  1132. # Increase $lines_hit in case of an execution
  1133. # count>0
  1134. if ($gcov_content[1] > 0) { $lines_hit++; }
  1135. }
  1136. # Remove already processed data from array
  1137. splice(@gcov_content,0,3);
  1138. }
  1139. # Write line statistics and section separator
  1140. printf(INFO_HANDLE "LF:%s\n", $lines_found);
  1141. printf(INFO_HANDLE "LH:%s\n", $lines_hit);
  1142. print(INFO_HANDLE "end_of_record\n");
  1143. # Remove .gcov file after processing
  1144. unlink($gcov_file);
  1145. }
  1146. # Check for files which show up in the graph file but were never
  1147. # processed
  1148. if (@unprocessed && @gcov_list)
  1149. {
  1150. foreach (@unprocessed)
  1151. {
  1152. warn("WARNING: no data found for $_\n");
  1153. }
  1154. }
  1155. if (!($output_filename && ($output_filename eq "-")))
  1156. {
  1157. close(INFO_HANDLE);
  1158. }
  1159. # Change back to initial directory
  1160. chdir($cwd);
  1161. }
  1162. #
  1163. # solve_relative_path(path, dir)
  1164. #
  1165. # Solve relative path components of DIR which, if not absolute, resides in PATH.
  1166. #
  1167. sub solve_relative_path($$)
  1168. {
  1169. my $path = $_[0];
  1170. my $dir = $_[1];
  1171. my $volume;
  1172. my $directories;
  1173. my $filename;
  1174. my @dirs; # holds path elements
  1175. my $result;
  1176. # Convert from Windows path to msys path
  1177. if( $^O eq "msys" )
  1178. {
  1179. # search for a windows drive letter at the beginning
  1180. ($volume, $directories, $filename) = File::Spec::Win32->splitpath( $dir );
  1181. if( $volume ne '' )
  1182. {
  1183. my $uppercase_volume;
  1184. # transform c/d\../e/f\g to Windows style c\d\..\e\f\g
  1185. $dir = File::Spec::Win32->canonpath( $dir );
  1186. # use Win32 module to retrieve path components
  1187. # $uppercase_volume is not used any further
  1188. ( $uppercase_volume, $directories, $filename ) = File::Spec::Win32->splitpath( $dir );
  1189. @dirs = File::Spec::Win32->splitdir( $directories );
  1190. # prepend volume, since in msys C: is always mounted to /c
  1191. $volume =~ s|^([a-zA-Z]+):|/\L$1\E|;
  1192. unshift( @dirs, $volume );
  1193. # transform to Unix style '/' path
  1194. $directories = File::Spec->catdir( @dirs );
  1195. $dir = File::Spec->catpath( '', $directories, $filename );
  1196. } else {
  1197. # eliminate '\' path separators
  1198. $dir = File::Spec->canonpath( $dir );
  1199. }
  1200. }
  1201. $result = $dir;
  1202. # Prepend path if not absolute
  1203. if ($dir =~ /^[^\/]/)
  1204. {
  1205. $result = "$path/$result";
  1206. }
  1207. # Remove //
  1208. $result =~ s/\/\//\//g;
  1209. # Remove .
  1210. $result =~ s/\/\.\//\//g;
  1211. $result =~ s/\/\.$/\//g;
  1212. # Remove trailing /
  1213. $result =~ s/\/$//g;
  1214. # Solve ..
  1215. while ($result =~ s/\/[^\/]+\/\.\.\//\//)
  1216. {
  1217. }
  1218. # Remove preceding ..
  1219. $result =~ s/^\/\.\.\//\//g;
  1220. return $result;
  1221. }
  1222. #
  1223. # match_filename(gcov_filename, list)
  1224. #
  1225. # Return a list of those entries of LIST which match the relative filename
  1226. # GCOV_FILENAME.
  1227. #
  1228. sub match_filename($@)
  1229. {
  1230. my ($filename, @list) = @_;
  1231. my ($vol, $dir, $file) = splitpath($filename);
  1232. my @comp = splitdir($dir);
  1233. my $comps = scalar(@comp);
  1234. my $entry;
  1235. my @result;
  1236. entry:
  1237. foreach $entry (@list) {
  1238. my ($evol, $edir, $efile) = splitpath($entry);
  1239. my @ecomp;
  1240. my $ecomps;
  1241. my $i;
  1242. # Filename component must match
  1243. if ($efile ne $file) {
  1244. next;
  1245. }
  1246. # Check directory components last to first for match
  1247. @ecomp = splitdir($edir);
  1248. $ecomps = scalar(@ecomp);
  1249. if ($ecomps < $comps) {
  1250. next;
  1251. }
  1252. for ($i = 0; $i < $comps; $i++) {
  1253. if ($comp[$comps - $i - 1] ne
  1254. $ecomp[$ecomps - $i - 1]) {
  1255. next entry;
  1256. }
  1257. }
  1258. push(@result, $entry),
  1259. }
  1260. return @result;
  1261. }
  1262. #
  1263. # solve_ambiguous_match(rel_filename, matches_ref, gcov_content_ref)
  1264. #
  1265. # Try to solve ambiguous matches of mapping (gcov file) -> (source code) file
  1266. # by comparing source code provided in the GCOV file with that of the files
  1267. # in MATCHES. REL_FILENAME identifies the relative filename of the gcov
  1268. # file.
  1269. #
  1270. # Return the one real match or die if there is none.
  1271. #
  1272. sub solve_ambiguous_match($$$)
  1273. {
  1274. my $rel_name = $_[0];
  1275. my $matches = $_[1];
  1276. my $content = $_[2];
  1277. my $filename;
  1278. my $index;
  1279. my $no_match;
  1280. local *SOURCE;
  1281. # Check the list of matches
  1282. foreach $filename (@$matches)
  1283. {
  1284. # Compare file contents
  1285. open(SOURCE, "<", $filename)
  1286. or die("ERROR: cannot read $filename!\n");
  1287. $no_match = 0;
  1288. for ($index = 2; <SOURCE>; $index += 3)
  1289. {
  1290. chomp;
  1291. # Also remove CR from line-end
  1292. s/\015$//;
  1293. if ($_ ne @$content[$index])
  1294. {
  1295. $no_match = 1;
  1296. last;
  1297. }
  1298. }
  1299. close(SOURCE);
  1300. if (!$no_match)
  1301. {
  1302. info("Solved source file ambiguity for $rel_name\n");
  1303. return $filename;
  1304. }
  1305. }
  1306. die("ERROR: could not match gcov data for $rel_name!\n");
  1307. }
  1308. #
  1309. # split_filename(filename)
  1310. #
  1311. # Return (path, filename, extension) for a given FILENAME.
  1312. #
  1313. sub split_filename($)
  1314. {
  1315. my @path_components = split('/', $_[0]);
  1316. my @file_components = split('\.', pop(@path_components));
  1317. my $extension = pop(@file_components);
  1318. return (join("/",@path_components), join(".",@file_components),
  1319. $extension);
  1320. }
  1321. #
  1322. # read_gcov_header(gcov_filename)
  1323. #
  1324. # Parse file GCOV_FILENAME and return a list containing the following
  1325. # information:
  1326. #
  1327. # (source, object)
  1328. #
  1329. # where:
  1330. #
  1331. # source: complete relative path of the source code file (gcc >= 3.3 only)
  1332. # object: name of associated graph file
  1333. #
  1334. # Die on error.
  1335. #
  1336. sub read_gcov_header($)
  1337. {
  1338. my $source;
  1339. my $object;
  1340. local *INPUT;
  1341. if (!open(INPUT, "<", $_[0]))
  1342. {
  1343. if ($ignore_errors[$ERROR_GCOV])
  1344. {
  1345. warn("WARNING: cannot read $_[0]!\n");
  1346. return (undef,undef);
  1347. }
  1348. die("ERROR: cannot read $_[0]!\n");
  1349. }
  1350. while (<INPUT>)
  1351. {
  1352. chomp($_);
  1353. # Also remove CR from line-end
  1354. s/\015$//;
  1355. if (/^\s+-:\s+0:Source:(.*)$/)
  1356. {
  1357. # Source: header entry
  1358. $source = $1;
  1359. }
  1360. elsif (/^\s+-:\s+0:Object:(.*)$/)
  1361. {
  1362. # Object: header entry
  1363. $object = $1;
  1364. }
  1365. else
  1366. {
  1367. last;
  1368. }
  1369. }
  1370. close(INPUT);
  1371. return ($source, $object);
  1372. }
  1373. #
  1374. # br_gvec_len(vector)
  1375. #
  1376. # Return the number of entries in the branch coverage vector.
  1377. #
  1378. sub br_gvec_len($)
  1379. {
  1380. my ($vec) = @_;
  1381. return 0 if (!defined($vec));
  1382. return (length($vec) * 8 / $BR_VEC_WIDTH) / $BR_VEC_ENTRIES;
  1383. }
  1384. #
  1385. # br_gvec_get(vector, number)
  1386. #
  1387. # Return an entry from the branch coverage vector.
  1388. #
  1389. sub br_gvec_get($$)
  1390. {
  1391. my ($vec, $num) = @_;
  1392. my $line;
  1393. my $block;
  1394. my $branch;
  1395. my $taken;
  1396. my $offset = $num * $BR_VEC_ENTRIES;
  1397. # Retrieve data from vector
  1398. $line = vec($vec, $offset + $BR_LINE, $BR_VEC_WIDTH);
  1399. $block = vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH);
  1400. $branch = vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH);
  1401. $taken = vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH);
  1402. # Decode taken value from an integer
  1403. if ($taken == 0) {
  1404. $taken = "-";
  1405. } else {
  1406. $taken--;
  1407. }
  1408. return ($line, $block, $branch, $taken);
  1409. }
  1410. #
  1411. # br_gvec_push(vector, line, block, branch, taken)
  1412. #
  1413. # Add an entry to the branch coverage vector.
  1414. #
  1415. sub br_gvec_push($$$$$)
  1416. {
  1417. my ($vec, $line, $block, $branch, $taken) = @_;
  1418. my $offset;
  1419. $vec = "" if (!defined($vec));
  1420. $offset = br_gvec_len($vec) * $BR_VEC_ENTRIES;
  1421. # Encode taken value into an integer
  1422. if ($taken eq "-") {
  1423. $taken = 0;
  1424. } else {
  1425. $taken++;
  1426. }
  1427. # Add to vector
  1428. vec($vec, $offset + $BR_LINE, $BR_VEC_WIDTH) = $line;
  1429. vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH) = $block;
  1430. vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH) = $branch;
  1431. vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH) = $taken;
  1432. return $vec;
  1433. }
  1434. #
  1435. # read_gcov_file(gcov_filename)
  1436. #
  1437. # Parse file GCOV_FILENAME (.gcov file format) and return the list:
  1438. # (reference to gcov_content, reference to gcov_branch, reference to gcov_func)
  1439. #
  1440. # gcov_content is a list of 3 elements
  1441. # (flag, count, source) for each source code line:
  1442. #
  1443. # $result[($line_number-1)*3+0] = instrumentation flag for line $line_number
  1444. # $result[($line_number-1)*3+1] = execution count for line $line_number
  1445. # $result[($line_number-1)*3+2] = source code text for line $line_number
  1446. #
  1447. # gcov_branch is a vector of 4 4-byte long elements for each branch:
  1448. # line number, block number, branch number, count + 1 or 0
  1449. #
  1450. # gcov_func is a list of 2 elements
  1451. # (number of calls, function name) for each function
  1452. #
  1453. # Die on error.
  1454. #
  1455. sub read_gcov_file($)
  1456. {
  1457. my $filename = $_[0];
  1458. my @result = ();
  1459. my $branches = "";
  1460. my @functions = ();
  1461. my $number;
  1462. my $exclude_flag = 0;
  1463. my $exclude_line = 0;
  1464. my $last_block = $UNNAMED_BLOCK;
  1465. my $last_line = 0;
  1466. local *INPUT;
  1467. if (!open(INPUT, "<", $filename)) {
  1468. if ($ignore_errors[$ERROR_GCOV])
  1469. {
  1470. warn("WARNING: cannot read $filename!\n");
  1471. return (undef, undef, undef);
  1472. }
  1473. die("ERROR: cannot read $filename!\n");
  1474. }
  1475. if ($gcov_version < $GCOV_VERSION_3_3_0)
  1476. {
  1477. # Expect gcov format as used in gcc < 3.3
  1478. while (<INPUT>)
  1479. {
  1480. chomp($_);
  1481. # Also remove CR from line-end
  1482. s/\015$//;
  1483. if (/^branch\s+(\d+)\s+taken\s+=\s+(\d+)/) {
  1484. next if (!$br_coverage);
  1485. next if ($exclude_line);
  1486. $branches = br_gvec_push($branches, $last_line,
  1487. $last_block, $1, $2);
  1488. } elsif (/^branch\s+(\d+)\s+never\s+executed/) {
  1489. next if (!$br_coverage);
  1490. next if ($exclude_line);
  1491. $branches = br_gvec_push($branches, $last_line,
  1492. $last_block, $1, '-');
  1493. }
  1494. elsif (/^call/ || /^function/)
  1495. {
  1496. # Function call return data
  1497. }
  1498. else
  1499. {
  1500. $last_line++;
  1501. # Check for exclusion markers
  1502. if (!$no_markers) {
  1503. if (/$EXCL_STOP/) {
  1504. $exclude_flag = 0;
  1505. } elsif (/$EXCL_START/) {
  1506. $exclude_flag = 1;
  1507. }
  1508. if (/$EXCL_LINE/ || $exclude_flag) {
  1509. $exclude_line = 1;
  1510. } else {
  1511. $exclude_line = 0;
  1512. }
  1513. }
  1514. # Source code execution data
  1515. if (/^\t\t(.*)$/)
  1516. {
  1517. # Uninstrumented line
  1518. push(@result, 0);
  1519. push(@result, 0);
  1520. push(@result, $1);
  1521. next;
  1522. }
  1523. $number = (split(" ",substr($_, 0, 16)))[0];
  1524. # Check for zero count which is indicated
  1525. # by ######
  1526. if ($number eq "######") { $number = 0; }
  1527. if ($exclude_line) {
  1528. # Register uninstrumented line instead
  1529. push(@result, 0);
  1530. push(@result, 0);
  1531. } else {
  1532. push(@result, 1);
  1533. push(@result, $number);
  1534. }
  1535. push(@result, substr($_, 16));
  1536. }
  1537. }
  1538. }
  1539. else
  1540. {
  1541. # Expect gcov format as used in gcc >= 3.3
  1542. while (<INPUT>)
  1543. {
  1544. chomp($_);
  1545. # Also remove CR from line-end
  1546. s/\015$//;
  1547. if (/^\s*(\d+|\$+):\s*(\d+)-block\s+(\d+)\s*$/) {
  1548. # Block information - used to group related
  1549. # branches
  1550. $last_line = $2;
  1551. $last_block = $3;
  1552. } elsif (/^branch\s+(\d+)\s+taken\s+(\d+)/) {
  1553. next if (!$br_coverage);
  1554. next if ($exclude_line);
  1555. $branches = br_gvec_push($branches, $last_line,
  1556. $last_block, $1, $2);
  1557. } elsif (/^branch\s+(\d+)\s+never\s+executed/) {
  1558. next if (!$br_coverage);
  1559. next if ($exclude_line);
  1560. $branches = br_gvec_push($branches, $last_line,
  1561. $last_block, $1, '-');
  1562. }
  1563. elsif (/^function\s+(.+)\s+called\s+(\d+)\s+/)
  1564. {
  1565. next if (!$func_coverage);
  1566. if ($exclude_line) {
  1567. next;
  1568. }
  1569. push(@functions, $2, $1);
  1570. }
  1571. elsif (/^call/)
  1572. {
  1573. # Function call return data
  1574. }
  1575. elsif (/^\s*([^:]+):\s*([^:]+):(.*)$/)
  1576. {
  1577. my ($count, $line, $code) = ($1, $2, $3);
  1578. $last_line = $line;
  1579. $last_block = $UNNAMED_BLOCK;
  1580. # Check for exclusion markers
  1581. if (!$no_markers) {
  1582. if (/$EXCL_STOP/) {
  1583. $exclude_flag = 0;
  1584. } elsif (/$EXCL_START/) {
  1585. $exclude_flag = 1;
  1586. }
  1587. if (/$EXCL_LINE/ || $exclude_flag) {
  1588. $exclude_line = 1;
  1589. } else {
  1590. $exclude_line = 0;
  1591. }
  1592. }
  1593. # <exec count>:<line number>:<source code>
  1594. if ($line eq "0")
  1595. {
  1596. # Extra data
  1597. }
  1598. elsif ($count eq "-")
  1599. {
  1600. # Uninstrumented line
  1601. push(@result, 0);
  1602. push(@result, 0);
  1603. push(@result, $code);
  1604. }
  1605. else
  1606. {
  1607. if ($exclude_line) {
  1608. push(@result, 0);
  1609. push(@result, 0);
  1610. } else {
  1611. # Check for zero count
  1612. if ($count eq "#####") {
  1613. $count = 0;
  1614. }
  1615. push(@result, 1);
  1616. push(@result, $count);
  1617. }
  1618. push(@result, $code);
  1619. }
  1620. }
  1621. }
  1622. }
  1623. close(INPUT);
  1624. if ($exclude_flag) {
  1625. warn("WARNING: unterminated exclusion section in $filename\n");
  1626. }
  1627. return(\@result, $branches, \@functions);
  1628. }
  1629. #
  1630. # Get the GCOV tool version. Return an integer number which represents the
  1631. # GCOV version. Version numbers can be compared using standard integer
  1632. # operations.
  1633. #
  1634. sub get_gcov_version()
  1635. {
  1636. local *HANDLE;
  1637. my $version_string;
  1638. my $result;
  1639. open(GCOV_PIPE, "-|", "$gcov_tool -v")
  1640. or die("ERROR: cannot retrieve gcov version!\n");
  1641. $version_string = <GCOV_PIPE>;
  1642. close(GCOV_PIPE);
  1643. $result = 0;
  1644. if ($version_string =~ /(\d+)\.(\d+)(\.(\d+))?/)
  1645. {
  1646. if (defined($4))
  1647. {
  1648. info("Found gcov version: $1.$2.$4\n");
  1649. $result = $1 << 16 | $2 << 8 | $4;
  1650. }
  1651. else
  1652. {
  1653. info("Found gcov version: $1.$2\n");
  1654. $result = $1 << 16 | $2 << 8;
  1655. }
  1656. }
  1657. return ($result, $version_string);
  1658. }
  1659. #
  1660. # info(printf_parameter)
  1661. #
  1662. # Use printf to write PRINTF_PARAMETER to stdout only when the $quiet flag
  1663. # is not set.
  1664. #
  1665. sub info(@)
  1666. {
  1667. if (!$quiet)
  1668. {
  1669. # Print info string
  1670. if (defined($output_filename) && ($output_filename eq "-"))
  1671. {
  1672. # Don't interfere with the .info output to STDOUT
  1673. printf(STDERR @_);
  1674. }
  1675. else
  1676. {
  1677. printf(@_);
  1678. }
  1679. }
  1680. }
  1681. #
  1682. # int_handler()
  1683. #
  1684. # Called when the script was interrupted by an INT signal (e.g. CTRl-C)
  1685. #
  1686. sub int_handler()
  1687. {
  1688. if ($cwd) { chdir($cwd); }
  1689. info("Aborted.\n");
  1690. exit(1);
  1691. }
  1692. #
  1693. # system_no_output(mode, parameters)
  1694. #
  1695. # Call an external program using PARAMETERS while suppressing depending on
  1696. # the value of MODE:
  1697. #
  1698. # MODE & 1: suppress STDOUT
  1699. # MODE & 2: suppress STDERR
  1700. #
  1701. # Return 0 on success, non-zero otherwise.
  1702. #
  1703. sub system_no_output($@)
  1704. {
  1705. my $mode = shift;
  1706. my $result;
  1707. local *OLD_STDERR;
  1708. local *OLD_STDOUT;
  1709. # Save old stdout and stderr handles
  1710. ($mode & 1) && open(OLD_STDOUT, ">>&", "STDOUT");
  1711. ($mode & 2) && open(OLD_STDERR, ">>&", "STDERR");
  1712. # Redirect to /dev/null
  1713. ($mode & 1) && open(STDOUT, ">", "/dev/null");
  1714. ($mode & 2) && open(STDERR, ">", "/dev/null");
  1715. debug("system(".join(' ', @_).")\n");
  1716. system(@_);
  1717. $result = $?;
  1718. # Close redirected handles
  1719. ($mode & 1) && close(STDOUT);
  1720. ($mode & 2) && close(STDERR);
  1721. # Restore old handles
  1722. ($mode & 1) && open(STDOUT, ">>&", "OLD_STDOUT");
  1723. ($mode & 2) && open(STDERR, ">>&", "OLD_STDERR");
  1724. return $result;
  1725. }
  1726. #
  1727. # read_config(filename)
  1728. #
  1729. # Read configuration file FILENAME and return a reference to a hash containing
  1730. # all valid key=value pairs found.
  1731. #
  1732. sub read_config($)
  1733. {
  1734. my $filename = $_[0];
  1735. my %result;
  1736. my $key;
  1737. my $value;
  1738. local *HANDLE;
  1739. if (!open(HANDLE, "<", $filename))
  1740. {
  1741. warn("WARNING: cannot read configuration file $filename\n");
  1742. return undef;
  1743. }
  1744. while (<HANDLE>)
  1745. {
  1746. chomp;
  1747. # Skip comments
  1748. s/#.*//;
  1749. # Remove leading blanks
  1750. s/^\s+//;
  1751. # Remove trailing blanks
  1752. s/\s+$//;
  1753. next unless length;
  1754. ($key, $value) = split(/\s*=\s*/, $_, 2);
  1755. if (defined($key) && defined($value))
  1756. {
  1757. $result{$key} = $value;
  1758. }
  1759. else
  1760. {
  1761. warn("WARNING: malformed statement in line $. ".
  1762. "of configuration file $filename\n");
  1763. }
  1764. }
  1765. close(HANDLE);
  1766. return \%result;
  1767. }
  1768. #
  1769. # apply_config(REF)
  1770. #
  1771. # REF is a reference to a hash containing the following mapping:
  1772. #
  1773. # key_string => var_ref
  1774. #
  1775. # where KEY_STRING is a keyword and VAR_REF is a reference to an associated
  1776. # variable. If the global configuration hashes CONFIG or OPT_RC contain a value
  1777. # for keyword KEY_STRING, VAR_REF will be assigned the value for that keyword.
  1778. #
  1779. sub apply_config($)
  1780. {
  1781. my $ref = $_[0];
  1782. foreach (keys(%{$ref}))
  1783. {
  1784. if (defined($opt_rc{$_})) {
  1785. ${$ref->{$_}} = $opt_rc{$_};
  1786. } elsif (defined($config->{$_})) {
  1787. ${$ref->{$_}} = $config->{$_};
  1788. }
  1789. }
  1790. }
  1791. #
  1792. # get_exclusion_data(filename)
  1793. #
  1794. # Scan specified source code file for exclusion markers and return
  1795. # linenumber -> 1
  1796. # for all lines which should be excluded.
  1797. #
  1798. sub get_exclusion_data($)
  1799. {
  1800. my ($filename) = @_;
  1801. my %list;
  1802. my $flag = 0;
  1803. local *HANDLE;
  1804. if (!open(HANDLE, "<", $filename)) {
  1805. warn("WARNING: could not open $filename\n");
  1806. return undef;
  1807. }
  1808. while (<HANDLE>) {
  1809. if (/$EXCL_STOP/) {
  1810. $flag = 0;
  1811. } elsif (/$EXCL_START/) {
  1812. $flag = 1;
  1813. }
  1814. if (/$EXCL_LINE/ || $flag) {
  1815. $list{$.} = 1;
  1816. }
  1817. }
  1818. close(HANDLE);
  1819. if ($flag) {
  1820. warn("WARNING: unterminated exclusion section in $filename\n");
  1821. }
  1822. return \%list;
  1823. }
  1824. #
  1825. # apply_exclusion_data(instr, graph)
  1826. #
  1827. # Remove lines from instr and graph data structures which are marked
  1828. # for exclusion in the source code file.
  1829. #
  1830. # Return adjusted (instr, graph).
  1831. #
  1832. # graph : file name -> function data
  1833. # function data : function name -> line data
  1834. # line data : [ line1, line2, ... ]
  1835. #
  1836. # instr : filename -> line data
  1837. # line data : [ line1, line2, ... ]
  1838. #
  1839. sub apply_exclusion_data($$)
  1840. {
  1841. my ($instr, $graph) = @_;
  1842. my $filename;
  1843. my %excl_data;
  1844. my $excl_read_failed = 0;
  1845. # Collect exclusion marker data
  1846. foreach $filename (sort_uniq_lex(keys(%{$graph}), keys(%{$instr}))) {
  1847. my $excl = get_exclusion_data($filename);
  1848. # Skip and note if file could not be read
  1849. if (!defined($excl)) {
  1850. $excl_read_failed = 1;
  1851. next;
  1852. }
  1853. # Add to collection if there are markers
  1854. $excl_data{$filename} = $excl if (keys(%{$excl}) > 0);
  1855. }
  1856. # Warn if not all source files could be read
  1857. if ($excl_read_failed) {
  1858. warn("WARNING: some exclusion markers may be ignored\n");
  1859. }
  1860. # Skip if no markers were found
  1861. return ($instr, $graph) if (keys(%excl_data) == 0);
  1862. # Apply exclusion marker data to graph
  1863. foreach $filename (keys(%excl_data)) {
  1864. my $function_data = $graph->{$filename};
  1865. my $excl = $excl_data{$filename};
  1866. my $function;
  1867. next if (!defined($function_data));
  1868. foreach $function (keys(%{$function_data})) {
  1869. my $line_data = $function_data->{$function};
  1870. my $line;
  1871. my @new_data;
  1872. # To be consistent with exclusion parser in non-initial
  1873. # case we need to remove a function if the first line
  1874. # was excluded
  1875. if ($excl->{$line_data->[0]}) {
  1876. delete($function_data->{$function});
  1877. next;
  1878. }
  1879. # Copy only lines which are not excluded
  1880. foreach $line (@{$line_data}) {
  1881. push(@new_data, $line) if (!$excl->{$line});
  1882. }
  1883. # Store modified list
  1884. if (scalar(@new_data) > 0) {
  1885. $function_data->{$function} = \@new_data;
  1886. } else {
  1887. # All of this function was excluded
  1888. delete($function_data->{$function});
  1889. }
  1890. }
  1891. # Check if all functions of this file were excluded
  1892. if (keys(%{$function_data}) == 0) {
  1893. delete($graph->{$filename});
  1894. }
  1895. }
  1896. # Apply exclusion marker data to instr
  1897. foreach $filename (keys(%excl_data)) {
  1898. my $line_data = $instr->{$filename};
  1899. my $excl = $excl_data{$filename};
  1900. my $line;
  1901. my @new_data;
  1902. next if (!defined($line_data));
  1903. # Copy only lines which are not excluded
  1904. foreach $line (@{$line_data}) {
  1905. push(@new_data, $line) if (!$excl->{$line});
  1906. }
  1907. # Store modified list
  1908. $instr->{$filename} = \@new_data;
  1909. }
  1910. return ($instr, $graph);
  1911. }
  1912. sub process_graphfile($$)
  1913. {
  1914. my ($file, $dir) = @_;
  1915. my $graph_filename = $file;
  1916. my $graph_dir;
  1917. my $graph_basename;
  1918. my $source_dir;
  1919. my $base_dir;
  1920. my $graph;
  1921. my $instr;
  1922. my $filename;
  1923. local *INFO_HANDLE;
  1924. info("Processing %s\n", abs2rel($file, $dir));
  1925. # Get path to data file in absolute and normalized form (begins with /,
  1926. # contains no more ../ or ./)
  1927. $graph_filename = solve_relative_path($cwd, $graph_filename);
  1928. # Get directory and basename of data file
  1929. ($graph_dir, $graph_basename) = split_filename($graph_filename);
  1930. $source_dir = $graph_dir;
  1931. if (is_compat($COMPAT_MODE_LIBTOOL)) {
  1932. # Avoid files from .libs dirs
  1933. $source_dir =~ s/\.libs$//;
  1934. }
  1935. # Construct base_dir for current file
  1936. if ($base_directory)
  1937. {
  1938. $base_dir = $base_directory;
  1939. }
  1940. else
  1941. {
  1942. $base_dir = $source_dir;
  1943. }
  1944. if ($gcov_version < $GCOV_VERSION_3_4_0)
  1945. {
  1946. if (is_compat($COMPAT_MODE_HAMMER))
  1947. {
  1948. ($instr, $graph) = read_bbg($graph_filename);
  1949. }
  1950. else
  1951. {
  1952. ($instr, $graph) = read_bb($graph_filename);
  1953. }
  1954. }
  1955. else
  1956. {
  1957. ($instr, $graph) = read_gcno($graph_filename);
  1958. }
  1959. # Try to find base directory automatically if requested by user
  1960. if ($rc_auto_base) {
  1961. $base_dir = find_base_from_graph($base_dir, $instr, $graph);
  1962. }
  1963. ($instr, $graph) = adjust_graph_filenames($base_dir, $instr, $graph);
  1964. if (!$no_markers) {
  1965. # Apply exclusion marker data to graph file data
  1966. ($instr, $graph) = apply_exclusion_data($instr, $graph);
  1967. }
  1968. # Check whether we're writing to a single file
  1969. if ($output_filename)
  1970. {
  1971. if ($output_filename eq "-")
  1972. {
  1973. *INFO_HANDLE = *STDOUT;
  1974. }
  1975. else
  1976. {
  1977. # Append to output file
  1978. open(INFO_HANDLE, ">>", $output_filename)
  1979. or die("ERROR: cannot write to ".
  1980. "$output_filename!\n");
  1981. }
  1982. }
  1983. else
  1984. {
  1985. # Open .info file for output
  1986. open(INFO_HANDLE, ">", "$graph_filename.info")
  1987. or die("ERROR: cannot create $graph_filename.info!\n");
  1988. }
  1989. # Write test name
  1990. printf(INFO_HANDLE "TN:%s\n", $test_name);
  1991. foreach $filename (sort(keys(%{$instr})))
  1992. {
  1993. my $funcdata = $graph->{$filename};
  1994. my $line;
  1995. my $linedata;
  1996. print(INFO_HANDLE "SF:$filename\n");
  1997. if (defined($funcdata) && $func_coverage) {
  1998. my @functions = sort {$funcdata->{$a}->[0] <=>
  1999. $funcdata->{$b}->[0]}
  2000. keys(%{$funcdata});
  2001. my $func;
  2002. # Gather list of instrumented lines and functions
  2003. foreach $func (@functions) {
  2004. $linedata = $funcdata->{$func};
  2005. # Print function name and starting line
  2006. print(INFO_HANDLE "FN:".$linedata->[0].
  2007. ",".filter_fn_name($func)."\n");
  2008. }
  2009. # Print zero function coverage data
  2010. foreach $func (@functions) {
  2011. print(INFO_HANDLE "FNDA:0,".
  2012. filter_fn_name($func)."\n");
  2013. }
  2014. # Print function summary
  2015. print(INFO_HANDLE "FNF:".scalar(@functions)."\n");
  2016. print(INFO_HANDLE "FNH:0\n");
  2017. }
  2018. # Print zero line coverage data
  2019. foreach $line (@{$instr->{$filename}}) {
  2020. print(INFO_HANDLE "DA:$line,0\n");
  2021. }
  2022. # Print line summary
  2023. print(INFO_HANDLE "LF:".scalar(@{$instr->{$filename}})."\n");
  2024. print(INFO_HANDLE "LH:0\n");
  2025. print(INFO_HANDLE "end_of_record\n");
  2026. }
  2027. if (!($output_filename && ($output_filename eq "-")))
  2028. {
  2029. close(INFO_HANDLE);
  2030. }
  2031. }
  2032. sub filter_fn_name($)
  2033. {
  2034. my ($fn) = @_;
  2035. # Remove characters used internally as function name delimiters
  2036. $fn =~ s/[,=]/_/g;
  2037. return $fn;
  2038. }
  2039. sub warn_handler($)
  2040. {
  2041. my ($msg) = @_;
  2042. warn("$tool_name: $msg");
  2043. }
  2044. sub die_handler($)
  2045. {
  2046. my ($msg) = @_;
  2047. die("$tool_name: $msg");
  2048. }
  2049. #
  2050. # graph_error(filename, message)
  2051. #
  2052. # Print message about error in graph file. If ignore_graph_error is set, return.
  2053. # Otherwise abort.
  2054. #
  2055. sub graph_error($$)
  2056. {
  2057. my ($filename, $msg) = @_;
  2058. if ($ignore[$ERROR_GRAPH]) {
  2059. warn("WARNING: $filename: $msg - skipping\n");
  2060. return;
  2061. }
  2062. die("ERROR: $filename: $msg\n");
  2063. }
  2064. #
  2065. # graph_expect(description)
  2066. #
  2067. # If debug is set to a non-zero value, print the specified description of what
  2068. # is expected to be read next from the graph file.
  2069. #
  2070. sub graph_expect($)
  2071. {
  2072. my ($msg) = @_;
  2073. if (!$debug || !defined($msg)) {
  2074. return;
  2075. }
  2076. print(STDERR "DEBUG: expecting $msg\n");
  2077. }
  2078. #
  2079. # graph_read(handle, bytes[, description, peek])
  2080. #
  2081. # Read and return the specified number of bytes from handle. Return undef
  2082. # if the number of bytes could not be read. If PEEK is non-zero, reset
  2083. # file position after read.
  2084. #
  2085. sub graph_read(*$;$$)
  2086. {
  2087. my ($handle, $length, $desc, $peek) = @_;
  2088. my $data;
  2089. my $result;
  2090. my $pos;
  2091. graph_expect($desc);
  2092. if ($peek) {
  2093. $pos = tell($handle);
  2094. if ($pos == -1) {
  2095. warn("Could not get current file position: $!\n");
  2096. return undef;
  2097. }
  2098. }
  2099. $result = read($handle, $data, $length);
  2100. if ($debug) {
  2101. my $op = $peek ? "peek" : "read";
  2102. my $ascii = "";
  2103. my $hex = "";
  2104. my $i;
  2105. print(STDERR "DEBUG: $op($length)=$result: ");
  2106. for ($i = 0; $i < length($data); $i++) {
  2107. my $c = substr($data, $i, 1);;
  2108. my $n = ord($c);
  2109. $hex .= sprintf("%02x ", $n);
  2110. if ($n >= 32 && $n <= 127) {
  2111. $ascii .= $c;
  2112. } else {
  2113. $ascii .= ".";
  2114. }
  2115. }
  2116. print(STDERR "$hex |$ascii|");
  2117. print(STDERR "\n");
  2118. }
  2119. if ($peek) {
  2120. if (!seek($handle, $pos, 0)) {
  2121. warn("Could not set file position: $!\n");
  2122. return undef;
  2123. }
  2124. }
  2125. if ($result != $length) {
  2126. return undef;
  2127. }
  2128. return $data;
  2129. }
  2130. #
  2131. # graph_skip(handle, bytes[, description])
  2132. #
  2133. # Read and discard the specified number of bytes from handle. Return non-zero
  2134. # if bytes could be read, zero otherwise.
  2135. #
  2136. sub graph_skip(*$;$)
  2137. {
  2138. my ($handle, $length, $desc) = @_;
  2139. if (defined(graph_read($handle, $length, $desc))) {
  2140. return 1;
  2141. }
  2142. return 0;
  2143. }
  2144. #
  2145. # sort_uniq(list)
  2146. #
  2147. # Return list in numerically ascending order and without duplicate entries.
  2148. #
  2149. sub sort_uniq(@)
  2150. {
  2151. my (@list) = @_;
  2152. my %hash;
  2153. foreach (@list) {
  2154. $hash{$_} = 1;
  2155. }
  2156. return sort { $a <=> $b } keys(%hash);
  2157. }
  2158. #
  2159. # sort_uniq_lex(list)
  2160. #
  2161. # Return list in lexically ascending order and without duplicate entries.
  2162. #
  2163. sub sort_uniq_lex(@)
  2164. {
  2165. my (@list) = @_;
  2166. my %hash;
  2167. foreach (@list) {
  2168. $hash{$_} = 1;
  2169. }
  2170. return sort keys(%hash);
  2171. }
  2172. #
  2173. # parent_dir(dir)
  2174. #
  2175. # Return parent directory for DIR. DIR must not contain relative path
  2176. # components.
  2177. #
  2178. sub parent_dir($)
  2179. {
  2180. my ($dir) = @_;
  2181. my ($v, $d, $f) = splitpath($dir, 1);
  2182. my @dirs = splitdir($d);
  2183. pop(@dirs);
  2184. return catpath($v, catdir(@dirs), $f);
  2185. }
  2186. #
  2187. # find_base_from_graph(base_dir, instr, graph)
  2188. #
  2189. # Try to determine the base directory of the graph file specified by INSTR
  2190. # and GRAPH. The base directory is the base for all relative filenames in
  2191. # the graph file. It is defined by the current working directory at time
  2192. # of compiling the source file.
  2193. #
  2194. # This function implements a heuristic which relies on the following
  2195. # assumptions:
  2196. # - all files used for compilation are still present at their location
  2197. # - the base directory is either BASE_DIR or one of its parent directories
  2198. # - files by the same name are not present in multiple parent directories
  2199. #
  2200. sub find_base_from_graph($$$)
  2201. {
  2202. my ($base_dir, $instr, $graph) = @_;
  2203. my $old_base;
  2204. my $best_miss;
  2205. my $best_base;
  2206. my %rel_files;
  2207. # Determine list of relative paths
  2208. foreach my $filename (keys(%{$instr}), keys(%{$graph})) {
  2209. next if (file_name_is_absolute($filename));
  2210. $rel_files{$filename} = 1;
  2211. }
  2212. # Early exit if there are no relative paths
  2213. return $base_dir if (!%rel_files);
  2214. do {
  2215. my $miss = 0;
  2216. foreach my $filename (keys(%rel_files)) {
  2217. if (!-e solve_relative_path($base_dir, $filename)) {
  2218. $miss++;
  2219. }
  2220. }
  2221. debug("base_dir=$base_dir miss=$miss\n");
  2222. # Exit if we find an exact match with no misses
  2223. return $base_dir if ($miss == 0);
  2224. # No exact match, aim for the one with the least source file
  2225. # misses
  2226. if (!defined($best_base) || $miss < $best_miss) {
  2227. $best_base = $base_dir;
  2228. $best_miss = $miss;
  2229. }
  2230. # Repeat until there's no more parent directory
  2231. $old_base = $base_dir;
  2232. $base_dir = parent_dir($base_dir);
  2233. } while ($old_base ne $base_dir);
  2234. return $best_base;
  2235. }
  2236. #
  2237. # adjust_graph_filenames(base_dir, instr, graph)
  2238. #
  2239. # Make relative paths in INSTR and GRAPH absolute and apply
  2240. # geninfo_adjust_src_path setting to graph file data.
  2241. #
  2242. sub adjust_graph_filenames($$$)
  2243. {
  2244. my ($base_dir, $instr, $graph) = @_;
  2245. foreach my $filename (keys(%{$instr})) {
  2246. my $old_filename = $filename;
  2247. # Convert to absolute canonical form
  2248. $filename = solve_relative_path($base_dir, $filename);
  2249. # Apply adjustment
  2250. if (defined($adjust_src_pattern)) {
  2251. $filename =~ s/$adjust_src_pattern/$adjust_src_replace/g;
  2252. }
  2253. if ($filename ne $old_filename) {
  2254. $instr->{$filename} = delete($instr->{$old_filename});
  2255. }
  2256. }
  2257. foreach my $filename (keys(%{$graph})) {
  2258. my $old_filename = $filename;
  2259. # Make absolute
  2260. # Convert to absolute canonical form
  2261. $filename = solve_relative_path($base_dir, $filename);
  2262. # Apply adjustment
  2263. if (defined($adjust_src_pattern)) {
  2264. $filename =~ s/$adjust_src_pattern/$adjust_src_replace/g;
  2265. }
  2266. if ($filename ne $old_filename) {
  2267. $graph->{$filename} = delete($graph->{$old_filename});
  2268. }
  2269. }
  2270. return ($instr, $graph);
  2271. }
  2272. #
  2273. # graph_cleanup(graph)
  2274. #
  2275. # Remove entries for functions with no lines. Remove duplicate line numbers.
  2276. # Sort list of line numbers numerically ascending.
  2277. #
  2278. sub graph_cleanup($)
  2279. {
  2280. my ($graph) = @_;
  2281. my $filename;
  2282. foreach $filename (keys(%{$graph})) {
  2283. my $per_file = $graph->{$filename};
  2284. my $function;
  2285. foreach $function (keys(%{$per_file})) {
  2286. my $lines = $per_file->{$function};
  2287. if (scalar(@$lines) == 0) {
  2288. # Remove empty function
  2289. delete($per_file->{$function});
  2290. next;
  2291. }
  2292. # Normalize list
  2293. $per_file->{$function} = [ sort_uniq(@$lines) ];
  2294. }
  2295. if (scalar(keys(%{$per_file})) == 0) {
  2296. # Remove empty file
  2297. delete($graph->{$filename});
  2298. }
  2299. }
  2300. }
  2301. #
  2302. # graph_find_base(bb)
  2303. #
  2304. # Try to identify the filename which is the base source file for the
  2305. # specified bb data.
  2306. #
  2307. sub graph_find_base($)
  2308. {
  2309. my ($bb) = @_;
  2310. my %file_count;
  2311. my $basefile;
  2312. my $file;
  2313. my $func;
  2314. my $filedata;
  2315. my $count;
  2316. my $num;
  2317. # Identify base name for this bb data.
  2318. foreach $func (keys(%{$bb})) {
  2319. $filedata = $bb->{$func};
  2320. foreach $file (keys(%{$filedata})) {
  2321. $count = $file_count{$file};
  2322. # Count file occurrence
  2323. $file_count{$file} = defined($count) ? $count + 1 : 1;
  2324. }
  2325. }
  2326. $count = 0;
  2327. $num = 0;
  2328. foreach $file (keys(%file_count)) {
  2329. if ($file_count{$file} > $count) {
  2330. # The file that contains code for the most functions
  2331. # is likely the base file
  2332. $count = $file_count{$file};
  2333. $num = 1;
  2334. $basefile = $file;
  2335. } elsif ($file_count{$file} == $count) {
  2336. # If more than one file could be the basefile, we
  2337. # don't have a basefile
  2338. $basefile = undef;
  2339. }
  2340. }
  2341. return $basefile;
  2342. }
  2343. #
  2344. # graph_from_bb(bb, fileorder, bb_filename)
  2345. #
  2346. # Convert data from bb to the graph format and list of instrumented lines.
  2347. # Returns (instr, graph).
  2348. #
  2349. # bb : function name -> file data
  2350. # : undef -> file order
  2351. # file data : filename -> line data
  2352. # line data : [ line1, line2, ... ]
  2353. #
  2354. # file order : function name -> [ filename1, filename2, ... ]
  2355. #
  2356. # graph : file name -> function data
  2357. # function data : function name -> line data
  2358. # line data : [ line1, line2, ... ]
  2359. #
  2360. # instr : filename -> line data
  2361. # line data : [ line1, line2, ... ]
  2362. #
  2363. sub graph_from_bb($$$)
  2364. {
  2365. my ($bb, $fileorder, $bb_filename) = @_;
  2366. my $graph = {};
  2367. my $instr = {};
  2368. my $basefile;
  2369. my $file;
  2370. my $func;
  2371. my $filedata;
  2372. my $linedata;
  2373. my $order;
  2374. $basefile = graph_find_base($bb);
  2375. # Create graph structure
  2376. foreach $func (keys(%{$bb})) {
  2377. $filedata = $bb->{$func};
  2378. $order = $fileorder->{$func};
  2379. # Account for lines in functions
  2380. if (defined($basefile) && defined($filedata->{$basefile})) {
  2381. # If the basefile contributes to this function,
  2382. # account this function to the basefile.
  2383. $graph->{$basefile}->{$func} = $filedata->{$basefile};
  2384. } else {
  2385. # If the basefile does not contribute to this function,
  2386. # account this function to the first file contributing
  2387. # lines.
  2388. $graph->{$order->[0]}->{$func} =
  2389. $filedata->{$order->[0]};
  2390. }
  2391. foreach $file (keys(%{$filedata})) {
  2392. # Account for instrumented lines
  2393. $linedata = $filedata->{$file};
  2394. push(@{$instr->{$file}}, @$linedata);
  2395. }
  2396. }
  2397. # Clean up array of instrumented lines
  2398. foreach $file (keys(%{$instr})) {
  2399. $instr->{$file} = [ sort_uniq(@{$instr->{$file}}) ];
  2400. }
  2401. return ($instr, $graph);
  2402. }
  2403. #
  2404. # graph_add_order(fileorder, function, filename)
  2405. #
  2406. # Add an entry for filename to the fileorder data set for function.
  2407. #
  2408. sub graph_add_order($$$)
  2409. {
  2410. my ($fileorder, $function, $filename) = @_;
  2411. my $item;
  2412. my $list;
  2413. $list = $fileorder->{$function};
  2414. foreach $item (@$list) {
  2415. if ($item eq $filename) {
  2416. return;
  2417. }
  2418. }
  2419. push(@$list, $filename);
  2420. $fileorder->{$function} = $list;
  2421. }
  2422. #
  2423. # read_bb_word(handle[, description])
  2424. #
  2425. # Read and return a word in .bb format from handle.
  2426. #
  2427. sub read_bb_word(*;$)
  2428. {
  2429. my ($handle, $desc) = @_;
  2430. return graph_read($handle, 4, $desc);
  2431. }
  2432. #
  2433. # read_bb_value(handle[, description])
  2434. #
  2435. # Read a word in .bb format from handle and return the word and its integer
  2436. # value.
  2437. #
  2438. sub read_bb_value(*;$)
  2439. {
  2440. my ($handle, $desc) = @_;
  2441. my $word;
  2442. $word = read_bb_word($handle, $desc);
  2443. return undef if (!defined($word));
  2444. return ($word, unpack("V", $word));
  2445. }
  2446. #
  2447. # read_bb_string(handle, delimiter)
  2448. #
  2449. # Read and return a string in .bb format from handle up to the specified
  2450. # delimiter value.
  2451. #
  2452. sub read_bb_string(*$)
  2453. {
  2454. my ($handle, $delimiter) = @_;
  2455. my $word;
  2456. my $value;
  2457. my $string = "";
  2458. graph_expect("string");
  2459. do {
  2460. ($word, $value) = read_bb_value($handle, "string or delimiter");
  2461. return undef if (!defined($value));
  2462. if ($value != $delimiter) {
  2463. $string .= $word;
  2464. }
  2465. } while ($value != $delimiter);
  2466. $string =~ s/\0//g;
  2467. return $string;
  2468. }
  2469. #
  2470. # read_bb(filename)
  2471. #
  2472. # Read the contents of the specified .bb file and return (instr, graph), where:
  2473. #
  2474. # instr : filename -> line data
  2475. # line data : [ line1, line2, ... ]
  2476. #
  2477. # graph : filename -> file_data
  2478. # file_data : function name -> line_data
  2479. # line_data : [ line1, line2, ... ]
  2480. #
  2481. # See the gcov info pages of gcc 2.95 for a description of the .bb file format.
  2482. #
  2483. sub read_bb($)
  2484. {
  2485. my ($bb_filename) = @_;
  2486. my $minus_one = 0x80000001;
  2487. my $minus_two = 0x80000002;
  2488. my $value;
  2489. my $filename;
  2490. my $function;
  2491. my $bb = {};
  2492. my $fileorder = {};
  2493. my $instr;
  2494. my $graph;
  2495. local *HANDLE;
  2496. open(HANDLE, "<", $bb_filename) or goto open_error;
  2497. binmode(HANDLE);
  2498. while (!eof(HANDLE)) {
  2499. $value = read_bb_value(*HANDLE, "data word");
  2500. goto incomplete if (!defined($value));
  2501. if ($value == $minus_one) {
  2502. # Source file name
  2503. graph_expect("filename");
  2504. $filename = read_bb_string(*HANDLE, $minus_one);
  2505. goto incomplete if (!defined($filename));
  2506. } elsif ($value == $minus_two) {
  2507. # Function name
  2508. graph_expect("function name");
  2509. $function = read_bb_string(*HANDLE, $minus_two);
  2510. goto incomplete if (!defined($function));
  2511. } elsif ($value > 0) {
  2512. # Line number
  2513. if (!defined($filename) || !defined($function)) {
  2514. warn("WARNING: unassigned line number ".
  2515. "$value\n");
  2516. next;
  2517. }
  2518. push(@{$bb->{$function}->{$filename}}, $value);
  2519. graph_add_order($fileorder, $function, $filename);
  2520. }
  2521. }
  2522. close(HANDLE);
  2523. ($instr, $graph) = graph_from_bb($bb, $fileorder, $bb_filename);
  2524. graph_cleanup($graph);
  2525. return ($instr, $graph);
  2526. open_error:
  2527. graph_error($bb_filename, "could not open file");
  2528. return undef;
  2529. incomplete:
  2530. graph_error($bb_filename, "reached unexpected end of file");
  2531. return undef;
  2532. }
  2533. #
  2534. # read_bbg_word(handle[, description])
  2535. #
  2536. # Read and return a word in .bbg format.
  2537. #
  2538. sub read_bbg_word(*;$)
  2539. {
  2540. my ($handle, $desc) = @_;
  2541. return graph_read($handle, 4, $desc);
  2542. }
  2543. #
  2544. # read_bbg_value(handle[, description])
  2545. #
  2546. # Read a word in .bbg format from handle and return its integer value.
  2547. #
  2548. sub read_bbg_value(*;$)
  2549. {
  2550. my ($handle, $desc) = @_;
  2551. my $word;
  2552. $word = read_bbg_word($handle, $desc);
  2553. return undef if (!defined($word));
  2554. return unpack("N", $word);
  2555. }
  2556. #
  2557. # read_bbg_string(handle)
  2558. #
  2559. # Read and return a string in .bbg format.
  2560. #
  2561. sub read_bbg_string(*)
  2562. {
  2563. my ($handle, $desc) = @_;
  2564. my $length;
  2565. my $string;
  2566. graph_expect("string");
  2567. # Read string length
  2568. $length = read_bbg_value($handle, "string length");
  2569. return undef if (!defined($length));
  2570. if ($length == 0) {
  2571. return "";
  2572. }
  2573. # Read string
  2574. $string = graph_read($handle, $length, "string");
  2575. return undef if (!defined($string));
  2576. # Skip padding
  2577. graph_skip($handle, 4 - $length % 4, "string padding") or return undef;
  2578. return $string;
  2579. }
  2580. #
  2581. # read_bbg_lines_record(handle, bbg_filename, bb, fileorder, filename,
  2582. # function)
  2583. #
  2584. # Read a bbg format lines record from handle and add the relevant data to
  2585. # bb and fileorder. Return filename on success, undef on error.
  2586. #
  2587. sub read_bbg_lines_record(*$$$$$)
  2588. {
  2589. my ($handle, $bbg_filename, $bb, $fileorder, $filename, $function) = @_;
  2590. my $string;
  2591. my $lineno;
  2592. graph_expect("lines record");
  2593. # Skip basic block index
  2594. graph_skip($handle, 4, "basic block index") or return undef;
  2595. while (1) {
  2596. # Read line number
  2597. $lineno = read_bbg_value($handle, "line number");
  2598. return undef if (!defined($lineno));
  2599. if ($lineno == 0) {
  2600. # Got a marker for a new filename
  2601. graph_expect("filename");
  2602. $string = read_bbg_string($handle);
  2603. return undef if (!defined($string));
  2604. # Check for end of record
  2605. if ($string eq "") {
  2606. return $filename;
  2607. }
  2608. $filename = $string;
  2609. if (!exists($bb->{$function}->{$filename})) {
  2610. $bb->{$function}->{$filename} = [];
  2611. }
  2612. next;
  2613. }
  2614. # Got an actual line number
  2615. if (!defined($filename)) {
  2616. warn("WARNING: unassigned line number in ".
  2617. "$bbg_filename\n");
  2618. next;
  2619. }
  2620. push(@{$bb->{$function}->{$filename}}, $lineno);
  2621. graph_add_order($fileorder, $function, $filename);
  2622. }
  2623. }
  2624. #
  2625. # read_bbg(filename)
  2626. #
  2627. # Read the contents of the specified .bbg file and return the following mapping:
  2628. # graph: filename -> file_data
  2629. # file_data: function name -> line_data
  2630. # line_data: [ line1, line2, ... ]
  2631. #
  2632. # See the gcov-io.h file in the SLES 9 gcc 3.3.3 source code for a description
  2633. # of the .bbg format.
  2634. #
  2635. sub read_bbg($)
  2636. {
  2637. my ($bbg_filename) = @_;
  2638. my $file_magic = 0x67626267;
  2639. my $tag_function = 0x01000000;
  2640. my $tag_lines = 0x01450000;
  2641. my $word;
  2642. my $tag;
  2643. my $length;
  2644. my $function;
  2645. my $filename;
  2646. my $bb = {};
  2647. my $fileorder = {};
  2648. my $instr;
  2649. my $graph;
  2650. local *HANDLE;
  2651. open(HANDLE, "<", $bbg_filename) or goto open_error;
  2652. binmode(HANDLE);
  2653. # Read magic
  2654. $word = read_bbg_value(*HANDLE, "file magic");
  2655. goto incomplete if (!defined($word));
  2656. # Check magic
  2657. if ($word != $file_magic) {
  2658. goto magic_error;
  2659. }
  2660. # Skip version
  2661. graph_skip(*HANDLE, 4, "version") or goto incomplete;
  2662. while (!eof(HANDLE)) {
  2663. # Read record tag
  2664. $tag = read_bbg_value(*HANDLE, "record tag");
  2665. goto incomplete if (!defined($tag));
  2666. # Read record length
  2667. $length = read_bbg_value(*HANDLE, "record length");
  2668. goto incomplete if (!defined($tag));
  2669. if ($tag == $tag_function) {
  2670. graph_expect("function record");
  2671. # Read function name
  2672. graph_expect("function name");
  2673. $function = read_bbg_string(*HANDLE);
  2674. goto incomplete if (!defined($function));
  2675. $filename = undef;
  2676. # Skip function checksum
  2677. graph_skip(*HANDLE, 4, "function checksum")
  2678. or goto incomplete;
  2679. } elsif ($tag == $tag_lines) {
  2680. # Read lines record
  2681. $filename = read_bbg_lines_record(HANDLE, $bbg_filename,
  2682. $bb, $fileorder, $filename,
  2683. $function);
  2684. goto incomplete if (!defined($filename));
  2685. } else {
  2686. # Skip record contents
  2687. graph_skip(*HANDLE, $length, "unhandled record")
  2688. or goto incomplete;
  2689. }
  2690. }
  2691. close(HANDLE);
  2692. ($instr, $graph) = graph_from_bb($bb, $fileorder, $bbg_filename);
  2693. graph_cleanup($graph);
  2694. return ($instr, $graph);
  2695. open_error:
  2696. graph_error($bbg_filename, "could not open file");
  2697. return undef;
  2698. incomplete:
  2699. graph_error($bbg_filename, "reached unexpected end of file");
  2700. return undef;
  2701. magic_error:
  2702. graph_error($bbg_filename, "found unrecognized bbg file magic");
  2703. return undef;
  2704. }
  2705. #
  2706. # read_gcno_word(handle[, description, peek])
  2707. #
  2708. # Read and return a word in .gcno format.
  2709. #
  2710. sub read_gcno_word(*;$$)
  2711. {
  2712. my ($handle, $desc, $peek) = @_;
  2713. return graph_read($handle, 4, $desc, $peek);
  2714. }
  2715. #
  2716. # read_gcno_value(handle, big_endian[, description, peek])
  2717. #
  2718. # Read a word in .gcno format from handle and return its integer value
  2719. # according to the specified endianness. If PEEK is non-zero, reset file
  2720. # position after read.
  2721. #
  2722. sub read_gcno_value(*$;$$)
  2723. {
  2724. my ($handle, $big_endian, $desc, $peek) = @_;
  2725. my $word;
  2726. my $pos;
  2727. $word = read_gcno_word($handle, $desc, $peek);
  2728. return undef if (!defined($word));
  2729. if ($big_endian) {
  2730. return unpack("N", $word);
  2731. } else {
  2732. return unpack("V", $word);
  2733. }
  2734. }
  2735. #
  2736. # read_gcno_string(handle, big_endian)
  2737. #
  2738. # Read and return a string in .gcno format.
  2739. #
  2740. sub read_gcno_string(*$)
  2741. {
  2742. my ($handle, $big_endian) = @_;
  2743. my $length;
  2744. my $string;
  2745. graph_expect("string");
  2746. # Read string length
  2747. $length = read_gcno_value($handle, $big_endian, "string length");
  2748. return undef if (!defined($length));
  2749. if ($length == 0) {
  2750. return "";
  2751. }
  2752. $length *= 4;
  2753. # Read string
  2754. $string = graph_read($handle, $length, "string and padding");
  2755. return undef if (!defined($string));
  2756. $string =~ s/\0//g;
  2757. return $string;
  2758. }
  2759. #
  2760. # read_gcno_lines_record(handle, gcno_filename, bb, fileorder, filename,
  2761. # function, big_endian)
  2762. #
  2763. # Read a gcno format lines record from handle and add the relevant data to
  2764. # bb and fileorder. Return filename on success, undef on error.
  2765. #
  2766. sub read_gcno_lines_record(*$$$$$$)
  2767. {
  2768. my ($handle, $gcno_filename, $bb, $fileorder, $filename, $function,
  2769. $big_endian) = @_;
  2770. my $string;
  2771. my $lineno;
  2772. graph_expect("lines record");
  2773. # Skip basic block index
  2774. graph_skip($handle, 4, "basic block index") or return undef;
  2775. while (1) {
  2776. # Read line number
  2777. $lineno = read_gcno_value($handle, $big_endian, "line number");
  2778. return undef if (!defined($lineno));
  2779. if ($lineno == 0) {
  2780. # Got a marker for a new filename
  2781. graph_expect("filename");
  2782. $string = read_gcno_string($handle, $big_endian);
  2783. return undef if (!defined($string));
  2784. # Check for end of record
  2785. if ($string eq "") {
  2786. return $filename;
  2787. }
  2788. $filename = $string;
  2789. if (!exists($bb->{$function}->{$filename})) {
  2790. $bb->{$function}->{$filename} = [];
  2791. }
  2792. next;
  2793. }
  2794. # Got an actual line number
  2795. if (!defined($filename)) {
  2796. warn("WARNING: unassigned line number in ".
  2797. "$gcno_filename\n");
  2798. next;
  2799. }
  2800. # Add to list
  2801. push(@{$bb->{$function}->{$filename}}, $lineno);
  2802. graph_add_order($fileorder, $function, $filename);
  2803. }
  2804. }
  2805. #
  2806. # determine_gcno_split_crc(handle, big_endian, rec_length)
  2807. #
  2808. # Determine if HANDLE refers to a .gcno file with a split checksum function
  2809. # record format. Return non-zero in case of split checksum format, zero
  2810. # otherwise, undef in case of read error.
  2811. #
  2812. sub determine_gcno_split_crc($$$)
  2813. {
  2814. my ($handle, $big_endian, $rec_length) = @_;
  2815. my $strlen;
  2816. my $overlong_string;
  2817. return 1 if ($gcov_version >= $GCOV_VERSION_4_7_0);
  2818. return 1 if (is_compat($COMPAT_MODE_SPLIT_CRC));
  2819. # Heuristic:
  2820. # Decide format based on contents of next word in record:
  2821. # - pre-gcc 4.7
  2822. # This is the function name length / 4 which should be
  2823. # less than the remaining record length
  2824. # - gcc 4.7
  2825. # This is a checksum, likely with high-order bits set,
  2826. # resulting in a large number
  2827. $strlen = read_gcno_value($handle, $big_endian, undef, 1);
  2828. return undef if (!defined($strlen));
  2829. $overlong_string = 1 if ($strlen * 4 >= $rec_length - 12);
  2830. if ($overlong_string) {
  2831. if (is_compat_auto($COMPAT_MODE_SPLIT_CRC)) {
  2832. info("Auto-detected compatibility mode for split ".
  2833. "checksum .gcno file format\n");
  2834. return 1;
  2835. } else {
  2836. # Sanity check
  2837. warn("Found overlong string in function record: ".
  2838. "try '--compat split_crc'\n");
  2839. }
  2840. }
  2841. return 0;
  2842. }
  2843. #
  2844. # read_gcno_function_record(handle, graph, big_endian, rec_length)
  2845. #
  2846. # Read a gcno format function record from handle and add the relevant data
  2847. # to graph. Return (filename, function) on success, undef on error.
  2848. #
  2849. sub read_gcno_function_record(*$$$$)
  2850. {
  2851. my ($handle, $bb, $fileorder, $big_endian, $rec_length) = @_;
  2852. my $filename;
  2853. my $function;
  2854. my $lineno;
  2855. my $lines;
  2856. graph_expect("function record");
  2857. # Skip ident and checksum
  2858. graph_skip($handle, 8, "function ident and checksum") or return undef;
  2859. # Determine if this is a function record with split checksums
  2860. if (!defined($gcno_split_crc)) {
  2861. $gcno_split_crc = determine_gcno_split_crc($handle, $big_endian,
  2862. $rec_length);
  2863. return undef if (!defined($gcno_split_crc));
  2864. }
  2865. # Skip cfg checksum word in case of split checksums
  2866. graph_skip($handle, 4, "function cfg checksum") if ($gcno_split_crc);
  2867. # Read function name
  2868. graph_expect("function name");
  2869. $function = read_gcno_string($handle, $big_endian);
  2870. return undef if (!defined($function));
  2871. # Read filename
  2872. graph_expect("filename");
  2873. $filename = read_gcno_string($handle, $big_endian);
  2874. return undef if (!defined($filename));
  2875. # Read first line number
  2876. $lineno = read_gcno_value($handle, $big_endian, "initial line number");
  2877. return undef if (!defined($lineno));
  2878. # Add to list
  2879. push(@{$bb->{$function}->{$filename}}, $lineno);
  2880. graph_add_order($fileorder, $function, $filename);
  2881. return ($filename, $function);
  2882. }
  2883. #
  2884. # read_gcno(filename)
  2885. #
  2886. # Read the contents of the specified .gcno file and return the following
  2887. # mapping:
  2888. # graph: filename -> file_data
  2889. # file_data: function name -> line_data
  2890. # line_data: [ line1, line2, ... ]
  2891. #
  2892. # See the gcov-io.h file in the gcc 3.3 source code for a description of
  2893. # the .gcno format.
  2894. #
  2895. sub read_gcno($)
  2896. {
  2897. my ($gcno_filename) = @_;
  2898. my $file_magic = 0x67636e6f;
  2899. my $tag_function = 0x01000000;
  2900. my $tag_lines = 0x01450000;
  2901. my $big_endian;
  2902. my $word;
  2903. my $tag;
  2904. my $length;
  2905. my $filename;
  2906. my $function;
  2907. my $bb = {};
  2908. my $fileorder = {};
  2909. my $instr;
  2910. my $graph;
  2911. local *HANDLE;
  2912. open(HANDLE, "<", $gcno_filename) or goto open_error;
  2913. binmode(HANDLE);
  2914. # Read magic
  2915. $word = read_gcno_word(*HANDLE, "file magic");
  2916. goto incomplete if (!defined($word));
  2917. # Determine file endianness
  2918. if (unpack("N", $word) == $file_magic) {
  2919. $big_endian = 1;
  2920. } elsif (unpack("V", $word) == $file_magic) {
  2921. $big_endian = 0;
  2922. } else {
  2923. goto magic_error;
  2924. }
  2925. # Skip version and stamp
  2926. graph_skip(*HANDLE, 8, "version and stamp") or goto incomplete;
  2927. while (!eof(HANDLE)) {
  2928. my $next_pos;
  2929. my $curr_pos;
  2930. # Read record tag
  2931. $tag = read_gcno_value(*HANDLE, $big_endian, "record tag");
  2932. goto incomplete if (!defined($tag));
  2933. # Read record length
  2934. $length = read_gcno_value(*HANDLE, $big_endian,
  2935. "record length");
  2936. goto incomplete if (!defined($length));
  2937. # Convert length to bytes
  2938. $length *= 4;
  2939. # Calculate start of next record
  2940. $next_pos = tell(HANDLE);
  2941. goto tell_error if ($next_pos == -1);
  2942. $next_pos += $length;
  2943. # Process record
  2944. if ($tag == $tag_function) {
  2945. ($filename, $function) = read_gcno_function_record(
  2946. *HANDLE, $bb, $fileorder, $big_endian,
  2947. $length);
  2948. goto incomplete if (!defined($function));
  2949. } elsif ($tag == $tag_lines) {
  2950. # Read lines record
  2951. $filename = read_gcno_lines_record(*HANDLE,
  2952. $gcno_filename, $bb, $fileorder,
  2953. $filename, $function,
  2954. $big_endian);
  2955. goto incomplete if (!defined($filename));
  2956. } else {
  2957. # Skip record contents
  2958. graph_skip(*HANDLE, $length, "unhandled record")
  2959. or goto incomplete;
  2960. }
  2961. # Ensure that we are at the start of the next record
  2962. $curr_pos = tell(HANDLE);
  2963. goto tell_error if ($curr_pos == -1);
  2964. next if ($curr_pos == $next_pos);
  2965. goto record_error if ($curr_pos > $next_pos);
  2966. graph_skip(*HANDLE, $next_pos - $curr_pos,
  2967. "unhandled record content")
  2968. or goto incomplete;
  2969. }
  2970. close(HANDLE);
  2971. ($instr, $graph) = graph_from_bb($bb, $fileorder, $gcno_filename);
  2972. graph_cleanup($graph);
  2973. return ($instr, $graph);
  2974. open_error:
  2975. graph_error($gcno_filename, "could not open file");
  2976. return undef;
  2977. incomplete:
  2978. graph_error($gcno_filename, "reached unexpected end of file");
  2979. return undef;
  2980. magic_error:
  2981. graph_error($gcno_filename, "found unrecognized gcno file magic");
  2982. return undef;
  2983. tell_error:
  2984. graph_error($gcno_filename, "could not determine file position");
  2985. return undef;
  2986. record_error:
  2987. graph_error($gcno_filename, "found unrecognized record format");
  2988. return undef;
  2989. }
  2990. sub debug($)
  2991. {
  2992. my ($msg) = @_;
  2993. return if (!$debug);
  2994. print(STDERR "DEBUG: $msg");
  2995. }
  2996. #
  2997. # get_gcov_capabilities
  2998. #
  2999. # Determine the list of available gcov options.
  3000. #
  3001. sub get_gcov_capabilities()
  3002. {
  3003. my $help = `$gcov_tool --help`;
  3004. my %capabilities;
  3005. foreach (split(/\n/, $help)) {
  3006. next if (!/--(\S+)/);
  3007. next if ($1 eq 'help');
  3008. next if ($1 eq 'version');
  3009. next if ($1 eq 'object-directory');
  3010. $capabilities{$1} = 1;
  3011. debug("gcov has capability '$1'\n");
  3012. }
  3013. return \%capabilities;
  3014. }
  3015. #
  3016. # parse_ignore_errors(@ignore_errors)
  3017. #
  3018. # Parse user input about which errors to ignore.
  3019. #
  3020. sub parse_ignore_errors(@)
  3021. {
  3022. my (@ignore_errors) = @_;
  3023. my @items;
  3024. my $item;
  3025. return if (!@ignore_errors);
  3026. foreach $item (@ignore_errors) {
  3027. $item =~ s/\s//g;
  3028. if ($item =~ /,/) {
  3029. # Split and add comma-separated parameters
  3030. push(@items, split(/,/, $item));
  3031. } else {
  3032. # Add single parameter
  3033. push(@items, $item);
  3034. }
  3035. }
  3036. foreach $item (@items) {
  3037. my $item_id = $ERROR_ID{lc($item)};
  3038. if (!defined($item_id)) {
  3039. die("ERROR: unknown argument for --ignore-errors: ".
  3040. "$item\n");
  3041. }
  3042. $ignore[$item_id] = 1;
  3043. }
  3044. }
  3045. #
  3046. # is_external(filename)
  3047. #
  3048. # Determine if a file is located outside of the specified data directories.
  3049. #
  3050. sub is_external($)
  3051. {
  3052. my ($filename) = @_;
  3053. my $dir;
  3054. foreach $dir (@internal_dirs) {
  3055. return 0 if ($filename =~ /^\Q$dir\/\E/);
  3056. }
  3057. return 1;
  3058. }
  3059. #
  3060. # compat_name(mode)
  3061. #
  3062. # Return the name of compatibility mode MODE.
  3063. #
  3064. sub compat_name($)
  3065. {
  3066. my ($mode) = @_;
  3067. my $name = $COMPAT_MODE_TO_NAME{$mode};
  3068. return $name if (defined($name));
  3069. return "<unknown>";
  3070. }
  3071. #
  3072. # parse_compat_modes(opt)
  3073. #
  3074. # Determine compatibility mode settings.
  3075. #
  3076. sub parse_compat_modes($)
  3077. {
  3078. my ($opt) = @_;
  3079. my @opt_list;
  3080. my %specified;
  3081. # Initialize with defaults
  3082. %compat_value = %COMPAT_MODE_DEFAULTS;
  3083. # Add old style specifications
  3084. if (defined($opt_compat_libtool)) {
  3085. $compat_value{$COMPAT_MODE_LIBTOOL} =
  3086. $opt_compat_libtool ? $COMPAT_VALUE_ON
  3087. : $COMPAT_VALUE_OFF;
  3088. }
  3089. # Parse settings
  3090. if (defined($opt)) {
  3091. @opt_list = split(/\s*,\s*/, $opt);
  3092. }
  3093. foreach my $directive (@opt_list) {
  3094. my ($mode, $value);
  3095. # Either
  3096. # mode=off|on|auto or
  3097. # mode (implies on)
  3098. if ($directive !~ /^(\w+)=(\w+)$/ &&
  3099. $directive !~ /^(\w+)$/) {
  3100. die("ERROR: Unknown compatibility mode specification: ".
  3101. "$directive!\n");
  3102. }
  3103. # Determine mode
  3104. $mode = $COMPAT_NAME_TO_MODE{lc($1)};
  3105. if (!defined($mode)) {
  3106. die("ERROR: Unknown compatibility mode '$1'!\n");
  3107. }
  3108. $specified{$mode} = 1;
  3109. # Determine value
  3110. if (defined($2)) {
  3111. $value = $COMPAT_NAME_TO_VALUE{lc($2)};
  3112. if (!defined($value)) {
  3113. die("ERROR: Unknown compatibility mode ".
  3114. "value '$2'!\n");
  3115. }
  3116. } else {
  3117. $value = $COMPAT_VALUE_ON;
  3118. }
  3119. $compat_value{$mode} = $value;
  3120. }
  3121. # Perform auto-detection
  3122. foreach my $mode (sort(keys(%compat_value))) {
  3123. my $value = $compat_value{$mode};
  3124. my $is_autodetect = "";
  3125. my $name = compat_name($mode);
  3126. if ($value == $COMPAT_VALUE_AUTO) {
  3127. my $autodetect = $COMPAT_MODE_AUTO{$mode};
  3128. if (!defined($autodetect)) {
  3129. die("ERROR: No auto-detection for ".
  3130. "mode '$name' available!\n");
  3131. }
  3132. if (ref($autodetect) eq "CODE") {
  3133. $value = &$autodetect();
  3134. $compat_value{$mode} = $value;
  3135. $is_autodetect = " (auto-detected)";
  3136. }
  3137. }
  3138. if ($specified{$mode}) {
  3139. if ($value == $COMPAT_VALUE_ON) {
  3140. info("Enabling compatibility mode ".
  3141. "'$name'$is_autodetect\n");
  3142. } elsif ($value == $COMPAT_VALUE_OFF) {
  3143. info("Disabling compatibility mode ".
  3144. "'$name'$is_autodetect\n");
  3145. } else {
  3146. info("Using delayed auto-detection for ".
  3147. "compatibility mode ".
  3148. "'$name'\n");
  3149. }
  3150. }
  3151. }
  3152. }
  3153. sub compat_hammer_autodetect()
  3154. {
  3155. if ($gcov_version_string =~ /suse/i && $gcov_version == 0x30303 ||
  3156. $gcov_version_string =~ /mandrake/i && $gcov_version == 0x30302)
  3157. {
  3158. info("Auto-detected compatibility mode for GCC 3.3 (hammer)\n");
  3159. return $COMPAT_VALUE_ON;
  3160. }
  3161. return $COMPAT_VALUE_OFF;
  3162. }
  3163. #
  3164. # is_compat(mode)
  3165. #
  3166. # Return non-zero if compatibility mode MODE is enabled.
  3167. #
  3168. sub is_compat($)
  3169. {
  3170. my ($mode) = @_;
  3171. return 1 if ($compat_value{$mode} == $COMPAT_VALUE_ON);
  3172. return 0;
  3173. }
  3174. #
  3175. # is_compat_auto(mode)
  3176. #
  3177. # Return non-zero if compatibility mode MODE is set to auto-detect.
  3178. #
  3179. sub is_compat_auto($)
  3180. {
  3181. my ($mode) = @_;
  3182. return 1 if ($compat_value{$mode} == $COMPAT_VALUE_AUTO);
  3183. return 0;
  3184. }