home.html 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747
  1. <!DOCTYPE html>
  2. <!--
  3. Metrix++, Copyright 2009-2013, Metrix++ Project
  4. Link: http://metrixplusplus.sourceforge.net
  5. This file is part of Metrix++ Tool.
  6. Metrix++ is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, version 3 of the License.
  9. Metrix++ is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Metrix++. If not, see <http://www.gnu.org/licenses/>.
  15. -->
  16. <html lang="en">
  17. <head>
  18. <meta charset="utf-8">
  19. <title>Metrix++ Project</title>
  20. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  21. <meta name="description" content="">
  22. <meta name="author" content="">
  23. <!-- Le styles -->
  24. <!--
  25. <link href="../../style.css" rel="stylesheet">
  26. -->
  27. <link href="assets/css/bootstrap.css" rel="stylesheet">
  28. <link href="assets/css/bootstrap-responsive.css" rel="stylesheet">
  29. <link href="assets/css/docs.css" rel="stylesheet">
  30. <link href="assets/js/google-code-prettify/prettify.css" rel="stylesheet">
  31. <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
  32. <!--[if lt IE 9]>
  33. <script src="assets/js/html5shiv.js"></script>
  34. <![endif]-->
  35. <!-- Le fav and touch icons -->
  36. <link rel="apple-touch-icon-precomposed" sizes="144x144" href="assets/ico/apple-touch-icon-144-precomposed.png">
  37. <link rel="apple-touch-icon-precomposed" sizes="114x114" href="assets/ico/apple-touch-icon-114-precomposed.png">
  38. <link rel="apple-touch-icon-precomposed" sizes="72x72" href="assets/ico/apple-touch-icon-72-precomposed.png">
  39. <link rel="apple-touch-icon-precomposed" href="assets/ico/apple-touch-icon-57-precomposed.png">
  40. <link rel="shortcut icon" href="assets/ico/favicon.png">
  41. <!--
  42. <script type="text/javascript">
  43. var _gaq = _gaq || [];
  44. _gaq.push(['_setAccount', 'UA-146052-10']);
  45. _gaq.push(['_trackPageview']);
  46. (function() {
  47. var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  48. ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  49. var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  50. })();
  51. </script>
  52. -->
  53. </head>
  54. <body data-spy="scroll" data-target=".bs-docs-sidebar">
  55. <!-- Navbar
  56. ================================================== -->
  57. <div class="navbar navbar-link navbar-fixed-top">
  58. <div class="navbar-inner">
  59. <div class="container">
  60. <button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
  61. <span class="icon-bar"></span>
  62. <span class="icon-bar"></span>
  63. <span class="icon-bar"></span>
  64. </button>
  65. <a class="brand" href="./index.html">Metrix++</a>
  66. <div class="nav-collapse collapse">
  67. <ul class="nav">
  68. <li class="">
  69. <a href="./index.html">Home</a>
  70. </li>
  71. </ul>
  72. </div>
  73. </div>
  74. </div>
  75. </div>
  76. <!-- Subhead
  77. ================================================== -->
  78. <header class="jumbotron" id="overview">
  79. <div id="myCarousel" class="carousel slide">
  80. <div class="carousel-inner">
  81. <div class="item active">
  82. <img src="assets/img/slide-01.jpg" alt="">
  83. <div class="container">
  84. <div class="carousel-caption">
  85. <h2>Multiple languages</h2>
  86. <p class="lead">&middot; C/C++, C# and Java.</p>
  87. <p class="lead">&middot; Recognises classes, interfaces, namespaces, functions, comments, preprocessor and much more.</p>
  88. </div>
  89. </div>
  90. </div>
  91. <div class="item">
  92. <img src="assets/img/slide-02.jpg" alt="">
  93. <div class="container">
  94. <div class="carousel-caption">
  95. <h2>Multiple metrics</h2>
  96. <p class="lead">&middot; Complexity, size and other.</p>
  97. </div>
  98. </div>
  99. </div>
  100. <div class="item">
  101. <img src="assets/img/slide-03.jpg" alt="">
  102. <div class="container">
  103. <div class="carousel-caption">
  104. <h2>High performance and scalability</h2>
  105. <p class="lead">&middot; Applicable to huge code bases: thousands of files per minute.</p>
  106. <p class="lead">&middot; Ultra-fast feedback on iterative re-run.</p>
  107. </div>
  108. </div>
  109. </div>
  110. <div class="item">
  111. <img src="assets/img/slide-01.jpg" alt="">
  112. <div class="container">
  113. <div class="carousel-caption">
  114. <h2>Effortless application to legacy code</h2>
  115. <p class="lead">&middot; Recognises legacy, modified and new code.</p>
  116. <p class="lead">&middot; Prevents from negative trends. Encourages positive.</p>
  117. </div>
  118. </div>
  119. </div>
  120. <div class="item">
  121. <img src="assets/img/slide-02.jpg" alt="">
  122. <div class="container">
  123. <div class="carousel-caption">
  124. <h2>Configurable</h2>
  125. <p class="lead">&middot; Define and apply your rules and policies.</p>
  126. <p class="lead">&middot; Integrate with your workflow.</p>
  127. </div>
  128. </div>
  129. </div>
  130. <div class="item">
  131. <img src="assets/img/slide-03.jpg" alt="">
  132. <div class="container">
  133. <div class="carousel-caption">
  134. <h2>Extendable via plugins</h2>
  135. <p class="lead">&middot; Define your custom metric.</p>
  136. <p class="lead">&middot; Add new language parser.</p>
  137. <p class="lead">&middot; Create advanced post-analysis tool.</p>
  138. </div>
  139. </div>
  140. </div>
  141. </div>
  142. <a class="left carousel-control" href="#myCarousel" data-slide="prev">&lsaquo;</a>
  143. <a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>
  144. </div>
  145. <div class="container">
  146. <div class="row">
  147. <div class="span3">
  148. </div>
  149. <div class="span9">
  150. <h5 class="text-right">Management of source code quality is possible.</h5>
  151. <p class="text-right">
  152. <a href="https://sourceforge.net/projects/metrixplusplus/files/latest/download"
  153. ><button type="button"class="btn btn-danger">Download</button></a>
  154. <!--
  155. <button type="button"class="btn btn-warning">Donate</button>
  156. -->
  157. </p>
  158. </div>
  159. </div>
  160. </div>
  161. </header>
  162. <div class="container"><div class="row">
  163. <!-- Docs nav
  164. ================================================== -->
  165. <div class="span3 bs-docs-sidebar">
  166. <ul class="nav nav-list bs-docs-sidenav">
  167. <!--<li><img src="../../logo_project.png"/><p>&nbsp;</p></li>-->
  168. <li><a href="#overview_section"><i class="icon-chevron-right"></i> Overview</a></li>
  169. <li><a href="#download_section"><i class="icon-chevron-right"></i> Download &amp; Install</a></li>
  170. <li><a href="#workflow_collect_section"><i class="icon-chevron-right"></i> Workflow: Collect data</a></li>
  171. <li><a href="#workflow_view_section"><i class="icon-chevron-right"></i> Workflow: View data</a></li>
  172. <li><a href="#workflow_view_summary_section"><i class="icon-hand-right"></i> &middot; summary &amp; distributions</a></li>
  173. <li><a href="#workflow_view_details_section"><i class="icon-hand-right"></i> &middot; details per file/region</a></li>
  174. <li><a href="#workflow_limit_section"><i class="icon-chevron-right"></i> Workflow: Apply thresholds</a></li>
  175. <li><a href="#workflow_limit_hotspots_section"><i class="icon-hand-right"></i> &middot; hotspots</a></li>
  176. <li><a href="#workflow_limit_control_section"><i class="icon-hand-right"></i> &middot; controlling trends</a></li>
  177. <li><a href="#workflow_other_section"><i class="icon-chevron-right"></i> Workflow: Other applications</a></li>
  178. <li><a href="#extend_section"><i class="icon-chevron-right"></i> Create plugin</a></li>
  179. <li><a href="#contribute_section"><i class="icon-chevron-right"></i> Feedback &amp; Contribute</a></li>
  180. </ul>
  181. </div>
  182. <!-- Sections
  183. ================================================== -->
  184. <div class="span9">
  185. <!-- Overview
  186. ================================================== -->
  187. <section id="overview_section">
  188. <div class="page-header">
  189. <h1>Overview</h1>
  190. </div>
  191. <h3>Highlights</h3>
  192. <p>Metrix++ is a tool to collect and analyse code metrics. Any metric is useless if it is not used.
  193. Metrix++ offers great usage capabilities and assists with variety of application use cases:</p>
  194. <ul>
  195. <li>Monitoring trends on <strong>daily</strong> basis. In order to take actions or make right decisions earlier.</li>
  196. <li>Enforcing trends on <strong>hourly</strong> basis, at every commit of code changes. In order to control quality in time, i.e. when it is not too late to redo.</li>
  197. <li>Assisiting on <strong>per minute</strong> basis during code refactoring and code development, where coding and quality standards matter.</li>
  198. </ul>
  199. <p>The workflow sections below demonstarate basic application usecases.</p>
  200. <h3>Languages supported</h3>
  201. <p>The tool can parse C/C++, C# and Java source code files. The parser identifies certain regions in the code,
  202. such as classes, functions, namespaces, interfaces, etc. It obviously detects comments, strings and code for preprocessor.
  203. The identified regions form a tree of nested source code blocks, which are refered after and additionally scanned by metrics plugins.
  204. This concept allows to attribute metrics per regions, what gives fine grained data and rich input to analysis tools.
  205. The following example demonstrates regions concept.</p>
  206. <table class="table">
  207. <thead>
  208. <tr>
  209. <th>Source code</th>
  210. <th>Regions tree [type: name: content type]</th>
  211. </tr>
  212. </thead>
  213. <tbody>
  214. <tr>
  215. <td>
  216. <pre class="prettyprint linenums">
  217. // simple C++ code
  218. #include &lt;myapi.h&gt;
  219. // I explain the following class
  220. class MyClass {
  221. public:
  222. int m_var; // some member
  223. // I explain the following function
  224. MyClass(): m_var(0) {
  225. char str[] = "unused string"
  226. // nested region for good measure
  227. struct MyStruct {};
  228. }
  229. // Do not judge ugly code below
  230. #define get_max(a,b) ((a)>(b)?(a):(b))
  231. set_max(int b) {
  232. m_var = get_max(m_var, b);
  233. }
  234. };
  235. // this is the last line
  236. </pre>
  237. </td>
  238. <td>
  239. <pre class="prettyprint linenums">
  240. file: __global__: comment
  241. file: __global__: code
  242. file: __global__: preprocessor
  243. file: __global__: code
  244. class: MyClass: comment
  245. class: MyClass: code
  246. class: MyClass: code
  247. class: MyClass: code, comment
  248. class: MyClass: code
  249. function: MyClass: comment
  250. function: MyClass: code
  251. function: MyClass: code, string
  252. function: MyClass: code
  253. struct: MyStruct: comment
  254. struct: MyStruct: code
  255. function: MyClass: code
  256. class: MyClass: code
  257. function: set_max: comment
  258. function: set_max: preprocessor
  259. function: set_max: code
  260. function: set_max: code
  261. function: set_max: code
  262. class: MyClass: code
  263. file: __global__: comment
  264. </pre>
  265. </td>
  266. </tr>
  267. </tbody>
  268. </table>
  269. <h3>Metrics</h3>
  270. <p>Metrics highlighed in blue are <strong>per file</strong> metrics. Other metrics are <strong>per region</strong> metrics.
  271. Region term is explained in the previous chapter.</p>
  272. <table class="table table-bordered">
  273. <thead>
  274. <tr>
  275. <th>Metric (enable option)</th>
  276. <th>Brief description</th>
  277. <th>Motivation</th>
  278. </tr>
  279. </thead>
  280. <tbody>
  281. <tr class="info">
  282. <td>std.general.size</td>
  283. <td>Size of a file in bytes.</td>
  284. <td rowspan="4"><ul><li>Monitoring growth of source code base.</li>
  285. <li>Normalizing other metrics.</li>
  286. <li>Preventing large files and regions (large things difficult to maintain).</li>
  287. <li>Predicting delivery dates by comparing
  288. <a href="http://www.compaid.com/caiInternet/casestudies/kanarticle2.pdf">S-shaped code base growth / change curves</a>.</li></ul></td>
  289. </tr>
  290. <tr>
  291. <td>std.code.length.total</td>
  292. <td>The same as 'std.general.size' metric, but attributed to code regions.</td>
  293. <td></td>
  294. </tr>
  295. <tr>
  296. <td>std.code.lines.total</td>
  297. <td>Number of non-blank lines of code of any content type (exectuable, comments, etc.)</td>
  298. <td></td>
  299. </tr>
  300. <tr>
  301. <td>std.code.lines.code</td>
  302. <td>Number of non-blank lines of code excluding preprocessor and comments.</td>
  303. <td></td>
  304. </tr>
  305. <tr>
  306. <td>std.code.lines.preprocessor</td>
  307. <td>Number of non-blank lines of preprocessor code.</td>
  308. <td><ul><li>Enforcing localisation of preprocessor code in a single place.</li>
  309. <li>Encouraging usage of safer code structures instead of preprocessor.</li></ul></td>
  310. </tr>
  311. <tr>
  312. <td>std.code.lines.comments</td>
  313. <td>Number of non-blank lines of comments.</td>
  314. <td><ul><li>Low number of comments may indicate about maintainability problems.</li></ul></td>
  315. </tr>
  316. <tr>
  317. <td>std.code.complexity.cyclomatic</td>
  318. <td>McCabe cyclomatic complexity metric.</td>
  319. <td colspan="2"><ul><li>Identification of highly complex code for review and refactoring.</li>
  320. <li>Preventing complex functions (complexity is a reason of many defects and a reason of expensive maintaintenance).</li></ul></td>
  321. </tr>
  322. <tr>
  323. <td>std.code.complexity.maxindent</td>
  324. <td>Maximum level of indentation of blocks within a region. For example, the following class has got
  325. the metric equal to 1 and the function has got it equal to 2:
  326. <pre class="prettyprint">
  327. class WeekClass {
  328. int isWeekend(int day) {
  329. if (day == SATURDAY ||
  330. day == SUNDAY) {
  331. return true;
  332. }
  333. return false;
  334. }
  335. }
  336. </pre>
  337. </td>
  338. </tr>
  339. <tr>
  340. <td>std.suppress</td>
  341. <td>An option enables collection of Metrix++ suppressions and 2 metrics: 'std.suppress:count' and
  342. 'std.suppress.file:count'. The first is number of suppressions per region.
  343. The second is the same but applies for file-scope metrics.</td>
  344. <td><ul><li>Suppressing false-positives.</li>
  345. <li>Managing the amount of suppressions. Usually there are no false-positives to suppress with the right metric,
  346. but could be exceptions in specific cases. Managing suppressions is about managing exceptions.
  347. If there are many exceptional cases, maybe something is wrong with a metric or an application of a metric.</li></ul></td>
  348. </tr>
  349. <tr class="info">
  350. <td>std.general.procerrors</td>
  351. <td>Number of errors detected by Metrix++ code parser.
  352. Errors, like mismatched brackets, may result in bad identification of regions.</td>
  353. <td><ul><li>Cleaning up errors to ensure reliable code scanning.</li></ul></td>
  354. </tr>
  355. <tr class="info">
  356. <td>std.general.proctime</td>
  357. <td>Seconds spent on processing a file.</td>
  358. <td><ul><li>Monitor and profile tool's performance.</li></ul></td>
  359. </tr>
  360. </tbody>
  361. </table>
  362. </section>
  363. <section id="download_section">
  364. <div class="page-header">
  365. <h1>Download &amp; Install</h1>
  366. </div>
  367. <p>In order to get the tool working, <a href="https://sourceforge.net/projects/metrixplusplus/files/latest/download">dowload the archive</a> with the latest stable version
  368. and unpack it to some folder. First run of the tool will trigger the installation within the folder,
  369. where it was launched.</p>
  370. <p>Corresponding checkout from the <a href="https://sourceforge.net/p/metrixplusplus/code">version control system</a> is the following:</p>
  371. <pre>svn checkout <a href="https://sourceforge.net/p/metrixplusplus/code">svn://svn.code.sf.net/p/metrixplusplus/code</a>/releases/latest Metrix++</pre>
  372. <p>In order to checkout the latest version under development, use this command:</p>
  373. <pre>svn checkout <a href="https://sourceforge.net/p/metrixplusplus/code">svn://svn.code.sf.net/p/metrixplusplus/code</a>/mainline Metrix++</pre>
  374. <h4>Prerequisites</h4>
  375. <p>Python Runtime Environment (version 2.7.* or later, version 3.* has not been tested)</p>
  376. <h4>License</h4>
  377. <p>This program is free software; you can redistribute it and/or modify it
  378. under the terms of the GNU General Public License as published by the
  379. Free Software Foundation; version 3 of the License.</p>
  380. <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  381. without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  382. See the GNU General Public License for more details.</p>
  383. <p>You should have received a copy of the GNU General Public License along with the Metrix++;
  384. if not, contact <a href="mailto:avkonst@users.sourceforge.net?subject=Metrix%2B%2B License">Project Administrator</a></p>
  385. </section>
  386. <div class="page-header">
  387. <h1>Workflow</h1>
  388. </div>
  389. <p>The workflow and application usecases are demonstrated using source code of
  390. <a href="http://www.boost.org/doc/libs/1_54_0/doc/html/interprocess.html">boost/interprocess</a> library.
  391. Boost versions 1.52 and 1.54 are used and refered below as 'previous' and 'current' accordingly.</p>
  392. <section id="workflow_collect_section">
  393. <h2>Collect data</h2>
  394. <p>The first obvious step is to collect the data.
  395. The 'collect' tool has got multiple options to enable various metrics plugins.
  396. Let's collect number of lines of code and cyclomatic complexity metrics
  397. for the previous (1.52.0 version) boost interprocess library. Assuming that 2 versions of boost library
  398. are unpacked in the current working directory:</p>
  399. <pre>
  400. &gt; cd boost_1_52_0
  401. &gt; python "/path/to/metrix++.py" collect --std.code.lines.code --std.code.complexity.cyclomatic -- boost/interprocess
  402. &gt; cd ../ # return back to working directory
  403. </pre>
  404. <p>The list of arguments after '--' enumerates paths where to read source files.
  405. As a result of execution of this command, a file metrixpp.db will be written in the current working directory.
  406. It can be redefined with help of --db-file option.</p>
  407. <p>Metrix++ has got rich functionality in comparing code bases and reducing processing scope to modified or new code.
  408. So, let's collect the same data for the current (1.54.0 version) boost interprocess library.</p>
  409. <pre>
  410. &gt; cd boost_1_54_0
  411. &gt; python "/path/to/metrix++.py" collect --std.code.lines.code --std.code.complexity.cyclomatic -- boost/interprocess --db-file-prev=../boost_1_52_0/metrixpp.db
  412. &gt; cd ../ # return back to working directory
  413. </pre>
  414. <p>The option --db-file-prev points out to the file with data collected in the previous step.
  415. So, eventually it executed iterative collection. It can speed up the exectuion significantly,
  416. depending on amount of changes between two version.</p>
  417. <p>Check other options of the collect tool by executing:</p>
  418. <pre>
  419. &gt; python "/path/to/metrix++.py" collect --help
  420. </pre>
  421. </section>
  422. <section id="workflow_view_section">
  423. <h2>View data</h2>
  424. </section>
  425. <section id="workflow_view_summary_section">
  426. <h3>Summary metrics and distribution tables/graphs</h3>
  427. <p>It is time to look at the data files collected (step above). The command:</p>
  428. <pre>
  429. &gt; python "/path/to/metrix++.py" view --db-file=boost_1_54_0/metrixpp.db
  430. </pre>
  431. <p>prints summary metrics, like minimum/maximum, and distribution/frequency tables:</p>
  432. <pre>
  433. :: info: Overall metrics for 'std.code.complexity:cyclomatic' metric
  434. Average : 0.652902698283
  435. Minimum : 0
  436. Maximum : 37
  437. Total : 1597.0
  438. Distribution : 2446 regions in total (including 0 suppressed)
  439. Metric value : Ratio : R-sum : Number of regions
  440. 0 : 0.771 : 0.771 : 1886 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  441. 1 : 0.110 : 0.881 : 268 |||||||||||
  442. 2 : 0.044 : 0.925 : 108 ||||
  443. 3 : 0.025 : 0.949 : 60 ||
  444. 4 : 0.016 : 0.966 : 40 ||
  445. 5 : 0.007 : 0.973 : 18 |
  446. 6 : 0.006 : 0.979 : 14 |
  447. 7 : 0.004 : 0.983 : 10
  448. 8 : 0.003 : 0.986 : 8
  449. 9 : 0.002 : 0.988 : 4
  450. 10 : 0.004 : 0.991 : 9
  451. 11 : 0.002 : 0.993 : 4
  452. 12 : 0.001 : 0.994 : 3
  453. 13 : 0.001 : 0.995 : 2
  454. 14 : 0.001 : 0.996 : 2
  455. 15-16 : 0.001 : 0.997 : 3
  456. 17-18 : 0.001 : 0.998 : 3
  457. 20 : 0.000 : 0.999 : 1
  458. 23-25 : 0.001 : 1.000 : 2
  459. 37 : 0.000 : 1.000 : 1
  460. :: info: Overall metrics for 'std.code.lines:code' metric
  461. Average : 6.64356984479
  462. Minimum : 0
  463. Maximum : 201
  464. Total : 23970.0
  465. Distribution : 3608 regions in total (including 0 suppressed)
  466. Metric value : Ratio : R-sum : Number of regions
  467. 0-1 : 0.088 : 0.088 : 319 |||||||||
  468. 2 : 0.320 : 0.409 : 1155 ||||||||||||||||||||||||||||||||
  469. 3 : 0.108 : 0.517 : 390 |||||||||||
  470. 4 : 0.081 : 0.598 : 294 ||||||||
  471. 5 : 0.080 : 0.678 : 290 ||||||||
  472. 6 : 0.061 : 0.739 : 220 ||||||
  473. 7 : 0.049 : 0.788 : 176 |||||
  474. 8 : 0.030 : 0.818 : 109 |||
  475. 9 : 0.025 : 0.843 : 89 ||
  476. 10-11 : 0.032 : 0.876 : 117 |||
  477. 12-13 : 0.020 : 0.895 : 71 ||
  478. 14 : 0.012 : 0.907 : 43 |
  479. 15-16 : 0.017 : 0.924 : 61 ||
  480. 17-19 : 0.015 : 0.939 : 55 ||
  481. 20-22 : 0.013 : 0.952 : 46 |
  482. 23-26 : 0.011 : 0.963 : 40 |
  483. 27-30 : 0.009 : 0.972 : 33 |
  484. 31-39 : 0.009 : 0.981 : 33 |
  485. 40-65 : 0.009 : 0.991 : 34 |
  486. 66-201 : 0.009 : 1.000 : 33 |
  487. :: info: Directory content:
  488. Directory : .
  489. </pre>
  490. <p>The same command with --db-file-prev option enables comparision and change trends are shown in [] brackets:</p>
  491. <pre>
  492. &gt; python "/path/to/metrix++.py" view --db-file=boost_1_54_0/metrixpp.db --db-file-prev=boost_1_52_0/metrixpp.db
  493. </pre>
  494. <pre>
  495. :: info: Overall metrics for 'std.code.complexity:cyclomatic' metric
  496. Average : 0.652902698283 [+0.00362138411453]
  497. Minimum : 0 [+0]
  498. Maximum : 37 [+1]
  499. Total : 1597.0 [+16.0]
  500. Distribution : 2446 [+11] regions in total (including 0 [+0] suppressed)
  501. Metric value : Ratio : R-sum : Number of regions
  502. 0 : 0.771 : 0.771 : 1886 [+5 ] |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  503. 1 : 0.110 : 0.881 : 268 [+4 ] |||||||||||
  504. 2 : 0.044 : 0.925 : 108 [+0 ] ||||
  505. 3 : 0.025 : 0.949 : 60 [+2 ] ||
  506. 4 : 0.016 : 0.966 : 40 [-1 ] ||
  507. 5 : 0.007 : 0.973 : 18 [-1 ] |
  508. 6 : 0.006 : 0.979 : 14 [+1 ] |
  509. 7 : 0.004 : 0.983 : 10 [+1 ]
  510. 8 : 0.003 : 0.986 : 8 [+1 ]
  511. 9 : 0.002 : 0.988 : 4 [+0 ]
  512. 10 : 0.004 : 0.991 : 9 [-2 ]
  513. 11 : 0.002 : 0.993 : 4 [+1 ]
  514. 12 : 0.001 : 0.994 : 3 [+0 ]
  515. 13 : 0.001 : 0.995 : 2 [+0 ]
  516. 14 : 0.001 : 0.996 : 2 [+0 ]
  517. 15-16 : 0.001 : 0.997 : 3 [-1 ]
  518. 17-18 : 0.001 : 0.998 : 3 [+1 ]
  519. 20 : 0.000 : 0.999 : 1 [+0 ]
  520. 23-25 : 0.001 : 1.000 : 2 [+0 ]
  521. 36-37 : 0.000 : 1.000 : 1 [+0 ]
  522. :: info: Overall metrics for 'std.code.lines:code' metric
  523. Average : 6.64356984479 [+0.012181964309]
  524. Minimum : 0 [+0]
  525. Maximum : 201 [+4]
  526. Total : 23970.0 [+223.0]
  527. Distribution : 3608 [+27] regions in total (including 0 [+0] suppressed)
  528. Metric value : Ratio : R-sum : Number of regions
  529. 0-1 : 0.088 : 0.088 : 319 [+3 ] |||||||||
  530. 2 : 0.320 : 0.409 : 1155 [+9 ] ||||||||||||||||||||||||||||||||
  531. 3 : 0.108 : 0.517 : 390 [-3 ] |||||||||||
  532. 4 : 0.081 : 0.598 : 294 [+7 ] ||||||||
  533. 5 : 0.080 : 0.678 : 290 [+7 ] ||||||||
  534. 6 : 0.061 : 0.739 : 220 [-1 ] ||||||
  535. 7 : 0.049 : 0.788 : 176 [-2 ] |||||
  536. 8 : 0.030 : 0.818 : 109 [-1 ] |||
  537. 9 : 0.025 : 0.843 : 89 [+4 ] ||
  538. 10-11 : 0.032 : 0.876 : 117 [+9 ] |||
  539. 12-13 : 0.020 : 0.895 : 71 [-9 ] ||
  540. 14 : 0.012 : 0.907 : 43 [+0 ] |
  541. 15-16 : 0.017 : 0.924 : 61 [+0 ] ||
  542. 17-19 : 0.015 : 0.939 : 55 [+6 ] ||
  543. 20-22 : 0.013 : 0.952 : 46 [-3 ] |
  544. 23-26 : 0.011 : 0.963 : 40 [+2 ] |
  545. 27-30 : 0.009 : 0.972 : 33 [-3 ] |
  546. 31-39 : 0.009 : 0.981 : 33 [+0 ] |
  547. 40-65 : 0.009 : 0.991 : 34 [+1 ] |
  548. 66-201 : 0.009 : 1.000 : 33 [+1 ] |
  549. :: info: Directory content:
  550. Directory : .
  551. </pre>
  552. <h4>Reducing analysis scope</h4>
  553. <p>There are two ways to reduce the analysis scope for the view tool. The first is to enumerate paths of interest.
  554. For example, the following command reduces scope to 'allocators' sub-directory within the processed code.</p>
  555. <pre>
  556. &gt; python "/path/to/metrix++.py" view --db-file=boost_1_54_0/metrixpp.db -- ./boost/interprocess/allocators
  557. </pre>
  558. <p>The second is to specify --scope-mode option, which instructs the tool to process only modified and/or new files/regions.
  559. For example, view the summary metrics for all modified and new regions:</p>
  560. <pre>
  561. &gt; python "/path/to/metrix++.py" view --db-file=boost_1_54_0/metrixpp.db --db-file-prev=boost_1_52_0/metrixpp.db --scope-mode=touched
  562. </pre>
  563. <pre>
  564. :: info: Overall metrics for 'std.code.complexity:cyclomatic' metric
  565. Average : 1.84924623116 [-0.0230941943761]
  566. Minimum : 0 [+0]
  567. Maximum : 37 [+1]
  568. Total : 368.0 [+16.0]
  569. Distribution : 199 [+11] regions in total (including 0 [+0] suppressed)
  570. Metric value : Ratio : R-sum : Number of regions
  571. 0 : 0.608 : 0.608 : 121 [+5 ] |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
  572. 1 : 0.131 : 0.739 : 26 [+4 ] |||||||||||||
  573. 2 : 0.070 : 0.809 : 14 [+0 ] |||||||
  574. 3 : 0.060 : 0.869 : 12 [+2 ] ||||||
  575. 4 : 0.015 : 0.884 : 3 [-1 ] ||
  576. 5 : 0.015 : 0.899 : 3 [-1 ] ||
  577. 6 : 0.005 : 0.905 : 1 [+1 ] |
  578. 7 : 0.015 : 0.920 : 3 [+1 ] ||
  579. 8 : 0.015 : 0.935 : 3 [+1 ] ||
  580. 9 : 0.010 : 0.945 : 2 [+0 ] |
  581. 10 : 0.010 : 0.955 : 2 [-2 ] |
  582. 11 : 0.005 : 0.960 : 1 [+1 ] |
  583. 12 : 0.005 : 0.965 : 1 [+0 ] |
  584. 13 : 0.005 : 0.970 : 1 [+0 ] |
  585. 16 : 0.005 : 0.975 : 1 [-1 ] |
  586. 17 : 0.005 : 0.980 : 1 [+0 ] |
  587. 18 : 0.010 : 0.990 : 2 [+1 ] |
  588. 20 : 0.005 : 0.995 : 1 [+0 ] |
  589. 36-37 : 0.005 : 1.000 : 1 [+0 ] |
  590. :: info: Overall metrics for 'std.code.lines:code' metric
  591. Average : 15.9645390071 [-0.815853149771]
  592. Minimum : 0 [+0]
  593. Maximum : 201 [+6]
  594. Total : 4502.0 [+223.0]
  595. Distribution : 282 [+27] regions in total (including 0 [+0] suppressed)
  596. Metric value : Ratio : R-sum : Number of regions
  597. 0-1 : 0.053 : 0.053 : 15 [+3 ] |||||
  598. 2 : 0.124 : 0.177 : 35 [+9 ] ||||||||||||
  599. 3 : 0.053 : 0.230 : 15 [-3 ] |||||
  600. 4 : 0.060 : 0.291 : 17 [+7 ] ||||||
  601. 5 : 0.089 : 0.379 : 25 [+7 ] |||||||||
  602. 6 : 0.060 : 0.440 : 17 [-1 ] ||||||
  603. 7 : 0.050 : 0.489 : 14 [-2 ] |||||
  604. 8-9 : 0.074 : 0.564 : 21 [+3 ] |||||||
  605. 10 : 0.035 : 0.599 : 10 [+5 ] ||||
  606. 11 : 0.082 : 0.681 : 23 [+4 ] ||||||||
  607. 12-13 : 0.043 : 0.723 : 12 [-9 ] ||||
  608. 14-15 : 0.039 : 0.762 : 11 [-1 ] ||||
  609. 16-18 : 0.028 : 0.791 : 8 [+4 ] |||
  610. 19-22 : 0.039 : 0.830 : 11 [+0 ] ||||
  611. 23-26 : 0.039 : 0.869 : 11 [+2 ] ||||
  612. 27-32 : 0.028 : 0.897 : 8 [-3 ] |||
  613. 38-50 : 0.025 : 0.922 : 7 [+0 ] ||
  614. 51-69 : 0.025 : 0.947 : 7 [+1 ] ||
  615. 71-100 : 0.032 : 0.979 : 9 [+2 ] |||
  616. 101-201 : 0.021 : 1.000 : 6 [-1 ] ||
  617. :: info: Directory content:
  618. Directory : .
  619. </pre>
  620. </section>
  621. <section id="workflow_view_details_section">
  622. <h3>Detailed metrics per file/region</h3>
  623. <p>The same view tool can print detailed metrics per file and per every region in the specified file.
  624. In order to get detailed metrics, enumerate files of interest after '--'. For example:</p>
  625. <pre>
  626. &gt; python "/path/to/metrix++.py" view --db-file=boost_1_54_0/metrixpp.db --db-file-prev=boost_1_52_0/metrixpp.db -- ./boost/interprocess/detail/managed_open_or_create_impl.hpp
  627. </pre>
  628. <p>produces output similar to this (truncated to make the page shorter):</p>
  629. <pre>
  630. ./interprocess/detail/managed_open_or_create_impl.hpp:302: info: Metrics per 'priv_open_or_create' region
  631. Region name : priv_open_or_create
  632. Region type : function
  633. Offsets : 8314-14526
  634. Line numbers : 301-467
  635. Modified : True
  636. std.code.complexity:cyclomatic: 37 [+1]
  637. std.code.lines:code: 148 [+4]
  638. </pre>
  639. </section>
  640. <section>
  641. <h3>More about the viewer</h3>
  642. <p>The 'view' command has got an option to alter the output format. It is possible to get the same data
  643. in xml pr python disctionary formats. This can be particularly useful for integration of the tool with
  644. other applications. For example, an editor may re-collect and show context based metrics when a file is saved.</p>
  645. <pre>
  646. &gt; python "/path/to/metrix++.py" view --db-file=boost_1_54_0/metrixpp.db --format=xml
  647. </pre>
  648. <p>Check other options of the view tool by executing:</p>
  649. <pre>
  650. &gt; python "/path/to/metrix++.py" view --help
  651. </pre>
  652. </section>
  653. <section id="workflow_limit_section">
  654. <h2>Apply thresholds</h2>
  655. <p>The viewer shows (above) that there are function with quite large value of cyclomatic complexity metric.
  656. Growth of this metric can be considered as negative trend. Metrix++ 'limit' tool offers the capability
  657. to organise the control over trends by applying limits to metric values.
  658. Exceeded limites are alarms in the quality management and control.</p>
  659. </section>
  660. <section id="workflow_limit_hotspots_section">
  661. <h3>Hotspots</h3>
  662. <p>Hotspots mode of the limit tool helps to identify top files/regions exceeding a metric threshold.
  663. Let's identify top 3 functions in boost interprocess library, which exceed limit of 15 points of
  664. cyclomatic complexity metric:</p>
  665. <pre>
  666. &gt; python "/path/to/metrix++.py" limit --db-file=boost_1_54_0/metrixpp.db --max-limit=std.code.complexity:cyclomatic:15 --hotspots=3
  667. </pre>
  668. <pre>
  669. ./interprocess/detail/managed_open_or_create_impl.hpp:302: warning: Metric 'std.code.complexity:cyclomatic' for region 'priv_open_or_create' exceeds the limit.
  670. Metric name : std.code.complexity:cyclomatic
  671. Region name : priv_open_or_create
  672. Metric value : 37
  673. Modified : None
  674. Change trend : None
  675. Limit : 15.0
  676. Suppressed : False
  677. ./interprocess/streams/vectorstream.hpp:284: warning: Metric 'std.code.complexity:cyclomatic' for region 'seekoff' exceeds the limit.
  678. Metric name : std.code.complexity:cyclomatic
  679. Region name : seekoff
  680. Metric value : 25
  681. Modified : None
  682. Change trend : None
  683. Limit : 15.0
  684. Suppressed : False
  685. ./interprocess/streams/bufferstream.hpp:174: warning: Metric 'std.code.complexity:cyclomatic' for region 'seekoff' exceeds the limit.
  686. Metric name : std.code.complexity:cyclomatic
  687. Region name : seekoff
  688. Metric value : 23
  689. Modified : None
  690. Change trend : None
  691. Limit : 15.0
  692. Suppressed : False
  693. </pre>
  694. </section>
  695. <section id="workflow_limit_control_section">
  696. <h3>Controlling trends</h3>
  697. <p>Exit code of the 'limit' tool is equal to number of warnings printed. This allows to use the tool
  698. as a static analysis tool during software build process. In this case, non-zero exit code means
  699. that there are violations to the agreed standards and it may fail the build. So, the same command
  700. without --hotspots option will print all regions/files exceeding the specified limit:</p>
  701. <pre>
  702. &gt; python "/path/to/metrix++.py" limit --db-file=boost_1_54_0/metrixpp.db --max-limit=std.code.complexity:cyclomatic:15
  703. </pre>
  704. <h4>Modes to exclude old code from the considiration</h4>
  705. <p>However, it is likely there are many warnings printed in this mode, especially if very old or legacy code is profiled
  706. against new metrics and coding rules. Although all warnings can be removed
  707. by re-factoring in scope of big task force activity, it is where many tools are rejected,
  708. because it is difficult to justify the initial cost of applying and integrating them.
  709. Metrix++ 'limit' tool has got an option --warn-mode, which helps to overcome this problem.</p>
  710. <p>--warn-mode=touched encourages re-factoring only for new and modified regions. It enables
  711. continuous refactoring. It does not matter how late the rule is applied or
  712. coding standard is modified. It is possible to do it anytime with zero initial investment.
  713. For example, applying it to boost interprocess library for a changes between 1.52 and 1.54 versions
  714. results in only 6 warnings:</p>
  715. <pre>
  716. &gt; python "/path/to/metrix++.py" limit --db-file=boost_1_54_0/metrixpp.db --db-file-prev=boost_1_52_0/metrixpp.db --max-limit=std.code.complexity:cyclomatic:15 --warn-mode=touched
  717. </pre>
  718. <pre>
  719. ./interprocess/detail/managed_open_or_create_impl.hpp:302: warning: Metric 'std.code.complexity:cyclomatic' for region 'priv_open_or_create' exceeds the limit.
  720. Metric name : std.code.complexity:cyclomatic
  721. Region name : priv_open_or_create
  722. Metric value : 37
  723. Modified : True
  724. Change trend : +1
  725. Limit : 15.0
  726. Suppressed : False
  727. ./interprocess/ipc/message_queue.hpp:375: warning: Metric 'std.code.complexity:cyclomatic' for region 'insert_at' exceeds the limit.
  728. Metric name : std.code.complexity:cyclomatic
  729. Region name : insert_at
  730. Metric value : 16
  731. Modified : True
  732. Change trend : 0
  733. Limit : 15.0
  734. Suppressed : False
  735. ./interprocess/mapped_region.hpp:575: warning: Metric 'std.code.complexity:cyclomatic' for region 'mapped_region' exceeds the limit.
  736. Metric name : std.code.complexity:cyclomatic
  737. Region name : mapped_region
  738. Metric value : 18
  739. Modified : True
  740. Change trend : +2
  741. Limit : 15.0
  742. Suppressed : False
  743. ./interprocess/mem_algo/detail/mem_algo_common.hpp:452: warning: Metric 'std.code.complexity:cyclomatic' for region 'priv_allocate_many' exceeds the limit.
  744. Metric name : std.code.complexity:cyclomatic
  745. Region name : priv_allocate_many
  746. Metric value : 20
  747. Modified : True
  748. Change trend : 0
  749. Limit : 15.0
  750. Suppressed : False
  751. ./interprocess/mem_algo/rbtree_best_fit.hpp:787: warning: Metric 'std.code.complexity:cyclomatic' for region 'priv_expand_both_sides' exceeds the limit.
  752. Metric name : std.code.complexity:cyclomatic
  753. Region name : priv_expand_both_sides
  754. Metric value : 17
  755. Modified : True
  756. Change trend : 0
  757. Limit : 15.0
  758. Suppressed : False
  759. ./interprocess/sync/windows/named_sync.hpp:98: warning: Metric 'std.code.complexity:cyclomatic' for region 'open_or_create' exceeds the limit.
  760. Metric name : std.code.complexity:cyclomatic
  761. Region name : open_or_create
  762. Metric value : 18
  763. Modified : True
  764. Change trend : 0
  765. Limit : 15.0
  766. Suppressed : False
  767. </pre>
  768. <p>If it is challenging or not beneficial to refactor everything touched,
  769. --warn-mode=trends simplifies the control over modified regions and only makes sure
  770. that there are no regressions in modified code. In other words, a warning is printed about modified region/file
  771. only if a metric exceeds the specified limit and the value of the metric has got negative trend in modification.
  772. It is possible to apply it anytime with zero initial investment and almost zero on-going investment around old code.
  773. For example, applying it to boost interprocess library for a changes between 1.52 and 1.54 versions
  774. results in only 2 warnings:</p>
  775. <pre>
  776. &gt; python "/path/to/metrix++.py" limit --db-file=boost_1_54_0/metrixpp.db --db-file-prev=boost_1_52_0/metrixpp.db --max-limit=std.code.complexity:cyclomatic:15 --warn-mode=trend
  777. </pre>
  778. <pre>
  779. ./interprocess/detail/managed_open_or_create_impl.hpp:302: warning: Metric 'std.code.complexity:cyclomatic' for region 'priv_open_or_create' exceeds the limit.
  780. Metric name : std.code.complexity:cyclomatic
  781. Region name : priv_open_or_create
  782. Metric value : 37
  783. Modified : True
  784. Change trend : +1
  785. Limit : 15.0
  786. Suppressed : False
  787. ./interprocess/mapped_region.hpp:575: warning: Metric 'std.code.complexity:cyclomatic' for region 'mapped_region' exceeds the limit.
  788. Metric name : std.code.complexity:cyclomatic
  789. Region name : mapped_region
  790. Metric value : 18
  791. Modified : True
  792. Change trend : +2
  793. Limit : 15.0
  794. Suppressed : False
  795. </pre>
  796. <p>--warn-mode=new drops control over existing code and ensures that warnings are only about new code.
  797. For example, applying it to boost interprocess library for a changes between 1.52 and 1.54 versions
  798. results in 0 warnings, so new code is totally compliant with the standard required in the example.</p>
  799. <pre>
  800. &gt; python "/path/to/metrix++.py" limit --db-file=boost_1_54_0/metrixpp.db --db-file-prev=boost_1_52_0/metrixpp.db --max-limit=std.code.complexity:cyclomatic:15 --warn-mode=new
  801. </pre>
  802. <h4>Suppressions</h4>
  803. <p>It is possible to suppress warnings on exceptional basis. Suppressions are collected from comments in code
  804. and used by the 'limit' tool to filter out unnecessary (suppressed) warnings.
  805. It allows to take fine grained control over false-positive warnings, if there are.</p>
  806. <p>In order to suppress a warning:</p>
  807. <ul>
  808. <li>per region metrics: put the metrix++ instruction in the comments before the region, for example:</li>
  809. <pre class="prettyprint linenums">
  810. // This function returns string typed
  811. // representation of a name of a color,
  812. // requested by color's id
  813. // metrix++: suppress std.code.complexity:cyclomatic
  814. std::string getColorName(int color_id)
  815. {
  816. switch (color_id)
  817. {
  818. case COLOR_RED:
  819. return std::string("red")
  820. case COLOR_GREEN:
  821. return std::string("green")
  822. case COLOR_BLUE:
  823. return std::string("blue")
  824. /* and so on */
  825. }
  826. }
  827. </pre>
  828. <li>per file metrics: put the metrix++ instruction in the comments at the beginning of a file, for example:</li>
  829. <pre class="prettyprint linenums">
  830. //
  831. // This file does processing of colors and brushes
  832. // Copyright is my company, 2013
  833. //
  834. // However, it is too long and big file, and there is no time
  835. // to split it into multiple file, so shut up the metrix++ warnings:
  836. // metrix++: suppress std.general:size
  837. //
  838. std::string getColorName(int color_id)
  839. {
  840. ...
  841. ...
  842. </pre>
  843. <li>activate collection of suppressions:</li>
  844. <pre>
  845. &gt; python "/path/to/metrix++.py" collect --std.suppress
  846. </pre>
  847. <li>run the 'limit' tool WITHOUT --disable-suppressions option:</li>
  848. <pre>
  849. &gt; python "/path/to/metrix++.py" limit ...
  850. </pre>
  851. </ul>
  852. <h5>Important notice:</h5>
  853. <ul><li>--std.suppress option enables collection of 2 metrics as well: 'std.suppress:count' and
  854. 'std.suppress.file:count'. The first is number of suppressions per region.
  855. The second is the same but applies to file-scope metrics.
  856. It allows to manage the amount of suppressions.
  857. Usually there are no false-positives to suppress with the <strong>right</strong> metric,
  858. but could be exceptions in specific cases. Managing suppressions is about managing exceptions.
  859. If there are many exceptional cases, maybe something is wrong with a metric or an application of a metric.
  860. Two code examples about colors above do not demonstrate the technically exceptional case,
  861. they likely demonstrate a case of a process exception, like "there is no time to do it proper now", or
  862. a case of wrong application of a metric, like "shut up the useless tool". So, be careful.
  863. The 'view' tool shows number of suppressions and its change trends on per metric basis.</li></ul>
  864. </section>
  865. <section id="workflow_other_section">
  866. <h2>Other applications</h2>
  867. <h3>Checking data file properties</h3>
  868. <p>Metrix++ 'info' tool is helpful to check properties of a data file, like settings used to write it,
  869. colected metrics and files processed. For example:</p>
  870. <pre>
  871. &gt; python "/path/to/metrix++.py" info --db-file=boost_1_54_0/metrixpp.db
  872. </pre>
  873. <pre>
  874. boost_1_54_0/metrixpp.db:: info: Created using plugins and settings:
  875. version : 1.0
  876. std.code.complexity:version: 1.1
  877. std.code.cpp:version: 1.1
  878. std.code.cpp:files: *.c,*.cc,*.cpp,*.cxx,*.h,*.hh,*.hpp,*.hxx
  879. std.code.cs:version: 1.0
  880. std.code.cs:files: *.cs
  881. std.code.java:version: 1.1
  882. std.code.java:files: *.java
  883. std.code.lines:version: 1.1
  884. test_workflow.db:: info: Collected metrics:
  885. std.code.complexity:cyclomatic:
  886. std.code.lines:code:
  887. :: info: Processed files and checksums:
  888. ./interprocess/allocators/detail/node_pool.hpp: 0xb099a7c3
  889. ./interprocess/allocators/detail/node_tools.hpp: 0xaaf5044d
  890. ./interprocess/anonymous_shared_memory.hpp: 0x2bf06cb0
  891. ./interprocess/containers/allocation_type.hpp: 0x8e95cda0
  892. ./interprocess/containers/containers_fwd.hpp: 0xa4d0d9f7
  893. ./interprocess/containers/deque.hpp: 0x6dbb77af
  894. ./interprocess/containers/flat_map.hpp: 0x6750338c
  895. ...
  896. </pre>
  897. <h3>Exporting results</h3>
  898. <p>Metrix++ 'export' tool exports data files to csv formated files. For example:</p>
  899. <pre>
  900. &gt; python "/path/to/metrix++.py" export --db-file=boost_1_54_0/metrixpp.db > boost_1_54_0/metrixpp.csv
  901. </pre>
  902. <pre>
  903. file,region,type,modified,line start,line end,std.code.complexity:cyclomatic,std.code.lines:code
  904. ./interprocess/allocators/detail/node_pool.hpp,__global__,global,,1,110,,0
  905. ./interprocess/allocators/detail/node_pool.hpp,boost,namespace,,33,105,,2
  906. ./interprocess/allocators/detail/node_pool.hpp,interprocess,namespace,,34,104,,2
  907. ./interprocess/allocators/detail/node_pool.hpp,ipcdetail,namespace,,35,103,,4
  908. ./interprocess/allocators/detail/node_pool.hpp,SegmentManager,class,,39,72,,16
  909. ...
  910. </pre>
  911. <p>Files with csv format can be opened by applications, like Microsoft Office Excel, with advanced analysis capabilities.
  912. For example, to draw this distribution graph:</p>
  913. <p align="center"><img src="assets/img/piechart.png"/></p>
  914. <p>It is not recommended to use export tool to implement custom post-analysis Metrix++ extensions.
  915. The main reason is non granted backward compatibility support for csv columns. Another main reason is that
  916. exporting is relatively slow process. It is recommended to use Metrix++ extensions API instead.</p>
  917. </section>
  918. <section id="extend_section">
  919. <div class="page-header">
  920. <h1>Create plugin</h1>
  921. <p>There are 3 types of plugins considered in this chapter:</p>
  922. <ul>
  923. <li>Metric plugin</li>
  924. <li>Language parser</li>
  925. <li>Post-processing / Analysis tool</li>
  926. </ul>
  927. <p>Tutorial for metric plugin is generic at the beginning and large portion of this is applied to
  928. all other plugins. You need to know python and python regular expressions library to write Metrix++ extensions.</p>
  929. </div>
  930. <h2>Metric plugin</h2>
  931. <p>The tutorial will explain how to create a plugin to count magic numbers in source code.
  932. It will be relatively simple at first and will be extended with additional configuration
  933. options and smarter counting logic.</p>
  934. <h4>Create placeholder for new plugin</h4>
  935. <ol>
  936. <li>All plugins are loaded by Metrix++ from standard places within the tool installation directory and
  937. from custom places specified in the METRIXPLUSPLUS_PATH environment variable.
  938. METRIXPLUSPLUS_PATH has got the same format as system PATH environment variable.
  939. So, the first step in plugin development is to set the METRIXPLUSPLUS_PATH to point out to
  940. the directory (or directories) where plugin is located.</li>
  941. <li>Create new python package 'myext', python lib 'magic.py' and 'magic.ini' file.</li>
  942. <pre>
  943. + working_directory (set in METRIXPLUSPLUS_PATH variable)
  944. \--+ myext
  945. \--- __init__.py
  946. \--- magic.py
  947. \--- magic.ini
  948. </pre>
  949. <li>__init__.py is empty file to make myext considered by python as a package.</li>
  950. <li>Edit magic.py to have the following content:
  951. <pre class="prettyprint linenums">
  952. import mpp.api
  953. class Plugin(mpp.api.Plugin):
  954. def initialize(self):
  955. print "Hello world"
  956. </pre>
  957. mpp.api package include Metrix++ API classes. mpp.api.Plugin is the base class, which can be loaded
  958. by Metrix++ engine and does nothing by default. In the code sample above it is extended to print
  959. "Hello world" on initialization.</li>
  960. <li>Edit magic.ini to have the following content:
  961. <pre class="prettyprint linenums">
  962. [Plugin]
  963. version: 1.0
  964. package: myext
  965. module: magic
  966. class: Plugin
  967. depends: None
  968. actions: collect
  969. enabled: True
  970. </pre>
  971. This file is a manifest for Metrix++ plugin loader. The fields in Plugin section are:
  972. <dl class="dl-horizontal">
  973. <dt>version</dt>
  974. <dd>a string representing the version, step up it every time when behaviour of a plugin
  975. or backward compatibility in api or data scope is changed</dd>
  976. <dt>package</dt>
  977. <dd>python package name where to load from</dd>
  978. <dt>module</dt>
  979. <dd>python module name (filename of *.py file) to load</dd>
  980. <dt>class</dt>
  981. <dd>name of a plugin class to instanciate</dd>
  982. <dt>depends</dt>
  983. <dd>list of plugin names to load, if it this plugin is loaded</dd>
  984. <dt>actions</dt>
  985. <dd>list of Metrix++ actions affected by this plugin</dd>
  986. <dt>enabled</dt>
  987. <dd>True or False, working status of a plugin</dd>
  988. </dl>
  989. </li>
  990. <li>Now run Metrix++ to see how this new plugin works:</li>
  991. <pre>&gt; python "/path/to/metrix++.py" collect</pre>
  992. <pre>Hello world</pre>
  993. </ol>
  994. <h4>Toogle option for the plugin</h4>
  995. <ol>
  996. <li>It is recommended to follow the convention for all plugins: 'run only if enabled'.
  997. So, let's extend the magic.py file to make it configurable
  998. <pre class="prettyprint linenums">
  999. import mpp.api
  1000. class Plugin(mpp.api.Plugin,
  1001. # make this instance configurable...
  1002. mpp.api.IConfigurable):
  1003. # ... and implement 2 interfaces
  1004. def declare_configuration(self, parser):
  1005. parser.add_option("--myext.magic.numbers", "--mmn",
  1006. action="store_true", default=False,
  1007. help="Enables collection of magic numbers metric [default: %default]")
  1008. def configure(self, options):
  1009. self.is_active_numbers = options.__dict__['myext.magic.numbers']
  1010. def initialize(self):
  1011. # use configuration option here
  1012. if self.is_active_numbers == True:
  1013. print "Hello world"
  1014. </pre>
  1015. parser argument is an instance of optparse.OptionParser class. It has got an extension to
  1016. accept multiple options of the same argument. Check std.tools.limit to see how to declare multiopt options, if you need.</li>
  1017. <li>Now run Metrix++ to see how this works:</li>
  1018. <pre>&gt; python "/path/to/metrix++.py" collect --myext.magic.numbers</pre>
  1019. <pre>Hello world</pre>
  1020. </ol>
  1021. <h4>Subscribe to notifications from parent plugins (or code parsers)</h4>
  1022. <ol>
  1023. <li>Every plugin works in a callback functions called by parent plugins.
  1024. Callback receives a reference to parent plugin, data object where to store metrics data,
  1025. and a flag indicating if there are changes in file or parent's settings since the previous collection.</li>
  1026. <pre class="prettyprint linenums">
  1027. import mpp.api
  1028. class Plugin(mpp.api.Plugin,
  1029. mpp.api.IConfigurable,
  1030. # declare that it can subscribe on notifications
  1031. mpp.api.Child):
  1032. def declare_configuration(self, parser):
  1033. parser.add_option("--myext.magic.numbers", "--mmn",
  1034. action="store_true", default=False,
  1035. help="Enables collection of magic numbers metric [default: %default]")
  1036. def configure(self, options):
  1037. self.is_active_numbers = options.__dict__['myext.magic.numbers']
  1038. def initialize(self):
  1039. if self.is_active_numbers == True:
  1040. # subscribe to notifications from all code parsers
  1041. self.subscribe_by_parents_interface(mpp.api.ICode, 'callback')
  1042. # parents (code parsers) will call the callback declared
  1043. def callback(self, parent, data, is_updated):
  1044. print parent.get_name(), data.get_path(), is_updated
  1045. </pre>
  1046. <li>Now run Metrix++ to see how this works. Try to do iterative scans (--db-file-prev option) to see how the
  1047. state of arguments is changed</li>
  1048. <pre>&gt; python "/path/to/metrix++.py" collect --myext.magic.numbers</pre>
  1049. <pre>std.code.cpp ./test.cpp True</pre>
  1050. </ol>
  1051. <h4>Implement simple metric based on regular expression pattern</h4>
  1052. <ol>
  1053. <li>Callback may execute counting, searcing and additional parsing and store results, using data argument.
  1054. 'data' argument is an instance of mpp.api.FileData class.
  1055. However, most metrics can be implemented
  1056. simplier, if mpp.api.MetricPluginMixin routines are used. MetricPluginMixin implements
  1057. declarative style for metrics based on searches by regular expression. It
  1058. cares about initialisation of database fields and properties.
  1059. It implements default callback which counts number of matches by regular expression for all
  1060. active declared metrics. So, let's utilise that:
  1061. </li>
  1062. <pre class="prettyprint linenums">
  1063. import mpp.api
  1064. import re
  1065. class Plugin(mpp.api.Plugin,
  1066. mpp.api.IConfigurable,
  1067. mpp.api.Child,
  1068. # reuse by inheriting standard metric facilities
  1069. mpp.api.MetricPluginMixin):
  1070. def declare_configuration(self, parser):
  1071. parser.add_option("--myext.magic.numbers", "--mmn",
  1072. action="store_true", default=False,
  1073. help="Enables collection of magic numbers metric [default: %default]")
  1074. def configure(self, options):
  1075. self.is_active_numbers = options.__dict__['myext.magic.numbers']
  1076. def initialize(self):
  1077. # declare metric rules
  1078. self.declare_metric(
  1079. self.is_active_numbers, # to count if active in callback
  1080. self.Field('numbers', int), # field name and type in the database
  1081. re.compile(r'''[0-9]+'''), # pattern to search
  1082. marker_type_mask=mpp.api.Marker.T.CODE, # search in code
  1083. region_type_mask=mpp.api.Region.T.ANY) # search in all types of regions
  1084. # use superclass facilities to initialize everything from declared fields
  1085. super(Plugin, self).initialize(fields=self.get_fields())
  1086. # subscribe to all code parsers if at least one metric is active
  1087. if self.is_active() == True:
  1088. self.subscribe_by_parents_interface(mpp.api.ICode)
  1089. </pre>
  1090. <li>Now run Metrix++ to count numbers in code files.</li>
  1091. <pre>&gt; python "/path/to/metrix++.py" collect --myext.magic.numbers</pre>
  1092. <li>Now view the results. At this stage it is fully working simple metric.</li>
  1093. <pre>&gt; python "/path/to/metrix++.py" view</pre>
  1094. <pre>
  1095. :: info: Overall metrics for 'myext.magic:numbers' metric
  1096. Average : 2.75
  1097. Minimum : 0
  1098. Maximum : 7
  1099. Total : 11.0
  1100. Distribution : 4 regions in total (including 0 suppressed)
  1101. Metric value : Ratio : R-sum : Number of regions
  1102. 0 : 0.250 : 0.250 : 1 |||||||||||||||||||||||||
  1103. 1 : 0.250 : 0.500 : 1 |||||||||||||||||||||||||
  1104. 3 : 0.250 : 0.750 : 1 |||||||||||||||||||||||||
  1105. 7 : 0.250 : 1.000 : 1 |||||||||||||||||||||||||
  1106. :: info: Directory content:
  1107. Directory : .
  1108. </pre>
  1109. </ol>
  1110. <h4>Extend regular expression incremental counting by smarter logic</h4>
  1111. <ol>
  1112. <li>At this stage the metric counts every number in source code.
  1113. However, we indent to spot only 'magic' numbers. Declared constant
  1114. is not a magic number, so it is better to exclude constants from counting.
  1115. It is easy to change default counter behaviour by implementing
  1116. a function with name '_&lt;metric_name&gt;_count'. </li>
  1117. <pre class="prettyprint linenums">
  1118. import mpp.api
  1119. import re
  1120. class Plugin(mpp.api.Plugin,
  1121. mpp.api.IConfigurable,
  1122. mpp.api.Child,
  1123. mpp.api.MetricPluginMixin):
  1124. def declare_configuration(self, parser):
  1125. parser.add_option("--myext.magic.numbers", "--mmn",
  1126. action="store_true", default=False,
  1127. help="Enables collection of magic numbers metric [default: %default]")
  1128. def configure(self, options):
  1129. self.is_active_numbers = options.__dict__['myext.magic.numbers']
  1130. def initialize(self):
  1131. # improve pattern to find declarations of constants
  1132. pattern_to_search = re.compile(
  1133. r'''(const\s+([_a-zA-Z][_a-zA-Z0-9]*\s+)+[=]\s*)?[-+]?[0-9]+''')
  1134. self.declare_metric(self.is_active_numbers,
  1135. self.Field('numbers', int),
  1136. pattern_to_search, # and use it here
  1137. marker_type_mask=mpp.api.Marker.T.CODE,
  1138. region_type_mask=mpp.api.Region.T.ANY)
  1139. super(Plugin, self).initialize(fields=self.get_fields())
  1140. if self.is_active() == True:
  1141. self.subscribe_by_parents_interface(mpp.api.ICode)
  1142. # implement custom counter behavior:
  1143. # increments counter by 1 only if single number spotted,
  1144. # but not declaration of a constant
  1145. def _numbers_count(self, alias, data, region, marker, match, count, counter_data):
  1146. if match.group(0).startswith('const'):
  1147. return count
  1148. return count + 1
  1149. </pre>
  1150. <li>Initialy counter is initialized by zero, but it is possible to
  1151. change it as well by implementing a function with name '_&lt;metric_name&gt;_count_initialize'.
  1152. Plugin we are implementing does not require this.</li>
  1153. <li>Now run Metrix++ to collect and view the results.</li>
  1154. <pre>&gt; python "/path/to/metrix++.py" collect --myext.magic.numbers</pre>
  1155. <pre>&gt; python "/path/to/metrix++.py" view</pre>
  1156. </ol>
  1157. <h4>Language specific regular expressions</h4>
  1158. <ol>
  1159. <li>In the previous step we added matching of constants assuming that identifiers
  1160. may have symbols '_', 'a-z', 'A-Z' and '0-9'. It is true for C++ but it is not complete for Java.
  1161. Java identifier may have '$' symbol in the identifier. So, let's add language specific pattern
  1162. in the declaration of the metric:</li>
  1163. <pre class="prettyprint linenums">
  1164. import mpp.api
  1165. import re
  1166. class Plugin(mpp.api.Plugin,
  1167. mpp.api.IConfigurable,
  1168. mpp.api.Child,
  1169. mpp.api.MetricPluginMixin):
  1170. def declare_configuration(self, parser):
  1171. parser.add_option("--myext.magic.numbers", "--mmn",
  1172. action="store_true", default=False,
  1173. help="Enables collection of magic numbers metric [default: %default]")
  1174. def configure(self, options):
  1175. self.is_active_numbers = options.__dict__['myext.magic.numbers']
  1176. def initialize(self):
  1177. # specialized pattern for java
  1178. pattern_to_search_java = re.compile(
  1179. r'''(const\s+([_$a-zA-Z][_$a-zA-Z0-9]*\s+)+[=]\s*)?[-+]?[0-9]+''')
  1180. # pattern for C++ and C# languages
  1181. pattern_to_search_cpp_cs = re.compile(
  1182. r'''(const\s+([_a-zA-Z][_a-zA-Z0-9]*\s+)+[=]\s*)?[-+]?[0-9]+''')
  1183. # pattern for all other languages
  1184. pattern_to_search = re.compile(
  1185. r'''[0-9]+''')
  1186. self.declare_metric(self.is_active_numbers,
  1187. self.Field('numbers', int),
  1188. # dictionary of patterns instead of a single one
  1189. {
  1190. 'std.code.java': pattern_to_search_java,
  1191. 'std.code.cpp': pattern_to_search_cpp_cs,
  1192. 'std.code.cs': pattern_to_search_cpp_cs,
  1193. '*': pattern_to_search
  1194. },
  1195. marker_type_mask=mpp.api.Marker.T.CODE,
  1196. region_type_mask=mpp.api.Region.T.ANY)
  1197. super(Plugin, self).initialize(fields=self.get_fields())
  1198. if self.is_active() == True:
  1199. self.subscribe_by_parents_interface(mpp.api.ICode)
  1200. def _numbers_count(self, alias, data, region, marker, match, count, counter_data):
  1201. if match.group(0).startswith('const'):
  1202. return count
  1203. return count + 1
  1204. </pre>
  1205. <li>Keys in the dictionary of patterns are names of parent plugins (references to code parsers).
  1206. The key '*' refers to any parser.</li>
  1207. <li>Now run Metrix++ to collect and view the results.</li>
  1208. <pre>&gt; python "/path/to/metrix++.py" collect --myext.magic.numbers</pre>
  1209. <pre>&gt; python "/path/to/metrix++.py" view</pre>
  1210. </ol>
  1211. <h4>Store only non-zero metric values</h4>
  1212. <ol>
  1213. <li>Most functions have the metric, which we are implemneting, equal to zero.
  1214. However, we are interested in finding code blocks having this metric greater than zero.
  1215. Zeros consumes the space in the data file. So, we can optimise the size of a data file,
  1216. if we exclude zero metric values. Let's declare this behavior for the metric.</li>
  1217. <pre class="prettyprint linenums">
  1218. import mpp.api
  1219. import re
  1220. class Plugin(mpp.api.Plugin,
  1221. mpp.api.IConfigurable,
  1222. mpp.api.Child,
  1223. mpp.api.MetricPluginMixin):
  1224. def declare_configuration(self, parser):
  1225. parser.add_option("--myext.magic.numbers", "--mmn",
  1226. action="store_true", default=False,
  1227. help="Enables collection of magic numbers metric [default: %default]")
  1228. def configure(self, options):
  1229. self.is_active_numbers = options.__dict__['myext.magic.numbers']
  1230. def initialize(self):
  1231. pattern_to_search_java = re.compile(
  1232. r'''(const\s+([_$a-zA-Z][_$a-zA-Z0-9]*\s+)+[=]\s*)?[-+]?[0-9]+''')
  1233. pattern_to_search_cpp_cs = re.compile(
  1234. r'''(const\s+([_a-zA-Z][_a-zA-Z0-9]*\s+)+[=]\s*)?[-+]?[0-9]+''')
  1235. pattern_to_search = re.compile(
  1236. r'''[0-9]+''')
  1237. self.declare_metric(self.is_active_numbers,
  1238. self.Field('numbers', int,
  1239. # optimize the size of datafile:
  1240. # store only non-zero results
  1241. non_zero=True),
  1242. {
  1243. 'std.code.java': pattern_to_search_java,
  1244. 'std.code.cpp': pattern_to_search_cpp_cs,
  1245. 'std.code.cs': pattern_to_search_cpp_cs,
  1246. '*': pattern_to_search
  1247. },
  1248. marker_type_mask=mpp.api.Marker.T.CODE,
  1249. region_type_mask=mpp.api.Region.T.ANY)
  1250. super(Plugin, self).initialize(fields=self.get_fields())
  1251. if self.is_active() == True:
  1252. self.subscribe_by_parents_interface(mpp.api.ICode)
  1253. def _numbers_count(self, alias, data, region, marker, match, count, counter_data):
  1254. if match.group(0).startswith('const'):
  1255. return count
  1256. return count + 1
  1257. </pre>
  1258. <li>Now run Metrix++ to collect and view the results.</li>
  1259. <pre>&gt; python "/path/to/metrix++.py" collect --myext.magic.numbers</pre>
  1260. <pre>&gt; python "/path/to/metrix++.py" view</pre>
  1261. </ol>
  1262. <h4>Additional per metric configuration options</h4>
  1263. <ol>
  1264. <li>It is typical that most numbers counted by the metric are equal to 0, -1 or 1.
  1265. They are not necessary magic numbers. 0 or 1 are typical variable initializers.
  1266. -1 is a typical negative return code. So, let's implement simplified version of the metric,
  1267. which does not count 0, -1 and 1, if the specific new option is set.</li>
  1268. <pre class="prettyprint linenums">
  1269. import mpp.api
  1270. import re
  1271. class Plugin(mpp.api.Plugin,
  1272. mpp.api.IConfigurable,
  1273. mpp.api.Child,
  1274. mpp.api.MetricPluginMixin):
  1275. def declare_configuration(self, parser):
  1276. parser.add_option("--myext.magic.numbers", "--mmn",
  1277. action="store_true", default=False,
  1278. help="Enables collection of magic numbers metric [default: %default]")
  1279. # Add new option
  1280. parser.add_option("--myext.magic.numbers.simplier", "--mmns",
  1281. action="store_true", default=False,
  1282. help="Is set, 0, -1 and 1 numbers are not counted [default: %default]")
  1283. def configure(self, options):
  1284. self.is_active_numbers = options.__dict__['myext.magic.numbers']
  1285. # remember the option here
  1286. self.is_active_numbers_simplier = options.__dict__['myext.magic.numbers.simplier']
  1287. def initialize(self):
  1288. pattern_to_search_java = re.compile(
  1289. r'''(const\s+([_$a-zA-Z][_$a-zA-Z0-9]*\s+)+[=]\s*)?[-+]?[0-9]+''')
  1290. pattern_to_search_cpp_cs = re.compile(
  1291. r'''(const\s+([_a-zA-Z][_a-zA-Z0-9]*\s+)+[=]\s*)?[-+]?[0-9]+''')
  1292. pattern_to_search = re.compile(
  1293. r'''[0-9]+''')
  1294. self.declare_metric(self.is_active_numbers,
  1295. self.Field('numbers', int,
  1296. non_zero=True),
  1297. {
  1298. 'std.code.java': pattern_to_search_java,
  1299. 'std.code.cpp': pattern_to_search_cpp_cs,
  1300. 'std.code.cs': pattern_to_search_cpp_cs,
  1301. '*': pattern_to_search
  1302. },
  1303. marker_type_mask=mpp.api.Marker.T.CODE,
  1304. region_type_mask=mpp.api.Region.T.ANY)
  1305. super(Plugin, self).initialize(fields=self.get_fields(),
  1306. # remember option settings in data file properties
  1307. # in order to detect changes in settings on iterative re-run
  1308. properties=[self.Property('number.simplier', self.is_active_numbers_simplier)])
  1309. if self.is_active() == True:
  1310. self.subscribe_by_parents_interface(mpp.api.ICode)
  1311. def _numbers_count(self, alias, data, region, marker, match, count, counter_data):
  1312. if (match.group(0).startswith('const') or
  1313. # use new option to exclude 0 and 1 numbers
  1314. (self.is_active_numbers_simplier == True and
  1315. match.group(0) in ['0', '1', '-1', '+1'])):
  1316. return count
  1317. return count + 1
  1318. </pre>
  1319. <li>Now run Metrix++ to collect and view the results.</li>
  1320. <pre>&gt; python "/path/to/metrix++.py" collect --myext.magic.numbers</pre>
  1321. <pre>&gt; python "/path/to/metrix++.py" view</pre>
  1322. <pre>
  1323. :: info: Overall metrics for 'myext.magic:numbers' metric
  1324. Average : 2.5 (excluding zero metric values)
  1325. Minimum : 2
  1326. Maximum : 3
  1327. Total : 5.0
  1328. Distribution : 2 regions in total (including 0 suppressed)
  1329. Metric value : Ratio : R-sum : Number of regions
  1330. 2 : 0.500 : 0.500 : 1 ||||||||||||||||||||||||||||||||||||||||||||||||||
  1331. 3 : 0.500 : 1.000 : 1 ||||||||||||||||||||||||||||||||||||||||||||||||||
  1332. :: info: Directory content:
  1333. Directory : .
  1334. </pre>
  1335. </ol>
  1336. <h4>Summary</h4>
  1337. <p>We have finished with the tutorial. The tutorial explained how to implement simple and advanced metric plugins.
  1338. We used built-in Metrix++ base classes. If you need to more advanced plugin capabilities,
  1339. override in your plugin class functions inherited from mpp.api base classes. Check code of standard plugins
  1340. to learn more techniques.</p>
  1341. <p></p>
  1342. <h2>Analysis tool plugin</h2>
  1343. <p>This tutorial will explain how to build custom Metrix++ command, which is bound to custom post-analysis tool.
  1344. We will implement the tool, which identifies all new and changed regions and counts number of added lines.
  1345. We skip calculating number of deleted lines, but it is easy to extend from what we get finally in the tutorial.</p>
  1346. <h4>New Metrix++ command / action</h4>
  1347. <ol>
  1348. <li>As in the tutorial for metric plugin, set the environment and
  1349. create new python package 'myext', python lib 'compare.py' and 'compare.ini' file.</li>
  1350. <pre>
  1351. + working_directory (set in METRIXPLUSPLUS_PATH variable)
  1352. \--+ myext
  1353. \--- __init__.py
  1354. \--- compare.py
  1355. \--- compare.ini
  1356. </pre>
  1357. <li>__init__.py is empty file to make myext considered by python as a package.</li>
  1358. <li>Edit compare.py to have the following content:
  1359. <pre class="prettyprint linenums">
  1360. import mpp.api
  1361. class Plugin(mpp.api.Plugin, mpp.api.IRunable):
  1362. def run(self, args):
  1363. print args
  1364. return 0
  1365. </pre>
  1366. Inheritance from mpp.api.IRunable declares that the plugin is runable and requires implementation of 'run' interface.</li>
  1367. <li>Edit compare.ini to have the following content:
  1368. <pre class="prettyprint linenums">
  1369. [Plugin]
  1370. version: 1.0
  1371. package: myext
  1372. module: compare
  1373. class: Plugin
  1374. depends: None
  1375. actions: compare
  1376. enabled: True
  1377. </pre>
  1378. This file is a manifest for Metrix++ plugin loader. Actions field has got new value 'compare'.
  1379. Metrix++ engine will automatically pick this action and will add it to the list of available commands.
  1380. This plugin will be loaded on 'compare' action.
  1381. </li>
  1382. <li>Now run Metrix++ to see how this new plugin works:</li>
  1383. <pre>&gt; python "/path/to/metrix++.py" compare -- path1 path2 path3</pre>
  1384. <pre>["path1", "path2", "path3"]</pre>
  1385. </ol>
  1386. <h4>Access data file loader and its' interfaces</h4>
  1387. <ol>
  1388. <li>By default, all post-analysis tools have got --db-file and --db-file-prev options. It is
  1389. because 'mpp.dbf' plugin is loaded for any action, including our new one 'compare'. In order to continue
  1390. the tutorial, we need to have 2 data files with 'std.code.lines:total' metric collected.
  1391. So, write to files by running:</li>
  1392. <pre>cd my_project_version_1
  1393. &gt; python "/path/to/metrix++.py" collect --std.code.lines.total</pre>
  1394. <pre>cd my_project_version_2
  1395. &gt; python "/path/to/metrix++.py" collect --std.code.lines.total</pre>
  1396. <li>Edit compare.py file to get the loader and iterate collected file paths:</li>
  1397. <pre class="prettyprint linenums">
  1398. import mpp.api
  1399. # load common utils for post processing tools
  1400. import mpp.utils
  1401. class Plugin(mpp.api.Plugin, mpp.api.IRunable):
  1402. def run(self, args):
  1403. # get data file reader using standard metrix++ plugin
  1404. loader = self.get_plugin('mpp.dbf').get_loader()
  1405. # iterate and print file length for every path in args
  1406. exit_code = 0
  1407. for path in (args if len(args) > 0 else [""]):
  1408. file_iterator = loader.iterate_file_data(path)
  1409. if file_iterator == None:
  1410. mpp.utils.report_bad_path(path)
  1411. exit_code += 1
  1412. continue
  1413. for file_data in file_iterator:
  1414. print file_data.get_path()
  1415. return exit_code
  1416. </pre>
  1417. <li>Now run Metrix++ to see how it works:</li>
  1418. <pre>&gt; python "/path/to/metrix++.py" compare --db-file=my_project_version_2/metrixpp.db --db-file-prev=my_project_version_1/metrixpp.db</pre>
  1419. </ol>
  1420. <h4>Identify added, modified files/regions and read metric data</h4>
  1421. <ol>
  1422. <li>Let's extend the logic of the tool to compare files and regions, read 'std.code.lines:total' metric
  1423. and calcuate the summary of number of added lines. mpp.utils.FileRegionsMatcher is helper class
  1424. which does matching and comparison of regions for 2 given mpp.api.FileData objects.</li>
  1425. <pre class="prettyprint linenums">
  1426. import mpp.api
  1427. import mpp.utils
  1428. import mpp.cout
  1429. class Plugin(mpp.api.Plugin, mpp.api.IRunable):
  1430. def run(self, args):
  1431. loader = self.get_plugin('mpp.dbf').get_loader()
  1432. # get previous db file loader
  1433. loader_prev = self.get_plugin('mpp.dbf').get_loader_prev()
  1434. exit_code = 0
  1435. for path in (args if len(args) > 0 else [""]):
  1436. added_lines = 0
  1437. file_iterator = loader.iterate_file_data(path)
  1438. if file_iterator == None:
  1439. mpp.utils.report_bad_path(path)
  1440. exit_code += 1
  1441. continue
  1442. for file_data in file_iterator:
  1443. added_lines += self._compare_file(file_data, loader, loader_prev)
  1444. mpp.cout.notify(path, '', mpp.cout.SEVERITY_INFO,
  1445. "Change trend report",
  1446. [('Added lines', added_lines)])
  1447. return exit_code
  1448. def _compare_file(self, file_data, loader, loader_prev):
  1449. # compare file with previous and return number of new lines
  1450. file_data_prev = loader_prev.load_file_data(file_data.get_path())
  1451. if file_data_prev == None:
  1452. return self._sum_file_regions_lines(file_data)
  1453. elif file_data.get_checksum() != file_data_prev.get_checksum():
  1454. return self._compare_file_regions(file_data, file_data_prev)
  1455. def _sum_file_regions_lines(self, file_data):
  1456. # just sum up the metric for all regions
  1457. result = 0
  1458. for region in file_data.iterate_regions():
  1459. result += region.get_data('std.code.lines', 'total')
  1460. def _compare_file_regions(self, file_data, file_data_prev):
  1461. # compare every region with previous and return number of new lines
  1462. matcher = mpp.utils.FileRegionsMatcher(file_data, file_data_prev)
  1463. result = 0
  1464. for region in file_data.iterate_regions():
  1465. if matcher.is_matched(region.get_id()) == False:
  1466. # if added region, just add the lines
  1467. result += region.get_data('std.code.lines', 'total')
  1468. elif matcher.is_modified(region.get_id()):
  1469. # if modified, add the difference in lines
  1470. region_prev = file_data_prev.get_region(
  1471. matcher.get_prev_id(region.get_id()))
  1472. result += (region.get_data('std.code.lines', 'total') -
  1473. region_prev.get_data('std.code.lines', 'total'))
  1474. return result
  1475. </pre>
  1476. <li>Now run Metrix++ to see how it works:</li>
  1477. <pre>&gt; python "/path/to/metrix++.py" compare --db-file=my_project_version_2/metrixpp.db --db-file-prev=my_project_version_1/metrixpp.db</pre>
  1478. <pre>
  1479. :: info: Change trend report
  1480. Added lines : 7
  1481. </pre>
  1482. </ol>
  1483. <h4>Summary</h4>
  1484. <p>We have finished with the tutorial. The tutorial explained how to read Metrix++ data files and
  1485. implement custom post-processing tools. Even if some existing Metrix++ code requires clean-up and refactoring,
  1486. check code of standard tool plugins to learn more techniques.</p>
  1487. <h2>Language parser plugin</h2>
  1488. <p>Unfortunately, there is no good documentation at this stage for this part.
  1489. Briefly, if metric plugin counts and stores data into FileData object,
  1490. tool plugin reads this data, language plugin construct the original structure of
  1491. FileData object. The orginal structure includes regions (like functions, classes, etc.)
  1492. and markers (like comments, strings, preprocessor, etc.).
  1493. Check code of existing parsers.</p>
  1494. <ul>
  1495. <li>a language parser plugin is registered in the same way as a metric plugin</li>
  1496. <li>it registers parser's callback in 'std.tools.collect' plugin</li>
  1497. <li>parses a file in a callback, called by 'std.tools.collect'</li>
  1498. <li>parser needs to identify markers and regions
  1499. and tell about this to file data object passed as an
  1500. argument for the callback.</li>
  1501. </ul>
  1502. <p>There are useful options and tools avaialble for
  1503. trobuleshooting purposes during development:</p>
  1504. <ul>
  1505. <li>metrix++.py debug generates html code showing parsed code structures and their boundaries</li>
  1506. <li>--nest-regions for view tool forces the viewer to indent subregions.</li>
  1507. <li>--general.log-level option is available for any command and is helpful to trace execution.</li>
  1508. </ul>
  1509. <p>Finally, if there are any questions or enquires, please,
  1510. feel free to <a href="https://sourceforge.net/p/metrixplusplus/tickets/new/">submit new question</a>.</p>
  1511. </section>
  1512. <section id="contribute_section">
  1513. <div class="page-header">
  1514. <h1>Feeback and contribute</h1>
  1515. </div>
  1516. <p>Now it is your turn. There are multiple ways how you can contribute and help to improve and progress Metrix++ project:</p>
  1517. <ul>
  1518. <li>Try Metrix++ and <a href="https://sourceforge.net/projects/metrixplusplus/reviews/new">post review</a></li>
  1519. <li><a href="https://sourceforge.net/p/metrixplusplus/tickets/new/">Submit new feature request or bug report</a></li>
  1520. <li><a href="https://sourceforge.net/p/metrixplusplus/tickets/new/">Ask a question</a></li>
  1521. <li>Share your patch files and ideas, colloborate by email to
  1522. <a href="mailto:avkonst@users.sourceforge.net?subject=Metrix%2B%2B Project Request">project administrator</a></li>
  1523. <li>Create and publish your plugin.
  1524. <a href="mailto:avkonst@users.sourceforge.net?subject=Metrix%2B%2B Plugin Reference">Request to refer</a>
  1525. to it from Metrix++ project space.</li>
  1526. <li><a href="mailto:avkonst@users.sourceforge.net?subject=Metrix%2B%2B Plugin Submission">Submit your plugin</a>
  1527. to include to the standard set</li>
  1528. <li>... and consider to
  1529. <a href="mailto:avkonst@users.sourceforge.net?subject=Metrix%2B%2B Join Request">join the project</a>!</li>
  1530. </ul>
  1531. </section>
  1532. </div> <!-- end for sections -->
  1533. </div></div> <!-- end for row and container -->
  1534. <!-- Footer
  1535. ================================================== -->
  1536. <footer class="footer">
  1537. <div class="container">
  1538. <div class="row">
  1539. <div class="span3">
  1540. <p><a href="http://sourceforge.net/projects/metrixplusplus/"><img src="http://sflogo.sourceforge.net/sflogo.php?group_id=275605&amp;type=3"
  1541. alt="Get Metrix++ at SourceForge.net. Fast, secure and Free Open Source software downloads" border="0"></a></p>
  1542. <p>&middot;</p>
  1543. <p>&middot; &middot;<script type="text/javascript" src="http://www.ohloh.net/p/485947/widgets/project_users_logo.js"></script></p>
  1544. <p><a href="http://freecode.com/projects/metrix"><img src="assets/img/fm_logo.png" width="130"></a></p>
  1545. <p>&middot;</p>
  1546. </div>
  1547. <div class="span9">
  1548. <p>Copyright <strong>&copy;</strong> 2009 - 2013, <a href="mailto:avkonst@users.sourceforge.net"><span class="normalImportance">Metrix++</span> Project</a></p>
  1549. <p>Code licensed under <a href="http://www.gnu.org/licenses/gpl.txt" target="_blank">GPL 3.0</a>, documentation under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</p>
  1550. <ul class="footer-links">
  1551. <li><a href="https://sourceforge.net/p/metrixplusplus/tickets/new/">Ask question</a></li>
  1552. <li class="muted">&middot;</li>
  1553. <li><a href="https://sourceforge.net/p/metrixplusplus/tickets/new/">Report defect</a></li>
  1554. <li class="muted">&middot;</li>
  1555. <li><a href="https://sourceforge.net/p/metrixplusplus/tickets/new/">Feature request</a></li>
  1556. <li class="muted">&middot;</li>
  1557. <li><a href="https://sourceforge.net/p/metrixplusplus/tickets/search/?q=%21status%3Awont-fix+%26%26+%21status%3Aclosed">Open issues</a></li>
  1558. <li class="muted">&middot;</li>
  1559. <li><a href="https://sourceforge.net/p/metrixplusplus/wiki/ChangeLog/">Changelog</a></li>
  1560. </ul>
  1561. </div>
  1562. </div>
  1563. </div>
  1564. </footer>
  1565. <!-- Le javascript
  1566. ================================================== -->
  1567. <!-- Placed at the end of the document so the pages load faster -->
  1568. <script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
  1569. <script src="assets/js/jquery.js"></script>
  1570. <script src="assets/js/bootstrap-transition.js"></script>
  1571. <script src="assets/js/bootstrap-alert.js"></script>
  1572. <script src="assets/js/bootstrap-modal.js"></script>
  1573. <script src="assets/js/bootstrap-dropdown.js"></script>
  1574. <script src="assets/js/bootstrap-scrollspy.js"></script>
  1575. <script src="assets/js/bootstrap-tab.js"></script>
  1576. <script src="assets/js/bootstrap-tooltip.js"></script>
  1577. <script src="assets/js/bootstrap-popover.js"></script>
  1578. <script src="assets/js/bootstrap-button.js"></script>
  1579. <script src="assets/js/bootstrap-collapse.js"></script>
  1580. <script src="assets/js/bootstrap-carousel.js"></script>
  1581. <script src="assets/js/bootstrap-typeahead.js"></script>
  1582. <script src="assets/js/bootstrap-affix.js"></script>
  1583. <script>
  1584. !function ($) {
  1585. $(function(){
  1586. // carousel demo
  1587. $('#myCarousel').carousel()
  1588. })
  1589. }(window.jQuery)
  1590. </script>
  1591. <script src="assets/js/holder/holder.js"></script>
  1592. <script src="assets/js/google-code-prettify/prettify.js"></script>
  1593. <script src="assets/js/application.js"></script>
  1594. <script>
  1595. </script>
  1596. <!-- Analytics
  1597. ================================================== -->
  1598. <!--
  1599. <script>
  1600. var _gauges = _gauges || [];
  1601. (function() {
  1602. var t = document.createElement('script');
  1603. t.type = 'text/javascript';
  1604. t.async = true;
  1605. t.id = 'gauges-tracker';
  1606. t.setAttribute('data-site-id', '4f0dc9fef5a1f55508000013');
  1607. t.src = '//secure.gaug.es/track.js';
  1608. var s = document.getElementsByTagName('script')[0];
  1609. s.parentNode.insertBefore(t, s);
  1610. })();
  1611. </script>
  1612. -->
  1613. </body>
  1614. </html>