1 # The MIT License (MIT)
3 # Copyright (c) 2014 Philippe Proulx <philippe.proulx@efficios.com>
5 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # of this software and associated documentation files (the "Software"), to deal
7 # in the Software without restriction, including without limitation the rights
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the Software is
10 # furnished to do so, subject to the following conditions:
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 from termcolor
import cprint
, colored
31 def _perror(msg
, exit_code
=1):
32 cprint('Error: {}'.format(msg
), 'red', attrs
=['bold'], file=sys
.stderr
)
37 cprint(':: {}'.format(msg
), 'blue', attrs
=['bold'], file=sys
.stderr
)
41 ap
= argparse
.ArgumentParser()
43 ap
.add_argument('-O', '--output', metavar
='OUTPUT', action
='store',
45 help='output directory of C files')
46 ap
.add_argument('-p', '--prefix', metavar
='PREFIX', action
='store',
48 help='custom prefix for C function and structure names')
49 ap
.add_argument('-s', '--static-inline', action
='store_true',
50 help='generate static inline C functions')
51 ap
.add_argument('-c', '--manual-clock', action
='store_true',
52 help='do not use a clock callback: pass clock value to tracing functions')
53 ap
.add_argument('metadata', metavar
='METADATA', action
='store',
54 help='CTF metadata input file')
57 args
= ap
.parse_args()
59 # validate output directory
60 if not os
.path
.isdir(args
.output
):
61 _perror('"{}" is not an existing directory'.format(args
.output
))
64 if not re
.match(r
'^[a-zA-Z_][a-zA-Z0-9_]*$', args
.prefix
):
65 _perror('"{}" is not a valid C identifier'.format(args
.prefix
))
67 # validate that metadata file exists
68 if not os
.path
.isfile(args
.metadata
):
69 _perror('"{}" is not an existing file'.format(args
.metadata
))
74 def _validate_struct(struct
):
75 if type(struct
) is not pytsdl
.tsdl
.Struct
:
76 raise RuntimeError('expecting a struct')
78 for name
, ftype
in struct
.fields
.items():
79 if type(ftype
) is pytsdl
.tsdl
.Sequence
:
80 raise RuntimeError('field "{}" is a dynamic array'.format(name
))
81 elif type(ftype
) is pytsdl
.tsdl
.Array
:
83 element
= ftype
.element
86 if type(element
) is pytsdl
.tsdl
.Sequence
:
87 raise RuntimeError('field "{}" contains a dynamic array'.format(name
))
88 elif type(element
) is pytsdl
.tsdl
.Variant
:
89 raise RuntimeError('field "{}" contains a variant (unsupported)'.format(name
))
90 elif type(element
) is pytsdl
.tsdl
.String
:
91 raise RuntimeError('field "{}" contains a string'.format(name
))
92 elif type(element
) is pytsdl
.tsdl
.Struct
:
93 _validate_struct(element
)
95 if type(element
) is pytsdl
.tsdl
.Array
:
96 element
= element
.element
99 elif type(ftype
) is pytsdl
.tsdl
.Variant
:
100 raise RuntimeError('field "{}" is a variant (unsupported)'.format(name
))
101 elif type(ftype
) is pytsdl
.tsdl
.String
:
102 raise RuntimeError('field "{}" is a string'.format(name
))
103 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
104 _validate_struct(ftype
)
107 def _validate_context_field(struct
):
108 if type(struct
) is not pytsdl
.tsdl
.Struct
:
109 raise RuntimeError('expecting a struct')
111 for name
, ftype
in struct
.fields
.items():
112 if type(ftype
) is pytsdl
.tsdl
.Variant
:
113 raise RuntimeError('field "{}" is a variant (unsupported)'.format(name
))
114 elif type(ftype
) is pytsdl
.tsdl
.Struct
:
115 _validate_struct(ftype
)
118 def _validate_integer(integer
, size
=None, align
=None, signed
=None):
119 if type(integer
) is not pytsdl
.tsdl
.Integer
:
120 raise RuntimeError('expected integer')
123 if integer
.size
!= size
:
124 raise RuntimeError('expected {}-bit integer'.format(size
))
126 if align
is not None:
127 if integer
.align
!= align
:
128 raise RuntimeError('expected integer with {}-bit alignment'.format(align
))
130 if signed
is not None:
131 if integer
.signed
!= signed
:
132 raise RuntimeError('expected {} integer'.format('signed' if signed
else 'unsigned'))
135 def _validate_packet_header(packet_header
):
137 _validate_struct(packet_header
)
138 except RuntimeError as e
:
139 _perror('packet header: {}'.format(e
))
141 # magic must be the first field
142 if 'magic' in packet_header
.fields
:
143 if list(packet_header
.fields
.keys())[0] != 'magic':
144 _perror('packet header: "magic" must be the first field')
146 _perror('packet header: missing "magic" field')
148 # magic must be a 32-bit unsigned integer, 32-bit aligned
150 _validate_integer(packet_header
['magic'], 32, 32, False)
151 except RuntimeError as e
:
152 _perror('packet header: "magic": {}'.format(e
))
154 # mandatory stream_id
155 if 'stream_id' not in packet_header
.fields
:
156 _perror('packet header: missing "stream_id" field')
158 # stream_id must be an unsigned integer
160 _validate_integer(packet_header
['stream_id'], signed
=False)
161 except RuntimeError as e
:
162 _perror('packet header: "stream_id": {}'.format(e
))
165 def _dot_name_to_str(name
):
166 return '.'.join(name
)
169 def _validate_clock(doc
, name
):
170 msg
= '"{}" does not name an existing clock'.format(_dot_name_to_str(name
))
173 raise RuntimeError(msg
)
175 if name
[0] != 'clock' or name
[2] != 'value':
178 if name
[1] not in doc
.clocks
:
179 raise RuntimeError(msg
)
182 def _compare_integers(int1
, int2
):
183 if type(int1
) is not pytsdl
.tsdl
.Integer
:
186 if type(int2
) is not pytsdl
.tsdl
.Integer
:
189 size
= int1
.size
== int2
.size
190 align
= int1
.align
== int2
.align
191 cmap
= int1
.map == int2
.map
192 base
= int1
.base
== int2
.base
193 encoding
= int1
.encoding
== int2
.encoding
194 signed
= int1
.signed
== int2
.signed
195 comps
= (size
, align
, cmap
, base
, encoding
, signed
)
197 return sum(comps
) == len(comps
)
200 def _validate_packet_context(doc
, stream
):
201 packet_context
= stream
.packet_context
205 _validate_struct(packet_context
)
206 except RuntimeError as e
:
207 _perror('stream {}: packet context: {}'.format(sid
, e
))
209 fields
= packet_context
.fields
211 # if timestamp_begin exists, timestamp_end must exist
212 if 'timestamp_begin' in fields
or 'timestamp_end' in fields
:
213 if 'timestamp_begin' not in fields
or 'timestamp_end' not in fields
:
214 _perror('stream {}: packet context: "timestamp_begin" must exist if "timestamp_end" exists'.format(sid
))
216 # timestamp_begin and timestamp_end must have the same integer
217 # as the event header's timestamp field (should exist by now)
218 timestamp
= stream
.event_header
['timestamp']
220 if not _compare_integers(fields
['timestamp_begin'], timestamp
):
221 _perror('stream {}: packet context: "timestamp_begin": integer type different from event header\'s "timestamp" field'.format(sid
))
223 if not _compare_integers(fields
['timestamp_end'], timestamp
):
224 _perror('stream {}: packet context: "timestamp_end": integer type different from event header\'s "timestamp" field'.format(sid
))
226 # content_size must exist and be an unsigned integer
227 if 'content_size' not in fields
:
228 _perror('stream {}: packet context: missing "content_size" field'.format(sid
))
231 _validate_integer(fields
['content_size'], 32, 32, False)
234 _validate_integer(fields
['content_size'], 64, 64, False)
236 _perror('stream {}: packet context: "content_size": expecting unsigned 32-bit/64-bit integer'.format(sid
))
238 # packet_size must exist and be an unsigned integer
239 if 'packet_size' not in fields
:
240 _perror('stream {}: packet context: missing "packet_size" field'.format(sid
))
243 _validate_integer(fields
['packet_size'], 32, 32, False)
246 _validate_integer(fields
['packet_size'], 64, 64, False)
248 _perror('stream {}: packet context: "packet_size": expecting unsigned 32-bit/64-bit integer'.format(sid
))
250 # if cpu_id exists, must be an unsigned integer
251 if 'cpu_id' in fields
:
253 _validate_integer(fields
['cpu_id'], signed
=False)
254 except RuntimeError as e
:
255 _perror('stream {}: packet context: "cpu_id": {}'.format(sid
, e
))
258 def _validate_event_header(doc
, stream
):
259 event_header
= stream
.event_header
263 _validate_struct(event_header
)
264 except RuntimeError as e
:
265 _perror('stream {}: event header: {}'.format(sid
, e
))
267 fields
= event_header
.fields
269 # id must exist and be an unsigned integer
270 if 'id' not in fields
:
271 _perror('stream {}: event header: missing "id" field'.format(sid
))
274 _validate_integer(fields
['id'], signed
=False)
275 except RuntimeError as e
:
276 _perror('stream {}: "id": {}'.format(sid
, format(e
)))
279 # timestamp must exist, be an unsigned integer and be mapped to a valid clock
280 if 'timestamp' not in fields
:
281 _perror('stream {}: event header: missing "timestamp" field'.format(sid
))
284 _validate_integer(fields
['timestamp'], signed
=False)
285 except RuntimeError as e
:
286 _perror('stream {}: "timestamp": {}'.format(sid
, format(e
)))
288 if fields
['timestamp'].map is None:
289 _perror('stream {}: "timestamp": integer must be mapped to an existing clock'.format(sid
))
292 _validate_clock(doc
, fields
['timestamp'].map)
293 except RuntimeError as e
:
294 _perror('stream {}: "timestamp": integer must be mapped to an existing clock'.format(sid
))
297 def _validate_stream_event_context(doc
, stream
):
298 stream_event_context
= stream
.event_context
301 if stream_event_context
is None:
305 _validate_context_field(stream_event_context
)
306 except RuntimeError as e
:
307 _perror('stream {}: event context: {}'.format(sid
, e
))
310 def _validate_headers_contexts(doc
):
312 _validate_packet_header(doc
.trace
.packet_header
)
315 for stream_id
, stream
in doc
.streams
.items():
316 _validate_event_header(doc
, stream
)
317 _validate_packet_context(doc
, stream
)
318 _validate_stream_event_context(doc
, stream
)
321 def _validate_metadata(doc
):
322 _validate_headers_contexts(doc
)
325 def gen_barectf(metadata
, output
, prefix
, static_inline
, manual_clock
):
326 # open CTF metadata file
328 with
open(metadata
) as f
:
331 _perror('cannot open/read CTF metadata file "{}"'.format(metadata
))
334 parser
= pytsdl
.parser
.Parser()
337 doc
= parser
.parse(tsdl
)
338 except pytsdl
.parser
.ParseError
as e
:
339 _perror('parse error: {}'.format(e
))
341 # validate CTF metadata against barectf constraints
342 _validate_metadata(doc
)
347 _pinfo(static_inline
)
353 gen_barectf(args
.metadata
, args
.output
, args
.prefix
, args
.static_inline
,
This page took 0.038184 seconds and 5 git commands to generate.