This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: MAXSTRINGLEN applied to printf()?


Sigh...that was the other reason I was afraid of posting code: it would turn out to be a stupid bug.  I think you may be right that it was simply not accounting for the commas.  Sorry for the distraction. :-/

Thanks,
Nick

On Aug 9, 2012, at 4:31 PM, Josh Stone <jistone@redhat.com> wrote:

> On 08/09/2012 12:51 PM, halcyonic@gmail.com wrote:
>> Sorry...didn't want to spam a lot of code and output.
> 
> Thanks - what you have here is not nearly large enough that I'd call it
> spam, and it's much better than us trying to guess what's going on.
> 
>> So, for example, I get a long output that contains this fragment:
>> 
>> ... ,"localhost.localdomain"localhost.localdomain_2012-8-8-23-47-30.bz2", ...
>> 
>> ...generated by the following probe (I've set string lengths to be
>> 2048).  The thing to notice is that any string concatenation I do
>> should always involve the addition of a pair of quotes, so I should
>> never be able to get an output that involves an odd number of quotes
>> (as per above).
> 
> But if your concatenation goes beyond MAXSTRINGLEN, then one of the pair
> of quotes may be silently truncated.
> 
>> So either I'm somehow getting intermingled output (I
>> don't think I am...the rest of the output looks perfectly fine), or
>> somehow a string is getting silently truncated somewhere (fyi, all
>> the filenames it's listing are of the form
>> localhost.localdomain_2012-8-8-23-47-30.bz2).
>> 
>> Any theories appreciated.
>> 
>> Thanks, Nick
>> 
>> -----
>> probe syscall.getdents.return {
>>    if ((execname() != "stap") && !is_fd_blacklisted(pid(), $fd)) {
>>    printf("{ \"arglist\":")
>>    arglist = "[ "
>>    if ($return > 0) {
>>        total_entries = 0
>>        dirent = $dirent
>>        total_bytes = 0
>>        current_bytes = 0
>>        while (total_bytes < $return) {
>>          if (dirent == 0) {
>>              break
>>          }
>>          nextarg = clean_string(user_string_warn(@cast(dirent, "struct linux_dirent")->d_name))
> 
> What is clean_string(), something to sanitize to printable characters?
> You might like user_string_quoted(), or "text_strn(str, 0, 1)" to quote
> it after the fact.
> 
>>          len = @cast(dirent, "struct linux_dirent")->d_reclen
>>          formatlen = strlen(nextarg) + 2
>>          dirent += len
>>          total_bytes += len
>>          total_entries += 1
>> 
>>          if (total_entries < 256) {
>>              if (current_bytes + formatlen > 2048) {
>>                printf("%s", arglist)
>>                arglist = ""
>>                current_bytes = 0
>>              }
>> 
>>              arglist .= "\"".nextarg."\""
>>              current_bytes += formatlen 
>>              if (total_bytes < $return) {
>>                arglist .= ","
>>              }
>>          }          
> 
> Ok, I see you're trying to avoid MAXSTRINGLEN here.  I think your bug
> may be simply when you add "," to arglist without also incrementing
> current_bytes, so arglist is longer than you think when you check if a
> printf is due.
> 
> Also, don't forget that a \0 terminator has to be present within
> MAXSTRINGLEN too, so you really only have 2047 bytes to play with.
> 
>>        }
>>    }
>>    printf("%s],", arglist)
>> #    arglist = substr(arglist, 0, strlen(arglist)-1)."]"
>> #    outstr = "{ "
>> #    outstr .= "\"arglist\": "
>> #    outstr .= arglist.","
>>    outstr .= "\"count\": "
>>    outstr .= sprintf("%u", $count).","
>>    outstr .= "\"execname\": \""
>>    outstr .= clean_string(execname())."\","
>>    outstr .= "\"fd\": "
>>    outstr .= sprintf("%d", $fd).","
>>    outstr .= "\"op\": \""
>>    outstr .= clean_string("GETDENTS")."\","
>>    outstr .= "\"pid\": "
>>    outstr .= sprintf("%d", pid()).","
>>    outstr .= "\"ppid\": "
>>    outstr .= sprintf("%d", ppid()).","
>>    outstr .= "\"return\": "
>>    outstr .= sprintf("%d", $return).","
>>    outstr .= "\"timestamp\": "
>>    outstr .= sprintf("%d", gettimeofday_ms()).","
>>    outstr .= "\"total_bytes\": "
>>    outstr .= sprintf("%u", total_bytes).","
>>    outstr .= "\"total_entries\": "
>>    outstr .= sprintf("%u", total_entries).","
>>    outstr .= "\"uid\": "
>>    outstr .= sprintf("%d", uid())."}\n"
>>    printf("%s", outstr)
>>  }
>> }
>> -----
> 
> With your MAXTRINGLEN=2048, you're probably not hitting limits here at
> the end, but every single one of these ".", ".=", and "sprintf" create
> string temporaries.  The code we generate for this will be pretty
> inefficient, with lots of string copies.  So I'd recommend building as
> much as you can directly into that final output format, e.g.
> 
>  printf("\"count\":%u,\"execname\":%s, ...\n",
>         $count, clean_string(execname()), ...)
> 
> 
> Josh


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]