Quantcast
Channel: Josh Stone » Josh Stone
Viewing all articles
Browse latest Browse all 10

Add new warnings to GCC with Python

0
0

I found myself getting annoyed lately with a certain C++ anti-pattern: functions which take string parameters by value unnecessarily.  The pattern itself isn’t a huge sin, and can have legitimate uses, but often functions would be just as happy with a string const& parameter, so copying it is a waste.  No matter how the optimized string copy is, it won’t be better than a simple reference.  Then throw more complex classes in the mix, and it would be good have a tool or diagnostic to tell when things are copied unnecessarily.

My first thought was to try this with elfutils libdw, or perhaps Dyninst SymtabAPI.  It shouldn’t be that hard to iterate debuginfo for all the functions in a binary, and scan their parameters for direct class or struct types.  I may still try this, actually, just as a case study for relatively simple example code.  However, one of my coworkers suggested this idea would also work as a GCC plugin, so I tried it with gcc-python-plugin and found it embarrassingly easy.  Voila!

import gcc

def on_pass_execution(p, fn):
    # This pass is called fairly early on, per-function, after the
    # CFG has been built.  Skip every other pass.
    if p.name != '*warn_function_return':
        return

    # I don't want notices for system headers.
    # (is data like -isystem available?)
    if fn.decl.location.file.startswith('/usr/'):
        return

    for parm, parmtype in zip(fn.decl.arguments,
                              fn.decl.type.argument_types):
        if type(parmtype) == gcc.RecordType:
            gcc.inform(parm.location,
                       'parameter type is not trivial')

gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
                      on_pass_execution)

I started with the hello-world example, which already shows how to hook every function and extract more info than I even need.  You’ll see that my case is even shorter in code, just checking the rough parameter type and issuing a notice.  In practice it looks like this:

$ cat test.cc
#include <string>
size_t len(std::string s) { return s.size(); }

$ gcc-with-python3 script.py -c test.cc
test.cc: In function ‘size_t len(std::string)’:
test.cc:2:24: note: parameter type is not trivial
 size_t len(std::string s) { return s.size(); }
                        ^

I got this working with my whole build just by tweaking make options:

make CXXLD=g++ CXX="gcc-with-python3 /path/to/script.py"

Pull that into Vim quickfix, and I’m now easily browsing through every instance of non-trivial parameter copies in my code!

Part of the reason this was so easy was that I just flag every direct class/struct parameter, regardless of whether an option like const& would fit. This might be a little more intelligent if it scanned the CFG to see if the parameter really could be constant. It could also relax a bit on what’s flagged, perhaps just types for which std::is_trivially_copyable returns false in C++11.  But for just scratching an itch, I’m amazed at how quick this simple plugin was to write.



Viewing all articles
Browse latest Browse all 10

Latest Images

Trending Articles





Latest Images