[squid-dev] [PATCH] Coverity(-inspired) fixes part four, HttpHeader refactor

Kinkie gkinkie at gmail.com
Fri Aug 21 17:31:48 UTC 2015


On Fri, Aug 21, 2015 at 6:39 PM, Alex Rousskov <
rousskov at measurement-factory.com> wrote:

> On 08/21/2015 04:37 AM, Kinkie wrote:
>
> >   the attached patch does:
>
> ... a long list of mostly unrelated changes ...
>
> Sigh. Some of the patch changes are not necessary at all. Some are
> useful. Some should be redone to minimize conflicts. Some are
> performance optimizations that should be considered very carefully. It
> would take too much time to sift through all of this and separate the
> good from the bad, so we would have to accept pretty much all of them,
> create a lot of conflicts, and probably miss bugs.
>

Sorry about that.
To defend the code, I'll comment that this has been extensively build-,
polygraph- coadvisor- and run-tested. It's hard - if possible at all - to
cover all code paths, I've done my best to be very cautious.

Sometimes, changing a lot of code is necessary. There is no way around
> that. However, combining many unrelated changes, some of which change a
> lot of code, and some of which are performance-sensitive or bug-prone,
> is virtually never necessary.
>
>
> > -        Tolower(name);
> > -
> > -        if (strcmp(name, "*") == 0) {
> > -            /* Can not handle "Vary: *" withtout ETag support */
> > -            safe_free(name);
> > +        SBuf name(item, ilen);
> > +        if (name.cmp("*",1) == 0) {
> >              vstr.clean();
> >              break;
> >          }
> > -
> > -        strListAdd(&vstr, name, ',');
> > +        name.toLower();
>
> Do you know why we need to convert header names to lowercase here? The
> header names are not a part of some Store hash, right? This conversion
> is expensive because most header names will be changed here, breaking
> SBuf reuse. If you know the reason, let's add a comment since you are
> changing this code anyway.
>

(pseudo) code removing memory management was:
while (e = get_list_element()) {
   e = lowerCase(e)
   if (e == "*") {
     abort_processing()
     break
   }
   vstr.append(e)
   //other stuff
}

new code is:
while (e = get_list_element()) {
  if (e == "*") {
    abort_processing()
    break
  }
  e = lowerCase(e)
  vstr.append(e)
  //some other stuff as above
}

Since lowerCase("*") == "*", I'd hope that the behavior is unchanged, if
anything it's a small optimization in a hopefully uncommon case (no change
in the common case)


>
> >     in src/http.cc:
> >
> >     * please mark use of name.c_str() with "XXX: performance regression,
> >     c_str() reallocates"
> >
> >
> > Actually, no in this case it doesn't unless ilen is exactly equal to one
> > of the MemBlob MemPool sizes (possible but unlikely, I estimate in the
> > 0.1% range).
>
> *If* we remove toLower(), then there will be other, very common cases
> where c_str() will reallocate in this context, either right now or when
> the surrounding code is optimized to be more SBuf-friendly. Without
> toLower(), the "name" SBuf should be pointing in the middle of another
> SBuf in most cases where name was already in lower case.
>

in the general case, I agree. I also hope that by the time the general case
comes, we won't need this very specific c_str().
In this case:
SBuf is allocated, lower-cased (guaranteed in place due to single
reference) and then - if there's room which should be a statistically
common case - a ghost \0 is appended to ensure termination.

I would honor Amos request for an XXX. In that XXX, I would not use the

> word "regression" because this change may not make performance worse
> than it was, but it is still bad code from performance point of view and
> should eventually be changed/optimized. I would write something like:
>
>   // XXX: will reallocate if toLower() above is removed
>

.. and strListGetItem will start emitting SBufs in place of (char*,
length).


>
> > -        if (strcmp(name, "*") == 0) {
> > -            /* Can not handle "Vary: *" withtout ETag support */
> > -            safe_free(name);
> > +        SBuf name(item, ilen);
> > +        if (name.cmp("*",1) == 0) {
>
> AFAICT, the new code looks for names starting with "*". The old code was
> looking for "*" names.
>

doh! you're right.


> > -    while ((e = getEntry(&pos))) {
> > -        if (e->id == id)
> > -            delAt(pos, count);
> > -    }
> > +    int sz_before = entries.size();
> > +    entries.erase(std::remove_if(entries.begin(), entries.end(),
> > +    [=](HttpHeaderEntry *e) { return e && e->id == id; }),
> > +    entries.end());
>
> This change is a performance regression AFAICT: Unlike the old delAt(),
> std::remove_if is very expensive for std::vector. Consider using
> std::replace_if without .erasing instead.
>

yes, it is a small regresson (from N to 2N
). Reimplemented using replace_if - readability goes a bit down the drain,
unfortunatley.


> > +enum HdrKind {
>
> Use C++11 class enums for global enums, especially when such use does
> not increase the amount of old code changes. In this specific case, it
> does not AFAICT.
>

Unfortunately it's not possible in ordeer to use it as a bit-field (I have
tried and the compiler refused)


>
>
> > -    for(HttpHeaderEntry *e : entries) {
> > +    for (HttpHeaderEntry *e : entries) {
>
> > -
> > -    return NULL;        /* not reached */
> > +    return nullptr;        /* not reached */
>
> >      /* check mask first */
> > -
> >      if (!CBIT_TEST(mask, id))
> >          return NULL;
>
> ...
>
> Avoid out of scope changes, especially whitespace changes and NULL
> renaming.
>

I can't check it before replying to this mail, but will fix before merge.


>
>
> > +    assert(0);
>
> s/0/false/
>
>
> > +    int sz_before = entries.size();
> > +    entries.erase(std::remove_if(entries.begin(), entries.end(),
> > +    [=](HttpHeaderEntry *e) { return e && e->id == id; }),
> > +    entries.end());
> > +    int count = entries.size() - sz_before;
>
> If you can make sz_before, *e, and/or count const, make them const.
>

Have to use a side-effect in the predicate if I want to use replace_if.


>
>
> > +        if (e != nullptr && e->id == id)
>
> You can save a lot of ink by writing "e" instead of "e != nullptr".
>
>
> As always, please post patches with at least 20 context lines to
> increase chances of reviewers spotting bugs.
>

 New patch attached according to spec.
"bzr send" unfortunately doesn't allow to specify context to be added :(

Thank you!

-- 
    Francesco
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squid-cache.org/pipermail/squid-dev/attachments/20150821/d0d7e13e/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: coverity-fixes-4-v3.patch
Type: application/octet-stream
Size: 209809 bytes
Desc: not available
URL: <http://lists.squid-cache.org/pipermail/squid-dev/attachments/20150821/d0d7e13e/attachment-0001.obj>


More information about the squid-dev mailing list