Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: third_party/WebKit/Source/build/scripts/make_instrumenting_probes.py

Issue 2790973002: [instrumentation] Introduce JSON5 config files for probe generator. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | third_party/WebKit/Source/build/scripts/templates/InstrumentingProbesImpl.cpp.tmpl » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2017 The Chromium Authors. All rights reserved. 1 # Copyright 2017 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import ast
5 import optparse 6 import optparse
6 import os.path 7 import os.path
7 import re 8 import re
8 import sys 9 import sys
9 10
10 # Path handling for libraries and templates 11 # Path handling for libraries and templates
11 # Paths have to be normalized because Jinja uses the exact template path to 12 # Paths have to be normalized because Jinja uses the exact template path to
12 # determine the hash used in the cache filename, and we need a pre-caching step 13 # determine the hash used in the cache filename, and we need a pre-caching step
13 # to be concurrency-safe. Use absolute path because __file__ is absolute if 14 # to be concurrency-safe. Use absolute path because __file__ is absolute if
14 # module is imported, and relative if executed directly. 15 # module is imported, and relative if executed directly.
15 # If paths differ between pre-caching and individual file compilation, the cache 16 # If paths differ between pre-caching and individual file compilation, the cache
16 # is regenerated, which causes a race condition and breaks concurrent build, 17 # is regenerated, which causes a race condition and breaks concurrent build,
17 # since some compile processes will try to read the partially written cache. 18 # since some compile processes will try to read the partially written cache.
18 module_path, module_filename = os.path.split(os.path.realpath(__file__)) 19 module_path, module_filename = os.path.split(os.path.realpath(__file__))
19 templates_dir = os.path.join(module_path, "templates") 20 templates_dir = os.path.join(module_path, "templates")
20 third_party_dir = os.path.normpath(os.path.join( 21 third_party_dir = os.path.normpath(os.path.join(module_path, os.pardir, os.pardi r, os.pardir, os.pardir))
21 module_path, os.pardir, os.pardir, os.pardir, os.pardir))
22 # jinja2 is in chromium's third_party directory. 22 # jinja2 is in chromium's third_party directory.
23 # Insert at 1 so at front to override system libraries, and 23 # Insert at 1 so at front to override system libraries, and
24 # after path[0] == invoking script dir 24 # after path[0] == invoking script dir
25 sys.path.insert(1, third_party_dir) 25 sys.path.insert(1, third_party_dir)
26 import jinja2 26 import jinja2
27 27
28 28
29 def _json5_loads(lines):
30 # Use json5.loads when json5 is available. Currently we use simple
31 # regexs to convert well-formed JSON5 to PYL format.
32 # Strip away comments and quote unquoted keys.
33 re_comment = re.compile(r"^\s*//.*$|//+ .*$", re.MULTILINE)
34 re_map_keys = re.compile(r"^\s*([$A-Za-z_][\w]*)\s*:", re.MULTILINE)
35 pyl = re.sub(re_map_keys, r"'\1':", re.sub(re_comment, "", lines))
36 # Convert map values of true/false to Python version True/False.
37 re_true = re.compile(r":\s*true\b")
38 re_false = re.compile(r":\s*false\b")
39 pyl = re.sub(re_true, ":True", re.sub(re_false, ":False", pyl))
40 return ast.literal_eval(pyl)
41
42
29 def to_singular(text): 43 def to_singular(text):
30 return text[:-1] if text[-1] == "s" else text 44 return text[:-1] if text[-1] == "s" else text
31 45
32 46
33 def to_lower_case(name): 47 def to_lower_case(name):
34 return name[:1].lower() + name[1:] 48 return name[:1].lower() + name[1:]
35 49
36 50
51 def agent_config(agent_name, field):
52 observers = config["observers"]
53 if agent_name not in observers:
54 return None
55 return observers[agent_name][field] if field in observers[agent_name] else N one
56
57
37 def agent_name_to_class(agent_name): 58 def agent_name_to_class(agent_name):
38 if agent_name == "Performance": 59 return agent_config(agent_name, "class") or "Inspector%sAgent" % agent_name
39 return "PerformanceMonitor" 60
40 elif agent_name == "TraceEvents": 61
41 return "InspectorTraceEvents" 62 def agent_name_to_include(agent_name):
42 elif agent_name == "PlatformTraceEvents": 63 include_path = agent_config(agent_name, "include") or config["settings"]["de fault_include"]
43 return "PlatformTraceEventsAgent" 64 return os.path.join(include_path, agent_name_to_class(agent_name) + ".h")
44 else:
45 return "Inspector%sAgent" % agent_name
46 65
47 66
48 def initialize_jinja_env(cache_dir): 67 def initialize_jinja_env(cache_dir):
49 jinja_env = jinja2.Environment( 68 jinja_env = jinja2.Environment(
50 loader=jinja2.FileSystemLoader(templates_dir), 69 loader=jinja2.FileSystemLoader(templates_dir),
51 # Bytecode cache is not concurrency-safe unless pre-cached: 70 # Bytecode cache is not concurrency-safe unless pre-cached:
52 # if pre-cached this is read-only, but writing creates a race condition. 71 # if pre-cached this is read-only, but writing creates a race condition.
53 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), 72 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir),
54 keep_trailing_newline=True, # newline-terminate generated files 73 keep_trailing_newline=True, # newline-terminate generated files
55 lstrip_blocks=True, # so can indent control flow tags 74 lstrip_blocks=True, # so can indent control flow tags
56 trim_blocks=True) 75 trim_blocks=True)
57 jinja_env.filters.update({ 76 jinja_env.filters.update({
58 "to_lower_case": to_lower_case, 77 "to_lower_case": to_lower_case,
59 "to_singular": to_singular, 78 "to_singular": to_singular,
60 "agent_name_to_class": agent_name_to_class}) 79 "agent_name_to_class": agent_name_to_class,
80 "agent_name_to_include": agent_name_to_include})
61 jinja_env.add_extension('jinja2.ext.loopcontrols') 81 jinja_env.add_extension('jinja2.ext.loopcontrols')
62 return jinja_env 82 return jinja_env
63 83
64 84
65 def match_and_consume(pattern, source): 85 def match_and_consume(pattern, source):
66 match = re.match(pattern, source) 86 match = re.match(pattern, source)
67 if match: 87 if match:
68 return match, source[len(match.group(0)):].strip() 88 return match, source[len(match.group(0)):].strip()
69 return None, source 89 return None, source
70 90
(...skipping 13 matching lines...) Expand all
84 return model 104 return model
85 105
86 106
87 class File(object): 107 class File(object):
88 def __init__(self, name, source): 108 def __init__(self, name, source):
89 self.name = name 109 self.name = name
90 self.header_name = self.name + "Inl" 110 self.header_name = self.name + "Inl"
91 self.includes = [include_inspector_header(base_name)] 111 self.includes = [include_inspector_header(base_name)]
92 self.forward_declarations = [] 112 self.forward_declarations = []
93 self.declarations = [] 113 self.declarations = []
94 self.defines = []
95 for line in map(str.strip, source.split("\n")): 114 for line in map(str.strip, source.split("\n")):
96 line = re.sub(r"\s{2,}", " ", line).strip() # Collapse whitespace 115 line = re.sub(r"\s{2,}", " ", line).strip() # Collapse whitespace
97 if len(line) == 0: 116 if len(line) == 0:
98 continue 117 continue
99 if line.startswith("#define"):
100 self.defines.append(line)
101 elif line.startswith("#include"): 118 elif line.startswith("#include"):
102 self.includes.append(line) 119 self.includes.append(line)
103 elif line.startswith("class ") or line.startswith("struct "): 120 elif line.startswith("class ") or line.startswith("struct "):
104 self.forward_declarations.append(line) 121 self.forward_declarations.append(line)
105 else: 122 else:
106 self.declarations.append(Method(line)) 123 self.declarations.append(Method(line))
107 self.includes.sort() 124 self.includes.sort()
108 self.forward_declarations.sort() 125 self.forward_declarations.sort()
109 126
110 127
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
187 self.member_type = "Member<%s>" % self.type[:-1] 204 self.member_type = "Member<%s>" % self.type[:-1]
188 else: 205 else:
189 self.member_type = self.type 206 self.member_type = self.type
190 207
191 208
192 def build_param_name(param_type): 209 def build_param_name(param_type):
193 base_name = re.match(r"(const |PassRefPtr<)?(\w*)", param_type).group(2) 210 base_name = re.match(r"(const |PassRefPtr<)?(\w*)", param_type).group(2)
194 return "param" + base_name 211 return "param" + base_name
195 212
196 213
214 def load_config(file_name):
215 default_config = {
216 "settings": {},
217 "observers": {}
218 }
219 if not file_name:
220 return default_config
221 with open(file_name) as config_file:
222 return _json5_loads(config_file.read()) or default_config
223
224
197 cmdline_parser = optparse.OptionParser() 225 cmdline_parser = optparse.OptionParser()
198 cmdline_parser.add_option("--output_dir") 226 cmdline_parser.add_option("--output_dir")
199 cmdline_parser.add_option("--template_dir") 227 cmdline_parser.add_option("--template_dir")
228 cmdline_parser.add_option("--config")
200 229
201 try: 230 try:
202 arg_options, arg_values = cmdline_parser.parse_args() 231 arg_options, arg_values = cmdline_parser.parse_args()
203 if len(arg_values) != 1: 232 if len(arg_values) != 1:
204 raise Exception("Exactly one plain argument expected (found %s)" % len(a rg_values)) 233 raise Exception("Exactly one plain argument expected (found %s)" % len(a rg_values))
205 input_path = arg_values[0] 234 input_path = arg_values[0]
206 output_dirpath = arg_options.output_dir 235 output_dirpath = arg_options.output_dir
207 if not output_dirpath: 236 if not output_dirpath:
208 raise Exception("Output directory must be specified") 237 raise Exception("Output directory must be specified")
238 config_file_name = arg_options.config
209 except Exception: 239 except Exception:
210 # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html 240 # Work with python 2 and 3 http://docs.python.org/py3k/howto/pyporting.html
211 exc = sys.exc_info()[1] 241 exc = sys.exc_info()[1]
212 sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc) 242 sys.stderr.write("Failed to parse command-line arguments: %s\n\n" % exc)
213 sys.stderr.write("Usage: <script> --output_dir <output_dir> InstrumentingPro bes.idl\n") 243 sys.stderr.write("Usage: <script> [options] InstrumentingProbes.idl\n")
244 sys.stderr.write("Options:\n")
245 sys.stderr.write("\t--output_dir <output_dir>\n")
246 sys.stderr.write("\t--config <config_file.json5>\n")
dgozman 2017/03/31 23:15:19 --template_dir ?
alph 2017/04/01 00:05:28 Done.
214 exit(1) 247 exit(1)
215 248
249 config = load_config(config_file_name)
216 jinja_env = initialize_jinja_env(output_dirpath) 250 jinja_env = initialize_jinja_env(output_dirpath)
217 all_agents = set() 251 all_agents = set()
218 all_defines = []
219 base_name = os.path.splitext(os.path.basename(input_path))[0] 252 base_name = os.path.splitext(os.path.basename(input_path))[0]
220 253
221 fin = open(input_path, "r") 254 fin = open(input_path, "r")
222 files = load_model_from_idl(fin.read()) 255 files = load_model_from_idl(fin.read())
223 fin.close() 256 fin.close()
224 257
225 for f in files: 258 for f in files:
226 for declaration in f.declarations: 259 for declaration in f.declarations:
227 for agent in declaration.agents: 260 for agent in declaration.agents:
228 all_agents.add(agent) 261 all_agents.add(agent)
229 all_defines += f.defines
230 262
231 template_context = { 263 template_context = {
232 "files": files, 264 "files": files,
233 "agents": all_agents, 265 "agents": all_agents,
234 "defines": all_defines, 266 "config": config,
235 "name": base_name, 267 "name": base_name,
236 "input_file": os.path.basename(input_path) 268 "input_file": os.path.basename(input_path)
237 } 269 }
238 cpp_template = jinja_env.get_template("/InstrumentingProbesImpl.cpp.tmpl") 270 cpp_template = jinja_env.get_template("/InstrumentingProbesImpl.cpp.tmpl")
239 cpp_file = open(output_dirpath + "/" + base_name + "Impl.cpp", "w") 271 cpp_file = open(output_dirpath + "/" + base_name + "Impl.cpp", "w")
240 cpp_file.write(cpp_template.render(template_context)) 272 cpp_file.write(cpp_template.render(template_context))
241 cpp_file.close() 273 cpp_file.close()
242 274
243 sink_h_template = jinja_env.get_template("/ProbeSink.h.tmpl") 275 sink_h_template = jinja_env.get_template("/ProbeSink.h.tmpl")
244 sink_h_file = open(output_dirpath + "/" + to_singular(base_name) + "Sink.h", "w" ) 276 sink_h_file = open(output_dirpath + "/" + to_singular(base_name) + "Sink.h", "w" )
245 sink_h_file.write(sink_h_template.render(template_context)) 277 sink_h_file.write(sink_h_template.render(template_context))
246 sink_h_file.close() 278 sink_h_file.close()
247 279
248 for f in files: 280 for f in files:
249 template_context["file"] = f 281 template_context["file"] = f
250 h_template = jinja_env.get_template("/InstrumentingProbesInl.h.tmpl") 282 h_template = jinja_env.get_template("/InstrumentingProbesInl.h.tmpl")
251 h_file = open(output_dirpath + "/" + f.header_name + ".h", "w") 283 h_file = open(output_dirpath + "/" + f.header_name + ".h", "w")
252 h_file.write(h_template.render(template_context)) 284 h_file.write(h_template.render(template_context))
253 h_file.close() 285 h_file.close()
OLDNEW
« no previous file with comments | « no previous file | third_party/WebKit/Source/build/scripts/templates/InstrumentingProbesImpl.cpp.tmpl » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698