pax_global_header 0000666 0000000 0000000 00000000064 12660356443 0014523 g ustar 00root root 0000000 0000000 52 comment=886a1e49a7c4290e725a4cdc6bcc24d316265678
gcc-python-plugin-0.15/ 0000775 0000000 0000000 00000000000 12660356443 0015017 5 ustar 00root root 0000000 0000000 gcc-python-plugin-0.15/.gitignore 0000664 0000000 0000000 00000001365 12660356443 0017014 0 ustar 00root root 0000000 0000000 *~
.*.sw[a-z]
*.o
*.so
a.out
*.pyc
test.png
# Generated during configuration:
config-tests
# Generated elsewhere in build:
autogenerated*
gcc-c-api/gcc-public-types.h
gcc-c-api/gcc-semiprivate-types.h
gcc-c-api/gcc-callgraph.h
gcc-c-api/gcc-cfg.h
gcc-c-api/gcc-constant.h
gcc-c-api/gcc-declaration.h
gcc-c-api/gcc-diagnostics.h
gcc-c-api/gcc-function.h
gcc-c-api/gcc-gimple.h
gcc-c-api/gcc-location.h
gcc-c-api/gcc-option.h
gcc-c-api/gcc-rtl.h
gcc-c-api/gcc-tree.h
gcc-c-api/gcc-type.h
gcc-c-api/gcc-variable.h
gcc-with-python.1.gz
print-gcc-version
# Generated by Sphinx
docs/_build
# Logfiles generated during the selftest suite:
*.c.cpychecker-log.txt
*.c.*-refcount-errors.html
*.c.*-refcount-traces.html
tests/plugin/dumpfiles/input.c.*t.test-pass
gcc-python-plugin-0.15/.travis.yml 0000664 0000000 0000000 00000000265 12660356443 0017133 0 ustar 00root root 0000000 0000000 language: c
compiler:
- gcc
script: make
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq gcc-4.6-plugin-dev python-six python-pygments graphviz python-lxml
gcc-python-plugin-0.15/COPYING 0000664 0000000 0000000 00000104513 12660356443 0016056 0 ustar 00root root 0000000 0000000 GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
gcc-python-plugin-0.15/LICENSES.txt 0000664 0000000 0000000 00000001773 12660356443 0016775 0 ustar 00root root 0000000 0000000 The C and Python code is licensed under the GPLv3 or later (see COPYING and the
per-file license information).
There are copies of various JavaScript and CSS libraries embedded within the
source tree, which have their own licenses as follows:
libcpychecker_html/extlib/jquery-1.7.1.min.js:
jquery.org/license (MIT or GPLv2)
libcpychecker_html/extlib/prefixfree-1.0.4.min.js:
MIT license
libcpychecker_html/extlib/reset-20110126.css:
Public Domain
There are also various images within:
libcpychecke_/html/images
These were taken from:
http://code.google.com/p/fugue-icons-src/
and are:
"© 2012 Yusuke Kamiyamane. All rights reserved.
These icons are available under a Creative Commons Attribution 3.0
License. "
http://creativecommons.org/licenses/by/3.0/
The test code contains historical examples of reference-count bugs:
* libcpychecker_html/test/example2
contains an embedded copy of part of an old version of pylibmc, which
is under a 3-clause BSD license
gcc-python-plugin-0.15/Makefile 0000664 0000000 0000000 00000032452 12660356443 0016465 0 ustar 00root root 0000000 0000000 # Copyright 2011, 2012, 2013 David Malcolm
# Copyright 2011, 2012, 2013 Red Hat, Inc.
#
# This is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# .
.PHONY: all clean debug dump_gimple plugin show-ssa tarball \
test-suite testcpychecker testcpybuilder \
man
PLUGIN_SOURCE_FILES= \
gcc-python.c \
gcc-python-attribute.c \
gcc-python-callbacks.c \
gcc-python-callgraph.c \
gcc-python-cfg.c \
gcc-python-closure.c \
gcc-python-diagnostics.c \
gcc-python-function.c \
gcc-python-gimple.c \
gcc-python-location.c \
gcc-python-option.c \
gcc-python-parameter.c \
gcc-python-pass.c \
gcc-python-pretty-printer.c \
gcc-python-rtl.c \
gcc-python-tree.c \
gcc-python-variable.c \
gcc-python-version.c \
gcc-python-wrapper.c \
PLUGIN_GENERATED_SOURCE_FILES:= \
autogenerated-callgraph.c \
autogenerated-casts.c \
autogenerated-cfg.c \
autogenerated-option.c \
autogenerated-function.c \
autogenerated-gimple.c \
autogenerated-location.c \
autogenerated-parameter.c \
autogenerated-pass.c \
autogenerated-pretty-printer.c \
autogenerated-rtl.c \
autogenerated-tree.c \
autogenerated-variable.c
PLUGIN_OBJECT_SOURCE_FILES:= $(patsubst %.c,%.o,$(PLUGIN_SOURCE_FILES))
PLUGIN_OBJECT_GENERATED_FILES:= $(patsubst %.c,%.o,$(PLUGIN_GENERATED_SOURCE_FILES))
PLUGIN_OBJECT_FILES:= $(PLUGIN_OBJECT_SOURCE_FILES) $(PLUGIN_OBJECT_GENERATED_FILES)
GCCPLUGINS_DIR:= $(shell $(CC) --print-file-name=plugin)
GENERATOR_DEPS=cpybuilder.py wrapperbuilder.py print-gcc-version
# The plugin supports both Python 2 and Python 3
#
# In theory we could have arbitrary combinations of python versions for each
# of:
# - python version used when running scripts during the build (e.g. to
# generate code)
# - python version we compile and link the plugin against
# - when running the plugin with the cpychecker script, the python version
# that the code is being compiled against
#
# However, to keep things simple, let's assume for now that all of these are
# the same version: we're building the plugin using the same version of Python
# as we're linking against, and that the cpychecker will be testing that same
# version of Python
#
# By default, build against "python", using "python-config" to query for
# compilation options. You can override this by passing other values for
# PYTHON and PYTHON_CONFIG when invoking "make" (or by simply hacking up this
# file): e.g.
# make PYTHON=python3 PYTHON_CONFIG=python3-config all
# The python interpreter to use:
PYTHON=python
# The python-config executable to use:
PYTHON_CONFIG=python-config
#PYTHON=python3
#PYTHON_CONFIG=python3-config
#PYTHON=python-debug
#PYTHON_CONFIG=python-debug-config
#PYTHON=python3-debug
#PYTHON_CONFIG=python3.3dm-config
PYTHON_INCLUDES=$(shell $(PYTHON_CONFIG) --includes)
PYTHON_LIBS=$(shell $(PYTHON_CONFIG) --libs)
# Support having multiple named plugins
# e.g. "python2.7" "python3.2mu" "python 3.2dmu" etc:
PLUGIN_NAME := python
PLUGIN_DSO := $(PLUGIN_NAME).so
PLUGIN_DIR := $(PLUGIN_NAME)
# For now, gcc-c-api is part of this project
# (Eventually it will be moved to its own project)
LIBGCC_C_API_SO := gcc-c-api/libgcc-c-api.so
CPPFLAGS+= -I$(GCCPLUGINS_DIR)/include -I$(GCCPLUGINS_DIR)/include/c-family -I. $(PYTHON_INCLUDES)
# Allow user to pick optimization, choose whether warnings are fatal,
# and choose debugging information level.
CFLAGS?=-O2 -Werror -g
# Force these settings
CFLAGS+= -fPIC -fno-strict-aliasing -Wall
LIBS+= $(PYTHON_LIBS)
ifneq "$(PLUGIN_PYTHONPATH)" ""
CPPFLAGS+= -DPLUGIN_PYTHONPATH='"$(PLUGIN_PYTHONPATH)"'
endif
all: autogenerated-config.h testcpybuilder test-suite testcpychecker
# What still needs to be wrapped?
api-report:
grep -nH -e "\.inner" gcc-*.c *.h generate-*.py
plugin: autogenerated-config.h $(PLUGIN_DSO)
# When running the plugin from a working copy, use LD_LIBARY_PATH=gcc-c-api
# so that the plugin can find its libgcc-c-api.so there
#
INVOCATION_ENV_VARS := LD_LIBRARY_PATH=gcc-c-api CC=$(CC)
# When installing, both the plugin and libgcc-c-api.so will be installed to
# $(GCCPLUGINS_DIR), so we give the plugin an RPATH of $(GCCPLUGINS_DIR)
# so that it finds the libgcc-c-api.so there (to support the case of having
# multiple GCCs installed)
#
$(PLUGIN_DSO): $(PLUGIN_OBJECT_FILES) $(LIBGCC_C_API_SO)
$(CC) \
$(CPPFLAGS) $(CFLAGS) $(LDFLAGS) \
-shared \
$(PLUGIN_OBJECT_FILES) \
-o $@ \
$(LIBS) \
-lgcc-c-api -Lgcc-c-api -Wl,-rpath=$(GCCPLUGINS_DIR)
$(LIBGCC_C_API_SO):
cd gcc-c-api && make libgcc-c-api.so CC=$(CC)
$(PLUGIN_OBJECT_GENERATED_FILES): CPPFLAGS+= $(if $(srcdir),-I$(srcdir))
# This is the standard .c->.o recipe, but it needs to be stated
# explicitly to support the case that $(srcdir) is not blank.
$(PLUGIN_OBJECT_SOURCE_FILES) $(PLUGIN_OBJECT_GENERATED_FILES): %.o: $(srcdir)%.c autogenerated-config.h $(srcdir)gcc-python.h $(LIBGCC_C_API_SO) autogenerated-EXTRA_CFLAGS.txt
$(COMPILE.c) $(shell cat autogenerated-EXTRA_CFLAGS.txt) $(OUTPUT_OPTION) $<
print-gcc-version: print-gcc-version.c autogenerated-EXTRA_CFLAGS.txt
$(CC) \
$(CPPFLAGS) $(CFLAGS) \
$(shell cat autogenerated-EXTRA_CFLAGS.txt) \
-o $@ \
$<
clean:
$(RM) *.so *.o gcc-c-api/*.o autogenerated*
$(RM) -r docs/_build
$(RM) -f gcc-with-$(PLUGIN_NAME).1 gcc-with-$(PLUGIN_NAME).1.gz
$(RM) -f print-gcc-version
cd gcc-c-api && make clean
find tests -name "*.o" -delete
autogenerated-config.h: $(addprefix $(srcdir),generate-config-h.py configbuilder.py)
$(PYTHON) $< -o $@ --gcc="$(CC)" --plugindir="$(GCCPLUGINS_DIR)"
autogenerated-%.txt: $(srcdir)%.txt.in
$(CPP) $(CPPFLAGS) -x c-header $^ -o $@
# autogenerated-EXTRA_CFLAGS.txt is a byproduct of making
# autogenerated-config.h:
autogenerated-EXTRA_CFLAGS.txt: autogenerated-config.h
# autogenerated-casts.h is a byproduct of making autogenerated-casts.c
autogenerated-casts.h: autogenerated-casts.c
$(PLUGIN_GENERATED_SOURCE_FILES): autogenerated-%.c: $(addprefix $(srcdir),generate-%-c.py $(GENERATOR_DEPS))
$(PYTHON) $< > $@
autogenerated-casts.c: autogenerated-gimple-types.txt autogenerated-tree-types.txt autogenerated-rtl-types.txt $(srcdir)generate-casts-c.py
$(PYTHON) $(srcdir)generate-casts-c.py autogenerated-casts.c autogenerated-casts.h
autogenerated-gimple.c: autogenerated-gimple-types.txt autogenerated-tree-types.txt autogenerated-rtl-types.txt $(srcdir)maketreetypes.py
autogenerated-tree.c: autogenerated-tree-types.txt $(srcdir)maketreetypes.py
autogenerated-rtl.c: autogenerated-rtl-types.txt $(srcdir)maketreetypes.py
autogenerated-variable.c: autogenerated-gimple-types.txt $(srcdir)maketreetypes.py
bindir=/usr/bin
mandir=/usr/share/man
UpperPluginName = $(shell $(PYTHON) -c"print('$(PLUGIN_NAME)'.upper())")
docs/_build/man/gcc-with-python.1: docs/gcc-with-python.rst
cd docs && $(MAKE) man
gcc-with-$(PLUGIN_NAME).1: docs/_build/man/gcc-with-python.1
# Fixup the generic manpage for this build:
cp docs/_build/man/gcc-with-python.1 gcc-with-$(PLUGIN_NAME).1
sed \
-i \
-e"s|gcc-with-python|gcc-with-$(PLUGIN_NAME)|g" \
gcc-with-$(PLUGIN_NAME).1
sed \
-i \
-e"s|GCC-WITH-PYTHON|GCC-WITH-$(UpperPluginName)|g" \
gcc-with-$(PLUGIN_NAME).1
gcc-with-$(PLUGIN_NAME).1.gz: gcc-with-$(PLUGIN_NAME).1
rm -f gcc-with-$(PLUGIN_NAME).1.gz
gzip gcc-with-$(PLUGIN_NAME).1
man: gcc-with-$(PLUGIN_NAME).1.gz
install: $(PLUGIN_DSO) gcc-with-$(PLUGIN_NAME).1.gz
mkdir -p $(DESTDIR)$(GCCPLUGINS_DIR)
cd gcc-c-api && $(MAKE) install
cp $(PLUGIN_DSO) $(DESTDIR)$(GCCPLUGINS_DIR)
mkdir -p $(DESTDIR)$(GCCPLUGINS_DIR)/$(PLUGIN_DIR)
cp -a gccutils $(DESTDIR)$(GCCPLUGINS_DIR)/$(PLUGIN_DIR)
cp -a libcpychecker $(DESTDIR)$(GCCPLUGINS_DIR)/$(PLUGIN_DIR)
# Create "gcc-with-" support script:
mkdir -p $(DESTDIR)$(bindir)
install -m 755 gcc-with-python $(DESTDIR)/$(bindir)/gcc-with-$(PLUGIN_NAME)
# Fixup the reference to the plugin in that script, from being expressed as
# a DSO filename with a path (for a working copy) to a name of an installed
# plugin within GCC's search directory:
sed \
-i \
-e"s|-fplugin=[^ ]*|-fplugin=$(PLUGIN_NAME)|" \
$(DESTDIR)$(bindir)/gcc-with-$(PLUGIN_NAME)
# Fixup the plugin name within -fplugin-arg-PLUGIN_NAME-script to match the
# name for this specific build:
sed \
-i \
-e"s|-fplugin-arg-python-script|-fplugin-arg-$(PLUGIN_NAME)-script|" \
$(DESTDIR)$(bindir)/gcc-with-$(PLUGIN_NAME)
mkdir -p $(DESTDIR)$(mandir)/man1
cp gcc-with-$(PLUGIN_NAME).1.gz $(DESTDIR)$(mandir)/man1
# Hint for debugging: add -v to the gcc options
# to get a command line for invoking individual subprocesses
# Doing so seems to require that paths be absolute, rather than relative
# to this directory
TEST_CFLAGS= \
-fplugin=$(CURDIR)/$(PLUGIN_DSO) \
-fplugin-arg-python-script=test.py
# A catch-all test for quick experimentation with the API:
test: plugin
$(INVOCATION_ENV_VARS) $(CC) -v $(TEST_CFLAGS) $(CURDIR)/test.c
# Selftest for the cpychecker.py code:
testcpychecker: plugin
$(INVOCATION_ENV_VARS) $(PYTHON) testcpychecker.py -v
# Selftest for the cpybuilder code:
testcpybuilder:
$(PYTHON) testcpybuilder.py -v
dump_gimple:
$(CC) -fdump-tree-gimple $(CURDIR)/test.c
debug: plugin
$(INVOCATION_ENV_VARS) $(CC) -v $(TEST_CFLAGS) $(CURDIR)/test.c
# A simple demo, to make it easy to demonstrate the cpychecker:
demo: plugin
$(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-cpychecker -c $(PYTHON_INCLUDES) demo.c
json-examples: plugin
$(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-cpychecker -I/usr/include/python2.7 -c libcpychecker_html/test/example1/bug.c
test-suite: plugin print-gcc-version
$(INVOCATION_ENV_VARS) $(PYTHON) run-test-suite.py
show-ssa: plugin
$(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-python examples/show-ssa.py test.c
html: docs/tables-of-passes.rst docs/passes.svg
cd docs && $(MAKE) html
# We commit this generated file to SCM to allow the docs to be built without
# needing to build the plugin:
docs/tables-of-passes.rst: plugin generate-tables-of-passes-rst.py
$(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-python generate-tables-of-passes-rst.py test.c > $@
# Likewise for this generated file:
docs/passes.svg: plugin generate-passes-svg.py
$(INVOCATION_ENV_VARS) $(srcdir)./gcc-with-python generate-passes-svg.py test.c
check-api:
xmllint --noout --relaxng $(srcdir)./gcc-c-api/api.rng $(srcdir)./gcc-c-api/*.xml
# Utility target, to help me to make releases
# - creates a tag in git (but does not push it; see "Notes to self on
# making a release" below)
# - creates a tarball
#
# The following assumes that VERSION has been set e.g.
# $ make tarball VERSION=0.4
$(HOME)/rpmbuild/SOURCES/%.tar.gz:
test -n "$(VERSION)"
-git tag -d v$(VERSION)
git tag -a v$(VERSION) -m"$(VERSION)"
git archive --format=tar --prefix=$*/ v$(VERSION) | gzip > $*.tar.gz
sha256sum $*.tar.gz
cp $*.tar.gz $@
tarball: $(HOME)/rpmbuild/SOURCES/gcc-python-plugin-$(VERSION).tar.gz
# Notes to self on making a release
# ---------------------------------
#
# Before tagging:
#
# * update the version/release in docs/conf.py
#
# * update the version in gcc-python-plugin.spec
#
# * add release notes to docs
#
# Test the candidate tarball via a scratch SRPM build locally (this
# achieves test coverage against python 2 and 3, for both debug and
# optimized python, on one arch, against the locally-installed version of
# gcc):
#
# $ make srpm VERSION=fixme
#
# $ make rpm VERSION=fixme
#
# Test the candidate tarball via a scratch SRPM build in Koji (this
# achieves test coverage against python 2 and 3, for both debug and
# optimized python, on both i686 and x86_64, against another version of
# gcc):
#
# $ make koji VERSION=fixme
#
# After successful testing of a candidate tarball:
#
# * push the tag:
#
# $ git push --tags
#
# * upload it to https://fedorahosted.org/releases/g/c/gcc-python-plugin/
# via:
#
# $ scp gcc-python-plugin-$(VERSION).tar.gz dmalcolm@fedorahosted.org:gcc-python-plugin
#
# * add version to Trac: https://fedorahosted.org/gcc-python-plugin/admin/ticket/versions
#
# * update release info at https://fedorahosted.org/gcc-python-plugin/wiki#Code
#
# * send release announcement:
#
# To: gcc@gcc.gnu.org, gcc-python-plugin@lists.fedorahosted.org, python-announce-list@python.org
# Subject: ANN: gcc-python-plugin $(VERSION)
# (etc)
#
# * build it into Fedora
# Utility target, for building test rpms:
srpm:
rpmbuild -bs gcc-python-plugin.spec
# Perform a test rpm build locally:
rpm:
rpmbuild -ba gcc-python-plugin.spec
# Perform a test (scratch) build in Koji:
# The following have been deleted from Koji:
# f16 was gcc 4.6
# f17 was gcc 4.7
# f19 was gcc 4.8
koji-gcc-5: srpm
koji build --scratch f23 ~/rpmbuild/SRPMS/gcc-python-plugin-$(VERSION)-1.fc20.src.rpm
koji-gcc-6: srpm
koji build --scratch f24 ~/rpmbuild/SRPMS/gcc-python-plugin-$(VERSION)-1.fc20.src.rpm
koji: koji-gcc-5 koji-gcc-6
gcc-python-plugin-0.15/README.rst 0000664 0000000 0000000 00000010701 12660356443 0016505 0 ustar 00root root 0000000 0000000 gcc-python
==========
This is a plugin for GCC, which links against libpython, and (I hope) allows
you to invoke arbitrary Python scripts from inside the compiler. The aim is to
allow you to write GCC plugins in Python.
The plugin is Free Software, licensed under the GPLv3 (or later).
It's still at the "experimental proof-of-concept stage"; expect crashes and
tracebacks (I'm new to insides of GCC, and I may have misunderstood things).
It's already possible to use this to add additional compiler errors/warnings,
e.g. domain-specific checks, or static analysis. One of my goals for this is
to "teach" GCC about the common mistakes people make when writing extensions
for CPython, but it could be used e.g. to teach GCC about GTK's
reference-counting semantics, or about locking in the Linux kernel, or about
signal-safety in APIs.
Other ideas include visualizations of code structure. Given a `gcc.CFG`
instance, `gccutils.render_to_dot(cfg)` and `gccutils.invoke_dot(cfg)` will
use graphviz and eog to plot a handy visualization of a control flow graph,
showing the source code interleaved with GCC's ``GIMPLE`` internal
representation.
Requirements
------------
* GCC: 4.6 or later (it uses APIs that weren't exposed to plugins in 4.5)
* Python: tested with 2.7 and 3.2; it may work with earlier versions
* "six": The libcpychecker code uses the "six" Python compatibility library to
smooth over Python 2 vs Python 3 differences, both at build-time and
run-time:
http://pypi.python.org/pypi/six/
Usage
-----
I use::
make
to build the plugin and run the tests
You can also use::
make demo
to demonstrate the new compiler errors.
All of my coding so far has been on Fedora 15 x86_64, using::
gcc-plugin-devel-4.6.0-0.15.fc15.x86_64
and I don't know to what extent it will be compatible with other versions and
architectures.
The code also makes some assumptions about the Python version you have
installed (grep for "PyRuntime" in the .py files). I've been using::
python-devel-2.7.1-5.fc15.x86_64
python-debug-2.7.1-5.fc15.x86_64
python3-debug-3.2-0.9.rc1.fc15.x86_64
python3-devel-3.2-0.9.rc1.fc15.x86_64
but you may have to hack up the `PyRuntime()` invocations in the code to get
it to build on other machines. Ultimately I want the plugin to be buildable
against multiple python versions, so there could be a python27.so,
python27-debug.so, python-32mu.so, python-32-dmu.so, etc (c.f. PEP-3149)
There isn't an installer yet. In theory you should be able to add these
arguments to the gcc invocation::
gcc -fplugin=python.so -fplugin-arg-python-script=PATH_TO_SCRIPT.py OTHER_ARGS
and have it run your script as the plugin starts up.
The plugin automatically adds the absolute path to its own directory to the
end of its `sys.path`, so that it can find support modules, such as gccutils.py
and `libcpychecker`.
The exact API is still in flux; you can currently connect to events by
registering callbacks e.g. to be called for each function in the source at
different passes.
It exposes GCC's various types as Python objects, within a "gcc" module. You
can see the API by running::
import gcc
help(gcc)
from within a script.
Overview of the code
--------------------
This is currently three projects in one:
gcc-python-*: the plugin for GCC. The entrypoint (`init_plugin`) is in
gcc-python.c
libcpychecker and cpychecker.py: a Python library (and a driver script),
written for the plugin, in which I'm building new compiler warnings to
help people find bugs in CPython extension code.
cpybuilder: a handy module for programatically generating C source code for
CPython extensions. I use this both to generate parts of the GCC plugin, and
also in the selftests for the cpychecker script. (I initially attempted to use
Cython for the former, but wrapping the "tree" type hierarchy required more
programatic control)
Coding style: Python and GCC each have their own coding style guide for C.
I've chosen to follow Python's (PEP-7), as I prefer it (although my code is
admittedly a mess in places).
You'll find API documentation within the "docs" directory, written in the
reStructuredText format (as is this file, in fact). If you have Sphinx
installed (http://sphinx.pocoo.org/), you can regenerate these docs using::
make html
within the `docs` directory. Sphinx is the `python-sphinx` package on a
Fedora/RHEL box.
More detailed documentation can be seen within `docs/getting-involved.rst`
Enjoy!
David Malcolm
gcc-python-plugin-0.15/configbuilder.py 0000664 0000000 0000000 00000014334 12660356443 0020212 0 ustar 00root root 0000000 0000000 # Copyright 2012 David Malcolm
# Copyright 2012 Red Hat, Inc.
#
# This is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# .
import os
import shutil
from subprocess import Popen, PIPE, check_output
import sys
def indent(prefix, text):
text = str(text)
return '\n'.join([prefix + line
for line in text.splitlines()])
class ConfigurationFailure(Exception):
pass
class CheckFor:
"""
Context manager for wrapping a feature test
The feature test should raise ConfigurationFailure to signify a failure
"""
def __init__(self, initmsg, mandatory, okmsg=None, failmsg='failed'):
self.initmsg = initmsg
self.mandatory = mandatory
self.okmsg = okmsg
self.failmsg = failmsg
self.result = None
# context manager hooks:
def __enter__(self):
sys.stdout.write('%s... ' % self.initmsg) # no newline
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type:
# exception occurred:
self.result = False
# is it one of ours, signifiying the failure of a test?
if isinstance(exc_val, ConfigurationFailure):
# Write the failure message:
sys.stdout.write('%s\n' % self.failmsg)
if self.mandatory:
# Print diagnostic information:
print(exc_val)
# terminate the build
sys.exit(1)
else:
return True # swallow the exception
else:
# some kind of unexpected error; propagate it:
return False
else:
# Assume success:
self.result = True
# Write the success message:
if self.okmsg:
sys.stdout.write('%s\n' % self.okmsg)
def succeeded(self):
# Did this test succeed?
return self.result
class Result:
pass
class OptionFlag(Result):
# the outcome of a feature test
def __init__(self, description, flag, defn):
self.description = description
self.flag = flag
self.defn = defn
def write_to(self, f):
f.write('/* %s */\n' % self.description)
if self.flag:
f.write('#define %s\n\n' % self.defn)
else:
f.write('#undef %s\n\n' % self.defn)
class ConfigBuilder:
def __init__(self, argv):
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-o', '--output-file')
args = parser.parse_args(argv[1:])
#print(args)
self.filename = args.output_file
self.dirname = 'config-tests'
if os.path.exists(self.dirname):
shutil.rmtree(self.dirname)
os.mkdir(self.dirname)
self.testid = 0
self.results = []
def make_test_dir(self, test):
self.testid += 1
dirname = ('%05i-' % self.testid) + test.initmsg
dirname = '-'.join(dirname.split())
dirpath = os.path.join(self.dirname, dirname)
os.mkdir(dirpath)
return dirpath
def write_outcome(self):
sys.stdout.write('writing %s\n' % self.filename)
with open(self.filename, 'w') as f:
f.write('/* autogenerated header file */\n\n')
for r in self.results:
r.write_to(f)
def compile(self, test, src, extraargs):
dirpath = self.make_test_dir(test)
srcpath = os.path.join(dirpath, 'feature-test.c')
with open(srcpath, 'w') as f:
f.write(src)
outpath = os.path.join(dirpath, 'feature-test.o')
args= [os.environ.get('CC', 'gcc'),
'-c', # don't run the linker (no main)
'-o', outpath]
args += extraargs
args += [srcpath]
p = Popen(args, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate()
c = p.wait()
if c != 0:
class CompilationError(ConfigurationFailure):
def __str__(self):
result = 'Test compilation failed with exit code %i\n' % c
result += ' The command was:\n'
result += ' %s\n' % ' '.join(args)
result += ' The source was: (in %s)\n' % srcpath
result += indent(' ', src) + '\n'
result += ' The stderr was:\n'
result += indent(' ', stderr) + '\n'
return result
raise CompilationError()
def capture_shell_output(self, initmsg, cmd):
with CheckFor(initmsg,
mandatory=True) as test:
out = check_output(cmd,
shell=True) # input must be trusted
out = str(out.decode())
sys.stdout.write('%s\n' % out.strip())
return out
def test_for_mandatory_c_header(self, header, extraargs):
with CheckFor('checking for %s' % header,
okmsg='found',
failmsg='not found',
mandatory=True) as test:
self.compile(test,
src='#include <%s>' % header,
extraargs=extraargs)
def test_c_compilation(self, initmsg, src, extraargs, description, defn):
with CheckFor(initmsg,
okmsg='yes',
failmsg='no',
mandatory=False) as test:
self.compile(test,
src,
extraargs)
self.results.append(OptionFlag(description,
test.succeeded(),
defn))
gcc-python-plugin-0.15/contributors.rst 0000664 0000000 0000000 00000000067 12660356443 0020311 0 ustar 00root root 0000000 0000000 Alexandre Lissy
David Malcolm
David Narvaez
Tom Tromey
gcc-python-plugin-0.15/cpybuilder.py 0000664 0000000 0000000 00000046245 12660356443 0017546 0 ustar 00root root 0000000 0000000 # Copyright 2011, 2012 David Malcolm
# Copyright 2011, 2012 Red Hat, Inc.
#
# This is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# .
from subprocess import Popen, PIPE
import re
# For the purpose of the GCC plugin, it's OK to assume that we're compiling
# with GCC itself, and thus we can use GCC extensions
with_gcc_extensions = True
def camel_case(txt):
return ''.join([word.title()
for word in txt.split('_')])
def nullable_ptr(ptr):
if ptr:
return ptr
else:
return 'NULL'
def simple_unaryfunc(identifier, typename, c_expression):
"""Define a simple unaryfunc, using a specifc PyObject subclass"""
self.add_defn("static PyObject *\n" +
"%s(%s *self)\n" % (identifier, typename) +
"{\n" +
" return %s;\n" % c_expression +
"}\n\n")
return identifier
class NamedEntity:
"""A thing within C code that has an identifier"""
def __init__(self, identifier):
self.identifier = identifier
def c_ptr_field(self, name, cast=None):
if hasattr(self, name):
val = getattr(self, name)
else:
val = None
if cast:
caststr = '(%s)' % cast
else:
caststr = ''
if with_gcc_extensions:
# Designate the initializer fields:
return ' .%s = %s%s,\n' % (name, caststr, nullable_ptr(val))
else:
return ' %s%s, /* %s */\n' % (caststr, nullable_ptr(val), name)
def unaryfunc_field(self, name):
return self.c_ptr_field(name, 'unaryfunc')
def c_src_field(self, name):
assert hasattr(self, name)
val = getattr(self, name)
if with_gcc_extensions:
# Designate the initializer fields:
return ' .%s = %s,\n' % (name, val)
else:
return ' %s, /* %s */\n' % (val, name)
def c_src_field_value(self, name, val, cast=None):
if cast:
caststr = '(%s)' % cast
else:
caststr = ''
if with_gcc_extensions:
# Designate the initializer fields:
return ' .%s = %s%s,\n' % (name, caststr, val)
else:
return ' %s%s, /* %s */\n' % (caststr, val, name)
class PyGetSetDef:
def __init__(self, name, get, set, doc, closure=None):
self.name = name
self.get = get
self.set = set
self.doc = doc
self.closure = closure
def c_defn(self):
result = ' {(char*)"%s",\n' % self.name
result += ' (getter)%s,\n' % nullable_ptr(self.get)
result += ' (setter)%s,\n' % nullable_ptr(self.set)
result += ' (char*)"%s",\n' % nullable_ptr(self.doc)
result += ' %s},\n' % nullable_ptr(self.closure)
return result
class PyGetSetDefTable(NamedEntity):
def __init__(self, identifier, gsdefs, identifier_prefix=None, typename=None):
NamedEntity.__init__(self, identifier)
self.gsdefs = gsdefs
self.identifier_prefix = identifier_prefix
self.typename = typename
def c_defn(self):
result = 'static PyGetSetDef %s[] = {\n' % self.identifier
for gsdef in self.gsdefs:
result += gsdef.c_defn()
result += ' {NULL} /* Sentinel */\n'
result += '};\n'
return result
def add_gsdef(self, name, getter, setter, doc, closure=None):
self.gsdefs.append(PyGetSetDef(name, getter, setter, doc, closure=None))
def add_simple_getter(self, cu, name, c_expression, doc):
assert self.identifier_prefix
assert self.typename
identifier = self.identifier_prefix + '_get_' + name
cu.add_simple_getter(identifier, self.typename, c_expression)
self.add_gsdef(name, identifier, None, doc)
METH_VARARGS = 'METH_VARARGS'
class PyMethodDef:
def __init__(self, name, fn_name, args, docstring):
self.name = name
self.fn_name = fn_name
#assert args in ('METH_VARARGS', ) # FIXME
self.args = args
self.docstring = docstring
def c_defn(self):
return (' {(char*)"%(name)s", %(fn_name)s, %(args)s,\n'
' (char*)"%(docstring)s"},\n' % self.__dict__)
class PyMethodTable(NamedEntity):
def __init__(self, identifier, methods):
NamedEntity.__init__(self, identifier)
self.methods = methods
def c_defn(self):
result = 'static PyMethodDef %s[] = {\n' % self.identifier
for method in self.methods:
result += method.c_defn()
result += ' {NULL, NULL, 0, NULL} /* Sentinel */\n'
result += '};\n'
return result
def add_method(self, name, fn_name, args, docstring):
self.methods.append(PyMethodDef(name, fn_name, args, docstring))
# See http://docs.python.org/c-api/typeobj.html#number-structs
class PyNumberMethods(NamedEntity):
def __init__(self, identifier):
NamedEntity.__init__(self, identifier)
def c_defn(self):
result = 'static PyNumberMethods %s = {\n' % self.identifier
result += self.c_ptr_field('nb_add')
result += self.c_ptr_field('nb_subtract')
result += self.c_ptr_field('nb_multiply')
result += '#if PY_MAJOR_VERSION < 3\n'
result += self.c_ptr_field('nb_divide')
result += '#endif\n'
result += self.c_ptr_field('nb_remainder')
result += self.c_ptr_field('nb_divmod')
result += self.c_ptr_field('nb_power')
result += self.unaryfunc_field('nb_negative')
result += self.unaryfunc_field('nb_positive')
result += self.unaryfunc_field('nb_absolute')
result += '#if PY_MAJOR_VERSION >= 3\n'
result += self.c_ptr_field('nb_bool')
result += '#else\n'
result += self.c_ptr_field('nb_nonzero')
result += '#endif\n'
result += self.unaryfunc_field('nb_invert')
result += self.c_ptr_field('nb_lshift')
result += self.c_ptr_field('nb_rshift')
result += self.c_ptr_field('nb_and')
result += self.c_ptr_field('nb_xor')
result += self.c_ptr_field('nb_or')
result += '#if PY_MAJOR_VERSION < 3\n'
result += self.c_ptr_field('nb_coerce')
result += '#endif\n'
result += self.unaryfunc_field('nb_int')
result += '#if PY_MAJOR_VERSION >= 3\n'
result += self.c_ptr_field('nb_reserved')
result += '#else\n'
result += self.unaryfunc_field('nb_long')
result += '#endif\n'
result += self.unaryfunc_field('nb_float')
result += '#if PY_MAJOR_VERSION < 3\n'
result += self.unaryfunc_field('nb_oct')
result += self.unaryfunc_field('nb_hex')
result += '#endif\n'
result += self.c_ptr_field('nb_inplace_add')
result += self.c_ptr_field('nb_inplace_subtract')
result += self.c_ptr_field('nb_inplace_multiply')
result += '#if PY_MAJOR_VERSION < 3\n'
result += self.c_ptr_field('nb_inplace_divide')
result += '#endif\n'
result += self.c_ptr_field('nb_inplace_remainder')
result += self.c_ptr_field('nb_inplace_power')
result += self.c_ptr_field('nb_inplace_lshift')
result += self.c_ptr_field('nb_inplace_rshift')
result += self.c_ptr_field('nb_inplace_and')
result += self.c_ptr_field('nb_inplace_xor')
result += self.c_ptr_field('nb_inplace_or')
result += self.c_ptr_field('nb_floor_divide')
result += self.c_ptr_field('nb_true_divide')
result += self.c_ptr_field('nb_inplace_floor_divide')
result += self.c_ptr_field('nb_inplace_true_divide')
result += self.unaryfunc_field('nb_index')
result += '};\n'
return result
def add_method(self, name, fn_name, args, docstring):
self.methods.append(PyMethodDef(name, fn_name, args, docstring))
class PyTypeObject(NamedEntity):
def __init__(self, identifier, localname, tp_name, struct_name, **kwargs):
NamedEntity.__init__(self, identifier)
self.localname = localname
self.tp_name = tp_name
self.struct_name = struct_name
self.__dict__.update(kwargs)
if not hasattr(self, 'tp_new'):
self.tp_new = 'PyType_GenericNew'
if not hasattr(self, 'tp_flags'):
self.tp_flags = 'Py_TPFLAGS_DEFAULT'
def c_defn(self):
result = '\n'
result += 'PyTypeObject %(identifier)s = {\n' % self.__dict__
result += self.c_initializer()
result += '};\n' % self.__dict__
result +='\n'
return result
def c_initializer(self):
if hasattr(self, 'ob_type'):
ob_type_str = getattr(self, 'ob_type')
else:
ob_type_str = 'NULL'
result = ' PyVarObject_HEAD_INIT(%s, 0)\n' % ob_type_str
result += ' "%(tp_name)s", /*tp_name*/\n' % self.__dict__
result += ' sizeof(%(struct_name)s), /*tp_basicsize*/\n' % self.__dict__
result += ' 0, /*tp_itemsize*/\n'
result += self.c_ptr_field('tp_dealloc')
result += self.c_ptr_field('tp_print')
result += self.c_ptr_field('tp_getattr')
result += self.c_ptr_field('tp_setattr')
result += '#if PY_MAJOR_VERSION < 3\n' % self.__dict__
result += ' 0, /*tp_compare*/\n' % self.__dict__
result += '#else\n' % self.__dict__
result += ' 0, /*reserved*/\n' % self.__dict__
result += '#endif\n' % self.__dict__
result += self.c_ptr_field('tp_repr')
result += self.c_ptr_field('tp_as_number')
result += self.c_ptr_field('tp_as_sequence')
result += self.c_ptr_field('tp_as_mapping')
result += self.c_ptr_field('tp_hash')
result += self.c_ptr_field('tp_call')
result += self.c_ptr_field('tp_str')
result += self.c_ptr_field('tp_getattro')
result += self.c_ptr_field('tp_setattro')
result += self.c_ptr_field('tp_as_buffer')
result += self.c_src_field('tp_flags')
result += ' 0, /*tp_doc*/\n'
result += self.c_ptr_field('tp_traverse')
result += self.c_ptr_field('tp_clear')
result += self.c_ptr_field('tp_richcompare')
result += ' 0, /* tp_weaklistoffset */\n'
result += self.c_ptr_field('tp_iter')
result += self.c_ptr_field('tp_iternext')
result += self.c_ptr_field('tp_methods')
result += self.c_ptr_field('tp_members')
result += self.c_ptr_field('tp_getset')
result += self.c_ptr_field('tp_base', 'PyTypeObject*')
result += self.c_ptr_field('tp_dict')
result += self.c_ptr_field('tp_descr_get')
result += self.c_ptr_field('tp_descr_set')
result += ' 0, /* tp_dictoffset */\n'
result += self.c_ptr_field('tp_init', 'initproc')
result += self.c_ptr_field('tp_alloc')
result += self.c_ptr_field('tp_new')
result += self.c_ptr_field('tp_free')
result += self.c_ptr_field('tp_is_gc')
result += self.c_ptr_field('tp_bases')
result += self.c_ptr_field('tp_mro')
result += self.c_ptr_field('tp_cache')
result += self.c_ptr_field('tp_subclasses')
result += self.c_ptr_field('tp_weaklist')
result += self.c_ptr_field('tp_del')
result += '#if PY_VERSION_HEX >= 0x02060000\n' % self.__dict__
result += ' 0, /*tp_version_tag*/\n' % self.__dict__
result += '#endif\n' % self.__dict__
result += '\n'
return result
def c_invoke_type_ready(self):
return (' if (PyType_Ready((PyTypeObject*)&%(identifier)s) < 0)\n'
' goto error;\n'
'\n') % self.__dict__
def c_invoke_add_to_module(self):
return (' Py_INCREF(&%(identifier)s);\n'
' PyModule_AddObject(m, "%(localname)s", (PyObject *)&%(identifier)s);\n'
'\n') % self.__dict__
class PyModule:
def __init__(self, modname, modmethods, moddoc):
self.modname = modname
self.moddoc = moddoc
assert (modmethods is None) or isinstance(modmethods, PyMethodTable)
self.modmethods = modmethods
if self.modmethods:
self.modmethods_as_ptr = self.modmethods.identifier
else:
self.modmethods_as_ptr = 'NULL'
def c_initfn_decl(self):
return ("""
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC init%(modname)s(void);
#else
PyMODINIT_FUNC PyInit_%(modname)s(void);
#endif
""" % self.__dict__)
def c_initfn_def_begin(self):
return ("""
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC init%(modname)s(void)
#else
PyMODINIT_FUNC PyInit_%(modname)s(void)
#endif
{
PyObject *m = NULL;
""" % self.__dict__)
def c_initfn_def_end(self):
return ("""
#if PY_MAJOR_VERSION < 3
return;
#else
return m;
#endif
error:
#if PY_MAJOR_VERSION < 3
return;
#else
Py_XDECREF(m);
return NULL;
#endif
}
""")
def c_py3k_moduledef(self):
return ("""
#if PY_MAJOR_VERSION >= 3
static PyModuleDef %(modname)smodule = {
PyModuleDef_HEAD_INIT,
"%(modname)s", /* m_name */
"%(moddoc)s", /* m_doc */
-1, /* m_size */
%(modmethods_as_ptr)s, /* m_methods */
NULL, NULL, NULL, NULL
};
#endif
""" % self.__dict__)
def c_invoke_ctor(self):
return ("""
#if PY_MAJOR_VERSION < 3
m = Py_InitModule3("%(modname)s", %(modmethods_as_ptr)s,
"%(moddoc)s");
#else
m = PyModule_Create(&%(modname)smodule);
#endif
if (!m) {
goto error;
}
""" % self.__dict__)
class CompilationUnit:
"""
A single C file
"""
def __init__(self):
self._includes = '#include \n'
self._prototypes = ''
self._definitions = ''
def add_include(self, path):
self._includes += '#include "%s"\n' % path
def add_decl(self, text):
self._prototypes += text
def add_defn(self, text):
self._definitions += text
def as_str(self):
return ('/* Autogenerated by cpybuilder */\n' +
self._includes +
self.make_header('Prototypes') +
self._prototypes +
self.make_header('Definitions') +
self._definitions)
def make_header(self, text):
return '\n/**** %s ****/\n\n' % text
def add_simple_getter(self, identifier, typename, c_expression):
"""Define a simple getter, suitable for use by a PyGetSetDef"""
self.add_defn("static PyObject *\n" +
"%s(%s *self, void *closure)\n" % (identifier, typename) +
"{\n" +
" return %s;\n" % c_expression +
"}\n\n")
return identifier
def add_simple_setter(self, identifier, typename, attrname, c_typecheck_fn, c_assignment):
"""Define a simple setter, suitable for use by a PyGetSetDef"""
self.add_defn("static int\n" +
"%s(%s *self, PyObject *value, void *closure)\n" % (identifier, typename) +
"{\n" +
" if (! %s(value)) {\n" % c_typecheck_fn +
" PyErr_SetString(PyExc_TypeError,\n" +
' "%s must be an int");\n' % attrname +
' return -1;\n'
' }\n' +
' %s;\n' % c_assignment +
" return 0;\n"
"}\n\n")
return identifier
def add_simple_int_setter(self, identifier, typename, attrname, c_assignment):
"""
Define a simple setter for an int-valued attribute, suitable for use
by a PyGetSetDef
"""
return self.add_simple_setter(identifier, typename, attrname,
'PyGccInt_Check',
c_assignment)
class SimpleModule:
"""
A C extension module built from a single C file
"""
def __init__(self):
self.cu = CompilationUnit()
self._modinit_preinit = ''
self._modinit_postinit = ''
def add_type_object(self, name, localname,
tp_name, struct_name, **kwargs):
pytype = PyTypeObject(name, localname, tp_name, struct_name, **kwargs)
self.cu.add_defn(pytype.c_defn())
self._modinit_preinit += pytype.c_invoke_type_ready()
self._modinit_postinit += pytype.c_invoke_add_to_module()
def add_module_init(self, modname, modmethods, moddoc):
pymod = PyModule(modname, modmethods, moddoc)
self.cu.add_decl(pymod.c_initfn_decl())
self.cu.add_defn(pymod.c_py3k_moduledef())
self.cu.add_defn(pymod.c_initfn_def_begin())
self.cu.add_defn(self._modinit_preinit)
self.cu.add_defn(pymod.c_invoke_ctor())
self.cu.add_defn(self._modinit_postinit)
self.cu.add_defn(pymod.c_initfn_def_end())
class SimpleBuild:
def __init__(self, sm, builddir='.'):
self.sm
#def generate_c(self):
# with open(sm.name
class CommandError(RuntimeError):
def __init__(self, out, err, p):
assert isinstance(out, str)
assert isinstance(err, str)
assert isinstance(p, Popen)
self.out = out
self.err = err
self.p = p
def __str__(self):
result = '\n'
result += 'returncode: %r\n' % self.p.returncode
result += ' %s\n' % self._describe_activity()
result += 'Stdout:\n'
result += self._indent(self.out)
result += '\nStderr:\n'
result += self._indent(self.err, 4)
result += self._extra_info()
return result
def _indent(self, txt, size=2):
return '\n'.join([' '*size + line for line in txt.splitlines()])
def _extra_info(self):
return ''
class PyRuntimeError(CommandError):
def __init__(self, runtime, cmd, out, err, p):
CommandError.__init__(self, out, err, p)
self.runtime = runtime
self.cmd = cmd
def _describe_activity(self):
return 'running: %s -c %r' % (self.runtime.executable , self.cmd)
from collections import namedtuple
class PyVersionInfo(namedtuple('PyVersionInfo', 'major minor micro releaselevel serial')):
@classmethod
def from_text(cls, txt):
# e.g.:
# sys.version_info(major=2, minor=7, micro=1, releaselevel='final', serial=0)
m = re.match('sys\.version_info\(major=([0-9]+), minor=([0-9]+), micro=([0-9]+), releaselevel=\'(.*)\', serial=([0-9]+)\)', txt)
return PyVersionInfo(major=int(m.group(1)),
minor=int(m.group(2)),
micro=int(m.group(3)),
releaselevel=m.group(4),
serial=int(m.group(5)))
gcc-python-plugin-0.15/cpychecker.py 0000664 0000000 0000000 00000001413 12660356443 0017510 0 ustar 00root root 0000000 0000000 # Copyright 2011 David Malcolm
# Copyright 2011 Red Hat, Inc.
#
# This is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# .
from libcpychecker import main
main()
gcc-python-plugin-0.15/demo.c 0000664 0000000 0000000 00000004522 12660356443 0016112 0 ustar 00root root 0000000 0000000 /*
Copyright 2011 David Malcolm
Copyright 2011 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
*/
/* Examples of mistakes made using the Python API */
#include
extern uint16_t htons(uint16_t hostshort);
PyObject *
socket_htons(PyObject *self, PyObject *args)
{
unsigned long x1, x2;
if (!PyArg_ParseTuple(args, "i:htons", &x1)) {
return NULL;
}
x2 = (int)htons((short)x1);
return PyInt_FromLong(x2);
}
PyObject *
not_enough_varargs(PyObject *self, PyObject *args)
{
if (!PyArg_ParseTuple(args, "i")) {
return NULL;
}
Py_RETURN_NONE;
}
PyObject *
too_many_varargs(PyObject *self, PyObject *args)
{
int i, j;
if (!PyArg_ParseTuple(args, "i", &i, &j)) {
return NULL;
}
Py_RETURN_NONE;
}
PyObject *
kwargs_example(PyObject *self, PyObject *args, PyObject *kwargs)
{
double x, y;
char *keywords[] = {"x", "y"};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "(ff):kwargs_example", keywords, &x, &y)) {
return NULL;
}
Py_RETURN_NONE;
}
extern int convert_to_ssize(PyObject *, Py_ssize_t *);
PyObject *
buggy_converter(PyObject *self, PyObject *args)
{
int i;
if (!PyArg_ParseTuple(args, "O&", convert_to_ssize, &i)) {
return NULL;
}
Py_RETURN_NONE;
}
PyObject *
make_a_list_of_random_ints_badly(PyObject *self,
PyObject *args)
{
PyObject *list, *item;
long count, i;
if (!PyArg_ParseTuple(args, "i", &count)) {
return NULL;
}
list = PyList_New(0);
for (i = 0; i < count; i++) {
item = PyLong_FromLong(random());
PyList_Append(list, item);
}
return list;
}
/*
PEP-7
Local variables:
c-basic-offset: 4
indent-tabs-mode: nil
End:
*/
gcc-python-plugin-0.15/docs/ 0000775 0000000 0000000 00000000000 12660356443 0015747 5 ustar 00root root 0000000 0000000 gcc-python-plugin-0.15/docs/0.10.rst 0000664 0000000 0000000 00000014265 12660356443 0017067 0 ustar 00root root 0000000 0000000 .. Copyright 2012 David Malcolm
Copyright 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
.. covers up to 9f38feba0c69add5a39b69faba528de955ff71a2
0.10
~~~~
Thanks to Buck Golemon, Daniele Varrazzo, David Narvaez, Eevee, Jason Mueller,
Kevin Pyle, Matt Rice and Tom Tromey for their contributions to this release.
Changes to the GCC Python Plugin
================================
* The plugin can now be used with Python 3.3 (fixing Unicode issues and
dict-ordering assumptions).
* The plugin now exposes inline assembler to Python scripts via
:py:class:`gcc.GimpleAsm`.
* There is a new `gccutils.sorted_callgraph()` function to get the callgraph in
topologically-sorted order.
* The test suite has been reworked to fix issues with checkouts on OS X
case-insensitive filesystems.
* C++ support: support for locating the global namespace (aka "::"), for locating
declarations and child namespaces within a namespace, and aliases.
* :py:class:`gcc.Declaration` now has an is_builtin attribute
* Numerous improvements to the plugin's Makefile
Improvements to :doc:`gcc-with-cpychecker `
========================================================
* By default, the refcount checker is now only run on code that includes
(implemented by checking if the "PyObject" typedef exists).
This greatly speeds up compilation of large projects for which the Python
extension modules are only a small subset of the source tree.
* Added some custom attributes for marking functions that set an exception,
either always, or when returning a negative value::
__attribute__((cpychecker_negative_result_sets_exception))
__attribute__((cpychecker_sets_exception))
* Improve descriptions of ranges: rather than emitting descriptions with the rather
vague "value", such as::
when considering range: 1 <= value <= 0x7fffffff
instead try to embed a descriptive name for the value, such as::
when considering range: 1 <= n <= 0x7fffffff
Mass recompile of Fedora 17's Python extension code
---------------------------------------------------
I ran the reference-count checker on all of the C/C++ Python extension modules
in Fedora 17 and `reported hundreds of genuine problems
`_,
many of which have been fixed.
In the process of doing this I found and fixed many problems in the checker
itself. For example:
* the checker now understand's GCC's `__builtin_expect`, fixing various
false reports about dereferencing NULL pointers when running the checker
on Cython-generated code in python-lxml-2.3
* added descriptions of part of SWIG and Cython's internal APIs to suppress
some false positives seen with SWIG and Cython-generated code.
* tweak the refcount rules to fix some false positives where the checker
erroneously considered the case of a deallocation by:
.. code-block:: c
Py_DECREF(obj);
where "obj" provably had other references not owned by the function being
analyzed, and thus for the case where obj->ob_refcnt > 1 the deallocation
could not happen.
The plugin also now has a triaging script which can examine all of the errors
within a build and provide a report, showing all of them in prioritized
categories.
The source tree now contains helper scripts for conducting such a mass recompile.
Pyscopg support
---------------
Daniele Varrazzo used the checker extensively on
`psycopg `_, the popular Python interface
to `PostgreSQL `_, and was able to find and fix
numerous subtle errors:
* https://fedorahosted.org/pipermail/gcc-python-plugin/2012-March/000229.html
* http://initd.org/psycopg/articles/2012/03/29/psycopg-245-released/
Experimental new error visualization
------------------------------------
The checker can now dump its internal representation in JSON form, via a new
`--dump-json` option, and an experimental new renderer can generate HTML from
this. An example can be seen here:
http://fedorapeople.org/~dmalcolm/gcc-python-plugin/2012-03-19/example/example.html
This is still a work-in-progress
C++ support
-----------
The checker is now able to run on C++ code: support has been added for methods,
references, "this", destructors, the :py:class:`gcc.GimpleNop` operation.
Coverage of the CPython API
---------------------------
The format code handling for Py_BuildValue was missing support for the
following codes:
* 'u' and 'u#'
* 'f' and 'd'
* 'D'
* 'c'
In addition, the handling for 's#' and 'z#' had a bug in which it erroneously
expected an int* or Py_ssize_t*, rather than just a int or Py_ssize_t.
This release fixes these issues, and gives full coverage of all valid
format codes for Py_BuildValue in Python 2.
This release adds heuristics for the behavior of the following CPython API
entrypoints:
* PyCode_New
* PyCObject_FromVoidPtrAndDesc
* PyDict_Size
* PyErr_Clear
* PyEval_CallMethod
* Py_FatalError
* PyFile_SoftSpace, PyFile_WriteObject, and PyFile_WriteString
* PyFloat_AsDouble and PyFloat_FromDouble
* PyFrame_New
* Py_GetVersion
* PyImport_AddModule
* PyIter_Next
* PyNumber_Int, PyNumber_Remainder
* PyObject_CallObject, PyObject_GetAttr, PyObject_GetAttrString, PyObject_GetItem, PyObject_SetAttr, and PyObject_SetAttrString
* PyOS_snprintf
* PyString_InternFromString
* PySequence_Concat, PySequence_GetSlice, PySequence_SetItem, PySequence_Size
* PySys_GetObject
* PyTraceBack_Here
* PyTuple_GetItem
* PyUnicodeUCS4_DecodeUTF8
* PyWeakref_GetObject
along with various other bugfixes.
gcc-python-plugin-0.15/docs/0.11.rst 0000664 0000000 0000000 00000005354 12660356443 0017067 0 ustar 00root root 0000000 0000000 .. Copyright 2012 David Malcolm
Copyright 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
.. this covers up to 9bfec18f7eebf9af2dde8b8db132cf97c7d9c17d
0.11
~~~~
Changes to the GCC Python Plugin
================================
The main change in this release is support for compiling the plugin with a
C++ compiler. Recent versions of GCC 4.7 are now built with C++ rather than
C, meaning that plugins must also be built with C++ (since all of GCC's
internal symbols are name-mangled). This release fixes the plugin's
Makefile so that it autodetects whether the plugin needs to be built with a
C or C++ compiler and (I hope) does the right thing automatically. I've also
made the necessary changes to the C source code of the plugin so that it is
compilable as either language.
This should enable the plugin to now be usable with recent builds of gcc
4.7.* (along with gcc 4.6).
The plugin doesn't yet support gcc 4.8 prereleases.
Other fixes:
* there is now a unique :py:class:`gcc.Edge` wrapper for each underlying
edge in GCC's control flow graphs, rather than the old erroneous
behavior of having multiple identical duplicate wrappers.
* fixed missing documentation for :py:class:`gcc.SsaName`, and
:py:class:`gcc.Edge`'s `true_value` and `false_value` flags
Internal improvements to :doc:`gcc-with-cpychecker `
=================================================================
The CPython static analysis code shipped with the plugin contains a detailed
description of the behavior of the
`CPython API `_ (e.g. which arguments will
lead to a segfault if NULL, and why; the possible outcomes of a call and
their impact on reference-counts; etc).
However, these descriptions were tightly bound to implementation details of
the checker.
This release introduces a new internal API to the analyzer for describing
the possible behaviors of CPython API entrypoints, in an attempt to decouple
these descriptions from the checker, and ports many of the descriptions to
using it.
These changes shouldn't be visible to users of the checker, but should make
future maintenance much easier.
gcc-python-plugin-0.15/docs/0.12.rst 0000664 0000000 0000000 00000010133 12660356443 0017057 0 ustar 00root root 0000000 0000000 .. Copyright 2013 David Malcolm
Copyright 2013 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
.. this covers up to cfd1a892b8e47ea18b757611c83528b22d85f4ce
0.12
~~~~
Changes to the GCC Python Plugin
================================
GCC 4.8 compatibility
---------------------
This release of the plugin adds support for gcc 4.8 (along with continued
support for gcc 4.7 and gcc 4.6).
gcc-c-api
---------
The source tree contains a new component: gcc-c-api. This provides a
wrapper library `libgcc-c-api.so` that hides much of the details of GCC's
internals (such as the binary layout of structures, and the differences
between GCC 4.6 through 4.8).
I plan for this to eventually be its own project, aiming at providing
a stable API and ABI for working with GCC, once it has proven itself in
the context of the python plugin.
The API provides an XML description of itself, which should greatly simplify
the job of generating bindings for accessing GCC internals from other
languages.
Link-Time Optimization support
------------------------------
The plugin can now be used with GCC's Link-Time Optimization feature (LTO),
allowing whole-program visualizations and analysis.
For example, you can rendering a whole-program "supergraph" of control flow
graphs using this invocation:
.. code-block:: bash
$ ./gcc-with-python \
examples/show-lto-supergraph.py \
-flto \
-flto-partition=none \
tests/examples/lto/input-*.c
which will render a bitmap of the supergraph like this:
.. figure:: sample-supergraph.png
:scale: 50 %
:alt: image of a supergraph
API improvements
----------------
Sane repr() implementations have been added to the following classes:
:py:class:`gcc.CaseLabelExpr`
:py:class:`gcc.GimpleLabel`
:py:class:`gcc.BasicBlock`
:py:class:`gcc.SsaName`
:py:class:`gcc.ArrayRef`
:py:class:`gcc.ComponentRef`
:py:class:`gcc.PointerType`
:py:class:`gcc.IntegerType`
:py:class:`gcc.Location`
:py:class:`gcc.Location` instances can now be compared and sorted. They
are ordered alphabetically by file, then by line number, then by column)
Other fixes
-----------
* the Makefile has a "make install" target (at last)
* prevent forkbomb when running with CC=gcc-with-cpychecker
* fixed memory leak within :py:meth:`gcc.Gimple.walk_tree`
* ensure that the result of :py:attr:`gcc.Cfg.basic_blocks`
can't contain any `None` items (which used to sometimes happen when
certain optimizations had occurred).
* run-test-suite.py now has a --show option, giving more verbose
information on what the test suite is doing
* fix hashing and equality for :py:class:`gcc.Function` and
:py:class:`gcc.Gimple`
* fix :py:meth:`gcc.IntegerCst.__hash__` and ensure it compares sanely
against `int`
* ensure that equivalent :py:class:`gcc.ComponentRef` objects have the
same hash and are equal
* ensure there is a unique gcc.CallgraphEdge for each underlying edge, and
a unique gcc.Cfg for each underlying control flow graph
* add a "label" attribute to gcc.GimpleLabel
* add :py:data:`gcc.GCC_VERSION`
Internal improvements to :doc:`gcc-with-cpychecker `
=================================================================
* fix exception on pointer comparisons
* fix exception on int-to-float casts
* fix traceback when analyzing a callsite that discards the LHS when an Outcome.returns() a value
* fix two different exceptions when casting an integer value to a pointer
* add example of refcounting bugs to "make demo"
* fix a traceback seen on bogus uses of Py_XDECREF()
gcc-python-plugin-0.15/docs/0.13.rst 0000664 0000000 0000000 00000007774 12660356443 0017101 0 ustar 00root root 0000000 0000000 .. Copyright 2014 David Malcolm
Copyright 2014 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
.. this covers up to ef48966bc952d2af637e6a34e92846af2c2210bf
0.13
~~~~
The major features in this release are:
* gcc 4.9 compatibility
* a major revamping to the HTML output from gcc-with-cpychecker
New dependency: ``lxml``. The new HTML output format uses lxml
internally.
Changes to the GCC Python Plugin
================================
GCC 4.9 compatibility
---------------------
This release of the plugin adds support for gcc 4.9 (along with continued
support for gcc 4.6, 4.7 and gcc 4.8).
Building against 4.9 requires a GCC 4.9 with the fix for
`GCC bug 63410 `_ applied.
Other fixes
-----------
* fixed a build-time incompatibility with Python 3.3.0
* various internal bug fixes:
* bug in garbage-collector integration (https://bugzilla.redhat.com/show_bug.cgi?id=864314)
* the test suite is now parallelized (using multiprocessing)
* improvements to Makefile
* improvements to documentation
* add gcc.Location.in_system_header attribute
Improvements to :doc:`gcc-with-cpychecker `
========================================================
The major improvement to :doc:`gcc-with-cpychecker ` is a big
revamp of the output.
A new "v2" HTML report is available, written to ``SOURCE_NAME.v2.html``
e.g. ``demo.c.v2.html``:
.. figure:: new-html-error-report.png
:alt: screenshot of the new kind of HTML report
The new HTML report is easier to read in the presence of complicated
control flow. It also include links to the API documentation for
calls made to the CPython API.
For both old-style and new-style reports, the wording of the messages has
been clarified:
* Reference-count tracking messages now largely eliminate the
``0 + N where N >=`` gobbledegook, since this was confusing to
everyone (including me). Instead, error reports
talk about references as owned vs borrowed references e.g.
* "refs: 1 owned"
* "refs: 0 owned 1 borrowed"
resorting to ranges::
refs: 0 owned + B borrowed where 1 <= B <= 0x80000000
only where necessary.
* Reports now add ``memory leak:`` and ``future use-after-free:``
prefixes where appropriate, to better indicate the issue.
* Objects are referred to more in terms the user is likely to
understand e.g. ``*dictA`` rather than ``PyDictObject``.
The checker also reports better source locations in its messages
e.g. in the presence of multiple ``return`` statements
(https://fedorahosted.org/gcc-python-plugin/ticket/58).
.. The v2 html output was first added in 0.10, but was "experimental", and
required hacking to access.
Other improvements
------------------
* Add a new test script: `tests/examples/find-global-state`, showing
examples of finding global state in the code being compiled.
* handle :c:func:`PySequence_DelItem`
* fix bug in handling of :c:func:`PyRun_SimpleStringFlags`
* fix issue with handling of :c:func:`PyArg_ParseTuple`
(https://fedorahosted.org/gcc-python-plugin/ticket/50)
* although we don't model the internals of C++ exceptions, fix things so
we don't crash with a traceback in the absense of ``-fno-exceptions``
(https://fedorahosted.org/gcc-python-plugin/ticket/51)
Contributors
============
Thanks to Buck Golemon, Denis Efremov, Philip Herron, and Tom Tromey for
their contributions to this release.
gcc-python-plugin-0.15/docs/0.14.rst 0000664 0000000 0000000 00000001504 12660356443 0017063 0 ustar 00root root 0000000 0000000 .. Copyright 2015 David Malcolm
Copyright 2015 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
0.14
~~~~
This releases adds support for gcc 5 (along with continued
support for gcc 4.6, 4.7, 4.8 and 4.9).
gcc-python-plugin-0.15/docs/0.15.rst 0000664 0000000 0000000 00000002336 12660356443 0017070 0 ustar 00root root 0000000 0000000 .. Copyright 2016 David Malcolm
Copyright 2016 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
0.15
~~~~
This releases adds support for gcc 6 (along with continued
support for gcc 4.6, 4.7, 4.8, 4.9 and 5).
Additionally, this release contains the following improvements
(contributed by Tom Tromey; thanks Tom):
* document :py:data:`gcc.PLUGIN_FINISH_TYPE`
* document :py:class:`gcc.EnumeralType`; add 'values' attribute
* add unqualified_equivalent to gcc.Type subclasses
* preserve qualifiers when adding more qualifiers
* fix include for gcc 4.9.2
* handle variadic function types
gcc-python-plugin-0.15/docs/0.7.rst 0000664 0000000 0000000 00000026311 12660356443 0017010 0 ustar 00root root 0000000 0000000 0.7
~~~
This is a major update to the GCC Python plugin.
The main example script, cpychecker, has seen numerous improvements, and has
now detected many reference-counting bugs in real-world CPython extension code.
The usability and signal:noise ratio is greatly improved over previous releases.
Changes to the GCC Python Plugin
================================
It's now possible to create custom GCC attributes from Python, allowing you to
add custom high-level annotation to a C API, and to write scripts that will
verify these properties. It's also possible to inject preprocessor macros
from Python. Taken together, this allows code like this:
.. literalinclude:: ../tests/examples/attributes-with-macros/input.c
:lines: 22-45
:language: c
Other improvements:
* gcc's debug dump facilities are now exposed via a Python API
* it's now possible to run Python commands in GCC (rather than scripts) using
-fplugin-arg-python-command
* improvements to the "source location" when reporting on an unhandled
Python exception. Amongst other tweaks, it's now possible for a script to
override this, which the cpychecker uses, so that if it can't handle a
particular line of C code, the GCC error report gives that location before
reporting the Python traceback (making debugging much easier).
* "switch" statements are now properly wrapped at the Python level
(gcc.GimpleSwitch)
* C bitfields are now wrapped at the Python level
* gcc.Type instances now have a "sizeof" attribute, and an "attributes"
attribute.
* added a gcc.Gimple.walk_tree method, to make it easy to visit all nodes
relating to a statement
* added a new example: spell-checking all string literals in code
Improvements to "cpychecker"
============================
The "libcpychecker" Python code is a large example of using the plugin: it
extends GCC with code that tries to detect various bugs in CPython extension
modules.
The cpychecker analyzes the paths that can be followed through a C function,
and verifies various properties, including reference-count handling.
As of this release, the pass has found many reference-counting bugs in
real-world code. You can see a list of the bugs that it has detected at:
http://gcc-python-plugin.readthedocs.org/en/latest/success.html
The checker is now *almost* capable of fully handling the C code within the
gcc python plugin itself.
The checker has also been reorganized to (I hope) make it easy to add checking
for other libraries and APIs.
Major rewrite of reference-count tracking
-----------------------------------------
I've rewritten the internals of how reference counts are tracked: the code now
makes a distinction betweeen all of the reference that can be analysed within a
single function, versus all of the other references that may exist in the rest
of the program.
This allows us to know for an object e.g. that the function doesn't directly
own any references, but that the reference count is still > 0 (a "borrowed
reference"), as compared to the case where the function owns a reference, but
we don't know of any in the rest of the program (this is typical when receiving
a "new reference" e.g. from a function call to a constructor).
Within the reference-count checker, we now look for memory locations that
store references to objects. If those locations not on the stack, then the
references they store are now assumed to legally count towards the ob_refcnt
that the function "owns". This is needed in order to correctly handle e.g.
the PyList_SET_ITEM() macro, which directly writes to the list's ob_item field,
"stealing" a reference: we can detect these references, and count them towards
the ob_refcnt value.
The checker can now detect types that look like PyObject subclasses at the C
level (by looking at the top-most fields), and uses this information in various
places.
The checker now exposes custom GCC attributes allowing you to mark APIs that
have non-standard reference-handling behavior:
.. code-block:: c
PyObject *foo(void)
CPYCHECKER_RETURNS_BORROWED_REF;
extern void bar(int i, PyObject *obj, int j, PyObject *other)
CPYCHECKER_STEALS_REFERENCE_TO_ARG(2)
CPYCHECKER_STEALS_REFERENCE_TO_ARG(4);
It also exposes an attribute allowing you to the run-time and compile-time
type information for a Python extension class:
.. code-block:: c
/* Define some PyObject subclass, as both a struct and a typedef */
struct OurObjectStruct {
PyObject_HEAD
/* other fields */
};
typedef struct OurObjectStruct OurExtensionObject;
/*
Declare the PyTypeObject, using the custom attribute to associate it with
the typedef above:
*/
extern PyTypeObject UserDefinedExtension_Type
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("OurExtensionObject");
Function calls with NULL-pointer arguments
------------------------------------------
The checker knows about various CPython API hooks that will crash on NULL
pointer arguments, and will emit warnings when it can determine a path through
the code that will lead to a definite call with a NULL value.
Dereferences of uninitialized pointers
--------------------------------------
The checker will now complain about paths through a function for which it can
prove that an uninitialized pointer will be dereferenced.
Error-reporting improvements
----------------------------
The error-reporting machinery can generate HTML reports: see e.g.:
http://readthedocs.org/docs/gcc-python-plugin/en/latest/cpychecker.html#reference-count-checking
and http://dmalcolm.livejournal.com/6560.html
The checker can now annotate its HTML (and textual) reports with information
showing how some pertinent aspect of the program's state changes during a
particular path through a function.
For example, when reporting on reference-counting errors, the HTML report
showing the flow through the function will now display all changes to an object's
ob_refcnt, together with all changes to what the value ought to be (e.g. due to
pointers being stored to persistent memory locations):
.. figure:: sample-html-error-report.png
:alt: screenshot of the HTML report
Similarly, when reporting on exception-handling errors, it now displays the
"history" of changes to the thread-local exception state.
There's also a debug mode which dumps _everything_ that changes within the
report, which is helpful for debugging the checker itself.
The error report will attempt to use the most representative name for a leaked
object, using a variable name or a C expression fragment as appropriate.
The checker will attempt to combine duplicate error reports, so that it will
only emit one error for all of the various traces of execution that exhibit a
particular reference-counting bug.
Finally, when writing out an HTML report, the path to the HTML is now noted
within gcc's regular stderr messages.
Signal:noise ratio improvements
-------------------------------
To suppress various false-positives that I commonly ran into on real code, the
checker now makes certain assumptions:
* When encountering an unknown function that returns a PyObject*, the
checker assumes that it will either return a new reference to a sane
object (with a sane ob_type), or return NULL and set the thread-local
exception state.
* The checker assumes that a PyObject* argument to a function is non-NULL
and has a >0 refcount, and has a sane ob_type (e.g. with a sane refcount
and tp_dealloc)
* When dereferencing a pointer that it has no knowledge about (e.g. a
pointer field in a structure), the checker now assumes that it's
non-NULL, unless it knows that NULL is a definite possibility i.e. it
optimistically assumes that you know what you're doing (this could be
turned into a command-line option). Note that for the cases where we
know that the pointer can _definitely_ be NULL, an error will still be
reported (e.g. when considering the various possible return values for a
function known to be able to return NULL).
Coverage of the CPython API
---------------------------
I've gone through much of the CPython API, "teaching" the checker about the
reference-count semantics of each API call (and which calls will crash if fed a
NULL pointer). This involves writing a simple fragment of Python code for
each function, which describes the various different affects that the call can
have on the internal state within the callee.
This release adds support for calls to the following:
* _PyObject_New
* Py_{Initialize|Finalize}
* Py_InitModule4
* PyArg_ParseTuple[AndKeywords], and the PY_SSIZE_T_CLEAN variants (only
partial coverage so far: "O", "O!" should work though)
* PyArg_UnpackTuple
* PyBool_FromLong
* Py_BuildValue and the PY_SSIZE_T_CLEAN variant (only partial coverage so
far)
* PyDict_{GetItem,GetItemString,New,SetItem,SetItemString}
* PyErr_{Format,NoMemory,Occurred,Print,PrintEx,SetFromErrno[WithFilename],
SetObject,SetString}
* PyEval_InitThreads
* PyGILState_{Ensure,Release}
* PyImport_{AppendInittab,ImportModule}
* PyInt_{AsLong,FromLong}
* PyList_Append
* PyLong_{FromString,FromVoidPtr}
* PyMem_{Malloc,Free}
* PyModule_Add{IntConstant,Object,StringConstant}
* PyObject_{Call,CallMethod,HasAttrString,IsTrue,Repr,Str}
* PyRun_{SimpleFileExFlags,SimpleStringFlags}
* PySequence_GetItem
* PyString_{AsString,FromFormat,FromString,FromStringAndSize}
* PyStructSequence_{InitType,New}
* PySys_SetObject
* PyTuple_{New,SetItem,Size}
* PyType_{IsSubtype,Ready}
I've been targetting those API entrypoints that I use myself in the plugin;
this is one area which is particularly amenable to patching, for anyone who
wants to get involved. I've also added a (disabled) hook that complains
about Python API entrypoints that weren't explicitly handled, to make it easy
to find gaps in our coverage of the CPython API.
Other user-visible improvments
------------------------------
* There's now a "gcc-with-cpychecker" harness, to make it easier to invoke
GCC with the cpychecker code from e.g. Makefiles
* The checker now respects `__attribute((nonnull))` on function arguments
when detecting NULL pointers
* Handle functions that don't return (e.g. "exit")
* Number the unknown heap regions, to clarify things when there's more than
one
Internal improvements
---------------------
* The cpychecker now has some protection against combinatorial explosion
for functions that have very large numbers of possible routes through
them. For such functions, the checker will emit a note on stderr and
not attempt to find reference-counting bugs in the function.
* The cpychecker is now done as a custom pass (rather than by wiring up a
callback associated with every pass)
* I've tuned the logging within the checker, eliminating some CPU/memory
consumption issues seen when analysing complicated C code. In particular,
the log message arguments are now only expanded when logging is enabled
(previously this was happening all the time).
* Lots of other internal improvements and bug fixes (e.g. handling of arrays
vs pointers, static vs auto local variables, add missing handlers for
various kinds of C expression, lots of work on improving the readability of
error messages)
gcc-python-plugin-0.15/docs/0.8.rst 0000664 0000000 0000000 00000020776 12660356443 0017022 0 ustar 00root root 0000000 0000000 .. Copyright 2012 David Malcolm
Copyright 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
0.8
~~~
Thanks to David Narvaez and Tom Tromey for their code contributions to this
release.
Changes to the GCC Python Plugin
================================
Initial C++ support
-------------------
This release adds the beginnings of C++ support: :py:class:`gcc.FunctionDecl`
instances now have a "fullname" attribute, along with "is_public",
"is_private", "is_protected", "is_static" booleans.
For example, given this code:
.. code-block:: c++
namespace Example {
struct Coord {
int x;
int y;
};
class Widget {
public:
void set_location(const struct Coord& coord);
};
};
`set_location`'s fullname is::
'void Example::Widget::set_location(const Example::Coord&)'
This is only present when the plugin is invoked from the C++ frontend
(`cc1plus`), gracefully handling the case when we're invoked from other
language frontends.
Similarly, :py:class:`gcc.MethodType` has gained an "argument_types" attribute.
Unconditional warnings
----------------------
The :py:func:`gcc.warning()` function in previous versions of the plugin required an
"option" argument, such as ``gcc.Option('-Wformat')``
It's now possible to emit an unconditional warning, by supplying `None` for
this argument, which is now the default value::
gcc.warning(func.start, 'this is an unconditional warning')
.. code-block:: bash
$ ./gcc-with-python script.py input.c
input.c:25:1: warning: this is an unconditional warning [enabled by default]
which will be an error if `-Werror` is supplied as a command-line argument to gcc:
.. code-block:: bash
$ ./gcc-with-python script.py -Werror input.c
input.c:25:1: error: this is an unconditional warning [-Werror]
Improvements to :doc:`gcc-with-cpychecker `
========================================================
The :doc:`"libcpychecker" ` Python code is a large example of
using the plugin: it extends GCC with code that tries to detect various bugs
in CPython extension modules.
As of this release, all of the errors emitted by the tool have been converted
to warnings. This should make `gcc-with-cpychecker` more usable as a drop-in
replacement for `gcc`: the first source file with a refcounting error should
no longer terminate the build (unless the program uses ``-Werror``, of
course).
Verification of PyMethodDef tables
----------------------------------
This release adds checking of tables of PyMethodDef initialization values, used
by Python extension modules for binding C functions to Python methods.
The checker will verify that the signatures of the callbacks match the
flags, and that the such tables are NULL terminated::
input.c:48:22: warning: flags do not match callback signature for 'test' within PyMethodDef table
input.c:48:22: note: expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *)" (2 arguments)
input.c:48:22: note: actual type of underlying callback: struct PyObject * (struct PyObject *, struct PyObject *, struct PyObject *) (3 arguments)
input.c:48:22: note: see http://docs.python.org/c-api/structures.html#PyMethodDef
Coverage of the CPython API
---------------------------
When the checker warns about code that can erroneously pass ``NULL`` to
various CPython API entrypoints which are known to implicitly dereference
those arguments, the checker will now add an explanatory note about why it
is complaining.
For example::
input.c: In function 'test':
input.c:38:33: warning: calling PyString_AsString with NULL (gcc.VarDecl('repr_args')) as argument 1 at input.c:38
input.c:31:15: note: when PyObject_Repr() fails at: repr_args = PyObject_Repr(args);
input.c:38:33: note: PyString_AsString() invokes Py_TYPE() on the pointer via the PyString_Check() macro, thus accessing (NULL)->ob_type
input.c:27:1: note: graphical error report for function 'test' written out to 'input.c.test-refcount-errors.html'
The checker will now verify the argument lists of invocations of
`PyObject_CallFunctionObjArgs
`_ and
`PyObject_CallMethodObjArgs
`_,
checking that all of the arguments are of the correct type
(PyObject* or subclasses), and that the list is NULL-terminated::
input.c: In function 'test':
input.c:33:5: warning: argument 2 had type char[12] * but was expecting a PyObject* (or subclass)
input.c:33:5: warning: arguments to PyObject_CallFunctionObjArgs were not NULL-terminated
This release also adds heuristics for the behavior of the following CPython API
entrypoints:
* PyArg_Parse
* PyCObject_{As,From}VoidPtr
* PyCallable_Check
* PyCapsule_GetPointer
* PyErr_{NewException,SetNone,WarnEx}
* PyEval_CallObjectWithKeywords
* PyEval_{Save,Restore}Thread (and thus the Py_{BEGIN,END}_ALLOW_THREADS
macros)
* PyList_{GetItem,Size}
* PyLong_FromLongLong
* PyMapping_Size
* PyModule_GetDict
* PyObject_AsFileDescriptor
* PyObject_Call{Function,FunctionObjArgs,MethodObjArgs}
* PyObject_Generic{Get,Set}Attr
* PyString_Size
* PyTuple_Pack
* PyUnicode_AsUTF8String
* Py_AtExit
Bug fixes
---------
* gcc-with-cpychecker will now try harder on functions that are too
complicated to fully handle. Previously, when a function was too
complicated for the reference-count tracker to fully analyze, it would give
up, performing no analysis. The checker will now try to obtain at least
some subset of the list of all traces through the function, and analyze
those. It will still note that the function was too complicated to fully
analyze.
Given that we do a depth-first traversal of the tree, and that "success"
transitions are typically visited before "failure" transitions, this means
that it should at least analyze the trace in which all functions calls
succeed, together with traces in which some of the later calls fail.
* the reference-count checker now correctly handles "static" `PyObject*` local
variables: a `static PyObject *` local preserves its value from call to call,
and can thus permanently own a reference.
Fixes a false-positive seen in psycopg2-2.4.2
(`psycopg/psycopgmodule.c:psyco_GetDecimalType`)
where the refcount checker erroneously reported that a reference was leaked.
* the checker for Py_BuildValue("O") (and "S" and "N") was being too strict,
requiring a (PyObject*). Although it's not explicitly documented, it's
clear that these can also accept pointers to any PyObject subclass.
Fixes a false positive seen when running gcc-with-cpychecker on
coverage-3.5.1b1.tar.gz, in which `coverage/tracer.c:Tracer_trace` passes a
PyFrameObject* as an argument to such a call.
* the reference-count checker now correctly suppresses reports about "leaks"
for traces that call a function that never return (such as `abort()`).
Fixes a false positive seen in rpm-4.9.1.2 in a handler for fatal errors:
(in python/rpmts-py.c:die) where the checker erroneously reported that a
reference was leaked.
* `tp_iternext` callbacks are allowed to return NULL without setting an
exception. The reference-count checker will now notice if a function is
used in such a role, and suppress warnings about such behavior.
* fixed various Python tracebacks (tickets
`#14 `_,
`#19 `_,
`#20 `_,
`#22 `_,
`#23 `_,
`#24 `_,
`#25 `_)
* various other fixes
gcc-python-plugin-0.15/docs/0.9.rst 0000664 0000000 0000000 00000013573 12660356443 0017020 0 ustar 00root root 0000000 0000000 .. Copyright 2012 David Malcolm
Copyright 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
.. covers up to 4642a564e03c9e2c8114bca206205ad9c8fbc308
0.9
~~~
Changes to the GCC Python Plugin
================================
The plugin now works with GCC 4.7 prereleases (ticket `#21 `_).
The plugin is now integrated with GCC's garbage collector: Python wrapper
objects keep their underlying GCC objects alive when GCC's garbage collector
runs, preventing segfaults that could occur if the underlying objects were
swept away from under us
(ticket `#1 `_).
It's now possible to attach Python callbacks to more GCC events:
:py:data:`gcc.PLUGIN_FINISH`, :py:data:`gcc.PLUGIN_GGC_START`,
:py:data:`gcc.PLUGIN_GGC_MARKING`, :py:data:`gcc.PLUGIN_GGC_FINISH`,
:py:data:`gcc.PLUGIN_FINISH_DECL` (gcc 4.7)
:py:class:`gcc.ArrayType` has gained a "range" attribute, allowing scripts
to detect out-of-bounds conditions in array-handling.
A number of memory leaks were fixed: these were found by
`running the plugin on itself
`_.
Various documentation improvements
(ticket `#6 `_,
ticket `#31 `_).
Improvements to :doc:`gcc-with-cpychecker `
========================================================
The :doc:`gcc-with-cpychecker ` tool has received some deep
internal improvements in this release.
The logic for analyzing the outcome of comparisons has been rewritten for this
release, fixing some significant bugs that could lead to the analyzer
incorrectly deciding whether or not a block of code was reachable.
Similarly, the logic for detecting loops has been rewritten, elimininating a
bug in which the checker would prematurely stop analyzing loops with
complicated termination conditions, and not analyze the body of the loop.
Doing so extended the reach of the checker, and enabled it to find the memory
leaks referred to above.
In addition, the checker now emits more detailed information on the ranges of
possible values it's considering when a comparison occurs against an unknown
value::
input.c: In function 'test':
input.c:41:5: warning: comparison against uninitialized data (item) at input.c:41 [enabled by default]
input.c:34:12: note: when PyList_New() succeeds at: result = PyList_New(len);
input.c:35:8: note: taking False path at: if (!result) {
input.c:39:12: note: reaching: for (i = 0; i < len; i++) {
input.c:39:5: note: when considering range: 1 <= value <= 0x7fffffff at: for (i = 0; i < len; i++) {
input.c:39:5: note: taking True path at: for (i = 0; i < len; i++) {
input.c:41:5: note: reaching: if (!item) {
The checker should do a better job of identifying PyObject subclasses.
Previously it was treating any struct beginning with "ob_refcnt" and "ob_type"
as a Python object (with some tweaks for python 3 and debug builds). It now
also covers structs that begin with a field that's a PyObject (or subclass),
since these are likely to also be PyObject subclasses.
Usage of deallocated memory
---------------------------
Previously, the checker would warn about paths through a function that could
return a pointer to deallocated memory, or which tried to read through such
a pointer. With this release, the checker will now also warn about paths
through a function in which a pointer to deallocated memory is passed to a
function.
For example, given this buggy code:
.. code-block:: c
extern void some_function(PyObject *);
void
test(PyObject *self, PyObject *args)
{
/* Create an object: */
PyObject *tmp = PyLong_FromLong(0x1000);
if (!tmp) {
return;
}
/*
Now decref the object. Depending on what other references are owned
on the object, it can reach a refcount of zero, and thus be deallocated:
*/
Py_DECREF(tmp);
/* BUG: the object being returned may have been deallocated */
some_function(tmp);
}
the checker will emit this warning::
input.c: In function 'test':
input.c:45: warning: passing pointer to deallocated memory as argument 1 of function at input.c:45: memory deallocated at input.c:42 [enabled by default]
input.c:32: note: when PyLong_FromLong() succeeds at: PyObject *tmp = PyLong_FromLong(0x1000);
input.c:34: note: taking False path at: if (!tmp) {
input.c:42: note: reaching: Py_DECREF(tmp);
input.c:42: note: when taking False path at: Py_DECREF(tmp);
input.c:42: note: reaching: Py_DECREF(tmp);
input.c:42: note: calling tp_dealloc on PyLongObject allocated at input.c:32 at: Py_DECREF(tmp);
input.c:45: note: reaching: foo(tmp);
input.c:30: note: graphical error report for function 'passing_dead_object_to_function' written out to 'input.c.test-refcount-errors.html'
Coverage of the CPython API
---------------------------
This release adds heuristics for the behavior of the following CPython API
entrypoints:
* PyString_Concat
* PyString_ConcatAndDel
along with various other bugfixes and documentation improvements.
..
gcc-python-plugin-0.15/docs/Makefile 0000664 0000000 0000000 00000011620 12660356443 0017407 0 ustar 00root root 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make a Texinfo document"
@echo " info to make an info page"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/gcc-python-plugin.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/gcc-python-plugin.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/gcc-python-plugin"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/gcc-python-plugin"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo pages are in $(BUILDDIR)/texinfo."
info: texinfo
cd $(BUILDDIR)/texinfo && $(MAKE) info
@echo
@echo "Build finished. The info pages are in $(BUILDDIR)/texinfo."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
gcc-python-plugin-0.15/docs/appendices.rst 0000664 0000000 0000000 00000001600 12660356443 0020611 0 ustar 00root root 0000000 0000000 .. Copyright 2012 David Malcolm
Copyright 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
Appendices
==========
The following contain tables of reference material that may be useful
when writing scripts.
.. toctree::
tables-of-passes.rst
operators.rst
gcc-python-plugin-0.15/docs/attributes.rst 0000664 0000000 0000000 00000011121 12660356443 0020663 0 ustar 00root root 0000000 0000000 .. Copyright 2011 David Malcolm
Copyright 2011 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
Creating custom GCC attributes
==============================
GNU C supports a non-standard `__attribute__(()) syntax
`_ for marking
declarations with additional information that may be of interest to the
optimizer, and for checking the correctness of the code.
The GCC Python plugin allows you to create custom attributes, which may
be of use to your scripts: you can use this to annotate C code with additional
information. For example, you could create a custom attribute for functions
describing the interaction of a function on mutex objects:
.. code-block:: c
extern void some_function(void)
__attribute__((claims_mutex("io")));
extern void some_other_function(void)
__attribute__((releases_mutex("io")));
and use this in a custom code-checker.
Custom attributes can take string and integer parameters. For example, the
above custom attributes take a single string parameter. A custom attribute can
take more than one parameter, or none at all.
To create custom attributes from Python, you need to wire up a callback
response to the :py:data:`gcc.PLUGIN_ATTRIBUTES` event:
.. literalinclude:: ../tests/examples/attributes/script.py
:lines: 39-40
:language: python
This callback should then call :py:func:`gcc.register_attribute` to associate
the name of the attribute with a Python callback to be called when the
attribute is encountered in C code.
.. py:function:: gcc.register_attribute(name, min_length, max_length, \
decl_required, type_required, \
function_type_required, \
callable)
Registers a new GCC attribute with the given *name* , usable in C source
code via ``__attribute__(())``.
:param name: the name of the new attribute
:type name: str
:param min_length: the minimum number of arguments expected when the attribute is used
:type min_length: int
:param max_length: the maximum number of arguments expected when the
attribute is used (-1 for no maximum)
:type max_length: int
:param decl_required:
:type decl_required:
:param type_required:
:type type_required:
:param function_type_required:
:type function_type_required:
:param callable: the callback to be invoked when the attribute is seen
:type callable: a callable object, such as a function
In this example, we can simply print when the attribute is seen, to verify that
the callback mechanism is working:
.. literalinclude:: ../tests/examples/attributes/script.py
:lines: 22-36
:language: python
Putting it all together, here is an example Python script for the plugin:
.. literalinclude:: ../tests/examples/attributes/script.py
:lines: 18-
:language: python
Compiling this test C source file:
.. literalinclude:: ../tests/examples/attributes/input.c
:lines: 22-29
:language: c
leads to this output from the script:
.. literalinclude:: ../tests/examples/attributes/stdout.txt
:language: bash
Using the preprocessor to guard attribute usage
-----------------------------------------------
Unfortunately, the above C code will only work when it is compiled with the
Python script that adds the custom attributes.
You can avoid this by using :py:func:`gcc.define_macro()` to pre-define a
preprocessor name (e.g. "WITH_ATTRIBUTE_CLAIMS_MUTEX") at the same time as when
you define the attribute:
.. literalinclude:: ../tests/examples/attributes-with-macros/script.py
:lines: 18-
:language: python
This way the user can write this C code instead, and have it work both with
and without the Python script:
.. literalinclude:: ../tests/examples/attributes-with-macros/input.c
:lines: 22-45
:language: c
giving this output from the script:
.. literalinclude:: ../tests/examples/attributes-with-macros/stdout.txt
:language: bash
gcc-python-plugin-0.15/docs/basics.rst 0000664 0000000 0000000 00000041623 12660356443 0017753 0 ustar 00root root 0000000 0000000 .. Copyright 2011, 2012 David Malcolm
Copyright 2011, 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
.. For notes on how to document Python in RST form, see e.g.:
.. http://sphinx.pocoo.org/domains.html#the-python-domain
Requirements
============
The plugin has the following requirements:
* GCC: 4.6 or later (it uses APIs that weren't exposed to plugins in 4.5)
* Python: tested with 2.7 and 3.2; it may work with earlier versions
* "six": The libcpychecker code uses the "six" Python compatibility library to
smooth over Python 2 vs Python 3 differences, both at build-time and
run-time:
http://pypi.python.org/pypi/six/
* "pygments": The libcpychecker code uses the "pygments" Python
syntax-highlighting library when writing out error reports:
http://pygments.org/
* "lxml": The libcpychecker code uses the "lxml" internally when writing
out error reports.
* graphviz: many of the interesting examples use "dot" to draw diagrams
(e.g. control-flow graphs), so it's worth having graphviz installed.
Prebuilt-packages
=================
Various distributions ship with pre-built copies of the plugin. If you're
using Fedora, you can install the plugin via RPM on Fedora 16 onwards using:
.. code-block:: bash
yum install gcc-python2-plugin
as root for the Python 2 build of the plugin, or:
.. code-block:: bash
yum install gcc-python3-plugin
for the Python 3 build of the plugin.
On Gentoo, use `layman` to add the `dMaggot` overlay and `emerge` the
`gcc-python-plugin` package. This will build the plugin for Python 2 and
Python 3 should you have both of them installed in your system. A live
ebuild is also provided to install the plugin from git sources.
Building the plugin from source
===============================
Build-time dependencies
-----------------------
If you plan to build the plugin from scratch, you'll need the build-time
dependencies.
On a Fedora box you can install them by running the following as root:
.. code-block:: bash
yum install gcc-plugin-devel python-devel python-six python-pygments graphviz
for building against Python 2, or:
.. code-block:: bash
yum install gcc-plugin-devel python3-devel python3-six python3-pygments graphviz
when building for Python 3.
Building the code
------------------
You can obtain the source code from git by using::
$ git clone git://git.fedorahosted.org/gcc-python-plugin.git
To build the plugin, run:
.. code-block:: bash
make plugin
To build the plugin and run the selftests, run:
.. code-block:: bash
make
You can also use::
make demo
to demonstrate the new compiler errors.
By default, the `Makefile` builds the plugin using the first ``python-config``
tool found in `$PATH` (e.g. `/usr/bin/python-config`), which is typically the
system copy of Python 2. You can override this (e.g. to build against
Python 3) by overriding the `PYTHON` and `PYTHON_CONFIG` Makefile variables
with:
.. code-block:: bash
make PYTHON=python3 PYTHON_CONFIG=python3-config
There isn't a well-defined process yet for installing the plugin (though the
rpm specfile in the source tree contains some work-in-progress towards this).
Some notes on GCC plugins can be seen at http://gcc.gnu.org/wiki/plugins and
http://gcc.gnu.org/onlinedocs/gccint/Plugins.html
.. note:: Unfortunately, the layout of the header files for GCC plugin
development has changed somewhat between different GCC releases. In
particular, older builds of GCC flattened the "c-family" directory in the
installed plugin headers.
This was fixed in this GCC commit:
http://gcc.gnu.org/viewcvs?view=revision&revision=176741
So if you're using an earlier build of GCC using the old layout you'll need
to apply the following patch (reversed with "-R") to the plugin's source
tree to get it to compile:
.. code-block:: bash
$ git show 215730cbec40a6fe482fabb7f1ecc3d747f1b5d2 | patch -p1 -R
If you have a way to make the plugin's source work with either layout,
please email the plugin's `mailing list
`_
Basic usage of the plugin
=========================
Once you've built the plugin, you can invoke a Python script like this:
.. code-block:: bash
gcc -fplugin=./python.so -fplugin-arg-python-script=PATH_TO_SCRIPT.py OTHER_ARGS
and have it run your script as the plugin starts up.
Alternatively, you can run a one-shot Python command like this:
.. code-block:: bash
gcc -fplugin=./python.so -fplugin-arg-python-command="python code" OTHER_ARGS
such as:
.. code-block:: bash
gcc -fplugin=./python.so -fplugin-arg-python-command="import sys; print(sys.path)" OTHER_ARGS
The plugin automatically adds the absolute path to its own directory to the
end of its `sys.path`, so that it can find support modules, such as gccutils.py
and `libcpychecker`.
There is also a helper script, `gcc-with-python`, which expects a python script
as its first argument, then regular gcc arguments:
.. code-block:: bash
./gcc-with-python PATH_TO_SCRIPT.py other args follow
For example, this command will use graphviz to draw how GCC "sees" the
internals of each function in `test.c` (within its SSA representation):
.. code-block:: bash
./gcc-with-python examples/show-ssa.py test.c
Most of the rest of this document describes the Python API visible for
scripting.
The plugin GCC's various types as Python objects, within a "gcc" module. You
can see the API by running the following within a script::
import gcc
help(gcc)
To make this easier, there's a script to do this for you:
.. code-block:: bash
./gcc-python-docs
from where you can review the built-in documentation strings (this document
may be easier to follow though).
The exact API is still in flux: and may well change (this is an early version
of the code; we may have to change things as GCC changes in future releases
also).
Debugging your script
---------------------
You can place a forced breakpoint in your script using this standard Python
one-liner::
import pdb; pdb.set_trace()
If Python reaches this location it will interrupt the compile and put you
within the `pdb` interactive debugger, from where you can investigate.
See http://docs.python.org/library/pdb.html#debugger-commands for more
information.
If an exception occurs during Python code, and isn't handled by a try/except
before returning into the plugin, the plugin prints the traceback to stderr and
treats it as an error:
.. code-block:: pytb
/home/david/test.c: In function ‘main’:
/home/david/test.c:28:1: error: Unhandled Python exception raised within callback
Traceback (most recent call last):
File "test.py", line 38, in my_pass_execution_callback
dot = gccutils.tree_to_dot(fun)
NameError: global name 'gccutils' is not defined
(In this case, it was a missing `import` statement in the script)
GCC reports errors at a particular location within the source code. For an
unhandled exception such as the one above, by default, the plugin reports
the error as occurring as the top of the current source function (or the last
location within the current source file for passes and callbacks that aren't
associated with a function).
You can override this using gcc.set_location:
.. py:function:: gcc.set_location(loc)
Temporarily overrides the error-reporting location, so that if an exception
occurs, it will use this `gcc.Location`, rather than the default. This may
be of use when debugging tracebacks from scripts. The location is reset
each time after returning from Python back to the plugin, after printing
any traceback.
Accessing parameters
--------------------
.. py:data:: gcc.argument_dict
Exposes the arguments passed to the plugin as a dictionary.
For example, running:
.. code-block:: bash
gcc -fplugin=python.so \
-fplugin-arg-python-script=test.py \
-fplugin-arg-python-foo=bar
with `test.py` containing::
import gcc
print(gcc.argument_dict)
has output::
{'script': 'test.py', 'foo': 'bar'}
.. py:data:: gcc.argument_tuple
Exposes the arguments passed to the plugin as a tuple of (key, value) pairs,
so you have ordering. (Probably worth removing, and replacing
:py:data:`argument_dict` with an OrderedDict instead; what about
duplicate args though?)
Adding new passes to the compiler
---------------------------------
You can create new compiler passes by subclassing the appropriate
:py:class:`gcc.Pass` subclasss. For example, here's how to wire up a new pass
that displays the control flow graph of each function:
.. literalinclude:: ../examples/show-gimple.py
:lines: 19-
:language: python
For more information, see :ref:`creating-new-passes`
Wiring up callbacks
-------------------
The other way to write scripts is to register callback functions
to be called when various events happen during compilation, such as using
:py:data:`gcc.PLUGIN_PASS_EXECUTION` to piggyback off of an existing GCC pass.
.. literalinclude:: ../examples/show-passes.py
:lines: 19-
:language: python
For more information, see :ref:`callbacks`
Generating custom errors and warnings
=====================================
.. py:function:: gcc.warning(location, message, option=None)
Emits a compiler warning at the given :py:class:`gcc.Location`, potentially
controlled by a :py:class:`gcc.Option`.
If no option is supplied (or `None` is supplied), then the warning is an
unconditional one, always issued::
gcc.warning(func.start, 'this is an unconditional warning')
.. code-block:: bash
$ ./gcc-with-python script.py input.c
input.c:25:1: warning: this is an unconditional warning [enabled by default]
and will be an error if `-Werror` is supplied as a command-line argument to
GCC:
.. code-block:: bash
$ ./gcc-with-python script.py -Werror input.c
input.c:25:1: error: this is an unconditional warning [-Werror]
It's possible to associate the warning with a command-line option, so that
it is controlled by that option.
For example, given this Python code::
gcc.warning(func.start, 'Incorrect formatting', gcc.Option('-Wformat'))
if the given warning is enabled, a warning will be printed to stderr:
.. code-block:: bash
$ ./gcc-with-python script.py input.c
input.c:25:1: warning: incorrect formatting [-Wformat]
If the given warning is being treated as an error (through the usage
of `-Werror`), then an error will be printed:
.. code-block:: bash
$ ./gcc-with-python script.py -Werror input.c
input.c:25:1: error: incorrect formatting [-Werror=format]
cc1: all warnings being treated as errors
.. code-block:: bash
$ ./gcc-with-python script.py -Werror=format input.c
input.c:25:1: error: incorrect formatting [-Werror=format]
cc1: some warnings being treated as errors
If the given warning is disabled, the warning will not be printed:
.. code-block:: bash
$ ./gcc-with-python script.py -Wno-format input.c
.. note:: Due to the way GCC implements some options, it's not always
possible for the plugin to fully disable some warnings. See
:py:attr:`gcc.Option.is_enabled` for more information.
The function returns a boolean, indicating whether or not anything was
actually printed.
.. py:function:: gcc.error(location, message)
Emits a compiler error at the given :py:class:`gcc.Location`.
For example::
gcc.error(func.start, 'something bad was detected')
would lead to this error being printed to stderr:
.. code-block:: bash
$ ./gcc-with-python script.py input.c
input.c:25:1: error: something bad was detected
.. py:function:: gcc.permerror(loc, str)
This is a wrapper around GCC's `permerror` function.
Expects an instance of :py:class:`gcc.Location` (not None) and a string
Emit a "permissive" error at that location, intended for things that really
ought to be errors, but might be present in legacy code.
In theory it's suppressable using "-fpermissive" at the GCC command line
(which turns it into a warning), but this only seems to be legal for C++
source files.
Returns True if the warning was actually printed, False otherwise
.. py:function:: gcc.inform(loc, str)
This is a wrapper around GCC's `inform` function.
Expects an instance of :py:class:`gcc.Location` (not None) and a string
Emit an informational message at that location.
For example::
gcc.inform(stmt.loc, 'this is where X was defined')
would lead to this informational message being printed to stderr:
.. code-block:: bash
$ ./gcc-with-python script.py input.c
input.c:23:3: note: this is where X was defined
Global data access
==================
.. py:function:: gcc.get_variables()
Get all variables in this compilation unit as a list of
:py:class:`gcc.Variable`
.. py:class:: gcc.Variable
Wrapper around GCC's `struct varpool_node`, representing a variable in
the code being compiled.
.. py:attribute:: decl
The declaration of this variable, as a :py:class:`gcc.Tree`
.. py:function:: gccutils.get_variables_as_dict()
Get a dictionary of all variables, where the keys are the variable names
(as strings), and the values are instances of :py:class:`gcc.Variable`
.. py:function:: gcc.maybe_get_identifier(str)
Get the :py:class:`gcc.IdentifierNode` with this name, if it exists,
otherwise None. (However, after the front-end has run, the identifier
node may no longer point at anything useful to you; see
:py:func:`gccutils.get_global_typedef` for an example of working
around this)
.. py:function:: gcc.get_translation_units()
Get a list of all :py:class:`gcc.TranslationUnitDecl` for the compilation
units within this invocation of GCC (that's "source code files" for the
layperson).
.. py:class:: gcc.TranslationUnitDecl
Subclass of :py:class:`gcc.Tree` representing a compilation unit
.. py:attribute:: block
The :py:class:`gcc.Block` representing global scope within this
source file.
.. py:attribute:: language
The source language of this translation unit, as a string
(e.g. "GNU C")
.. py:function:: gcc.get_global_namespace()
C++ only: locate the :py:class:`gcc.NamespaceDecl` for the global
namespace (a.k.a. "::")
.. py:function:: gccutils.get_global_typedef(name)
Given a string `name`, look for a C/C++ `typedef` in global scope with
that name, returning it as a :py:class:`gcc.TypeDecl`, or None if it
wasn't found
.. py:function:: gccutils.get_global_vardecl_by_name(name)
Given a string `name`, look for a C/C++ variable in global scope with
that name, returning it as a :py:class:`gcc.VarDecl`, or None if it
wasn't found
.. py:function:: gccutils.get_field_by_name(decl, name)
Given one of a :py:class:`gcc.RecordType`, :py:class:`gcc.UnionType`, or
:py:class:`gcc.QualUnionType`, along with a string `name`, look for a
field with that name within the given struct or union, returning it as a
:py:class:`gcc.FieldDecl`, or None if it wasn't found
Working with source code
========================
.. py:function:: gccutils.get_src_for_loc(loc)
Given a :py:class:`gcc.Location`, get the source line as a string
(without trailing whitespace or newlines)
.. py:class:: gcc.Location
Wrapper around GCC's `location_t`, representing a location within the source
code. Use :py:func:`gccutils.get_src_for_loc` to get at the line of actual
source code.
The output from __repr__ looks like this::
gcc.Location(file='./src/test.c', line=42)
The output from__str__ looks like this::
./src/test.c:42
.. py:attribute:: file
(string) Name of the source file (or header file)
.. py:attribute:: line
(int) Line number within source file (starting at 1, not 0)
.. py:attribute:: column
(int) Column number within source file (starting at 1, not 0)
.. py:attribute:: in_system_header
(bool) This attribute flags locations that are within a system header
file. It may be of use when writing custom warnings, so that you
can filter out issues in system headers, leaving just those within
the user's code::
# Don't report on issues found in system headers:
if decl.location.in_system_header:
return
gcc-python-plugin-0.15/docs/callbacks.rst 0000664 0000000 0000000 00000024664 12660356443 0020434 0 ustar 00root root 0000000 0000000 .. Copyright 2012 David Malcolm
Copyright 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
.. _callbacks:
Working with callbacks
======================
One way to work with GCC from the Python plugin is via callbacks. It's possible
to register callback functions, which will be called when various events happen
during compilation.
For example, it's possible to piggyback off of an existing GCC pass by using
:py:data:`gcc.PLUGIN_PASS_EXECUTION` to piggyback off of an existing GCC pass.
.. py:function:: gcc.register_callback(event_id, function, [extraargs,] **kwargs)
Wire up a python function as a callback. It will be called when the given
event occurs during compilation. For some events, the callback will be
called just once; for other events, the callback is called once per
function within the source code being compiled. In the latter case, the
plugin passes a :py:class:`gcc.Function` instance as a parameter to your
callback, so that you can work on it::
import gcc
def my_pass_execution_callback(*args, **kwargs):
print('my_pass_execution_callback was called: args=%r kwargs=%r'
% (args, kwargs))
gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
my_pass_execution_callback)
The exact arguments passed to your callback vary: consult the documentation
for the particular event you are wiring up to (see below).
You can pass additional arguments when registering the callback - they will
be passed to the callback after any normal arguments. This is denoted in the
descriptions of events below by `*extraargs`.
You can also supply keyword arguments: they will be passed on as keyword
arguments to the callback. This is denoted in the description of events
below by `**kwargs`.
The various events are exposed as constants within the `gcc` module and
directly wrap GCC's plugin mechanism.
The following GCC events are currently usable from the Python plugin via
:py:func:`gcc.register_callback()`:
=============================================== =========
ID Meaning
=============================================== =========
:py:data:`gcc.PLUGIN_ATTRIBUTES` For :doc:`creating custom GCC attributes `
:py:data:`gcc.PLUGIN_PRE_GENERICIZE` For working with the AST in the C and C++ frontends
:py:data:`gcc.PLUGIN_PASS_EXECUTION` Called before each pass is executed
:py:data:`gcc.PLUGIN_FINISH_UNIT` At the end of working with a translation unit (aka source file)
:py:data:`gcc.PLUGIN_FINISH_TYPE` After a type has been parsed
:py:data:`gcc.PLUGIN_FINISH_DECL` After a declaration has been parsed (GCC 4.7 or later)
:py:data:`gcc.PLUGIN_FINISH` Called before GCC exits
=============================================== =========
.. py:data:: gcc.PLUGIN_ATTRIBUTES
Called when GCC is creating attributes for use with its non-standard
`__attribute__(()) syntax
`_.
If you want to create custom GCC attributes, you should register a callback
on this event and call :py:func:`gcc.register_attribute()` from within that
callback, so that they are created at the same time as the GCC's built-in
attributes.
No arguments are passed to your callback other than those that you supply
yourself when registering it:
(`*extraargs`, `**kwargs`)
See :doc:`creating custom GCC attributes ` for examples and
more information.
.. py:data:: gcc.PLUGIN_PASS_EXECUTION
Called when GCC is about to run one of its passes.
Arguments passed to the callback are:
(`ps`, `fun`, `*extraargs`, `**kwargs`)
where `ps` is a :py:class:`gcc.Pass` and `fun` is a :py:class:`gcc.Function`.
Your callback will typically be called many times: there are many passes,
and each can be invoked zero or more times per function (in the code being
compiled)
More precisely, some passes have a "gate check": the pass first checks a
condition, and only executes if the condition is true.
Any callback registered with `gcc.PLUGIN_PASS_EXECUTION` will get called
if this condition succeeds.
The actual work of the pass is done after the callbacks return.
In pseudocode::
if pass.has_gate_condition:
if !pass.test_gate_condition():
return
invoke_all_callbacks()
actually_do_the_pass()
For passes working on individual functions, all of the above is done
per-function.
To connect to a specific pass, you can simply add a conditional based on the
name of the pass::
import gcc
def my_callback(ps, fun):
if ps.name != '*warn_function_return':
# Not the pass we want
return
# Do something here
print(fun.decl.name)
gcc.register_callback(gcc.PLUGIN_PASS_EXECUTION,
my_callback)
.. py:data:: gcc.PLUGIN_PRE_GENERICIZE
Arguments passed to the callback are:
(`fndecl`, `*extraargs`, `**kwargs`)
where `fndecl` is a :py:class:`gcc.Tree` representing a function declaration
within the source code being compiled.
.. py:data:: gcc.PLUGIN_FINISH_UNIT
Called when GCC has finished compiling a particular translation unit.
Arguments passed to the callback are:
(`*extraargs`, `**kwargs`)
.. py:data:: gcc.PLUGIN_FINISH_DECL
.. note:: Only available in GCC 4.7 onwards.
Called when GCC has finished compiling a declaration (variables,
functions, parameters to functions, types, etc)
Arguments passed to the callback are:
(`decl`, `*extraargs`, `**kwargs`)
where `decl` is a :py:class:`gcc.Declaration`.
.. py:data:: gcc.PLUGIN_FINISH_TYPE
Called when GCC has finished parsing a type. Arguments to the
callback are:
(`type`, `*extraargs`, `**kwargs`)
where `type` is a :py:class:`gcc.Type`.
.. py:data:: gcc.PLUGIN_FINISH
Called before GCC exits.
Arguments passed to the callback are:
(`*extraargs`, `**kwargs`)
The remaining GCC events aren't yet usable from the plugin; an attempt to
register a callback on them will lead to an exception being raised. Email
the `gcc-python-plugin's mailing list
`_ if you're
interested in working with these):
=============================================== =========
ID Meaning
=============================================== =========
:py:data:`gcc.PLUGIN_PASS_MANAGER_SETUP` To hook into pass manager
:py:data:`gcc.PLUGIN_INFO` Information about the plugin
:py:data:`gcc.PLUGIN_GGC_START` For interacting with GCC's garbage collector
:py:data:`gcc.PLUGIN_GGC_MARKING` (ditto)
:py:data:`gcc.PLUGIN_GGC_END` (ditto)
:py:data:`gcc.PLUGIN_REGISTER_GGC_ROOTS` (ditto)
:py:data:`gcc.PLUGIN_REGISTER_GGC_CACHES` (ditto)
:py:data:`gcc.PLUGIN_START_UNIT` Called before processing a translation unit (aka source file)
:py:data:`gcc.PLUGIN_PRAGMAS` For registering pragmas
:py:data:`gcc.PLUGIN_ALL_PASSES_START` Called before the first pass of the :ref:`"all other passes" gcc.Pass catchall `
:py:data:`gcc.PLUGIN_ALL_PASSES_END` Called after last pass of the :ref:`"all other passes" gcc.Pass catchall `
:py:data:`gcc.PLUGIN_ALL_IPA_PASSES_START` Called before the first IPA pass
:py:data:`gcc.PLUGIN_ALL_IPA_PASSES_END` Called after last IPA pass
:py:data:`gcc.PLUGIN_OVERRIDE_GATE` Provides a way to disable a built-in pass
:py:data:`gcc.PLUGIN_EARLY_GIMPLE_PASSES_START`
:py:data:`gcc.PLUGIN_EARLY_GIMPLE_PASSES_END`
:py:data:`gcc.PLUGIN_NEW_PASS`
=============================================== =========
.. Notes on the other callback events
.. py:data:: gcc.PLUGIN_PRAGMAS
gcc_data=0x0
Called from: c_common_init () at ../../gcc/c-family/c-opts.c:1052
.. py:data:: gcc.PLUGIN_START_UNIT
gcc_data=0x0
Called from: compile_file () at ../../gcc/toplev.c:573
.. py:data:: gcc.PLUGIN_PRE_GENERICIZE
gcc_data is: tree fndecl;
Called from: finish_function () at ../../gcc/c-decl.c:8323
.. py:data:: gcc.PLUGIN_OVERRIDE_GATE
gcc_data::
&gate_status
bool gate_status;
Called from : execute_one_pass (pass=0x1011340) at ../../gcc/passes.c:1520
.. py:data:: gcc.PLUGIN_ALL_IPA_PASSES_START
gcc_data=0x0
Called from: ipa_passes () at ../../gcc/cgraphunit.c:1779
.. py:data:: gcc.PLUGIN_EARLY_GIMPLE_PASSES_START
gcc_data=0x0
Called from: execute_ipa_pass_list (pass=0x1011fa0) at ../../gcc/passes.c:1927
.. py:data:: gcc.PLUGIN_EARLY_GIMPLE_PASSES_END
gcc_data=0x0
Called from: execute_ipa_pass_list (pass=0x1011fa0) at ../../gcc/passes.c:1930
.. py:data:: gcc.PLUGIN_ALL_IPA_PASSES_END
gcc_data=0x0
Called from: ipa_passes () at ../../gcc/cgraphunit.c:1821
.. py:data:: gcc.PLUGIN_ALL_PASSES_START
gcc_data=0x0
Called from: tree_rest_of_compilation (fndecl=0x7ffff16b1f00) at ../../gcc/tree-optimize.c:420
.. py:data:: gcc.PLUGIN_ALL_PASSES_END
gcc_data=0x0
Called from: tree_rest_of_compilation (fndecl=0x7ffff16b1f00) at ../../gcc/tree-optimize.c:425
.. py:data:: gcc.PLUGIN_FINISH
gcc_data=0x0
Called from: toplev_main (argc=17, argv=0x7fffffffdfc8) at ../../gcc/toplev.c:1970
.. py:data:: gcc.PLUGIN_FINISH_TYPE
gcc_data=tree
Called from c_parser_declspecs (parser=0x7fffef559730, specs=0x15296d0, scspec_ok=1 '\001', typespec_ok=1 '\001', start_attr_ok=, la=cla_nonabstract_decl) at ../../gcc/c-parser.c:2111
.. py:data:: gcc.PLUGIN_PRAGMA
gcc_data=0x0
Called from: init_pragma at ../../gcc/c-family/c-pragma.c:1321
to "Allow plugins to register their own pragmas."
gcc-python-plugin-0.15/docs/callgraph.rst 0000664 0000000 0000000 00000004277 12660356443 0020450 0 ustar 00root root 0000000 0000000 .. Copyright 2011 David Malcolm
Copyright 2011 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
Interprocedural analysis (IPA)
==============================
GCC builds a "call graph", recording which functions call which other
functions, and it uses this for various optimizations.
It is constructed by the `"*build_cgraph_edges"` pass.
In case it's of interest, it is available via the following Python API:
.. py:function:: gcc.get_callgraph_nodes()
Get a list of all :py:class:`gcc.CallgraphNode` instances
.. py:function:: gccutils.callgraph_to_dot()
Return the GraphViz source for a rendering of the current callgraph, as a
string.
Here's an example of such a rendering:
.. figure:: sample-callgraph.png
:alt: image of a call graph
.. py:class:: gcc.CallgraphNode
.. py:attribute:: decl
The :py:class:`gcc.FunctionDecl` for this node within the callgraph
.. py:attribute:: callees
The function calls made by this function, as a list of :py:class:`gcc.CallgraphEdge` instances
.. py:attribute:: callers
The places that call this function, as a list of :py:class:`gcc.CallgraphEdge` instances
Internally, this wraps a `struct cgraph_node *`
.. py:class:: gcc.CallgraphEdge
.. py:attribute:: caller
The function that makes this call, as a :py:class:`gcc.CallgraphNode`
.. py:attribute:: callee
The function that is called here, as a :py:class:`gcc.CallgraphNode`
.. py:attribute:: call_stmt
The :py:class:`gcc.GimpleCall` statememt for the function call
Internally, this wraps a `struct cgraph_edge *`
gcc-python-plugin-0.15/docs/cfg.rst 0000664 0000000 0000000 00000012101 12660356443 0017233 0 ustar 00root root 0000000 0000000 .. Copyright 2011 David Malcolm
Copyright 2011 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
Working with functions and control flow graphs
==============================================
Many of the plugin events are called for each function within the source code
being compiled. Each time, the plugin passes a :py:class:`gcc.Function`
instance as a parameter to your callback, so that you can work on it.
You can get at the control flow graph of a :py:class:`gcc.Function` via its
``cfg`` attribute. This is an instance of :py:class:`gcc.Cfg`.
.. py:class:: gcc.Function
Wrapper around one of GCC's ``struct function *``
.. py:attribute:: cfg
An instance of :py:class:`gcc.Cfg` for this function (or None during early
passes)
.. py:attribute:: decl
The declaration of this function, as a :py:class:`gcc.FunctionDecl`
.. py:attribute:: local_decls
List of :py:class:`gcc.VarDecl` for the function's local variables. It
does not contain arguments; for those see the `arguments` property of
the function's `decl`.
Note that for locals with initializers, `initial` only seems to get set
on those `local_decls` that are static variables. For other locals, it
appears that you have to go into the gimple representation to locate
assignments.
.. py:attribute:: start
The :py:class:`gcc.Location` of the beginning of the function
.. py:attribute:: end
The :py:class:`gcc.Location` of the end of the function
.. py:attribute:: funcdef_no
Integer: a sequence number for profiling, debugging, etc.
.. py:class:: gcc.Cfg
A ``gcc.Cfg`` is a wrapper around GCC's `struct control_flow_graph`.
.. py:attribute:: basic_blocks
List of :py:class:`gcc.BasicBlock`, giving all of the basic blocks within
this CFG
.. py:attribute:: entry
Instance of :py:class:`gcc.BasicBlock`: the entrypoint for this CFG
.. py:attribute:: exit
Instance of :py:class:`gcc.BasicBlock`: the final one within this CFG
.. py:method:: get_block_for_label(labeldecl)
Given a :py:class:`gcc.LabelDecl`, get the corresponding
:py:class:`gcc.BasicBlock`
You can use ``gccutils.cfg_to_dot`` to render a gcc.Cfg as a graphviz
diagram. It will render the diagram, showing each basic block, with
source code on the left-hand side, interleaved with the "gimple"
representation on the right-hand side. Each block is labelled with its
index, and edges are labelled with appropriate flags.
For example, given this sample C code:
.. literalinclude:: ../test.c
:lines: 33-48
:language: c
then the following Python code::
dot = gccutils.cfg_to_dot(fun.cfg)
gccutils.invoke_dot(dot)
will render a CFG bitmap like this:
.. figure:: sample-gimple-cfg.png
:scale: 50 %
:alt: image of a control flow graph
.. py:class:: gcc.BasicBlock
A ``gcc.BasicBlock`` is a wrapper around GCC's `basic_block` type.
.. py:attribute:: index
The index of the block (an int), as seen in the cfg_to_dot rendering.
.. py:attribute:: preds
The list of predecessor :py:class:`gcc.Edge` instances leading into this
block
.. py:attribute:: succs
The list of successor :py:class:`gcc.Edge` instances leading out of this
block
.. py:attribute:: phi_nodes
The list of :py:class:`gcc.GimplePhi` phoney functions at the top of this
block, if appropriate for this pass, or None
.. py:attribute:: gimple
The list of :py:class:`gcc.Gimple` instructions, if appropriate for this
pass, or None
.. py:attribute:: rtl
The list of :py:class:`gcc.Rtl` expressions, if appropriate for this
pass, or None
.. py:class:: gcc.Edge
A wrapper around GCC's `edge` type.
.. py:attribute:: src
The source :py:class:`gcc.BasicBlock` of this edge
.. py:attribute:: dest
The destination :py:class:`gcc.BasicBlock` of this edge
.. py:attribute:: true_value
Boolean: `True` if this edge is taken when a :py:class:`gcc.GimpleCond`
conditional is true, `False` otherwise
.. py:attribute:: false_value
Boolean: `True` if this edge is taken when a :py:class:`gcc.GimpleCond`
conditional is false, `False` otherwise
.. py:attribute:: complex
Boolean: `True` if this edge is "special" e.g. due to
exception-handling, or some other kind of "strange" control flow transfer,
`False` otherwise
.. various other EDGE_ booleans, though it's not clear that they should be
documented
gcc-python-plugin-0.15/docs/conf.py 0000664 0000000 0000000 00000020036 12660356443 0017247 0 ustar 00root root 0000000 0000000 # Copyright 2011, 2012, 2013 David Malcolm
# Copyright 2011, 2012 Red Hat, Inc.
#
# This is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see
# .
# -*- coding: utf-8 -*-
#
# gcc-python-plugin documentation build configuration file, created by
# sphinx-quickstart on Wed Jun 1 15:53:48 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'gcc-python-plugin'
copyright = u'2011-2016, David Malcolm'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.15'
# The full version, including alpha/beta/rc tags.
release = '0.15'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'gcc-python-plugindoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'gcc-python-plugin.tex', u'gcc-python-plugin Documentation',
u'David Malcolm', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'gcc-python-plugin', u'gcc-python-plugin Documentation',
[u'David Malcolm'], 1),
('gcc-with-python', 'gcc-with-python', u'Run GCC whilst invoking a Python script',
[u'David Malcolm'], 1)
]
# -- Options for Texinfo output ------------------------------------------------
texinfo_documents = [
(master_doc, 'gcc-python-plugin', 'gcc-python-plugin Documentation',
'David Malcolm', 'gcc-python-plugin', 'gcc-python-plugin Documentation',
'Software development', False)
]
gcc-python-plugin-0.15/docs/cpychecker.rst 0000664 0000000 0000000 00000111032 12660356443 0020617 0 ustar 00root root 0000000 0000000 .. Copyright 2011, 2012 David Malcolm
Copyright 2011, 2012 Red Hat, Inc.
This is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
.. _cpychecker:
Usage example: a static analysis tool for CPython extension code
================================================================
.. note:: This code is under heavy development, and still contains bugs. It
is not unusual to see Python tracebacks when running the checker. You
should verify what the checker reports before acting on it: it could be
wrong.
An example of using the plugin is a static analysis tool I'm working on which
checks the C source of CPython extension modules for common coding errors.
This was one of my main motivations for writing the GCC plugin, and I often
need to extend the plugin to support this use case.
For this reason, the checker is embedded within the gcc-python source tree
itself for now:
* `gcc-with-cpychecker` is a harness script, which invokes GCC, adding
the arguments necessary to use the Python plugin, using the
`libcpychecker` Python code
* the `libcpychecker` subdirectory contains the code that does the actual
work
* various test cases (in the source tree, below `tests/cpychecker`)
gcc-with-cpychecker
-------------------
`gcc-with-cpychecker` is a harness script, which invokes GCC, adding
the arguments necessary to use the Python plugin, using the
`libcpychecker` Python code
You should be able to use the checker on arbitrary CPython extension
code by replacing "gcc" with "gcc-with-cpychecker" in your build with
something like::
make CC=/path/to/built/plugin/gcc-with-cpychecker
to override the Makefile variable `CC`.
You may need to supply an absolute path, especially if the "make" recursively
invokes "make" within subdirectories (thus having a different working
directory).
Similarly, for projects that use `distutils
`_, the code is typically built
with an invocation like this::
python setup.py build
This respects the environment variable `CC`, so typically you can replace the
above with something like this in order to add the additional checks::
CC=/path/to/built/plugin/gcc-with-cpychecker python setup.py build
Additional arguments for `gcc-with-cpychecker`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. program:: gcc-with-cpychecker
.. cmdoption:: --maxtrans
Set the maximum number of transitions to consider within each function
before pruning the analysis tree. You may need to increase this limit
for complicated functions.
.. cmdoption:: --dump-json
Dump a JSON representation of any problems. For example, given a function
`foo.c`, if any warnings or errors are found in function `bar`, a file
`foo.c.bar.json` will be written out in JSON form.
Reference-count checking
------------------------
The checker attempts to analyze all possible paths through each function,
tracking the various ``PyObject*`` objects encountered.
For each path through the function and ``PyObject*``, it determines what the
reference count ought to be at the end of the function, issuing warnings for
any that are incorrect.
The warnings are in two forms: the classic textual output to GCC's standard
error stream, together with an HTML report indicating the flow through the
function, in graphical form.
For example, given this buggy C code:
.. code-block:: c
PyObject *
test(PyObject *self, PyObject *args)
{
PyObject *list;
PyObject *item;
list = PyList_New(1);
if (!list)
return NULL;
item = PyLong_FromLong(42);
/* This error handling is incorrect: it's missing an
invocation of Py_DECREF(list): */
if (!item)
return NULL;
/* This steals a reference to item; item is not leaked when we get here: */
PyList_SetItem(list, 0, item);
return list;
}
the checker emits these messages to stderr::
input.c: In function 'test':
input.c:38:1: warning: ob_refcnt of '*list' is 1 too high [enabled by default]
input.c:38:1: note: was expecting final ob_refcnt to be N + 0 (for some unknown N)
input.c:38:1: note: but final ob_refcnt is N + 1
input.c:27:10: note: PyListObject allocated at: list = PyList_New(1);
input.c:27:10: note: when PyList_New() succeeds at: list = PyList_New(1);
input.c:27:10: note: ob_refcnt is now refs: 1 + N where N >= 0
input.c:28:8: note: taking False path at: if (!list)
input.c:30:10: note: reaching: item = PyLong_FromLong(42);
input.c:30:10: note: when PyLong_FromLong() fails at: item = PyLong_FromLong(42);
input.c:33:8: note: taking True path at: if (!item)
input.c:34:9: note: reaching: return NULL;
input.c:38:1: note: returning
input.c:24:1: note: graphical error report for function 'test' written out to 'input.c.test-refcount-errors.html'
along with this HTML report (as referred to by the final line on stderr):
.. figure:: sample-html-error-report.png
:alt: screenshot of the HTML report
The HTML report is intended to be relatively self-contained, and thus easy to
attach to bug tracking systems (it embeds its own CSS inline, and references
the JavaScript it uses via URLs to the web).
.. note:: The arrow graphics in the HTML form of the report are added by using
the JSPlumb JavaScript library to generate HTML 5