Add support for HHBC ops with 5 immediates
[hiphop-php.git] / hphp / runtime / base / code-coverage.cpp
blob4975ae18816c099060f4d2394117ec4562e4b329
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/code-coverage.h"
18 #include <fstream>
19 #include <vector>
21 #include "hphp/runtime/base/execution-context.h"
22 #include "hphp/runtime/base/type-array.h"
23 #include "hphp/runtime/base/type-string.h"
24 #include "hphp/runtime/ext/extension.h"
26 #include "hphp/util/logger.h"
28 namespace HPHP {
29 ///////////////////////////////////////////////////////////////////////////////
32 * The function below will be called by the interpreter on each
33 * evaluated expression when running hhvm with:
34 * $ hhvm -v Eval.RecordCodeCoverage=1 <phpfile>
36 * The (line0, line1) pair is supposed to represent the start and end
37 * of an evaluated expression. One should normally increment the line
38 * usage counters for all the lines between line0 and line1 but certain
39 * constructs like including a file, eval-ing a string, or even
40 * executing a for loop do not have a good value for line1.
42 * Indeed on this toy cover.php file:
44 * <?php
46 * echo "here\n";
47 * for($i = 0; $i < 2; $i++) {
48 * echo "here $i\n";
49 * }
51 * echo 1 +
52 * 2 +
53 * (3 + 4);
55 * eval("echo 'bar\n';");
57 * and with this command:
59 * $ hhvm -v Eval.RecordCodeCoverage=1 \
60 * -v Eval.CodeCoverageOutputFile=/tmp/cov.log \
61 * -f cover.php
63 * one get this output (with appropriate printf in this file):
65 * /home/pad/cover.php, (1, 12)
66 * /home/pad/cover.php, (3, 3)
67 * here
68 * /home/pad/cover.php, (4, 6)
69 * /home/pad/cover.php, (4, 4)
70 * /home/pad/cover.php, (5, 5)
71 * here 0
72 * /home/pad/cover.php, (4, 4)
73 * /home/pad/cover.php, (4, 4)
74 * /home/pad/cover.php, (5, 5)
75 * here 1
76 * /home/pad/cover.php, (4, 4)
77 * /home/pad/cover.php, (4, 4)
78 * /home/pad/cover.php, (8, 10)
79 * /home/pad/cover.php, (8, 9)
80 * /home/pad/cover.php, (8, 10)
81 * 6/home/pad/cover.php, (12, 12)
82 * /home/pad/cover.php, (12, 12)
83 * /home/pad/cover.php, (1, 2)
84 * /home/pad/cover.php, (1, 2)
86 * So right now it is just simpler to ignore line1.
88 void CodeCoverage::Record(const char *filename, int line0, int line1) {
89 if (!filename || !*filename || line0 <= 0 || line1 <= 0 || line0 > line1) {
90 return;
92 Logger::Verbose("%s, (%d, %d)\n", filename, line0, line1);
94 CodeCoverageMap::iterator iter = m_hits.find(filename);
95 if (iter == m_hits.end()) {
96 std::vector<int> &lines = m_hits[filename];
97 lines.resize(line1 + 1);
98 for (int i = line0; i <= line0 /* should be line1 one day */; i++) {
99 lines[i] = 1;
101 } else {
102 std::vector<int> &lines = iter->second;
103 if ((int)lines.size() < line1 + 1) {
104 lines.resize(line1 + 1);
106 for (int i = line0; i <= line0 /* should be line1 one day */; i++) {
107 ++lines[i];
112 Array CodeCoverage::Report(bool sys /* = true */) {
113 Array ret = Array::Create();
114 for (CodeCoverageMap::const_iterator iter = m_hits.begin();
115 iter != m_hits.end(); ++iter) {
116 if (!sys && Extension::IsSystemlibPath(iter->first)) {
117 continue;
119 const std::vector<int> &lines = iter->second;
120 Array tmp = Array::Create();
121 for (int i = 1; i < (int)lines.size(); i++) {
122 if (lines[i]) {
123 tmp.set(i, Variant((int64_t)lines[i]));
126 ret.set(String(iter->first), Variant(tmp));
129 return ret;
132 void CodeCoverage::Report(const std::string &filename) {
133 std::ofstream f(filename.c_str());
134 if (!f) {
135 Logger::Error("unable to open %s", filename.c_str());
136 return;
139 f << "{\n";
140 for (CodeCoverageMap::const_iterator iter = m_hits.begin();
141 iter != m_hits.end();) {
142 const std::vector<int> &lines = iter->second;
143 f << "\"" << iter->first << "\": [";
144 int count = lines.size();
145 for (int i = 0 /* not 1 */; i < count; i++) {
146 f << lines[i];
147 if (i < count - 1) {
148 f << ",";
151 f << "]";
152 if (++iter != m_hits.end()) {
153 f << ",";
155 f << "\n";
157 f << "}\n";
159 f.close();
162 void CodeCoverage::Reset() {
163 m_hits.clear();
164 resetCoverageCounters();
167 ///////////////////////////////////////////////////////////////////////////////