From: Harsh Bora <harsh@linux.vnet.ibm.com>
To: Stefan Hajnoczi <stefanha@gmail.com>
Cc: qemu-devel@nongnu.org, stefanha@linux.vnet.ibm.com
Subject: Re: [Qemu-devel] [PATCH v7 2/3] Simpletrace v2: Support multiple arguments, strings.
Date: Wed, 18 Jul 2012 00:31:57 +0530 [thread overview]
Message-ID: <5005B6A5.7030000@linux.vnet.ibm.com> (raw)
In-Reply-To: <CAJSP0QVLSY7Sei2SzNXc1=V6DfCpn4hq1XWUF7vQnC_vWdGb1w@mail.gmail.com>
On 07/17/2012 08:51 PM, Stefan Hajnoczi wrote:
> On Tue, Jul 3, 2012 at 10:20 AM, Harsh Prateek Bora
> <harsh@linux.vnet.ibm.com> wrote:
>> Existing simpletrace backend allows to trace at max 6 args and does not
>> support strings. This newer tracelog format gets rid of fixed size records
>> and therefore allows to trace variable number of args including strings.
>>
>> Sample trace with strings:
>> v9fs_version 0.000 tag=0xffff id=0x64 msize=0x2000 version=9P2000.L
>> v9fs_version_return 6.705 tag=0xffff id=0x64 msize=0x2000 version=9P2000.L
>>
>> Signed-off-by: Harsh Prateek Bora <harsh@linux.vnet.ibm.com>
>> ---
>> scripts/tracetool/backend/simple.py | 84 +++++++++---
>> trace/simple.c | 256 ++++++++++++++++++++++-------------
>> trace/simple.h | 39 +++++-
>> 3 files changed, 260 insertions(+), 119 deletions(-)
>>
>> diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py
>> index fbb5717..d3cf4da 100644
>> --- a/scripts/tracetool/backend/simple.py
>> +++ b/scripts/tracetool/backend/simple.py
>> @@ -15,9 +15,16 @@ __email__ = "stefanha@linux.vnet.ibm.com"
>>
>> from tracetool import out
>>
>> +def is_string(arg):
>> + strtype = ('const char*', 'char*', 'const char *', 'char *')
>> + if arg.lstrip().startswith(strtype):
>> + return True
>> + else:
>> + return False
>>
>> def c(events):
>> out('#include "trace.h"',
>> + '#include "trace/simple.h"',
>> '',
>> 'TraceEvent trace_list[] = {')
>>
>> @@ -26,30 +33,69 @@ def c(events):
>> name = e.name,
>> )
>>
>> - out('};')
>> -
>> -def h(events):
>> - out('#include "trace/simple.h"',
>> + out('};',
>> '')
>>
>> - for num, e in enumerate(events):
>> - if len(e.args):
>> - argstr = e.args.names()
>> - arg_prefix = ', (uint64_t)(uintptr_t)'
>> - cast_args = arg_prefix + arg_prefix.join(argstr)
>> - simple_args = (str(num) + cast_args)
>> - else:
>> - simple_args = str(num)
>> + for num, event in enumerate(events):
>> + sizes = []
>> + for type_, name in event.args:
>> + if is_string(type_):
>> + sizes.append("4 + ((" + name + " ? strlen(" + name + ") : 0) % MAX_TRACE_STRLEN)")
>
> trace_record_write_str() and this code both use % to truncate the
> string. If the string is 512 characters long you get an empty string.
> That's weird and not normally how truncation works.
>
> Perhaps it's better to change this Python code to emit something like:
> size_t arg%(num)d_len = %(name)s ? MAX(strlen(%(name)s, MAX_TRACE_STRLEN)) : 0;
>
I think we need to use MIN instead of MAX, right ?
Another question below ..
> Then you can emit the following to actual store the string:
>
> out(' trace_record_write_str(&rec, %(name)s, arg%(num)d_len);',
> name=name, num=num)
>
> The difference is that MAX() is used to truncate and we only do strlen() once.
>
>> + else:
>> + sizes.append("8")
>> + sizestr = " + ".join(sizes)
>> + if len(event.args) == 0:
>> + sizestr = '0'
>>
>> - out('static inline void trace_%(name)s(%(args)s)',
>> + out('void trace_%(name)s(%(args)s)',
>> '{',
>> - ' trace%(argc)d(%(trace_args)s);',
>> - '}',
>> - name = e.name,
>> - args = e.args,
>> - argc = len(e.args),
>> - trace_args = simple_args,
>> + ' TraceBufferRecord rec;',
>> + '',
>> + ' if (!trace_list[%(event_id)s].state) {',
>> + ' return;',
>> + ' }',
>> + '',
>> + ' if (trace_record_start(&rec, %(event_id)s, %(size_str)s)) {',
>> + ' return; /* Trace Buffer Full, Event Dropped ! */',
>> + ' }',
>> + name = event.name,
>> + args = event.args,
>> + event_id = num,
>> + size_str = sizestr,
>> )
>>
>> + if len(event.args) > 0:
>> + for type_, name in event.args:
>> + # string
>> + if is_string(type_):
>> + out(' trace_record_write_str(&rec, %(name)s);',
>> + name = name,
>> + )
>> + # pointer var (not string)
>> + elif type_.endswith('*'):
>> + out(' trace_record_write_u64(&rec, (uint64_t)(uint64_t *)%(name)s);',
>> + name = name,
>> + )
>> + # primitive data type
>> + else:
>> + out(' trace_record_write_u64(&rec, (uint64_t)%(name)s);',
>> + name = name,
>> + )
>> +
>> + out(' trace_record_finish(&rec);',
>> + '}',
>> + '')
>> +
>> +
>> +def h(events):
>> + out('#include "trace/simple.h"',
>> + '')
>> +
>> + for event in events:
>> + out('void trace_%(name)s(%(args)s);',
>> + name = event.name,
>> + args = event.args,
>> + )
>> + out('')
>> out('#define NR_TRACE_EVENTS %d' % len(events))
>> out('extern TraceEvent trace_list[NR_TRACE_EVENTS];')
>> diff --git a/trace/simple.c b/trace/simple.c
>> index b64bcf4..3c14568 100644
>> --- a/trace/simple.c
>> +++ b/trace/simple.c
>> @@ -27,7 +27,7 @@
>> #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
[..snip..]
>> /**
>> * Read a trace record from the trace buffer
>> *
>> @@ -75,16 +96,31 @@ static char *trace_file_name = NULL;
>> *
>> * Returns false if the record is not valid.
>> */
>> -static bool get_trace_record(unsigned int idx, TraceRecord *record)
>> +static bool get_trace_record(unsigned int idx, TraceRecord **recordptr)
>> {
>> - if (!(trace_buf[idx].event & TRACE_RECORD_VALID)) {
>> + uint8_t rec_hdr[sizeof(TraceRecord)];
>> + uint64_t event_flag = 0;
>> + TraceRecord *record = (TraceRecord *) rec_hdr;
>
> Declaring rec_hdr as a uint8_t array is only because you're trying to
> avoid a cast later? The easiest way to make this nice is to do what
> memset(), memcpy() and friends do: just use a void *buf argument.
> That way a caller can pass a TraceRecord* or any other pointer without
> explicit casts, unions, or the uint8_t array trick you are using.
Are you suggesting to use malloc() here?
void *rec_hdr = malloc(sizeof(TraceRecord));
I kept a static array to make sure structure padding doesnt take place.
I am not sure if using malloc here is recommended as we are reading
header and then writing this header byte-by-byte?
~ Harsh
>
>> + /* read the event flag to see if its a valid record */
>> + read_from_buffer(idx, rec_hdr, sizeof(event_flag));
>> +
>> + if (!(record->event & TRACE_RECORD_VALID)) {
>> return false;
>> }
>>
>> __sync_synchronize(); /* read memory barrier before accessing record */
>> -
>> - *record = trace_buf[idx];
>> - record->event &= ~TRACE_RECORD_VALID;
>> + /* read the record header to know record length */
>> + read_from_buffer(idx, rec_hdr, sizeof(TraceRecord));
>> + *recordptr = g_malloc(record->length);
>> + /* make a copy of record to avoid being overwritten */
>> + read_from_buffer(idx, (uint8_t *)*recordptr, record->length);
>> + smp_rmb(); /* memory barrier before clearing valid flag */
>
> You are adding smp_*() instead of __sync_synchronize(). Please either
> continue to use __sync_synchronize() or convert everything to smp_*()
> barriers from qemu-barrier.h.
>
>> + (*recordptr)->event &= ~TRACE_RECORD_VALID;
>> + /* clear the trace buffer range for consumed record otherwise any byte
>> + * with its MSB set may be considered as a valid event id when the writer
>> + * thread crosses this range of buffer again.
>> + */
>> + clear_buffer_range(idx, record->length);
>> return true;
>> }
>>
>> @@ -120,29 +156,41 @@ static void wait_for_trace_records_available(void)
>>
>> static gpointer writeout_thread(gpointer opaque)
>> {
>> - TraceRecord record;
>> - unsigned int writeout_idx = 0;
>> - unsigned int num_available, idx;
>> + TraceRecord *recordptr, *dropped_ptr;
>> + union {
>> + TraceRecord rec;
>> + uint8_t bytes[sizeof(TraceRecord) + sizeof(uint64_t)];
>> + } dropped;
>> + dropped_ptr = (TraceRecord *)dropped.bytes;
>
> dropped_ptr isn't needed, just use dropped.rec.
>
>> + unsigned int idx = 0;
>> + uint64_t dropped_count;
>> size_t unused __attribute__ ((unused));
>>
>> for (;;) {
>> wait_for_trace_records_available();
>>
>> - num_available = trace_idx - writeout_idx;
>> - if (num_available > TRACE_BUF_LEN) {
>> - record = (TraceRecord){
>> - .event = DROPPED_EVENT_ID,
>> - .x1 = num_available,
>> - };
>> - unused = fwrite(&record, sizeof(record), 1, trace_fp);
>> - writeout_idx += num_available;
>> + if (dropped_events) {
>> + dropped_ptr->event = DROPPED_EVENT_ID,
>> + dropped_ptr->timestamp_ns = get_clock();
>> + dropped_ptr->length = sizeof(TraceRecord) + sizeof(dropped_events),
>> + dropped_ptr->reserved = 0;
>> + while (1) {
>> + dropped_count = dropped_events;
>> + smp_rmb();
>
> Not needed, glib says g_atomic_int_compare_and_exchange() "acts as a
> full compiler and hardware memory barrier".
>
> http://developer.gnome.org/glib/2.32/glib-Atomic-Operations.html#g-atomic-int-compare-and-exchange
>
>> + if (g_atomic_int_compare_and_exchange((gint *)&dropped_events,
>> + dropped_count, 0)) {
>> + break;
>> + }
>> + }
>
> It's a shame we have to loop when a simple atomic exchange would have
> done the job better but glib doesn't seem to support that.
>
> Stefan
>
next prev parent reply other threads:[~2012-07-17 19:02 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-07-03 9:20 [Qemu-devel] [PATCH v7 0/3] Simpletrace v2: Support multiple args, strings Harsh Prateek Bora
2012-07-03 9:20 ` [Qemu-devel] [PATCH v7 1/3] monitor: remove unused do_info_trace Harsh Prateek Bora
2012-07-03 9:20 ` [Qemu-devel] [PATCH v7 2/3] Simpletrace v2: Support multiple arguments, strings Harsh Prateek Bora
2012-07-17 15:21 ` Stefan Hajnoczi
2012-07-17 19:01 ` Harsh Bora [this message]
2012-07-17 20:08 ` Harsh Bora
2012-07-18 8:59 ` Stefan Hajnoczi
2012-07-18 8:53 ` Stefan Hajnoczi
2012-07-03 9:20 ` [Qemu-devel] [PATCH v7 3/3] Update simpletrace.py for new log format Harsh Prateek Bora
2012-07-17 14:07 ` [Qemu-devel] [PATCH v7 0/3] Simpletrace v2: Support multiple args, strings Stefan Hajnoczi
2012-07-17 14:14 ` Stefan Hajnoczi
2012-07-17 15:23 ` Stefan Hajnoczi
2012-07-17 19:51 ` Harsh Bora
2012-07-18 8:51 ` Stefan Hajnoczi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5005B6A5.7030000@linux.vnet.ibm.com \
--to=harsh@linux.vnet.ibm.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@gmail.com \
--cc=stefanha@linux.vnet.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).