Skip to content

Commit 146bf84

Browse files
Merge pull request #3688 from patrick-sko:master
PiperOrigin-RevId: 333636812
2 parents 6409082 + e74672a commit 146bf84

File tree

8 files changed

+981
-0
lines changed

8 files changed

+981
-0
lines changed

BUILD.bazel

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ java_binary(
8787
runtime_deps = [":compiler_lib"],
8888
)
8989

90+
java_binary(
91+
name = "instrumentationReporter",
92+
main_class = "com.google.javascript.jscomp.instrumentation.reporter.ProductionInstrumentationReporter",
93+
runtime_deps = [":compiler_lib"],
94+
)
95+
9096
java_library(
9197
name = "externs",
9298
resources = [":externs_zip"],
@@ -216,6 +222,7 @@ java_library(
216222
"contrib/externs/chai-3.5.js",
217223
] + glob([
218224
"test/com/google/javascript/jscomp/testdata/**/*",
225+
"test/com/google/javascript/jscomp/instrumentation/reporter/testdata/**/*",
219226
"test/com/google/javascript/refactoring/examples/testdata/**/*",
220227
]),
221228
resources = glob([
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* Copyright 2020 The Closure Compiler Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.javascript.jscomp.instrumentation.reporter;
18+
19+
import static com.google.common.base.Preconditions.checkState;
20+
21+
import com.google.auto.value.AutoValue;
22+
import com.google.common.annotations.GwtIncompatible;
23+
import com.google.common.base.Splitter;
24+
import com.google.common.collect.ImmutableList;
25+
import com.google.common.collect.ImmutableMap;
26+
import com.google.debugging.sourcemap.Base64VLQ;
27+
import com.google.gson.Gson;
28+
import com.google.gson.GsonBuilder;
29+
import com.google.gson.reflect.TypeToken;
30+
import com.google.javascript.jscomp.instrumentation.reporter.ProductionInstrumentationReporter.InstrumentationType;
31+
import java.io.IOException;
32+
import java.lang.reflect.Type;
33+
import java.util.ArrayList;
34+
import java.util.LinkedHashMap;
35+
import java.util.List;
36+
import java.util.Map;
37+
import java.util.function.Predicate;
38+
39+
/**
40+
* The class contains the data which is provided by the Production Instrumentation pass in the
41+
* closure compiler. It will read the data from the file name created with the
42+
* --instrument_mapping_report and is required to follow the format used by Closure Compiler class
43+
* VariableMap.
44+
*/
45+
@GwtIncompatible
46+
final class InstrumentationMapping {
47+
48+
final ImmutableList<String> fileNames;
49+
final ImmutableMap<String, Location> parameterMapping;
50+
51+
private InstrumentationMapping(
52+
ImmutableList<String> fileNames, ImmutableMap<String, Location> parameterMapping) {
53+
this.fileNames = fileNames;
54+
this.parameterMapping = parameterMapping;
55+
}
56+
57+
/**
58+
* Parses the file found at location mappingFilePath and populates the properties of this class
59+
*/
60+
public static InstrumentationMapping parse(String mappingFilePath) throws IOException {
61+
62+
Map<String, Location> localParameterMapping = new LinkedHashMap<>();
63+
64+
String instrumentationMappingFile = ProductionInstrumentationReporter.readFile(mappingFilePath);
65+
List<String> linesOfFile =
66+
Splitter.on('\n').omitEmptyStrings().splitToList(instrumentationMappingFile);
67+
68+
// Report should contain at least three lines
69+
checkState(linesOfFile.size() >= 3, "Malformed report %s", linesOfFile);
70+
71+
String fileNamesLine = linesOfFile.get(0).trim();
72+
String functionNamesLine = linesOfFile.get(1).trim();
73+
String typesLine = linesOfFile.get(2).trim();
74+
75+
checkState(fileNamesLine.startsWith("FileNames:"));
76+
checkState(functionNamesLine.startsWith("FunctionNames:"));
77+
checkState(typesLine.startsWith("Types:"));
78+
79+
String fileNamesAsJsonArray = fileNamesLine.substring(fileNamesLine.indexOf(":") + 1);
80+
String functionNamesAsJsonArray =
81+
functionNamesLine.substring(functionNamesLine.indexOf(":") + 1);
82+
String typesAsJsonArray = typesLine.substring(typesLine.indexOf(":") + 1);
83+
84+
Type stringListType = new TypeToken<List<String>>() {}.getType();
85+
86+
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
87+
List<String> localFileNames = gson.fromJson(fileNamesAsJsonArray, stringListType);
88+
List<String> functionNames = gson.fromJson(functionNamesAsJsonArray, stringListType);
89+
90+
List<String> typesAsStringList = gson.fromJson(typesAsJsonArray, stringListType);
91+
List<InstrumentationType> types = InstrumentationType.convertFromStringList(typesAsStringList);
92+
93+
for (int i = 3; i < linesOfFile.size(); ++i) {
94+
String lineItem = linesOfFile.get(i);
95+
String id = lineItem.substring(0, lineItem.indexOf(':'));
96+
String encodedDetails = lineItem.substring(lineItem.indexOf(':') + 1);
97+
98+
StringCharIterator encodedDetailsAsCharIt = new StringCharIterator(encodedDetails);
99+
100+
Location temp =
101+
Location.create(
102+
localFileNames.get(Base64VLQ.decode(encodedDetailsAsCharIt)),
103+
functionNames.get(Base64VLQ.decode(encodedDetailsAsCharIt)),
104+
types.get(Base64VLQ.decode(encodedDetailsAsCharIt)),
105+
Base64VLQ.decode(encodedDetailsAsCharIt),
106+
Base64VLQ.decode(encodedDetailsAsCharIt));
107+
localParameterMapping.putIfAbsent(id, temp);
108+
}
109+
110+
return new InstrumentationMapping(
111+
ImmutableList.copyOf(localFileNames), ImmutableMap.copyOf(localParameterMapping));
112+
}
113+
114+
public String getFileName(String id) {
115+
return parameterMapping.get(id).fileName();
116+
}
117+
118+
public String getFunctionName(String id) {
119+
return parameterMapping.get(id).functionName();
120+
}
121+
122+
public InstrumentationType getType(String id) {
123+
return parameterMapping.get(id).type();
124+
}
125+
126+
public int getLineNo(String id) {
127+
return parameterMapping.get(id).lineNo();
128+
}
129+
130+
public int getColNo(String id) {
131+
return parameterMapping.get(id).colNo();
132+
}
133+
134+
/**
135+
* This function will return a list of unique parameters which match the criteria specified by the
136+
* predicate. As an example, it can return a list of all unique parameters which match a specific
137+
* file name.
138+
*/
139+
public List<String> getAllMatchingValues(Predicate<String> comparisonPredicate) {
140+
List<String> result = new ArrayList<>();
141+
for (String key : parameterMapping.keySet()) {
142+
if (comparisonPredicate.test(key)) {
143+
result.add(key);
144+
}
145+
}
146+
return result;
147+
}
148+
149+
@AutoValue
150+
abstract static class Location {
151+
152+
static Location create(String a, String b, InstrumentationType c, int d, int e) {
153+
return new AutoValue_InstrumentationMapping_Location(a, b, c, d, e);
154+
}
155+
156+
abstract String fileName();
157+
158+
abstract String functionName();
159+
160+
abstract InstrumentationType type();
161+
162+
abstract int lineNo();
163+
164+
abstract int colNo();
165+
}
166+
167+
/**
168+
* A implementation of the Base64VLQ CharIterator used for decoding the mappings encoded in the
169+
* JSON string.
170+
*/
171+
private static class StringCharIterator implements Base64VLQ.CharIterator {
172+
173+
final String content;
174+
final int length;
175+
int current = 0;
176+
177+
StringCharIterator(String content) {
178+
this.content = content;
179+
this.length = content.length();
180+
}
181+
182+
@Override
183+
public char next() {
184+
return content.charAt(current++);
185+
}
186+
187+
@Override
188+
public boolean hasNext() {
189+
return current < length;
190+
}
191+
}
192+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* Copyright 2020 The Closure Compiler Authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.javascript.jscomp.instrumentation.reporter;
18+
19+
import static java.nio.charset.StandardCharsets.UTF_8;
20+
21+
import com.google.common.annotations.GwtIncompatible;
22+
import com.google.common.collect.ImmutableList;
23+
import com.google.common.io.CharStreams;
24+
import com.google.gson.Gson;
25+
import com.google.gson.GsonBuilder;
26+
import com.google.gson.reflect.TypeToken;
27+
import com.google.javascript.jscomp.instrumentation.reporter.ProfilingReport.ProfilingData;
28+
import java.io.File;
29+
import java.io.IOException;
30+
import java.io.Writer;
31+
import java.lang.reflect.Type;
32+
import java.nio.file.Files;
33+
import java.nio.file.Paths;
34+
import java.util.ArrayList;
35+
import java.util.List;
36+
import java.util.Map;
37+
import java.util.stream.Collectors;
38+
import org.kohsuke.args4j.CmdLineException;
39+
import org.kohsuke.args4j.CmdLineParser;
40+
import org.kohsuke.args4j.Option;
41+
42+
/**
43+
* This class will read a file that contains the instrumentation mapping generated by the compiler
44+
* production instrumentation pass, and also a list of other files which are the reports sent by the
45+
* instrumented production code. It will then take these inputs and generate a single JSON which
46+
* provides a detailed breakdown of each instrumentation point.
47+
*/
48+
@GwtIncompatible
49+
final class ProductionInstrumentationReporter {
50+
51+
@Option(
52+
name = "--mapping_file",
53+
usage = "The file name of the mapping generated by the production instrumentation pass.",
54+
required = true)
55+
private String instrumentationMappingLocation = "";
56+
57+
@Option(
58+
name = "--reports_directory",
59+
usage =
60+
"The folder/directory which contains all the reports created by the instrumented"
61+
+ " production code.",
62+
required = true)
63+
private String instrumentationReportsDirectory = "";
64+
65+
@Option(
66+
name = "--result_output",
67+
usage =
68+
"Use this flag to provide the name of the final report that will be generated by"
69+
+ "this reporter.",
70+
required = true)
71+
private String finalResultOutput = "";
72+
73+
public static void main(String[] args) {
74+
try {
75+
new ProductionInstrumentationReporter().doMain(args);
76+
} catch (IOException e) {
77+
System.err.println(e.getMessage());
78+
}
79+
}
80+
81+
/** This function reads a file at the given filePath and converts the contents into a string. */
82+
public static String readFile(String filePath) throws IOException {
83+
return CharStreams.toString(Files.newBufferedReader(Paths.get(filePath), UTF_8));
84+
}
85+
86+
/**
87+
* Reads all files found in folder and converts the contents of each file to a Map<String,
88+
* ProfilingData> data structure where it is a mapping of the unique param value to the encoded
89+
* values. The folder contains all the reports sent by the instrumented production code.
90+
*/
91+
private ImmutableList<Map<String, ProfilingData>> getAllExecutionResults(File folder)
92+
throws IOException {
93+
List<Map<String, ProfilingData>> result = new ArrayList<>();
94+
95+
for (final File fileEntry : folder.listFiles()) {
96+
String executionResult = readFile(fileEntry.getAbsolutePath());
97+
Type type = new TypeToken<Map<String, ProfilingData>>() {}.getType();
98+
Map<String, ProfilingData> executedInstrumentationData =
99+
new Gson().fromJson(executionResult, type);
100+
101+
result.add(executedInstrumentationData);
102+
}
103+
104+
return ImmutableList.copyOf(result);
105+
}
106+
107+
/**
108+
* Creates a file with the given fileName (including extension) with the contents of the file
109+
* being provided by fileContents.
110+
*/
111+
private void createFile(String fileName, String fileContents) throws IOException {
112+
113+
File fold = new File(fileName);
114+
fold.delete();
115+
File myObj = new File(fileName);
116+
myObj.createNewFile();
117+
118+
Writer myWriter = Files.newBufferedWriter(Paths.get(fileName), UTF_8);
119+
myWriter.write(fileContents);
120+
myWriter.close();
121+
}
122+
123+
private void doMain(String[] args) throws IOException {
124+
125+
parseCmdLineArguments(args);
126+
127+
InstrumentationMapping instrumentationMapping =
128+
InstrumentationMapping.parse(instrumentationMappingLocation);
129+
130+
File folder = new File(instrumentationReportsDirectory);
131+
132+
ImmutableList<Map<String, ProfilingData>> listOfExecutionResults =
133+
getAllExecutionResults(folder);
134+
135+
ProfilingReport profilingReport =
136+
ProfilingReport.createProfilingReport(instrumentationMapping, listOfExecutionResults);
137+
138+
Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
139+
createFile(finalResultOutput, gson.toJson(profilingReport));
140+
}
141+
142+
private void parseCmdLineArguments(String[] args) {
143+
CmdLineParser parser = new CmdLineParser(this);
144+
parser.setUsageWidth(80);
145+
146+
try {
147+
parser.parseArgument(args);
148+
} catch (CmdLineException e) {
149+
System.err.println(e.getMessage());
150+
return;
151+
}
152+
}
153+
154+
public enum InstrumentationType {
155+
FUNCTION,
156+
BRANCH,
157+
BRANCH_DEFAULT;
158+
159+
public static List<InstrumentationType> convertFromStringList(List<String> typesAsString) {
160+
return typesAsString.stream().map(InstrumentationType::valueOf).collect(Collectors.toList());
161+
}
162+
}
163+
}

0 commit comments

Comments
 (0)