qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
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 01:38:35 +0530	[thread overview]
Message-ID: <5005C643.5000606@linux.vnet.ibm.com> (raw)
In-Reply-To: <5005B6A5.7030000@linux.vnet.ibm.com>

On 07/18/2012 12:31 AM, Harsh Bora wrote:
> 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?

Ah, I confused it with trace_record_finish where we write back the 
previously read header, which is not the case here. However, I still 
feel using an array is better here probably because:
1) We anyway cant use memcpy here to read from global buffer, we have to 
use read_from_buffer to take care of buffer boundaries.
2) Isnt malloc() expensive for such a small allocation requirement?

regards,
Harsh

>
> ~ 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
>>
>
>
>

  reply	other threads:[~2012-07-17 20:09 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
2012-07-17 20:08       ` Harsh Bora [this message]
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=5005C643.5000606@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).