flt.lttng-utils.debug-info: honor component's initial log level
[babeltrace.git] / src / plugins / lttng-utils / debug-info / trace-ir-metadata-copy.c
CommitLineData
ca9f27f3
FD
1/*
2 * Babeltrace - Trace IR metadata object copy
3 *
4 * Copyright (c) 2015 EfficiOS Inc. and Linux Foundation
5 * Copyright (c) 2018 Philippe Proulx <pproulx@efficios.com>
6 * Copyright (c) 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
3a3d15f3 27#define BT_LOG_OUTPUT_LEVEL log_level
350ad6c1 28#define BT_LOG_TAG "PLUGIN/FLT.LTTNG-UTILS.DEBUG-INFO/TRACE-IR-META-COPY"
3a3d15f3 29#include "logging/log.h"
ca9f27f3
FD
30
31#include <inttypes.h>
32#include <stdint.h>
33
578e048b 34#include "common/assert.h"
ca9f27f3
FD
35
36#include "trace-ir-metadata-copy.h"
37#include "trace-ir-metadata-field-class-copy.h"
38#include "utils.h"
39
40BT_HIDDEN
41int copy_trace_class_content(const bt_trace_class *in_trace_class,
3a3d15f3 42 bt_trace_class *out_trace_class, bt_logging_level log_level)
ca9f27f3
FD
43{
44 int ret = 0;
45 uint64_t i, env_field_count;
46 const char *in_trace_class_name;
ca9f27f3
FD
47
48 BT_LOGD("Copying content of trace class: in-tc-addr=%p, out-tc-addr=%p",
49 in_trace_class, out_trace_class);
50
51 /* Use the same stream class ids as in the origin trace class. */
52 bt_trace_class_set_assigns_automatic_stream_class_id(out_trace_class,
53 BT_FALSE);
54
55 in_trace_class_name = bt_trace_class_get_name(in_trace_class);
56 if (in_trace_class_name) {
57 bt_trace_class_set_name(out_trace_class, in_trace_class_name);
58 }
59
2d836ebb
FD
60 /*
61 * Do not copy the trace class UUID as it may be modified and should no
62 * longer have the same UUID.
63 */
ca9f27f3
FD
64
65 /*
66 * Go over all the entries in the environment section of the trace class
67 * and copy the content to the new trace class.
68 */
69 env_field_count = bt_trace_class_get_environment_entry_count(in_trace_class);
70 for (i = 0; i < env_field_count; i++) {
71 const char *value_name;
72 const bt_value *value = NULL;
73 bt_trace_class_status trace_class_status;
74
75 bt_trace_class_borrow_environment_entry_by_index_const(
76 in_trace_class, i, &value_name, &value);
77
78 BT_LOGD("Copying trace class environnement entry: "
79 "index=%" PRId64 ", value-addr=%p, value-name=%s",
80 i, value, value_name);
81
82 BT_ASSERT(value_name);
83 BT_ASSERT(value);
84
fdd3a2da 85 if (bt_value_is_signed_integer(value)) {
ca9f27f3
FD
86 trace_class_status =
87 bt_trace_class_set_environment_entry_integer(
88 out_trace_class, value_name,
fdd3a2da
PP
89 bt_value_signed_integer_get(
90 value));
ca9f27f3
FD
91 } else if (bt_value_is_string(value)) {
92 trace_class_status =
93 bt_trace_class_set_environment_entry_string(
94 out_trace_class, value_name,
95 bt_value_string_get(value));
96 } else {
97 abort();
98 }
99
100 if (trace_class_status != BT_TRACE_CLASS_STATUS_OK) {
101 ret = -1;
102 goto error;
103 }
104 }
105
106 BT_LOGD("Copied content of trace class: in-tc-addr=%p, out-tc-addr=%p",
107 in_trace_class, out_trace_class);
108error:
109 return ret;
110}
111
112static
113int copy_clock_class_content(const bt_clock_class *in_clock_class,
3a3d15f3 114 bt_clock_class *out_clock_class, bt_logging_level log_level)
ca9f27f3
FD
115{
116 bt_clock_class_status status;
117 const char *clock_class_name, *clock_class_description;
118 int64_t seconds;
119 uint64_t cycles;
120 bt_uuid in_uuid;
121 int ret = 0;
122
123 BT_LOGD("Copying content of clock class: in-cc-addr=%p, out-cc-addr=%p",
124 in_clock_class, out_clock_class);
125
126 clock_class_name = bt_clock_class_get_name(in_clock_class);
127
128 if (clock_class_name) {
129 status = bt_clock_class_set_name(out_clock_class, clock_class_name);
130 if (status != BT_CLOCK_CLASS_STATUS_OK) {
131 BT_LOGE("Error setting clock class' name cc-addr=%p, name=%p",
132 out_clock_class, clock_class_name);
133 out_clock_class = NULL;
134 ret = -1;
135 goto error;
136 }
137 }
138
139 clock_class_description = bt_clock_class_get_description(in_clock_class);
140
141 if (clock_class_description) {
142 status = bt_clock_class_set_description(out_clock_class,
143 clock_class_description);
144 if (status != BT_CLOCK_CLASS_STATUS_OK) {
145 BT_LOGE("Error setting clock class' description cc-addr=%p, "
146 "name=%p", out_clock_class, clock_class_description);
147 out_clock_class = NULL;
148 ret = -1;
149 goto error;
150 }
151 }
152
153 in_uuid = bt_clock_class_get_uuid(in_clock_class);
154 if (in_uuid) {
155 bt_clock_class_set_uuid(out_clock_class, in_uuid);
156 }
157
158 bt_clock_class_set_frequency(out_clock_class,
159 bt_clock_class_get_frequency(in_clock_class));
160 bt_clock_class_set_precision(out_clock_class,
161 bt_clock_class_get_precision(in_clock_class));
162 bt_clock_class_get_offset(in_clock_class, &seconds, &cycles);
163 bt_clock_class_set_offset(out_clock_class, seconds, cycles);
164 bt_clock_class_set_origin_is_unix_epoch(out_clock_class,
165 bt_clock_class_origin_is_unix_epoch(in_clock_class));
166
167 BT_LOGD("Copied content of clock class: in-cc-addr=%p, out-cc-addr=%p",
168 in_clock_class, out_clock_class);
169
170error:
171 return ret;
172}
173
174static
175bt_clock_class *borrow_mapped_clock_class(
176 struct trace_ir_metadata_maps *md_maps,
177 const bt_clock_class *in_clock_class)
178{
179 BT_ASSERT(md_maps);
180 BT_ASSERT(in_clock_class);
181
182 return g_hash_table_lookup(md_maps->clock_class_map,
183 (gpointer) in_clock_class);
184}
185
186static
187bt_clock_class *create_new_mapped_clock_class(
188 bt_self_component *self_comp,
189 struct trace_ir_metadata_maps *md_maps,
190 const bt_clock_class *in_clock_class)
191{
192 bt_clock_class *out_clock_class;
193 int ret;
3a3d15f3 194 bt_logging_level log_level = md_maps->log_level;
ca9f27f3
FD
195
196 BT_LOGD("Creating new mapped clock class: in-cc-addr=%p",
197 in_clock_class);
198
199 BT_ASSERT(md_maps);
200 BT_ASSERT(in_clock_class);
201
202 BT_ASSERT(!borrow_mapped_clock_class(md_maps, in_clock_class));
203
204 out_clock_class = bt_clock_class_create(self_comp);
205 if (!out_clock_class) {
206 BT_LOGE_STR("Cannot create clock class");
207 goto end;
208 }
209 /* If not, create a new one and add it to the mapping. */
3a3d15f3
PP
210 ret = copy_clock_class_content(in_clock_class, out_clock_class,
211 log_level);
ca9f27f3
FD
212 if (ret) {
213 BT_LOGE_STR("Cannot copy clock class");
214 goto end;
215 }
216
217 g_hash_table_insert(md_maps->clock_class_map,
218 (gpointer) in_clock_class, out_clock_class);
219
220 BT_LOGD("Created new mapped clock class: in-cc-addr=%p, out-cc-addr=%p",
221 in_clock_class, out_clock_class);
222end:
223 return out_clock_class;
224}
225
226BT_HIDDEN
227int copy_stream_class_content(struct trace_ir_maps *ir_maps,
228 const bt_stream_class *in_stream_class,
229 bt_stream_class *out_stream_class)
230{
231 struct trace_ir_metadata_maps *md_maps;
232 const bt_clock_class *in_clock_class;
233 bt_clock_class *out_clock_class;
234 const bt_field_class *in_packet_context_fc, *in_common_context_fc;
235 bt_field_class *out_packet_context_fc, *out_common_context_fc;
236 bt_stream_class_status status;
237 const char *in_name;
238 int ret = 0;
3a3d15f3 239 bt_logging_level log_level = ir_maps->log_level;
ca9f27f3
FD
240
241 BT_LOGD("Copying content of stream class: in-sc-addr=%p, out-sc-addr=%p",
242 in_stream_class, out_stream_class);
243
244 md_maps = borrow_metadata_maps_from_input_stream_class(ir_maps, in_stream_class);
245 in_clock_class = bt_stream_class_borrow_default_clock_class_const(
246 in_stream_class);
247
248 if (in_clock_class) {
249 /* Copy the clock class. */
250 out_clock_class =
251 borrow_mapped_clock_class(md_maps, in_clock_class);
252 if (!out_clock_class) {
253 out_clock_class = create_new_mapped_clock_class(
254 ir_maps->self_comp, md_maps,
255 in_clock_class);
256 }
257 bt_stream_class_set_default_clock_class(out_stream_class,
258 out_clock_class);
259
260 }
261
9b24b6aa 262 bt_stream_class_set_packets_have_beginning_default_clock_snapshot(
649934d2 263 out_stream_class,
9b24b6aa 264 bt_stream_class_packets_have_beginning_default_clock_snapshot(
649934d2 265 in_stream_class));
9b24b6aa 266 bt_stream_class_set_packets_have_end_default_clock_snapshot(
649934d2 267 out_stream_class,
9b24b6aa 268 bt_stream_class_packets_have_end_default_clock_snapshot(
649934d2 269 in_stream_class));
2e90378a
PP
270 bt_stream_class_set_supports_discarded_events(
271 out_stream_class,
272 bt_stream_class_supports_discarded_events(in_stream_class),
273 bt_stream_class_discarded_events_have_default_clock_snapshots(
274 in_stream_class));
275 bt_stream_class_set_supports_discarded_packets(
276 out_stream_class,
277 bt_stream_class_supports_discarded_packets(in_stream_class),
278 bt_stream_class_discarded_packets_have_default_clock_snapshots(
279 in_stream_class));
649934d2 280
ca9f27f3
FD
281 in_name = bt_stream_class_get_name(in_stream_class);
282 if (in_name) {
283 status = bt_stream_class_set_name(out_stream_class, in_name);
284 if (status != BT_STREAM_CLASS_STATUS_OK) {
285 BT_LOGE("Error set stream class name: out-sc-addr=%p, "
286 "name=%s", out_stream_class, in_name);
287 ret = -1;
288 goto error;
289 }
290 }
291
292 bt_stream_class_set_assigns_automatic_stream_id(out_stream_class,
293 BT_FALSE);
294 bt_stream_class_set_assigns_automatic_event_class_id(out_stream_class,
295 BT_FALSE);
296
297 /*
298 * Add the input packet context field class to the context to
299 * resolution in the further steps.
300 */
301 in_packet_context_fc =
302 bt_stream_class_borrow_packet_context_field_class_const(
303 in_stream_class);
304 md_maps->fc_resolving_ctx->packet_context =
305 in_packet_context_fc;
306
307 if (in_packet_context_fc) {
308 /* Copy packet context. */
309 out_packet_context_fc = create_field_class_copy(
310 md_maps, in_packet_context_fc);
311
312 ret = copy_field_class_content(md_maps,
313 in_packet_context_fc, out_packet_context_fc);
314 if (ret) {
315 ret = -1;
316 goto error;
317 }
318
319 status = bt_stream_class_set_packet_context_field_class(
320 out_stream_class, out_packet_context_fc);
321 if (status != BT_STREAM_CLASS_STATUS_OK) {
322 BT_LOGE("Error setting stream class' packet context "
323 "field class: sc-addr=%p, packet-fc-addr=%p",
324 out_stream_class, out_packet_context_fc);
325 ret = -1;
326 goto error;
327 }
328 }
329
330 /*
331 * Add the input common context field class to the context to
332 * resolution in the further steps.
333 */
334 in_common_context_fc =
335 bt_stream_class_borrow_event_common_context_field_class_const(
336 in_stream_class);
337 md_maps->fc_resolving_ctx->event_common_context =
338 in_common_context_fc;
339
340 if (in_common_context_fc) {
341 /* Copy common context. */
342 /* TODO: I find it a bit awkward to have this special function
343 * here to add the debug-info field class. I would like to
344 * abstract that.*/
345 out_common_context_fc = create_field_class_copy(
346 md_maps, in_common_context_fc);
347
348 ret = copy_event_common_context_field_class_content(
349 md_maps, ir_maps->debug_info_field_class_name,
350 in_common_context_fc, out_common_context_fc);
351 if (ret) {
352 goto error;
353 }
354
355 status = bt_stream_class_set_event_common_context_field_class(
356 out_stream_class, out_common_context_fc);
357 if (status != BT_STREAM_CLASS_STATUS_OK) {
358 BT_LOGE("Error setting stream class' packet context "
359 "field class: sc-addr=%p, packet-fc-addr=%p",
360 out_stream_class, out_common_context_fc);
361 ret = -1;
362 goto error;
363 }
364 }
365
366 /* Set packet snapshot boolean fields. */
367 BT_LOGD("Copied content of stream class: in-sc-addr=%p, out-sc-addr=%p",
368 in_stream_class, out_stream_class);
369error:
370 return ret;
371}
372
373BT_HIDDEN
374int copy_event_class_content(struct trace_ir_maps *ir_maps,
375 const bt_event_class *in_event_class,
376 bt_event_class *out_event_class)
377{
378 struct trace_ir_metadata_maps *md_maps;
379 const char *in_event_class_name, *in_emf_uri;
380 bt_property_availability prop_avail;
3a3d15f3 381 bt_event_class_log_level ec_log_level;
ca9f27f3
FD
382 bt_event_class_status status;
383 bt_field_class *out_specific_context_fc, *out_payload_fc;
384 const bt_field_class *in_event_specific_context, *in_event_payload;
385 int ret = 0;
3a3d15f3 386 bt_logging_level log_level = ir_maps->log_level;
ca9f27f3
FD
387
388 BT_LOGD("Copying content of event class: in-ec-addr=%p, out-ec-addr=%p",
389 in_event_class, out_event_class);
390
391 /* Copy event class name. */
392 in_event_class_name = bt_event_class_get_name(in_event_class);
393 if (in_event_class_name) {
394 status = bt_event_class_set_name(out_event_class, in_event_class_name);
395 if (status != BT_EVENT_CLASS_STATUS_OK) {
396 BT_LOGE("Error setting event class' name: ec-addr=%p, "
397 "name=%s", out_event_class, in_event_class_name);
398 ret = -1;
399 goto error;
400 }
401 }
402
403 /* Copy event class loglevel. */
3a3d15f3
PP
404 prop_avail = bt_event_class_get_log_level(in_event_class,
405 &ec_log_level);
ca9f27f3
FD
406 if (prop_avail == BT_PROPERTY_AVAILABILITY_AVAILABLE) {
407 bt_event_class_set_log_level(out_event_class,
3a3d15f3 408 ec_log_level);
ca9f27f3
FD
409 }
410
411 /* Copy event class emf uri. */
412 in_emf_uri = bt_event_class_get_emf_uri(in_event_class);
413 if (in_emf_uri) {
414 status = bt_event_class_set_emf_uri(out_event_class, in_emf_uri);
415 if (status != BT_EVENT_CLASS_STATUS_OK) {
416 BT_LOGE("Error setting event class' emf uri: ec-addr=%p, "
417 "emf uri=%s", out_event_class, in_emf_uri);
418 ret = -1;
419 goto error;
420 }
421 }
422
423 md_maps = borrow_metadata_maps_from_input_event_class(ir_maps, in_event_class);
424 /*
425 * Add the input event class' specific ctx to te
426 * context.
427 */
428 in_event_specific_context =
429 bt_event_class_borrow_specific_context_field_class_const(
430 in_event_class);
431
432 md_maps->fc_resolving_ctx->event_specific_context =
433 in_event_specific_context;
434
435 if (in_event_specific_context) {
436 /* Copy the specific context of this event class. */
437 out_specific_context_fc = create_field_class_copy(md_maps,
438 in_event_specific_context);
439
5e1abef6 440 ret = copy_field_class_content(md_maps,
ca9f27f3
FD
441 in_event_specific_context, out_specific_context_fc);
442 if (ret) {
443 goto error;
444 }
445 /*
446 * Add the output specific context to the output event
447 * class.
448 */
449 status = bt_event_class_set_specific_context_field_class(
450 out_event_class, out_specific_context_fc);
451 if (status != BT_EVENT_CLASS_STATUS_OK) {
452 BT_LOGE("Error setting event class' specific context "
453 "field class: ec-addr=%p, ctx-fc-addr=%p",
454 out_event_class, out_specific_context_fc);
455 ret = -1;
456 goto error;
457 }
458 }
459
460 /*
461 * Add the input event class' payload field class to
462 * the context.
463 */
464 in_event_payload = bt_event_class_borrow_payload_field_class_const(
465 in_event_class);
466
467 md_maps->fc_resolving_ctx->event_payload = in_event_payload;
468
469 if (in_event_payload) {
470 /* Copy the payload of this event class. */
471 out_payload_fc = create_field_class_copy(md_maps,
472 in_event_payload);
5e1abef6 473 ret = copy_field_class_content(md_maps,
ca9f27f3
FD
474 in_event_payload, out_payload_fc);
475 if (ret) {
476 goto error;
477 }
478
479 /* Add the output payload to the output event class. */
480 status = bt_event_class_set_payload_field_class(
481 out_event_class, out_payload_fc);
482 if (status != BT_EVENT_CLASS_STATUS_OK) {
483 BT_LOGE("Error setting event class' payload "
484 "field class: ec-addr=%p, payload-fc-addr=%p",
485 out_event_class, out_payload_fc);
486 ret = -1;
487 goto error;
488 }
489 }
490
491 BT_LOGD("Copied content of event class: in-ec-addr=%p, out-ec-addr=%p",
492 in_event_class, out_event_class);
493error:
494 return ret;
495}
496
497BT_HIDDEN
498int copy_event_common_context_field_class_content(
499 struct trace_ir_metadata_maps *md_maps,
500 const char *debug_info_fc_name,
501 const bt_field_class *in_field_class,
502 bt_field_class *out_field_class)
503{
504 bt_field_class_status status;
505 bt_field_class *debug_field_class = NULL, *bin_field_class = NULL,
506 *func_field_class = NULL, *src_field_class = NULL;
507 int ret = 0;
3a3d15f3 508 bt_logging_level log_level = md_maps->log_level;
ca9f27f3
FD
509
510 BT_LOGD("Copying content of event common context field class: "
511 "in-fc-addr=%p, out-fc-addr=%p", in_field_class, out_field_class);
512
513 /* Copy the content of the input common context. */
514 ret = copy_field_class_content(md_maps, in_field_class, out_field_class);
515 if (ret) {
516 goto error;
517 }
518
519 /*
520 * If this event common context has the necessary fields to compute the
521 * debug information append the debug-info field class to the event
522 * common context.
523 */
524 if (is_event_common_ctx_dbg_info_compatible(in_field_class, debug_info_fc_name)) {
525 /*
526 * The struct field and 3 sub-fields are not stored in the
527 * field class map because they don't have input equivalent.
528 * We need to put our reference each of these field classes
529 * once they are added to their respective containing field
530 * classes.
531 */
532 debug_field_class = bt_field_class_structure_create(
533 md_maps->output_trace_class);
534 if (!debug_field_class) {
535 BT_LOGE_STR("Failed to create debug_info structure.");
536 ret = -1;
537 goto error;
538 }
539
540 bin_field_class = bt_field_class_string_create(
541 md_maps->output_trace_class);
542 if (!bin_field_class) {
543 BT_LOGE_STR("Failed to create string for field=bin.");
544 ret = -1;
545 goto error;
546 }
547
548 func_field_class = bt_field_class_string_create(
549 md_maps->output_trace_class);
550 if (!func_field_class) {
551 BT_LOGE_STR("Failed to create string for field=func.");
552 ret = -1;
553 goto error;
554 }
555
556 src_field_class = bt_field_class_string_create(
557 md_maps->output_trace_class);
558 if (!src_field_class) {
559 BT_LOGE_STR("Failed to create string for field=src.");
560 ret = -1;
561 goto error;
562 }
563
564 status = bt_field_class_structure_append_member(
565 debug_field_class, "bin", bin_field_class);
566 if (status != BT_FIELD_CLASS_STATUS_OK) {
567 BT_LOGE_STR("Failed to add a field to debug_info "
568 "struct: field=bin.");
569 ret = -1;
570 goto error;
571 }
572 BT_FIELD_CLASS_PUT_REF_AND_RESET(bin_field_class);
573
574 status = bt_field_class_structure_append_member(
575 debug_field_class, "func", func_field_class);
576 if (status != BT_FIELD_CLASS_STATUS_OK) {
577 BT_LOGE_STR("Failed to add a field to debug_info "
578 "struct: field=func.");
579 ret = -1;
580 goto error;
581 }
582 BT_FIELD_CLASS_PUT_REF_AND_RESET(func_field_class);
583
584 status = bt_field_class_structure_append_member(
585 debug_field_class, "src", src_field_class);
586 if (status != BT_FIELD_CLASS_STATUS_OK) {
587 BT_LOGE_STR("Failed to add a field to debug_info "
588 "struct: field=src.");
589 ret = -1;
590 goto error;
591 }
592 BT_FIELD_CLASS_PUT_REF_AND_RESET(src_field_class);
593
594 /*Add the filled debug-info field class to the common context. */
595 status = bt_field_class_structure_append_member(out_field_class,
596 debug_info_fc_name,
597 debug_field_class);
598 if (status != BT_FIELD_CLASS_STATUS_OK) {
599 BT_LOGE_STR("Failed to add debug_info field to "
600 "event common context.");
601 ret = -1;
602 goto error;
603 }
604 BT_FIELD_CLASS_PUT_REF_AND_RESET(debug_field_class);
605 }
606 BT_LOGD("Copied content of event common context field class: "
607 "in-fc-addr=%p, out-fc-addr=%p", in_field_class, out_field_class);
608 goto end;
609
610error:
611 if (debug_field_class) {
612 bt_field_class_put_ref(debug_field_class);
613 }
614 if (bin_field_class) {
615 bt_field_class_put_ref(bin_field_class);
616 }
617 if (func_field_class) {
618 bt_field_class_put_ref(func_field_class);
619 }
620 if (src_field_class) {
621 bt_field_class_put_ref(src_field_class);
622 }
623end:
624 return ret;
625}
626
627BT_HIDDEN
628bt_field_class *create_field_class_copy(struct trace_ir_metadata_maps *md_maps,
629 const bt_field_class *in_field_class)
630{
631 return create_field_class_copy_internal(md_maps, in_field_class);
632}
633
634BT_HIDDEN
635int copy_field_class_content(struct trace_ir_metadata_maps *md_maps,
636 const bt_field_class *in_field_class,
637 bt_field_class *out_field_class)
638{
639 return copy_field_class_content_internal(md_maps, in_field_class,
640 out_field_class);
641}
This page took 0.069093 seconds and 4 git commands to generate.