1 // Copyright 2012 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 
18 #include "build_log.h"
19 #include "graph.h"
20 #include "manifest_parser.h"
21 #include "state.h"
22 #include "util.h"
23 #include "metrics.h"
24 
25 #ifndef _WIN32
26 #include <unistd.h>
27 #endif
28 
29 using namespace std;
30 
31 const char kTestFilename[] = "BuildLogPerfTest-tempfile";
32 
33 struct NoDeadPaths : public BuildLogUser {
IsPathDeadNoDeadPaths34   virtual bool IsPathDead(StringPiece) const { return false; }
35 };
36 
WriteTestData(string* err)37 bool WriteTestData(string* err) {
38   BuildLog log;
39 
40   NoDeadPaths no_dead_paths;
41   if (!log.OpenForWrite(kTestFilename, no_dead_paths, err))
42     return false;
43 
44   /*
45   A histogram of command lengths in chromium. For example, 407 builds,
46   1.4% of all builds, had commands longer than 32 bytes but shorter than 64.
47        32    407   1.4%
48        64    183   0.6%
49       128   1461   5.1%
50       256    791   2.8%
51       512   1314   4.6%
52      1024   6114  21.3%
53      2048  11759  41.0%
54      4096   2056   7.2%
55      8192   4567  15.9%
56     16384     13   0.0%
57     32768      4   0.0%
58     65536      5   0.0%
59   The average command length is 4.1 kB and there were 28674 commands in total,
60   which makes for a total log size of ~120 MB (also counting output filenames).
61 
62   Based on this, write 30000 many 4 kB long command lines.
63   */
64 
65   // ManifestParser is the only object allowed to create Rules.
66   const size_t kRuleSize = 4000;
67   string long_rule_command = "gcc ";
68   for (int i = 0; long_rule_command.size() < kRuleSize; ++i) {
69     char buf[80];
70     sprintf(buf, "-I../../and/arbitrary/but/fairly/long/path/suffixed/%d ", i);
71     long_rule_command += buf;
72   }
73   long_rule_command += "$in -o $out\n";
74 
75   State state;
76   ManifestParser parser(&state, NULL);
77   if (!parser.ParseTest("rule cxx\n  command = " + long_rule_command, err))
78     return false;
79 
80   // Create build edges. Using ManifestParser is as fast as using the State api
81   // for edge creation, so just use that.
82   const int kNumCommands = 30000;
83   string build_rules;
84   for (int i = 0; i < kNumCommands; ++i) {
85     char buf[80];
86     sprintf(buf, "build input%d.o: cxx input%d.cc\n", i, i);
87     build_rules += buf;
88   }
89 
90   if (!parser.ParseTest(build_rules, err))
91     return false;
92 
93   for (int i = 0; i < kNumCommands; ++i) {
94     log.RecordCommand(state.edges_[i],
95                       /*start_time=*/100 * i,
96                       /*end_time=*/100 * i + 1,
97                       /*mtime=*/0);
98   }
99 
100   return true;
101 }
102 
main()103 int main() {
104   vector<int> times;
105   string err;
106 
107   if (!WriteTestData(&err)) {
108     fprintf(stderr, "Failed to write test data: %s\n", err.c_str());
109     return 1;
110   }
111 
112   {
113     // Read once to warm up disk cache.
114     BuildLog log;
115     if (log.Load(kTestFilename, &err) == LOAD_ERROR) {
116       fprintf(stderr, "Failed to read test data: %s\n", err.c_str());
117       return 1;
118     }
119   }
120   const int kNumRepetitions = 5;
121   for (int i = 0; i < kNumRepetitions; ++i) {
122     int64_t start = GetTimeMillis();
123     BuildLog log;
124     if (log.Load(kTestFilename, &err) == LOAD_ERROR) {
125       fprintf(stderr, "Failed to read test data: %s\n", err.c_str());
126       return 1;
127     }
128     int delta = (int)(GetTimeMillis() - start);
129     printf("%dms\n", delta);
130     times.push_back(delta);
131   }
132 
133   int min = times[0];
134   int max = times[0];
135   float total = 0;
136   for (size_t i = 0; i < times.size(); ++i) {
137     total += times[i];
138     if (times[i] < min)
139       min = times[i];
140     else if (times[i] > max)
141       max = times[i];
142   }
143 
144   printf("min %dms  max %dms  avg %.1fms\n",
145          min, max, total / times.size());
146 
147   unlink(kTestFilename);
148 
149   return 0;
150 }
151