From: Julian Seward Date: Tue, 19 Oct 2004 13:11:35 +0000 (+0000) Subject: Add a complete copy of the cvs head as of 19 Oct 04, for experimenting X-Git-Tag: svn/VALGRIND_3_0_1^2~965 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=074ebfd812535fa35bf10ac53799460306a99ea0;p=thirdparty%2Fvalgrind.git Add a complete copy of the cvs head as of 19 Oct 04, for experimenting with integration purposes. git-svn-id: svn://svn.valgrind.org/vex/trunk@369 --- diff --git a/VEX/head20041019/.cvsignore b/VEX/head20041019/.cvsignore new file mode 100644 index 0000000000..b6b5af993f --- /dev/null +++ b/VEX/head20041019/.cvsignore @@ -0,0 +1,22 @@ +Makefile.in +Makefile +acinclude.m4 +aclocal.m4 +configure +config.h* +stamp-h* +valgrind +valgrind.spec +cachegrind +vg_annotate +vg_cachegen +default.supp +bin +lib +include +share +cachegrind.out.* +autom4te.cache +autom4te-*.cache +valgrind.pc +.in_place diff --git a/VEX/head20041019/ACKNOWLEDGEMENTS b/VEX/head20041019/ACKNOWLEDGEMENTS new file mode 100644 index 0000000000..36317308e1 --- /dev/null +++ b/VEX/head20041019/ACKNOWLEDGEMENTS @@ -0,0 +1,26 @@ + +The following people contributed in some way to valgrind, during its +long journey over the past two years or so. Here's a list. If I have +forgotten you, I do apologise; let me know (jseward@acm.org) and I'll +fix it. + +Donna Robinson + for many reasons, including endless encouragement, and + persuading me I wasn't crazy to try doing this + +Rob Noble + for early encouragement, support, suggestions, and asking of + many questions + +Reuben Thomas + for discussions about value tag operations, and making me + laugh + +Various KDE folks, for suffering recent versions of valgrind, + providing many patches, questions and helpful feedback + Dirk Mueller + Stephan Kulow + Michael Matz + Simon Hausmann + David Faure + Ellis Whitehead diff --git a/VEX/head20041019/AUTHORS b/VEX/head20041019/AUTHORS new file mode 100644 index 0000000000..725f4620b2 --- /dev/null +++ b/VEX/head20041019/AUTHORS @@ -0,0 +1,32 @@ + +Julian Seward, jseward@acm.org, was the original author, creating the +dynamic translation framework, memcheck stuff, and the +signal/syscall/threads support gunk. + +Nicholas Nethercote, njn25@cam.ac.uk, did the core/tool +generalisation, and wrote Cachegrind and some of the other tools, and +tons of other stuff, including code generation improvments. + +Jeremy Fitzhardinge, jeremy@goop.org, wrote Helgrind, and lots of +syscall/signal simulation stuff, including a complete redesign of how +syscalls and signals are handled. Also code generation improvements. + +Tom Hughes, thh@cyberscience.com, did a vast number of bug fixes, and +helped out with support for more recent Linux/glibc versions. + +Robert Walsh, rjwalsh@durables.org, added file descriptor leakage +checking, new library interception machinery, support for client +allocation pools, and minor other tweakage. + +readelf's dwarf2 source line reader, written by Nick Clifton, was +modified to be used in Valgrind by Daniel Berlin. + +Michael Matz and Simon Hausmann modified the GNU binutils +demangler(s) for use in Valgrind. + +Dirk Mueller contrib'd the malloc-free mismatch checking stuff, +and other bits and pieces. + +Lots of other people sent bug reports, patches, and very +helpful feedback. I thank you all. + diff --git a/VEX/head20041019/COPYING b/VEX/head20041019/COPYING new file mode 100644 index 0000000000..d60c31a97a --- /dev/null +++ b/VEX/head20041019/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE 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. + + 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 +convey 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision 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, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This 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 Library General +Public License instead of this License. diff --git a/VEX/head20041019/CVS/Entries b/VEX/head20041019/CVS/Entries new file mode 100644 index 0000000000..76f702d787 --- /dev/null +++ b/VEX/head20041019/CVS/Entries @@ -0,0 +1,43 @@ +/.cvsignore/1.7/Tue Mar 23 19:52:03 2004// +/ACKNOWLEDGEMENTS/1.1.1.1/Fri Mar 22 01:29:20 2002// +/AUTHORS/1.8/Tue Jul 20 14:18:51 2004// +/COPYING/1.1.1.1/Fri Mar 22 01:27:57 2002// +/FAQ.txt/1.23/Sun Jul 18 10:35:36 2004// +/INSTALL/1.1.1.1/Fri Mar 22 01:28:00 2002// +/Makefile.all.am/1.1/Wed Sep 1 23:20:46 2004// +/Makefile.am/1.71/Sat Oct 9 15:59:05 2004// +/Makefile.core-AM_CPPFLAGS.am/1.3/Fri Sep 10 14:23:58 2004// +/Makefile.tool-flags.am/1.2/Sat Sep 11 18:27:43 2004// +/Makefile.tool-inplace.am/1.1/Wed Sep 1 23:20:46 2004// +/Makefile.tool.am/1.3/Sat Sep 11 16:45:24 2004// +/NEWS/1.26/Tue Aug 31 00:14:02 2004// +/NOTES.syscalls/1.1/Mon Oct 13 22:26:54 2003// +/README/1.19/Wed Apr 21 09:17:19 2004// +/README_DEVELOPERS/1.3/Sat Oct 9 15:59:05 2004// +/README_MISSING_SYSCALL_OR_IOCTL/1.8/Fri Sep 10 14:23:58 2004// +/README_PACKAGERS/1.5/Tue Aug 24 13:56:54 2004// +/TODO/1.2/Tue Jun 18 16:31:21 2002// +/autogen.sh/1.3/Tue Dec 16 02:15:21 2003// +/configure.in/1.129/Mon Oct 18 18:07:48 2004// +/glibc-2.1.supp/1.11/Sun Apr 25 12:02:31 2004// +/glibc-2.2.supp/1.25/Sat Jul 17 14:16:03 2004// +/glibc-2.3.supp/1.15/Tue Jul 20 22:42:44 2004// +/make-uninstall-docs/1.2/Fri Nov 14 17:47:51 2003// +/valgrind.pc.in/1.2/Thu Oct 14 10:22:19 2004// +/valgrind.spec.in/1.16/Fri Sep 3 13:45:26 2004// +/xfree-3.supp/1.6/Fri Nov 14 17:47:51 2003// +/xfree-4.supp/1.9/Fri Nov 14 17:47:51 2003// +D/addrcheck//// +D/auxprogs//// +D/cachegrind//// +D/corecheck//// +D/coregrind//// +D/docs//// +D/helgrind//// +D/include//// +D/lackey//// +D/massif//// +D/memcheck//// +D/nightly//// +D/none//// +D/tests//// diff --git a/VEX/head20041019/CVS/Repository b/VEX/head20041019/CVS/Repository new file mode 100644 index 0000000000..e7af412919 --- /dev/null +++ b/VEX/head20041019/CVS/Repository @@ -0,0 +1 @@ +valgrind diff --git a/VEX/head20041019/CVS/Root b/VEX/head20041019/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/CVS/Template b/VEX/head20041019/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/FAQ.txt b/VEX/head20041019/FAQ.txt new file mode 100644 index 0000000000..26a31d67df --- /dev/null +++ b/VEX/head20041019/FAQ.txt @@ -0,0 +1,437 @@ +Valgrind FAQ, version 2.1.2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Last revised 18 July 2004 +~~~~~~~~~~~~~~~~~~~~~~~~~ + +1. Background +2. Compiling, installing and configuring +3. Valgrind aborts unexpectedly +4. Valgrind behaves unexpectedly +5. Memcheck doesn't find my bug +6. Miscellaneous + + +----------------------------------------------------------------- +1. Background +----------------------------------------------------------------- + +1.1. How do you pronounce "Valgrind"? + +The "Val" as in the world "value". The "grind" is pronounced with a +short 'i' -- ie. "grinned" (rhymes with "tinned") rather than "grined" +(rhymes with "find"). + +Don't feel bad: almost everyone gets it wrong at first. + +----------------------------------------------------------------- + +1.2. Where does the name "Valgrind" come from? + +From Nordic mythology. Originally (before release) the project was +named Heimdall, after the watchman of the Nordic gods. He could "see a +hundred miles by day or night, hear the grass growing, see the wool +growing on a sheep's back" (etc). This would have been a great name, +but it was already taken by a security package "Heimdal". + +Keeping with the Nordic theme, Valgrind was chosen. Valgrind is the +name of the main entrance to Valhalla (the Hall of the Chosen Slain in +Asgard). Over this entrance there resides a wolf and over it there is +the head of a boar and on it perches a huge eagle, whose eyes can see to +the far regions of the nine worlds. Only those judged worthy by the +guardians are allowed to pass through Valgrind. All others are refused +entrance. + +It's not short for "value grinder", although that's not a bad guess. + + +----------------------------------------------------------------- +2. Compiling, installing and configuring +----------------------------------------------------------------- + +2.1. When I trying building Valgrind, 'make' dies partway with an + assertion failure, something like this: make: expand.c:489: + + allocated_variable_append: Assertion + `current_variable_set_list->next != 0' failed. + +It's probably a bug in 'make'. Some, but not all, instances of version 3.79.1 +have this bug, see www.mail-archive.com/bug-make@gnu.org/msg01658.html. Try +upgrading to a more recent version of 'make'. Alternatively, we have heard +that unsetting the CFLAGS environment variable avoids the problem. + + +----------------------------------------------------------------- +3. Valgrind aborts unexpectedly +----------------------------------------------------------------- + +3.1. Programs run OK on Valgrind, but at exit produce a bunch of errors a bit + like this + + ==20755== Invalid read of size 4 + ==20755== at 0x40281C8A: _nl_unload_locale (loadlocale.c:238) + ==20755== by 0x4028179D: free_mem (findlocale.c:257) + ==20755== by 0x402E0962: __libc_freeres (set-freeres.c:34) + ==20755== by 0x40048DCC: vgPlain___libc_freeres_wrapper + (vg_clientfuncs.c:585) + ==20755== Address 0x40CC304C is 8 bytes inside a block of size 380 free'd + ==20755== at 0x400484C9: free (vg_clientfuncs.c:180) + ==20755== by 0x40281CBA: _nl_unload_locale (loadlocale.c:246) + ==20755== by 0x40281218: free_mem (setlocale.c:461) + ==20755== by 0x402E0962: __libc_freeres (set-freeres.c:34) + + and then die with a segmentation fault. + +When the program exits, Valgrind runs the procedure __libc_freeres() in +glibc. This is a hook for memory debuggers, so they can ask glibc to +free up any memory it has used. Doing that is needed to ensure that +Valgrind doesn't incorrectly report space leaks in glibc. + +Problem is that running __libc_freeres() in older glibc versions causes +this crash. + +WORKAROUND FOR 1.1.X and later versions of Valgrind: use the +--run-libc-freeres=no flag. You may then get space leak reports for +glibc-allocations (please _don't_ report these to the glibc people, +since they are not real leaks), but at least the program runs. + +----------------------------------------------------------------- + +3.2. My (buggy) program dies like this: + valgrind: vg_malloc2.c:442 (bszW_to_pszW): + Assertion `pszW >= 0' failed. + +If Memcheck (the memory checker) shows any invalid reads, invalid writes +and invalid frees in your program, the above may happen. Reason is that +your program may trash Valgrind's low-level memory manager, which then +dies with the above assertion, or something like this. The cure is to +fix your program so that it doesn't do any illegal memory accesses. The +above failure will hopefully go away after that. + +----------------------------------------------------------------- + +3.3. My program dies, printing a message like this along the way: + + disInstr: unhandled instruction bytes: 0x66 0xF 0x2E 0x5 + +Older versions did not support some x86 instructions, particularly +SSE/SSE2 instructions. Try a newer Valgrind; we now support almost all +instructions. If it still happens with newer versions, if the failing +instruction is an SSE/SSE2 instruction, you might be able to recompile +your program without it by using the flag -march to gcc. Either way, +let us know and we'll try to fix it. + +Another possibility is that your program has a bug and erroneously jumps +to a non-code address, in which case you'll get a SIGILL signal. +Memcheck/Addrcheck may issue a warning just before this happens, but they +might not if the jump happens to land in addressable memory. + +----------------------------------------------------------------- + +3.4. My program dies like this: + + error: /lib/librt.so.1: symbol __pthread_clock_settime, version + GLIBC_PRIVATE not defined in file libpthread.so.0 with link time + reference + +This is a total swamp. Nevertheless there is a way out. It's a problem +which is not easy to fix. Really the problem is that /lib/librt.so.1 +refers to some symbols __pthread_clock_settime and +__pthread_clock_gettime in /lib/libpthread.so which are not intended to +be exported, ie they are private. + +Best solution is to ensure your program does not use /lib/librt.so.1. + +However .. since you're probably not using it directly, or even +knowingly, that's hard to do. You might instead be able to fix it by +playing around with coregrind/vg_libpthread.vs. Things to try: + +Remove this + + GLIBC_PRIVATE { + __pthread_clock_gettime; + __pthread_clock_settime; + }; + +or maybe remove this + + GLIBC_2.2.3 { + __pthread_clock_gettime; + __pthread_clock_settime; + } GLIBC_2.2; + +or maybe add this + + GLIBC_2.2.4 { + __pthread_clock_gettime; + __pthread_clock_settime; + } GLIBC_2.2; + + GLIBC_2.2.5 { + __pthread_clock_gettime; + __pthread_clock_settime; + } GLIBC_2.2; + +or some combination of the above. After each change you need to delete +coregrind/libpthread.so and do make && make install. + +I just don't know if any of the above will work. If you can find a +solution which works, I would be interested to hear it. + +To which someone replied: + + I deleted this: + + GLIBC_2.2.3 { + __pthread_clock_gettime; + __pthread_clock_settime; + } GLIBC_2.2; + + and it worked. + + +----------------------------------------------------------------- +4. Valgrind behaves unexpectedly +----------------------------------------------------------------- + +4.1. I try running "valgrind my_program", but my_program runs normally, + and Valgrind doesn't emit any output at all. + +For versions prior to 2.1.1: + +Valgrind doesn't work out-of-the-box with programs that are entirely +statically linked. It does a quick test at startup, and if it detects +that the program is statically linked, it aborts with an explanation. + +This test may fail in some obscure cases, eg. if you run a script under +Valgrind and the script interpreter is statically linked. + +If you still want static linking, you can ask gcc to link certain +libraries statically. Try the following options: + + -Wl,-Bstatic -lmyLibrary1 -lotherLibrary -Wl,-Bdynamic + +Just make sure you end with -Wl,-Bdynamic so that libc is dynamically +linked. + +If you absolutely cannot use dynamic libraries, you can try statically +linking together all the .o files in coregrind/, all the .o files of the +tool of your choice (eg. those in memcheck/), and the .o files of your +program. You'll end up with a statically linked binary that runs +permanently under Valgrind's control. Note that we haven't tested this +procedure thoroughly. + + +For versions 2.1.1 and later: + +Valgrind does now work with static binaries, although beware that some +of the tools won't operate as well as normal, because they have access +to less information about how the program runs. Eg. Memcheck will miss +some errors that it would otherwise find. This is because Valgrind +doesn't replace malloc() and friends with its own versions. It's best +if your program is dynamically linked with glibc. + +----------------------------------------------------------------- + +4.2. My threaded server process runs unbelievably slowly on Valgrind. + So slowly, in fact, that at first I thought it had completely + locked up. + +We are not completely sure about this, but one possibility is that +laptops with power management fool Valgrind's timekeeping mechanism, +which is (somewhat in error) based on the x86 RDTSC instruction. A +"fix" which is claimed to work is to run some other cpu-intensive +process at the same time, so that the laptop's power-management +clock-slowing does not kick in. We would be interested in hearing more +feedback on this. + +Another possible cause is that versions prior to 1.9.6 did not support +threading on glibc 2.3.X systems well. Hopefully the situation is much +improved with 1.9.6 and later versions. + +----------------------------------------------------------------- + +4.3. My program uses the C++ STL and string classes. Valgrind + reports 'still reachable' memory leaks involving these classes + at the exit of the program, but there should be none. + +First of all: relax, it's probably not a bug, but a feature. Many +implementations of the C++ standard libraries use their own memory pool +allocators. Memory for quite a number of destructed objects is not +immediately freed and given back to the OS, but kept in the pool(s) for +later re-use. The fact that the pools are not freed at the exit() of +the program cause Valgrind to report this memory as still reachable. +The behaviour not to free pools at the exit() could be called a bug of +the library though. + +Using gcc, you can force the STL to use malloc and to free memory as +soon as possible by globally disabling memory caching. Beware! Doing +so will probably slow down your program, sometimes drastically. + +- With gcc 2.91, 2.95, 3.0 and 3.1, compile all source using the STL + with -D__USE_MALLOC. Beware! This is removed from gcc starting with + version 3.3. + +- With 3.2.2 and later, you should export the environment variable + GLIBCPP_FORCE_NEW before running your program. + +There are other ways to disable memory pooling: using the malloc_alloc +template with your objects (not portable, but should work for gcc) or +even writing your own memory allocators. But all this goes beyond the +scope of this FAQ. Start by reading +http://gcc.gnu.org/onlinedocs/libstdc++/ext/howto.html#3 if you +absolutely want to do that. But beware: + +1) there are currently changes underway for gcc which are not totally + reflected in the docs right now ("now" == 26 Apr 03) + +2) allocators belong to the more messy parts of the STL and people went + at great lengths to make it portable across platforms. Chances are + good that your solution will work on your platform, but not on + others. + +----------------------------------------------------------------------------- +4.4. The stack traces given by Memcheck (or another tool) aren't helpful. + How can I improve them? + +If they're not long enough, use --num-callers to make them longer. + +If they're not detailed enough, make sure you are compiling with -g to add +debug information. And don't strip symbol tables (programs should be +unstripped unless you run 'strip' on them; some libraries ship stripped). + +Also, -fomit-frame-pointer and -fstack-check can make stack traces worse. + +Some example sub-traces: + + With debug information and unstripped (best): + + Invalid write of size 1 + at 0x80483BF: really (malloc1.c:20) + by 0x8048370: main (malloc1.c:9) + + With no debug information, unstripped: + + Invalid write of size 1 + at 0x80483BF: really (in /auto/homes/njn25/grind/head5/a.out) + by 0x8048370: main (in /auto/homes/njn25/grind/head5/a.out) + + With no debug information, stripped: + + Invalid write of size 1 + at 0x80483BF: (within /auto/homes/njn25/grind/head5/a.out) + by 0x8048370: (within /auto/homes/njn25/grind/head5/a.out) + by 0x42015703: __libc_start_main (in /lib/tls/libc-2.3.2.so) + by 0x80482CC: (within /auto/homes/njn25/grind/head5/a.out) + + With debug information and -fomit-frame-pointer: + + Invalid write of size 1 + at 0x80483C4: really (malloc1.c:20) + by 0x42015703: __libc_start_main (in /lib/tls/libc-2.3.2.so) + by 0x80482CC: ??? (start.S:81) + +----------------------------------------------------------------- +5. Memcheck doesn't find my bug +----------------------------------------------------------------- + +5.1. I try running "valgrind --tool=memcheck my_program" and get + Valgrind's startup message, but I don't get any errors and I know + my program has errors. + +By default, Valgrind only traces the top-level process. So if your +program spawns children, they won't be traced by Valgrind by default. +Also, if your program is started by a shell script, Perl script, or +something similar, Valgrind will trace the shell, or the Perl +interpreter, or equivalent. + +To trace child processes, use the --trace-children=yes option. + +If you are tracing large trees of processes, it can be less disruptive +to have the output sent over the network. Give Valgrind the flag +--log-socket=127.0.0.1:12345 (if you want logging output sent to port +12345 on localhost). You can use the valgrind-listener program to +listen on that port: + + valgrind-listener 12345 + +Obviously you have to start the listener process first. See the +documentation for more details. + +----------------------------------------------------------------- + +5.2. Why doesn't Memcheck find the array overruns in this program? + + int static[5]; + + int main(void) + { + int stack[5]; + + static[5] = 0; + stack [5] = 0; + + return 0; + } + +Unfortunately, Memcheck doesn't do bounds checking on static or stack +arrays. We'd like to, but it's just not possible to do in a reasonable +way that fits with how Memcheck works. Sorry. + +----------------------------------------------------------------- + +5.3. My program dies with a segmentation fault, but Memcheck doesn't give + any error messages before it, or none that look related. + +One possibility is that your program accesses to memory with +inappropriate permissions set, such as writing to read-only memory. +Maybe your program is writing to a static string like this: + + char* s = "hello"; + s[0] = 'j'; + +or something similar. Writing to read-only memory can also apparently +make LinuxThreads behave strangely. + + +----------------------------------------------------------------- +6. Miscellaneous +----------------------------------------------------------------- + +6.1. I tried writing a suppression but it didn't work. Can you + write my suppression for me? + +Yes! Use the --gen-suppressions=yes feature to spit out suppressions +automatically for you. You can then edit them if you like, eg. +combining similar automatically generated suppressions using wildcards +like '*'. + +If you really want to write suppressions by hand, read the manual +carefully. Note particularly that C++ function names must be _mangled_. + +----------------------------------------------------------------- + +6.2. With Memcheck/Addrcheck's memory leak detector, what's the + difference between "definitely lost", "possibly lost", "still + reachable", and "suppressed"? + +The details are in section 3.6 of the manual. + +In short: + + - "definitely lost" means your program is leaking memory -- fix it! + + - "possibly lost" means your program is probably leaking memory, + unless you're doing funny things with pointers. + + - "still reachable" means your program is probably ok -- it didn't + free some memory it could have. This is quite common and often + reasonable. Don't use --show-reachable=yes if you don't want to see + these reports. + + - "suppressed" means that a leak error has been suppressed. There are + some suppressions in the default suppression files. You can ignore + suppressed errors. + +----------------------------------------------------------------- + +(this is the end of the FAQ.) diff --git a/VEX/head20041019/INSTALL b/VEX/head20041019/INSTALL new file mode 100644 index 0000000000..b42a17ac46 --- /dev/null +++ b/VEX/head20041019/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/VEX/head20041019/Makefile.all.am b/VEX/head20041019/Makefile.all.am new file mode 100644 index 0000000000..d1b45fca3d --- /dev/null +++ b/VEX/head20041019/Makefile.all.am @@ -0,0 +1,8 @@ + +## This file should be included by *every* Makefile.am, except those for docs/ +## and tests/ subdirectories. + +valdir = $(libdir)/valgrind +inplacedir = $(top_builddir)/.in_place + + diff --git a/VEX/head20041019/Makefile.am b/VEX/head20041019/Makefile.am new file mode 100644 index 0000000000..74f35a9637 --- /dev/null +++ b/VEX/head20041019/Makefile.am @@ -0,0 +1,59 @@ + +AUTOMAKE_OPTIONS = foreign 1.6 dist-bzip2 + +include $(top_srcdir)/Makefile.all.am + +## include must be first for tool.h +## addrcheck must come after memcheck, for mac_*.o +SUBDIRS = include coregrind . docs tests auxprogs \ + memcheck \ + addrcheck \ + cachegrind \ + corecheck \ + helgrind \ + massif \ + lackey \ + none + +SUPP_FILES = \ + glibc-2.1.supp glibc-2.2.supp glibc-2.3.supp \ + xfree-3.supp xfree-4.supp + +dist_val_DATA = $(SUPP_FILES) default.supp + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = valgrind.pc + +BUILT_SOURCES = default.supp valgrind.pc + +DISTCLEANFILES = default.supp + +default.supp: $(SUPP_FILES) + +## Preprend @PERL@ because tests/vg_regtest isn't executable +regtest: check + @PERL@ tests/vg_regtest --all + +EXTRA_DIST = \ + FAQ.txt \ + ACKNOWLEDGEMENTS \ + README_DEVELOPERS \ + README_PACKAGERS \ + README_MISSING_SYSCALL_OR_IOCTL TODO \ + valgrind.spec.in valgrind.pc.in \ + Makefile.all.am Makefile.tool.am Makefile.core-AM_CPPFLAGS.am \ + Makefile.tool-inplace.am + +install-exec-hook: + $(mkinstalldirs) $(DESTDIR)$(valdir) + rm -f $(DESTDIR)$(valdir)/libpthread.so.0 + $(LN_S) libpthread.so $(DESTDIR)$(valdir)/libpthread.so.0 + +all-local: + mkdir -p $(inplacedir) + rm -f $(addprefix $(inplacedir)/,default.supp $(SUPP_FILES)) + ln -s ../default.supp $(inplacedir) + ln -s $(addprefix ../$(top_srcdir)/,$(SUPP_FILES)) $(inplacedir) + +distclean-local: + rm -rf $(inplacedir) diff --git a/VEX/head20041019/Makefile.core-AM_CPPFLAGS.am b/VEX/head20041019/Makefile.core-AM_CPPFLAGS.am new file mode 100644 index 0000000000..2e889b4f10 --- /dev/null +++ b/VEX/head20041019/Makefile.core-AM_CPPFLAGS.am @@ -0,0 +1,8 @@ +add_includes = -I$(top_builddir)/coregrind -I$(top_srcdir)/coregrind \ + -I$(top_srcdir)/coregrind/$(VG_ARCH) \ + -I$(top_srcdir)/coregrind/$(VG_PLATFORM) \ + -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_srcdir)/include/$(VG_ARCH) + +AM_CPPFLAGS = $(add_includes) + diff --git a/VEX/head20041019/Makefile.tool-flags.am b/VEX/head20041019/Makefile.tool-flags.am new file mode 100644 index 0000000000..90de4dc18a --- /dev/null +++ b/VEX/head20041019/Makefile.tool-flags.am @@ -0,0 +1,10 @@ +## Need $(top_builddir)/include because tool.h is built from tool.h.base; +## otherwise it will not work if builddir != srcdir. +add_includes = -I$(top_builddir)/include -I$(top_srcdir)/include \ + -I$(top_srcdir)/include/$(VG_ARCH) + +AM_CPPFLAGS = $(add_includes) +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -O -fomit-frame-pointer \ + @PREFERRED_STACK_BOUNDARY@ -g +AM_CCASFLAGS = $(add_includes) + diff --git a/VEX/head20041019/Makefile.tool-inplace.am b/VEX/head20041019/Makefile.tool-inplace.am new file mode 100644 index 0000000000..75d4991df9 --- /dev/null +++ b/VEX/head20041019/Makefile.tool-inplace.am @@ -0,0 +1,4 @@ +all-local: + mkdir -p $(inplacedir) + -rm -f $(addprefix $(inplacedir)/,$(val_PROGRAMS)) + ln -f -s $(addprefix ../$(subdir)/,$(val_PROGRAMS)) $(inplacedir) diff --git a/VEX/head20041019/Makefile.tool.am b/VEX/head20041019/Makefile.tool.am new file mode 100644 index 0000000000..39aa6a15af --- /dev/null +++ b/VEX/head20041019/Makefile.tool.am @@ -0,0 +1,6 @@ + +SUBDIRS = . tests docs + +include $(top_srcdir)/Makefile.all.am +include $(top_srcdir)/Makefile.tool-flags.am +include $(top_srcdir)/Makefile.tool-inplace.am diff --git a/VEX/head20041019/NEWS b/VEX/head20041019/NEWS new file mode 100644 index 0000000000..1ae1b64e10 --- /dev/null +++ b/VEX/head20041019/NEWS @@ -0,0 +1,739 @@ + +Stable release 2.2.0 (31 August 2004) -- CHANGES RELATIVE TO 2.0.0 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2.2.0 brings nine months worth of improvements and bug fixes. We +believe it to be a worthy successor to 2.0.0. There are literally +hundreds of bug fixes and minor improvements. There are also some +fairly major user-visible changes: + +* A complete overhaul of handling of system calls and signals, and + their interaction with threads. In general, the accuracy of the + system call, thread and signal simulations is much improved: + + - Blocking system calls behave exactly as they do when running + natively (not on valgrind). That is, if a syscall blocks only the + calling thread when running natively, than it behaves the same on + valgrind. No more mysterious hangs because V doesn't know that some + syscall or other, should block only the calling thread. + + - Interrupted syscalls should now give more faithful results. + + - Signal contexts in signal handlers are supported. + +* Improvements to NPTL support to the extent that V now works + properly on NPTL-only setups. + +* Greater isolation between Valgrind and the program being run, so + the program is less likely to inadvertently kill Valgrind by + doing wild writes. + +* Massif: a new space profiling tool. Try it! It's cool, and it'll + tell you in detail where and when your C/C++ code is allocating heap. + Draws pretty .ps pictures of memory use against time. A potentially + powerful tool for making sense of your program's space use. + +* File descriptor leakage checks. When enabled, Valgrind will print out + a list of open file descriptors on exit. + +* Improved SSE2/SSE3 support. + +* Time-stamped output; use --time-stamp=yes + + + +Stable release 2.2.0 (31 August 2004) -- CHANGES RELATIVE TO 2.1.2 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2.2.0 is not much different from 2.1.2, released seven weeks ago. +A number of bugs have been fixed, most notably #85658, which gave +problems for quite a few people. There have been many internal +cleanups, but those are not user visible. + +The following bugs have been fixed since 2.1.2: + +85658 Assert in coregrind/vg_libpthread.c:2326 (open64) != + (void*)0 failed + This bug was reported multiple times, and so the following + duplicates of it are also fixed: 87620, 85796, 85935, 86065, + 86919, 86988, 87917, 88156 + +80716 Semaphore mapping bug caused by unmap (sem_destroy) + (Was fixed prior to 2.1.2) + +86987 semctl and shmctl syscalls family is not handled properly + +86696 valgrind 2.1.2 + RH AS2.1 + librt + +86730 valgrind locks up at end of run with assertion failure + in __pthread_unwind + +86641 memcheck doesn't work with Mesa OpenGL/ATI on Suse 9.1 + (also fixes 74298, a duplicate of this) + +85947 MMX/SSE unhandled instruction 'sfence' + +84978 Wrong error "Conditional jump or move depends on + uninitialised value" resulting from "sbbl %reg, %reg" + +86254 ssort() fails when signed int return type from comparison is + too small to handle result of unsigned int subtraction + +87089 memalign( 4, xxx) makes valgrind assert + +86407 Add support for low-level parallel port driver ioctls. + +70587 Add timestamps to Valgrind output? (wishlist) + +84937 vg_libpthread.c:2505 (se_remap): Assertion `res == 0' + (fixed prior to 2.1.2) + +86317 cannot load libSDL-1.2.so.0 using valgrind + +86989 memcpy from mac_replace_strmem.c complains about + uninitialized pointers passed when length to copy is zero + +85811 gnu pascal symbol causes segmentation fault; ok in 2.0.0 + +79138 writing to sbrk()'d memory causes segfault + +77369 sched deadlock while signal received during pthread_join + and the joined thread exited + +88115 In signal handler for SIGFPE, siginfo->si_addr is wrong + under Valgrind + +78765 Massif crashes on app exit if FP exceptions are enabled + +Additionally there are the following changes, which are not +connected to any bug report numbers, AFAICS: + +* Fix scary bug causing mis-identification of SSE stores vs + loads and so causing memcheck to sometimes give nonsense results + on SSE code. + +* Add support for the POSIX message queue system calls. + +* Fix to allow 32-bit Valgrind to run on AMD64 boxes. Note: this does + NOT allow Valgrind to work with 64-bit executables - only with 32-bit + executables on an AMD64 box. + +* At configure time, only check whether linux/mii.h can be processed + so that we don't generate ugly warnings by trying to compile it. + +* Add support for POSIX clocks and timers. + + + +Developer (cvs head) release 2.1.2 (18 July 2004) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2.1.2 contains four months worth of bug fixes and refinements. +Although officially a developer release, we believe it to be stable +enough for widespread day-to-day use. 2.1.2 is pretty good, so try it +first, although there is a chance it won't work. If so then try 2.0.0 +and tell us what went wrong." 2.1.2 fixes a lot of problems present +in 2.0.0 and is generally a much better product. + +Relative to 2.1.1, a large number of minor problems with 2.1.1 have +been fixed, and so if you use 2.1.1 you should try 2.1.2. Users of +the last stable release, 2.0.0, might also want to try this release. + +The following bugs, and probably many more, have been fixed. These +are listed at http://bugs.kde.org. Reporting a bug for valgrind in +the http://bugs.kde.org is much more likely to get you a fix than +mailing developers directly, so please continue to keep sending bugs +there. + +76869 Crashes when running any tool under Fedora Core 2 test1 + This fixes the problem with returning from a signal handler + when VDSOs are turned off in FC2. + +69508 java 1.4.2 client fails with erroneous "stack size too small". + This fix makes more of the pthread stack attribute related + functions work properly. Java still doesn't work though. + +71906 malloc alignment should be 8, not 4 + All memory returned by malloc/new etc is now at least + 8-byte aligned. + +81970 vg_alloc_ThreadState: no free slots available + (closed because the workaround is simple: increase + VG_N_THREADS, rebuild and try again.) + +78514 Conditional jump or move depends on uninitialized value(s) + (a slight mishanding of FP code in memcheck) + +77952 pThread Support (crash) (due to initialisation-ordering probs) + (also 85118) + +80942 Addrcheck wasn't doing overlap checking as it should. +78048 return NULL on malloc/new etc failure, instead of asserting +73655 operator new() override in user .so files often doesn't get picked up +83060 Valgrind does not handle native kernel AIO +69872 Create proper coredumps after fatal signals +82026 failure with new glibc versions: __libc_* functions are not exported +70344 UNIMPLEMENTED FUNCTION: tcdrain +81297 Cancellation of pthread_cond_wait does not require mutex +82872 Using debug info from additional packages (wishlist) +83025 Support for ioctls FIGETBSZ and FIBMAP +83340 Support for ioctl HDIO_GET_IDENTITY +79714 Support for the semtimedop system call. +77022 Support for ioctls FBIOGET_VSCREENINFO and FBIOGET_FSCREENINFO +82098 hp2ps ansification (wishlist) +83573 Valgrind SIGSEGV on execve +82999 show which cmdline option was erroneous (wishlist) +83040 make valgrind VPATH and distcheck-clean (wishlist) +83998 Assertion `newfd > vgPlain_max_fd' failed (see below) +82722 Unchecked mmap in as_pad leads to mysterious failures later +78958 memcheck seg faults while running Mozilla +85416 Arguments with colon (e.g. --logsocket) ignored + + +Additionally there are the following changes, which are not +connected to any bug report numbers, AFAICS: + +* Rearranged address space layout relative to 2.1.1, so that + Valgrind/tools will run out of memory later than currently in many + circumstances. This is good news esp. for Calltree. It should + be possible for client programs to allocate over 800MB of + memory when using memcheck now. + +* Improved checking when laying out memory. Should hopefully avoid + the random segmentation faults that 2.1.1 sometimes caused. + +* Support for Fedora Core 2 and SuSE 9.1. Improvements to NPTL + support to the extent that V now works properly on NPTL-only setups. + +* Renamed the following options: + --logfile-fd --> --log-fd + --logfile --> --log-file + --logsocket --> --log-socket + to be consistent with each other and other options (esp. --input-fd). + +* Add support for SIOCGMIIPHY, SIOCGMIIREG and SIOCSMIIREG ioctls and + improve the checking of other interface related ioctls. + +* Fix building with gcc-3.4.1. + +* Remove limit on number of semaphores supported. + +* Add support for syscalls: set_tid_address (258), acct (51). + +* Support instruction "repne movs" -- not official but seems to occur. + +* Implement an emulated soft limit for file descriptors in addition to + the current reserved area, which effectively acts as a hard limit. The + setrlimit system call now simply updates the emulated limits as best + as possible - the hard limit is not allowed to move at all and just + returns EPERM if you try and change it. This should stop reductions + in the soft limit causing assertions when valgrind tries to allocate + descriptors from the reserved area. + (This actually came from bug #83998). + +* Major overhaul of Cachegrind implementation. First user-visible change + is that cachegrind.out files are now typically 90% smaller than they + used to be; code annotation times are correspondingly much smaller. + Second user-visible change is that hit/miss counts for code that is + unloaded at run-time is no longer dumped into a single "discard" pile, + but accurately preserved. + +* Client requests for telling valgrind about memory pools. + + + +Developer (cvs head) release 2.1.1 (12 March 2004) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +2.1.1 contains some internal structural changes needed for V's +long-term future. These don't affect end-users. Most notable +user-visible changes are: + +* Greater isolation between Valgrind and the program being run, so + the program is less likely to inadvertently kill Valgrind by + doing wild writes. + +* Massif: a new space profiling tool. Try it! It's cool, and it'll + tell you in detail where and when your C/C++ code is allocating heap. + Draws pretty .ps pictures of memory use against time. A potentially + powerful tool for making sense of your program's space use. + +* Fixes for many bugs, including support for more SSE2/SSE3 instructions, + various signal/syscall things, and various problems with debug + info readers. + +* Support for glibc-2.3.3 based systems. + +We are now doing automatic overnight build-and-test runs on a variety +of distros. As a result, we believe 2.1.1 builds and runs on: +Red Hat 7.2, 7.3, 8.0, 9, Fedora Core 1, SuSE 8.2, SuSE 9. + + +The following bugs, and probably many more, have been fixed. These +are listed at http://bugs.kde.org. Reporting a bug for valgrind in +the http://bugs.kde.org is much more likely to get you a fix than +mailing developers directly, so please continue to keep sending bugs +there. + +69616 glibc 2.3.2 w/NPTL is massively different than what valgrind expects +69856 I don't know how to instrument MMXish stuff (Helgrind) +73892 valgrind segfaults starting with Objective-C debug info + (fix for S-type stabs) +73145 Valgrind complains too much about close() +73902 Shadow memory allocation seems to fail on RedHat 8.0 +68633 VG_N_SEMAPHORES too low (V itself was leaking semaphores) +75099 impossible to trace multiprocess programs +76839 the `impossible' happened: disInstr: INT but not 0x80 ! +76762 vg_to_ucode.c:3748 (dis_push_segreg): Assertion `sz == 4' failed. +76747 cannot include valgrind.h in c++ program +76223 parsing B(3,10) gave NULL type => impossible happens +75604 shmdt handling problem +76416 Problems with gcc 3.4 snap 20040225 +75614 using -gstabs when building your programs the `impossible' happened +75787 Patch for some CDROM ioctls CDORM_GET_MCN, CDROM_SEND_PACKET, +75294 gcc 3.4 snapshot's libstdc++ have unsupported instructions. + (REP RET) +73326 vg_symtab2.c:272 (addScopeRange): Assertion `range->size > 0' failed. +72596 not recognizing __libc_malloc +69489 Would like to attach ddd to running program +72781 Cachegrind crashes with kde programs +73055 Illegal operand at DXTCV11CompressBlockSSE2 (more SSE opcodes) +73026 Descriptor leak check reports port numbers wrongly +71705 README_MISSING_SYSCALL_OR_IOCTL out of date +72643 Improve support for SSE/SSE2 instructions +72484 valgrind leaves it's own signal mask in place when execing +72650 Signal Handling always seems to restart system calls +72006 The mmap system call turns all errors in ENOMEM +71781 gdb attach is pretty useless +71180 unhandled instruction bytes: 0xF 0xAE 0x85 0xE8 +69886 writes to zero page cause valgrind to assert on exit +71791 crash when valgrinding gimp 1.3 (stabs reader problem) +69783 unhandled syscall: 218 +69782 unhandled instruction bytes: 0x66 0xF 0x2B 0x80 +70385 valgrind fails if the soft file descriptor limit is less + than about 828 +69529 "rep; nop" should do a yield +70827 programs with lots of shared libraries report "mmap failed" + for some of them when reading symbols +71028 glibc's strnlen is optimised enough to confuse valgrind + + + + +Unstable (cvs head) release 2.1.0 (15 December 2003) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +For whatever it's worth, 2.1.0 actually seems pretty darn stable to me +(Julian). It looks eminently usable, and given that it fixes some +significant bugs, may well be worth using on a day-to-day basis. +2.1.0 is known to build and pass regression tests on: SuSE 9, SuSE +8.2, RedHat 8. + +2.1.0 most notably includes Jeremy Fitzhardinge's complete overhaul of +handling of system calls and signals, and their interaction with +threads. In general, the accuracy of the system call, thread and +signal simulations is much improved. Specifically: + +- Blocking system calls behave exactly as they do when running + natively (not on valgrind). That is, if a syscall blocks only the + calling thread when running natively, than it behaves the same on + valgrind. No more mysterious hangs because V doesn't know that some + syscall or other, should block only the calling thread. + +- Interrupted syscalls should now give more faithful results. + +- Finally, signal contexts in signal handlers are supported. As a + result, konqueror on SuSE 9 no longer segfaults when notified of + file changes in directories it is watching. + +Other changes: + +- Robert Walsh's file descriptor leakage checks. When enabled, + Valgrind will print out a list of open file descriptors on + exit. Along with each file descriptor, Valgrind prints out a stack + backtrace of where the file was opened and any details relating to the + file descriptor such as the file name or socket details. + To use, give: --track-fds=yes + +- Implemented a few more SSE/SSE2 instructions. + +- Less crud on the stack when you do 'where' inside a GDB attach. + +- Fixed the following bugs: + 68360: Valgrind does not compile against 2.6.0-testX kernels + 68525: CVS head doesn't compile on C90 compilers + 68566: pkgconfig support (wishlist) + 68588: Assertion `sz == 4' failed in vg_to_ucode.c (disInstr) + 69140: valgrind not able to explicitly specify a path to a binary. + 69432: helgrind asserts encountering a MutexErr when there are + EraserErr suppressions + +- Increase the max size of the translation cache from 200k average bbs + to 300k average bbs. Programs on the size of OOo (680m17) are + thrashing the cache at the smaller size, creating large numbers of + retranslations and wasting significant time as a result. + + + +Stable release 2.0.0 (5 Nov 2003) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +2.0.0 improves SSE/SSE2 support, fixes some minor bugs, and +improves support for SuSE 9 and the Red Hat "Severn" beta. + +- Further improvements to SSE/SSE2 support. The entire test suite of + the GNU Scientific Library (gsl-1.4) compiled with Intel Icc 7.1 + 20030307Z '-g -O -xW' now works. I think this gives pretty good + coverage of SSE/SSE2 floating point instructions, or at least the + subset emitted by Icc. + +- Also added support for the following instructions: + MOVNTDQ UCOMISD UNPCKLPS UNPCKHPS SQRTSS + PUSH/POP %{FS,GS}, and PUSH %CS (Nb: there is no POP %CS). + +- CFI support for GDB version 6. Needed to enable newer GDBs + to figure out where they are when using --gdb-attach=yes. + +- Fix this: + mc_translate.c:1091 (memcheck_instrument): Assertion + `u_in->size == 4 || u_in->size == 16' failed. + +- Return an error rather than panicing when given a bad socketcall. + +- Fix checking of syscall rt_sigtimedwait(). + +- Implement __NR_clock_gettime (syscall 265). Needed on Red Hat Severn. + +- Fixed bug in overlap check in strncpy() -- it was assuming the src was 'n' + bytes long, when it could be shorter, which could cause false + positives. + +- Support use of select() for very large numbers of file descriptors. + +- Don't fail silently if the executable is statically linked, or is + setuid/setgid. Print an error message instead. + +- Support for old DWARF-1 format line number info. + + + +Snapshot 20031012 (12 October 2003) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Three months worth of bug fixes, roughly. Most significant single +change is improved SSE/SSE2 support, mostly thanks to Dirk Mueller. + +20031012 builds on Red Hat Fedora ("Severn") but doesn't really work +(curiosly, mozilla runs OK, but a modest "ls -l" bombs). I hope to +get a working version out soon. It may or may not work ok on the +forthcoming SuSE 9; I hear positive noises about it but haven't been +able to verify this myself (not until I get hold of a copy of 9). + +A detailed list of changes, in no particular order: + +- Describe --gen-suppressions in the FAQ. + +- Syscall __NR_waitpid supported. + +- Minor MMX bug fix. + +- -v prints program's argv[] at startup. + +- More glibc-2.3 suppressions. + +- Suppressions for stack underrun bug(s) in the c++ support library + distributed with Intel Icc 7.0. + +- Fix problems reading /proc/self/maps. + +- Fix a couple of messages that should have been suppressed by -q, + but weren't. + +- Make Addrcheck understand "Overlap" suppressions. + +- At startup, check if program is statically linked and bail out if so. + +- Cachegrind: Auto-detect Intel Pentium-M, also VIA Nehemiah + +- Memcheck/addrcheck: minor speed optimisations + +- Handle syscall __NR_brk more correctly than before. + +- Fixed incorrect allocate/free mismatch errors when using + operator new(unsigned, std::nothrow_t const&) + operator new[](unsigned, std::nothrow_t const&) + +- Support POSIX pthread spinlocks. + +- Fixups for clean compilation with gcc-3.3.1. + +- Implemented more opcodes: + - push %es + - push %ds + - pop %es + - pop %ds + - movntq + - sfence + - pshufw + - pavgb + - ucomiss + - enter + - mov imm32, %esp + - all "in" and "out" opcodes + - inc/dec %esp + - A whole bunch of SSE/SSE2 instructions + +- Memcheck: don't bomb on SSE/SSE2 code. + + +Snapshot 20030725 (25 July 2003) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Fixes some minor problems in 20030716. + +- Fix bugs in overlap checking for strcpy/memcpy etc. + +- Do overlap checking with Addrcheck as well as Memcheck. + +- Fix this: + Memcheck: the `impossible' happened: + get_error_name: unexpected type + +- Install headers needed to compile new skins. + +- Remove leading spaces and colon in the LD_LIBRARY_PATH / LD_PRELOAD + passed to non-traced children. + +- Fix file descriptor leak in valgrind-listener. + +- Fix longstanding bug in which the allocation point of a + block resized by realloc was not correctly set. This may + have caused confusing error messages. + + +Snapshot 20030716 (16 July 2003) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +20030716 is a snapshot of our current CVS head (development) branch. +This is the branch which will become valgrind-2.0. It contains +significant enhancements over the 1.9.X branch. + +Despite this being a snapshot of the CVS head, it is believed to be +quite stable -- at least as stable as 1.9.6 or 1.0.4, if not more so +-- and therefore suitable for widespread use. Please let us know asap +if it causes problems for you. + +Two reasons for releasing a snapshot now are: + +- It's been a while since 1.9.6, and this snapshot fixes + various problems that 1.9.6 has with threaded programs + on glibc-2.3.X based systems. + +- So as to make available improvements in the 2.0 line. + +Major changes in 20030716, as compared to 1.9.6: + +- More fixes to threading support on glibc-2.3.1 and 2.3.2-based + systems (SuSE 8.2, Red Hat 9). If you have had problems + with inconsistent/illogical behaviour of errno, h_errno or the DNS + resolver functions in threaded programs, 20030716 should improve + matters. This snapshot seems stable enough to run OpenOffice.org + 1.1rc on Red Hat 7.3, SuSE 8.2 and Red Hat 9, and that's a big + threaded app if ever I saw one. + +- Automatic generation of suppression records; you no longer + need to write them by hand. Use --gen-suppressions=yes. + +- strcpy/memcpy/etc check their arguments for overlaps, when + running with the Memcheck or Addrcheck skins. + +- malloc_usable_size() is now supported. + +- new client requests: + - VALGRIND_COUNT_ERRORS, VALGRIND_COUNT_LEAKS: + useful with regression testing + - VALGRIND_NON_SIMD_CALL[0123]: for running arbitrary functions + on real CPU (use with caution!) + +- The GDB attach mechanism is more flexible. Allow the GDB to + be run to be specified by --gdb-path=/path/to/gdb, and specify + which file descriptor V will read its input from with + --input-fd=. + +- Cachegrind gives more accurate results (wasn't tracking instructions in + malloc() and friends previously, is now). + +- Complete support for the MMX instruction set. + +- Partial support for the SSE and SSE2 instruction sets. Work for this + is ongoing. About half the SSE/SSE2 instructions are done, so + some SSE based programs may work. Currently you need to specify + --skin=addrcheck. Basically not suitable for real use yet. + +- Significant speedups (10%-20%) for standard memory checking. + +- Fix assertion failure in pthread_once(). + +- Fix this: + valgrind: vg_intercept.c:598 (vgAllRoadsLeadToRome_select): + Assertion `ms_end >= ms_now' failed. + +- Implement pthread_mutexattr_setpshared. + +- Understand Pentium 4 branch hints. Also implemented a couple more + obscure x86 instructions. + +- Lots of other minor bug fixes. + +- We have a decent regression test system, for the first time. + This doesn't help you directly, but it does make it a lot easier + for us to track the quality of the system, especially across + multiple linux distributions. + + You can run the regression tests with 'make regtest' after 'make + install' completes. On SuSE 8.2 and Red Hat 9 I get this: + + == 84 tests, 0 stderr failures, 0 stdout failures == + + On Red Hat 8, I get this: + + == 84 tests, 2 stderr failures, 1 stdout failure == + corecheck/tests/res_search (stdout) + memcheck/tests/sigaltstack (stderr) + + sigaltstack is probably harmless. res_search doesn't work + on R H 8 even running natively, so I'm not too worried. + + On Red Hat 7.3, a glibc-2.2.5 system, I get these harmless failures: + + == 84 tests, 2 stderr failures, 1 stdout failure == + corecheck/tests/pth_atfork1 (stdout) + corecheck/tests/pth_atfork1 (stderr) + memcheck/tests/sigaltstack (stderr) + + You need to run on a PII system, at least, since some tests + contain P6-specific instructions, and the test machine needs + access to the internet so that corecheck/tests/res_search + (a test that the DNS resolver works) can function. + +As ever, thanks for the vast amount of feedback :) and bug reports :( +We may not answer all messages, but we do at least look at all of +them, and tend to fix the most frequently reported bugs. + + + +Version 1.9.6 (7 May 2003 or thereabouts) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Major changes in 1.9.6: + +- Improved threading support for glibc >= 2.3.2 (SuSE 8.2, + RedHat 9, to name but two ...) It turned out that 1.9.5 + had problems with threading support on glibc >= 2.3.2, + usually manifested by threaded programs deadlocking in system calls, + or running unbelievably slowly. Hopefully these are fixed now. 1.9.6 + is the first valgrind which gives reasonable support for + glibc-2.3.2. Also fixed a 2.3.2 problem with pthread_atfork(). + +- Majorly expanded FAQ.txt. We've added workarounds for all + common problems for which a workaround is known. + +Minor changes in 1.9.6: + +- Fix identification of the main thread's stack. Incorrect + identification of it was causing some on-stack addresses to not get + identified as such. This only affected the usefulness of some error + messages; the correctness of the checks made is unchanged. + +- Support for kernels >= 2.5.68. + +- Dummy implementations of __libc_current_sigrtmin, + __libc_current_sigrtmax and __libc_allocate_rtsig, hopefully + good enough to keep alive programs which previously died for lack of + them. + +- Fix bug in the VALGRIND_DISCARD_TRANSLATIONS client request. + +- Fix bug in the DWARF2 debug line info loader, when instructions + following each other have source lines far from each other + (e.g. with inlined functions). + +- Debug info reading: read symbols from both "symtab" and "dynsym" + sections, rather than merely from the one that comes last in the + file. + +- New syscall support: prctl(), creat(), lookup_dcookie(). + +- When checking calls to accept(), recvfrom(), getsocketopt(), + don't complain if buffer values are NULL. + +- Try and avoid assertion failures in + mash_LD_PRELOAD_and_LD_LIBRARY_PATH. + +- Minor bug fixes in cg_annotate. + + + +Version 1.9.5 (7 April 2003) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It occurs to me that it would be helpful for valgrind users to record +in the source distribution the changes in each release. So I now +attempt to mend my errant ways :-) Changes in this and future releases +will be documented in the NEWS file in the source distribution. + +Major changes in 1.9.5: + +- (Critical bug fix): Fix a bug in the FPU simulation. This was + causing some floating point conditional tests not to work right. + Several people reported this. If you had floating point code which + didn't work right on 1.9.1 to 1.9.4, it's worth trying 1.9.5. + +- Partial support for Red Hat 9. RH9 uses the new Native Posix + Threads Library (NPTL), instead of the older LinuxThreads. + This potentially causes problems with V which will take some + time to correct. In the meantime we have partially worked around + this, and so 1.9.5 works on RH9. Threaded programs still work, + but they may deadlock, because some system calls (accept, read, + write, etc) which should be nonblocking, in fact do block. This + is a known bug which we are looking into. + + If you can, your best bet (unfortunately) is to avoid using + 1.9.5 on a Red Hat 9 system, or on any NPTL-based distribution. + If your glibc is 2.3.1 or earlier, you're almost certainly OK. + +Minor changes in 1.9.5: + +- Added some #errors to valgrind.h to ensure people don't include + it accidentally in their sources. This is a change from 1.0.X + which was never properly documented. The right thing to include + is now memcheck.h. Some people reported problems and strange + behaviour when (incorrectly) including valgrind.h in code with + 1.9.1 -- 1.9.4. This is no longer possible. + +- Add some __extension__ bits and pieces so that gcc configured + for valgrind-checking compiles even with -Werror. If you + don't understand this, ignore it. Of interest to gcc developers + only. + +- Removed a pointless check which caused problems interworking + with Clearcase. V would complain about shared objects whose + names did not end ".so", and refuse to run. This is now fixed. + In fact it was fixed in 1.9.4 but not documented. + +- Fixed a bug causing an assertion failure of "waiters == 1" + somewhere in vg_scheduler.c, when running large threaded apps, + notably MySQL. + +- Add support for the munlock system call (124). + +Some comments about future releases: + +1.9.5 is, we hope, the most stable Valgrind so far. It pretty much +supersedes the 1.0.X branch. If you are a valgrind packager, please +consider making 1.9.5 available to your users. You can regard the +1.0.X branch as obsolete: 1.9.5 is stable and vastly superior. There +are no plans at all for further releases of the 1.0.X branch. + +If you want a leading-edge valgrind, consider building the cvs head +(from SourceForge), or getting a snapshot of it. Current cool stuff +going in includes MMX support (done); SSE/SSE2 support (in progress), +a significant (10-20%) performance improvement (done), and the usual +large collection of minor changes. Hopefully we will be able to +improve our NPTL support, but no promises. + diff --git a/VEX/head20041019/NOTES.syscalls b/VEX/head20041019/NOTES.syscalls new file mode 100644 index 0000000000..86e3231a84 --- /dev/null +++ b/VEX/head20041019/NOTES.syscalls @@ -0,0 +1,195 @@ +- works on stock 2.4 kernels, but the scheduler loop must poll +- works on RH9 2.4.20-18.9 kernel, but doesn't seem quite as stable + as 2.5/2.6 + for pending signals rather than relying on the kernel delivering them + to the right place. +- most tested on 2.6.0-test1 and up + +- running job-control programs (ie, bash) under Valgrind won't work + properly without a kernel patch (as of 2.6.0-test2-mm2). This is because + threads in a thread group don't follow the thread group leader's changes + in process group ID, and they can't change it for themselves. + +- SA_NOCLDWAIT doesn't work properly if the program is actually blocked + in wait4() when SIGCHLD arrives; the wait4() will return details for + the exiting child. In other circumstances children should be quietly reaped. + [ This may be fixable when running under RH2.4 and 2.6, since we can + set NOCLDWAIT in the kernel's state without risk of losing our child + threads. ] + +- 2.4 has somewhat disfunctional thread/signal interactions, so many test + do not work as well under 2.4. In general, it should be no worse than + the old signal code. I don't intend spending a lot of time fixing this + because 2.6 is nearly ready for widespread use. + +TODO: + +- support application use of clone(). Interesting question is which + options do we support? Do we need to implement futex as well, or can + we just use the kernel's implementation? + +======================================== +Testing + +I've been testing with the Posix test suite: +http://sourceforge.net/projects/posixtest/, version 1.2.0. + +---------------------------------------- +Expected failures: + +conformance/interfaces/sigwaitinfo/6-1.test + pthread_kill() calls the tkill syscall, which causes a code of + SI_TKILL rather than the SI_USER which this test expects. + +conformance/interfaces/sigrelse/3-*.test + glibc bug in sigrelse(), which fails without Valgrind too. + +conformance/interfaces/pthread_barrier_*/* + Valgrind's libpthreads doesn't implement pthread_barrier_*. + (There are some passes, but I don't know why.) + +conformance/interfaces/pthread_cond_timedwait/2-3 + This test is just completely broken. It does expose a problem + in Valgrind's mutex implementation - it is too dependent on + the client code not doing stupid stuff. This test makes + Valgrind have an assertion failure. + +conformance/interfaces/pthread_condattr_getpshared/* + pthread_condattr_getpshared not implemented + +conformance/interfaces/pthread_condattr_setpshared/* + pthread_condattr_setpshared not implemented + +conformance/interfaces/pthread_key_create/speculative/5-1 + Valgrind should cope with key overload + +conformance/interfaces/pthread_mutex_timedlock/* + not implemented + +conformance/interfaces/pthread_rwlock_rdlock/2-1: + relies on pthread_setschedparam + +conformance/interfaces/pthread_rwlock_timedrdlock/* + valgrind's libpthread.so: UNIMPLEMENTED FUNCTION: pthread_rwlock_timedrdlock + +conformance/interfaces/pthread_rwlockattr_getpshared/* + pthread_rwlockattr_getpshared not implemented + +conformance/interfaces/pthread_rwlockattr_setpshared/* + pthread_rwlockattr_setpshared not implemented + +conformance/interfaces/sched_rr_get_interval/* + syscall 161 (sched_rr_get_interval) not implemented + +conformance/interfaces/sigaction/21-1 + Subtle problem: if app specifies SA_NOCLDWAIT on their SIGCHLD + signal handler, Valgrind will attempt to catch the SIGCHLD and + wait4() on all the children before returning to the app. + However, if the app was running a wait4() at the time the + SIGCHLD arrives, it will get the child's status. Quite what + the app is doing running wait4() when it explicitly asked for + it to be useless, I'm not sure... + +conformance/interfaces/sigaction/17-{3,6,8,12} + (2.4) These fail under 2.4 because they deal with SIGSEGV, SIGBUS + and SIGILL. These signals can only be delivered if there's a + thread immediately ready to handle them, but cannot be left + pending indefinitely. These tests hang forever because the + signal is discarded rather than delivered. + +conformance/interfaces/sigqueue/{1,4,8}-1 + (2.4) Signals that we route manually do not have queued data + associated with them - they are routed with tkill. Also + pending signals are only kept in a mask, not in a queue, so + there can only be one at a time. + +---------------------------------------- + +Still to investigate: + +conformance/interfaces/pthread_detach/4-1 + ++conformance/interfaces/pthread_rwlock_rdlock/4-1: execution: FAILED: Output: ++main: attempt write lock ++main: acquired write lock ++sig_thread: attemp read lock ++main: fire SIGUSR1 to sig_thread ++SIGUSR1 was not caught by sig_thread + + ++conformance/interfaces/pthread_rwlock_unlock/4-1: execution: FAILED: Output: ++Test FAILED: Incorrect error code, expected 0 or EINVAL, got 1 + ++conformance/interfaces/pthread_rwlock_wrlock/2-1: execution: FAILED: Output: ++main: attempt write lock ++sig_thread: attempt write lock ++main: fire SIGUSR1 to sig_thread ++The signal handler did not get called. + ++conformance/interfaces/pthread_rwlock_wrlock/3-1: execution: FAILED: Output: ++ ++sched status: ++ ++Thread 1: status = WaitCV, associated_mx = 0x40115910, associated_cv = 0x401158E0 ++==11243== at 0x40102962: pthread_cond_wait (vg_libpthread.c:1093) ++==11243== by 0x40104976: __pthread_rwlock_wrlock (vg_libpthread.c:2619) ++==11243== by 0x8048588: main (3-1.c:53) ++==11243== by 0x4013DA46: __libc_start_main (in /lib/libc-2.3.2.so) ++ ++==11243== Warning: pthread scheduler exited due to deadlock ++ ++valgrind: vg_main.c:1619 (vgPlain_main): Assertion `vgPlain_threads[vgPlain_last_run_tid].status == VgTs_Runnable' failed. ++ ++sched status: ++ ++Thread 1: status = WaitCV, associated_mx = 0x40115910, associated_cv = 0x401158E0 ++==11243== at 0x40102962: pthread_cond_wait (vg_libpthread.c:1093) ++==11243== by 0x40104976: __pthread_rwlock_wrlock (vg_libpthread.c:2619) ++==11243== by 0x8048588: main (3-1.c:53) ++==11243== by 0x4013DA46: __libc_start_main (in /lib/libc-2.3.2.so) + + ++conformance/interfaces/sem_close/1-1.test: +/home/jeremy/bk/valgrind/syscalls/coregrind/.in_place/libpthread.so.0: +version `GLIBC_2.1.1' not found (required by +conformance/interfaces/sem_close/1-1.test) + ++conformance/interfaces/sem_timedwait/6-1: execution: FAILED: Output: ++TEST FAILED ++conformance/interfaces/sem_timedwait/6-2: execution: FAILED: Output: ++TEST FAILED + ++conformance/interfaces/sem_timedwait/9-1: execution: FAILED: Output: ++In handler ++TEST FAILED: errno != EINTR + + +conformance/interfaces/sigaction/10-1: + Used to work. Mysterious. Works everywhere except in the test harness... + + ++conformance/interfaces/sigpause/1-2: execution: FAILED: Output: ++ ++valgrind: vg_mylibc.c:1324 (vgPlain_read_millisecond_timer): Assertion `rdtsc_now > rdtsc_cal_end_raw' failed. ++ ++sched status: ++ ++Thread 1: status = Sleeping, associated_mx = 0x0, associated_cv = 0x0 ++==19929== at 0x401D6765: __GI___libc_nanosleep (in /lib/libc-2.3.2.so) ++==19929== by 0x80485C1: main (1-2.c:65) ++==19929== by 0x4013DA46: __libc_start_main (in /lib/libc-2.3.2.so) ++==19929== by 0x8048494: ??? (start.S:81) ++ ++Thread 2: status = WaitSys, associated_mx = 0x0, associated_cv = 0x0 ++==19929== at 0x40150796: __libc_sigsuspend (in /lib/libc-2.3.2.so) ++==19929== by 0x401509B3: __GI___sigpause (in /lib/libc-2.3.2.so) ++==19929== by 0x804857C: a_thread_func (1-2.c:48) ++==19929== by 0x40102099: thread_wrapper (vg_libpthread.c:667) + + + +---------------------------------------- + +Fixes: +conformance/interfaces/pthread_detach/4-2 + This fails under NPTL, but passes under Valgrind diff --git a/VEX/head20041019/README b/VEX/head20041019/README new file mode 100644 index 0000000000..32b21ecf62 --- /dev/null +++ b/VEX/head20041019/README @@ -0,0 +1,100 @@ + +Release notes for Valgrind +~~~~~~~~~~~~~~~~~~~~~~~~~~ +If you are building a binary package of Valgrind for distribution, +please read README_PACKAGERS. It contains some important information. + +If you are developing Valgrind, please read README_DEVELOPERS. It contains +some useful information. + +For instructions on how to build/install, see the end of this file. + +Valgrind works on most, reasonably recent Linux setups. If you have +problems, consult FAQ.txt to see if there are workarounds. + +Executive Summary +~~~~~~~~~~~~~~~~~ +Valgrind is a GPL'd system for debugging and profiling x86-Linux programs. +With the tools that come with Valgrind, you can automatically detect +many memory management and threading bugs, avoiding hours of frustrating +bug-hunting, making your programs more stable. You can also perform +detailed profiling to help speed up your programs. + +The Valgrind distribution includes five tools: two memory error +detectors, a thread error detector, a cache profiler and a heap profiler. +Several other tools have been built with Valgrind. + +To give you an idea of what Valgrind tools do, when a program is run +under the supervision of the first memory error detector tool, all reads +and writes of memory are checked, and calls to malloc/new/free/delete +are intercepted. As a result, it can detect problems such as: + + Use of uninitialised memory + Reading/writing memory after it has been free'd + Reading/writing off the end of malloc'd blocks + Reading/writing inappropriate areas on the stack + Memory leaks -- where pointers to malloc'd blocks are lost forever + Passing of uninitialised and/or unaddressible memory to system calls + Mismatched use of malloc/new/new [] vs free/delete/delete [] + Overlaps of arguments to strcpy() and related functions + Some abuses of the POSIX pthread API + +Problems like these can be difficult to find by other means, often +lying undetected for long periods, then causing occasional, +difficult-to-diagnose crashes. When one of these errors occurs, you can +attach GDB to your program, so you can poke around and see what's going +on. + +Valgrind is closely tied to details of the CPU, operating system and +to a less extent, compiler and basic C libraries. This makes it +difficult to make it portable, so I have chosen at the outset to +concentrate on what I believe to be a widely used platform: x86/Linux. + +Valgrind is licensed under the GNU General Public License, version 2. +Read the file COPYING in the source distribution for details. + + +Documentation +~~~~~~~~~~~~~ +A comprehensive user guide is supplied. Point your browser at +$PREFIX/share/doc/valgrind/manual.html, where $PREFIX is whatever you +specified with --prefix= when building. + + +Building and installing it +~~~~~~~~~~~~~~~~~~~~~~~~~~ +To install from CVS : + + 0. Check out the code from CVS, following the instructions at + http://developer.kde.org/source/anoncvs.html. The 'modulename' is + "valgrind". + + 1. cd into the source directory. + + 2. Run ./autogen.sh to setup the environment (you need the standard + autoconf tools to do so). + +To install from a tar.bz2 distribution: + + 3. Run ./configure, with some options if you wish. The standard + options are documented in the INSTALL file. The only interesting + one is the usual --prefix=/where/you/want/it/installed. + + 4. Do "make". + + 5. Do "make install", possibly as root if the destination permissions + require that. + + 6. See if it works. Try "valgrind --tool=memcheck ls -l". Either + this works, or it bombs out with some complaint. In that case, + please let us know (see valgrind.kde.org/bugs.html). + +Important! Do not move the valgrind installation into a place +different from that specified by --prefix at build time. This will +cause things to break in subtle ways, mostly when Valgrind handles +fork/exec calls. + + +Julian Seward (jseward@acm.org) +Nick Nethercote (njn25@cam.ac.uk) +Jeremy Fitzhardinge (jeremy@goop.org) diff --git a/VEX/head20041019/README_DEVELOPERS b/VEX/head20041019/README_DEVELOPERS new file mode 100644 index 0000000000..a7fa4689c4 --- /dev/null +++ b/VEX/head20041019/README_DEVELOPERS @@ -0,0 +1,49 @@ + +Building and not installing it +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To run Valgrind without having to install it, run coregrind/valgrind (prefix +with "sh" because it's not executable) with the --in-place= option, where + is the root of the source tree (and must be an absolute path). Eg: + + sh ~/grind/head4/coregrind/valgrind --in-place=/homes/njn25/grind/head4 + +This allows you to compile and run with "make" instead of "make install", +saving you time. + +I recommend compiling with "make --quiet" to further reduce the amount of +output spewed out during compilation, letting you actually see any errors, +warnings, etc. + + +Running the regression tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To build and run all the regression tests, run "make [--quiet] regtest". + +To run a subset of the regression tests, execute: + + perl tests/vg_regtest + +where is a directory (all tests within will be run) or a single +.vgtest test file, or the name of a program which has a like-named .vgtest +file. Eg: + + perl tests/vg_regtest memcheck + perl tests/vg_regtest memcheck/tests/badfree.vgtest + perl tests/vg_regtest memcheck/tests/badfree + + +Debugging Valgrind with GDB +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To debug Valgrind itself with GDB, start Valgrind like this: + + valgrind --tool=none --wait-for-gdb=yes + +Then start gdb like this in another terminal: + + gdb /usr/lib/valgrind/stage2 + +Where is the pid valgrind printed. Then set whatever breakpoints +you want and do this in gdb: + + jump *$eip + diff --git a/VEX/head20041019/README_MISSING_SYSCALL_OR_IOCTL b/VEX/head20041019/README_MISSING_SYSCALL_OR_IOCTL new file mode 100644 index 0000000000..ad919252ec --- /dev/null +++ b/VEX/head20041019/README_MISSING_SYSCALL_OR_IOCTL @@ -0,0 +1,166 @@ + +Dealing with missing system call or ioctl wrappers in Valgrind +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +You're probably reading this because Valgrind bombed out whilst +running your program, and advised you to read this file. The good +news is that, in general, it's easy to write the missing syscall or +ioctl wrappers you need, so that you can continue your debugging. If +you send the resulting patches to me, then you'll be doing a favour to +all future Valgrind users too. + +Note that an "ioctl" is just a special kind of system call, really; so +there's not a lot of need to distinguish them (at least conceptually) +in the discussion that follows. + +All this machinery is in coregrind/vg_syscalls.c. + + +What are syscall/ioctl wrappers? What do they do? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Valgrind does what it does, in part, by keeping track of everything your +program does. When a system call happens, for example a request to read +part of a file, control passes to the Linux kernel, which fulfills the +request, and returns control to your program. The problem is that the +kernel will often change the status of some part of your program's memory +as a result, and tools (instrumentation plug-ins) may need to know about +this. + +Syscall and ioctl wrappers have two jobs: + +1. Tell a tool what's about to happen, before the syscall takes place. A + tool could perform checks beforehand, eg. if memory about to be written + is actually writeable. This part is useful, but not strictly + essential. + +2. Tell a tool what just happened, after a syscall takes place. This is + so it can update its view of the program's state, eg. that memory has + just been written to. This step is essential. + +The "happenings" mostly involve reading/writing of memory. + +So, let's look at an example of a wrapper for a system call which +should be familiar to many Unix programmers. + + +The syscall wrapper for time() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Removing the debug printing clutter, it looks like this: + + PRE(time) + { + /* time_t time(time_t *t); */ + MAYBE_PRINTF("time ( %p )\n",arg1); + if (arg1 != (UInt)NULL) { + SYSCALL_TRACK( pre_mem_write, tid, "time", arg1, sizeof(time_t) ); + } + } + + POST(time) + { + if (arg1 != (UInt)NULL) { + VG_TRACK( post_mem_write, arg1, sizeof(time_t) ); + } + } + +The first thing we do happens before the syscall occurs, in the PRE() function: +if a non-NULL buffer is passed in as the argument, tell the tool that the +buffer is about to be written to: + + if (arg1 != (UInt)NULL) { + SYSCALL_TRACK( pre_mem_write, tst, "time", arg1, sizeof(time_t) ); + } + +Finally, the really important bit, after the syscall occurs, in the POST() +function: if, and only if, the system call was successful, tell the tool that +the memory was written: + + if (arg1 != (UInt)NULL) { + VG_TRACK( post_mem_write, arg1, sizeof(time_t) ); + } + +The POST() function won't be called if the syscall failed, so you +don't need to worry about checking that in the POST() function. +(Note: this is sometimes a bug; some syscalls do return results when +they "fail" - for example, nanosleep returns the amount of unslept +time if interrupted. TODO: add another per-syscall flag for this +case.) + + +Writing your own syscall wrappers (see below for ioctl wrappers) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +If Valgrind tells you that system call NNN is unimplemented, do the +following: + +1. Find out the name of the system call: + + grep NNN /usr/include/asm/unistd.h + + This should tell you something like __NR_mysyscallname. + Copy this entry to coregrind/$(VG_PLATFORM)/vki_unistd.h. + +2. Do 'man 2 mysyscallname' to get some idea of what the syscall + does. Note that the actual kernel interface can differ from this, + so you might also want to check a version of the Linux kernel + source. + + NOTE: any syscall which has something to do with signals or + threads is probably "special", and needs more careful handling. + Post something to valgrind-developers if you aren't sure. + + +3. Add a case to the already-huge collection of wrappers in + coregrind/vg_syscalls.c. For each in-memory parameter which is + read or written by the syscall, do one of + + SYSCALL_TRACK( pre_mem_read, ... ) + SYSCALL_TRACK( pre_mem_read_asciiz, ... ) + SYSCALL_TRACK( pre_mem_write, ... ) + + for that parameter. Then do the syscall. Then, if the syscall + succeeds, issue suitable VG_TRACK( post_mem_write, ... ) calls. + (There's no need for post_mem_read calls.) + + Also, add it to the sys_info[] array; use SYSBA if it requires a + PRE() and POST() function, and SYSB_ if it only requires a PRE() + function. The 2nd arg of these macros indicate if the syscall + could possibly block. + + If you find this difficult, read the wrappers for other syscalls + for ideas. A good tip is to look for the wrapper for a syscall + which has a similar behaviour to yours, and use it as a + starting point. + + If you need structure definitions for your syscall, you can copy + structure definitions from the kernel headers into + include/vg_kerneliface.h, with the appropriate vki_* name + mangling. Alternatively, you can #include headers for structure + definitions, put your #includes into vg_unsafe.h (copying + syscall-related things into vg_kerneliface.h is preferred though). + + Test it. + + Note that a common error is to call VG_TRACK( post_mem_write, ... ) + with 0 (NULL) as the first (address) argument. This usually means + your logic is slightly inadequate. It's a sufficiently common bug + that there's a built-in check for it, and you'll get a "probably + sanity check failure" for the syscall wrapper you just made, if this + is the case. + + +4. Once happy, send us the patch. Pretty please. + + + + +Writing your own ioctl wrappers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Is pretty much the same as writing syscall wrappers, except that all +the action happens within PRE(ioctl) and POST(ioctl). + +There's a default case, sometimes it isn't correct and you have to write a +more specific case to get the right behaviour. + +As above, please create a bug report and attach the patch as described +on http://valgrind.kde.org/bugs.html + diff --git a/VEX/head20041019/README_PACKAGERS b/VEX/head20041019/README_PACKAGERS new file mode 100644 index 0000000000..7f220b1e2f --- /dev/null +++ b/VEX/head20041019/README_PACKAGERS @@ -0,0 +1,88 @@ + +Greetings, packaging person! This information is aimed at people +building binary distributions of Valgrind. + +Thanks for taking the time and effort to make a binary distribution +of Valgrind. The following notes may save you some trouble. + + +-- (Unfortunate but true) When you configure to build with the + --prefix=/foo/bar/xyzzy option, the prefix /foo/bar/xyzzy gets + baked into valgrind. The consequence is that you _must_ install + valgrind at the location specified in the prefix. If you don't, + it may appear to work, but will break doing some obscure things, + particularly doing fork() and exec(). + + So you can't build a relocatable RPM / whatever from Valgrind. + + +-- Don't strip the debug info off stage2 or libpthread.so. + Valgrind will still work if you do, but it will generate less + helpful error messages. Here's an example: + + Mismatched free() / delete / delete [] + at 0x40043249: free (vg_clientfuncs.c:171) + by 0x4102BB4E: QGArray::~QGArray(void) (tools/qgarray.cpp:149) + by 0x4C261C41: PptDoc::~PptDoc(void) (include/qmemarray.h:60) + by 0x4C261F0E: PptXml::~PptXml(void) (pptxml.cc:44) + Address 0x4BB292A8 is 0 bytes inside a block of size 64 alloc'd + at 0x4004318C: __builtin_vec_new (vg_clientfuncs.c:152) + by 0x4C21BC15: KLaola::readSBStream(int) const (klaola.cc:314) + by 0x4C21C155: KLaola::stream(KLaola::OLENode const *) (klaola.cc:416) + by 0x4C21788F: OLEFilter::convert(QCString const &) (olefilter.cc:272) + + This tells you that some memory allocated with new[] was freed with + free(). If stage2 was stripped the message would look like this: + + Mismatched free() / delete / delete [] + at 0x40043249: (inside stage2) + by 0x4102BB4E: QGArray::~QGArray(void) (tools/qgarray.cpp:149) + by 0x4C261C41: PptDoc::~PptDoc(void) (include/qmemarray.h:60) + by 0x4C261F0E: PptXml::~PptXml(void) (pptxml.cc:44) + Address 0x4BB292A8 is 0 bytes inside a block of size 64 alloc'd + at 0x4004318C: (inside stage2) + by 0x4C21BC15: KLaola::readSBStream(int) const (klaola.cc:314) + by 0x4C21C155: KLaola::stream(KLaola::OLENode const *) (klaola.cc:416) + by 0x4C21788F: OLEFilter::convert(QCString const &) (olefilter.cc:272) + + This isn't so helpful. Although you can tell there is a mismatch, + the names of the allocating and deallocating functions are no longer + visible. The same kind of thing occurs in various other messages + from valgrind. + + +-- Try and ensure that the /usr/include/asm/unistd.h file on the + build machine contains an entry for all the system calls that + the kernels on the target machines can actually support. On my + Red Hat 7.2 (kernel 2.4.9) box the highest-numbered entry is + #define __NR_fcntl64 221 + but I have heard of 2.2 boxes where it stops at 179 or so. + + Reason for this is that at build time, support for syscalls + is compiled in -- or not -- depending on which of these __NR_* + symbols is defined. Problems arise when /usr/include/asm/unistd.h + fails to give an entry for a system call which is actually + available in the target kernel. In that case, valgrind will + abort if asked to handle such a syscall. This is despite the + fact that (usually) valgrind's sources actually contain the + code to deal with the syscall. + + Several people have reported having this problem. So, please + be aware of it. If it's useful, the syscall wrappers are + all done in vg_syscall_mem.c; you might want to have a little + look in there. + + +-- Please test the final installation works by running it on + something huge. I suggest checking that it can start and + exit successfully both Mozilla-1.0 and OpenOffice.org 1.0. + I use these as test programs, and I know they fairly thoroughly + exercise Valgrind. The command lines to use are: + + valgrind -v --trace-children=yes --workaround-gcc296-bugs=yes mozilla + + valgrind -v --trace-children=yes --workaround-gcc296-bugs=yes soffice + + +If you find any more hints/tips for packaging, please report +it as a bugreport. See http://valgrind.kde.org/bugs.html for details. diff --git a/VEX/head20041019/TODO b/VEX/head20041019/TODO new file mode 100644 index 0000000000..738e509449 --- /dev/null +++ b/VEX/head20041019/TODO @@ -0,0 +1,15 @@ + +Doesn't run +~~~~~~~~~~~ +User Mode Linux. +Wine. + + +Desirable +~~~~~~~~~ +Stack: make return address into NoAccess ? + + +Future +~~~~~~ +Automatic invariant inference and checking. diff --git a/VEX/head20041019/addrcheck/.cvsignore b/VEX/head20041019/addrcheck/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/addrcheck/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/addrcheck/CVS/Entries b/VEX/head20041019/addrcheck/CVS/Entries new file mode 100644 index 0000000000..1295d46963 --- /dev/null +++ b/VEX/head20041019/addrcheck/CVS/Entries @@ -0,0 +1,5 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:20 2002// +/Makefile.am/1.52/Wed Sep 1 23:20:46 2004// +/ac_main.c/1.66/Mon Aug 23 15:06:21 2004// +D/docs//// +D/tests//// diff --git a/VEX/head20041019/addrcheck/CVS/Repository b/VEX/head20041019/addrcheck/CVS/Repository new file mode 100644 index 0000000000..57d147f8fd --- /dev/null +++ b/VEX/head20041019/addrcheck/CVS/Repository @@ -0,0 +1 @@ +valgrind/addrcheck diff --git a/VEX/head20041019/addrcheck/CVS/Root b/VEX/head20041019/addrcheck/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/addrcheck/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/addrcheck/CVS/Template b/VEX/head20041019/addrcheck/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/Makefile.am b/VEX/head20041019/addrcheck/Makefile.am new file mode 100644 index 0000000000..dba0a481ec --- /dev/null +++ b/VEX/head20041019/addrcheck/Makefile.am @@ -0,0 +1,23 @@ +include $(top_srcdir)/Makefile.tool.am + +# include memcheck/ for mac_shared.h +AM_CPPFLAGS += -I$(top_srcdir)/memcheck + +val_PROGRAMS = vgskin_addrcheck.so vgpreload_addrcheck.so + +vgskin_addrcheck_so_SOURCES = ac_main.c +vgskin_addrcheck_so_LDFLAGS = -shared +vgskin_addrcheck_so_LDADD = \ + ../memcheck/mac_leakcheck.o \ + ../memcheck/mac_malloc_wrappers.o \ + ../memcheck/mac_needs.o + +vgpreload_addrcheck_so_SOURCES = +vgpreload_addrcheck_so_LDADD = \ + $(top_builddir)/coregrind/vg_replace_malloc.o \ + ../memcheck/mac_replace_strmem.o +vgpreload_addrcheck_so_DEPENDENCIES = \ + $(top_builddir)/coregrind/vg_replace_malloc.o \ + ../memcheck/mac_replace_strmem.o +vgpreload_addrcheck_so_LDFLAGS = -shared -Wl,-z,interpose,-z,initfirst + diff --git a/VEX/head20041019/addrcheck/ac_main.c b/VEX/head20041019/addrcheck/ac_main.c new file mode 100644 index 0000000000..c5e2b51eba --- /dev/null +++ b/VEX/head20041019/addrcheck/ac_main.c @@ -0,0 +1,1343 @@ + +/*--------------------------------------------------------------------*/ +/*--- The AddrCheck tool: like MemCheck, but only does address ---*/ +/*--- checking. No definedness checking. ---*/ +/*--- ac_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of AddrCheck, a lightweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mac_shared.h" +#include "memcheck.h" +//#include "vg_profile.c" + + +/*------------------------------------------------------------*/ +/*--- Comparing and printing errors ---*/ +/*------------------------------------------------------------*/ + +void SK_(pp_SkinError) ( Error* err ) +{ + MAC_Error* err_extra = VG_(get_error_extra)(err); + + switch (VG_(get_error_kind)(err)) { + case CoreMemErr: + VG_(message)(Vg_UserMsg, "%s contains unaddressable byte(s)", + VG_(get_error_string)(err)); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + break; + + case ParamErr: + VG_(message)(Vg_UserMsg, + "Syscall param %s contains unaddressable byte(s)", + VG_(get_error_string)(err) ); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); + break; + + case UserErr: + VG_(message)(Vg_UserMsg, + "Unaddressable byte(s) found during client check request"); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); + break; + + default: + MAC_(pp_shared_SkinError)(err); + break; + } +} + +/*------------------------------------------------------------*/ +/*--- Suppressions ---*/ +/*------------------------------------------------------------*/ + +Bool SK_(recognised_suppression) ( Char* name, Supp* su ) +{ + return MAC_(shared_recognised_suppression)(name, su); +} + +#define DEBUG(fmt, args...) //VG_(printf)(fmt, ## args) + +/*------------------------------------------------------------*/ +/*--- Low-level support for memory checking. ---*/ +/*------------------------------------------------------------*/ + +/* All reads and writes are checked against a memory map, which + records the state of all memory in the process. The memory map is + organised like this: + + The top 16 bits of an address are used to index into a top-level + map table, containing 65536 entries. Each entry is a pointer to a + second-level map, which records the accesibililty and validity + permissions for the 65536 bytes indexed by the lower 16 bits of the + address. Each byte is represented by one bit, indicating + accessibility. So each second-level map contains 8192 bytes. This + two-level arrangement conveniently divides the 4G address space + into 64k lumps, each size 64k bytes. + + All entries in the primary (top-level) map must point to a valid + secondary (second-level) map. Since most of the 4G of address + space will not be in use -- ie, not mapped at all -- there is a + distinguished secondary map, which indicates `not addressible and + not valid' writeable for all bytes. Entries in the primary map for + which the entire 64k is not in use at all point at this + distinguished map. + + [...] lots of stuff deleted due to out of date-ness + + As a final optimisation, the alignment and address checks for + 4-byte loads and stores are combined in a neat way. The primary + map is extended to have 262144 entries (2^18), rather than 2^16. + The top 3/4 of these entries are permanently set to the + distinguished secondary map. For a 4-byte load/store, the + top-level map is indexed not with (addr >> 16) but instead f(addr), + where + + f( XXXX XXXX XXXX XXXX ____ ____ ____ __YZ ) + = ____ ____ ____ __YZ XXXX XXXX XXXX XXXX or + = ____ ____ ____ __ZY XXXX XXXX XXXX XXXX + + ie the lowest two bits are placed above the 16 high address bits. + If either of these two bits are nonzero, the address is misaligned; + this will select a secondary map from the upper 3/4 of the primary + map. Because this is always the distinguished secondary map, a + (bogus) address check failure will result. The failure handling + code can then figure out whether this is a genuine addr check + failure or whether it is a possibly-legitimate access at a + misaligned address. */ + + +/*------------------------------------------------------------*/ +/*--- Function declarations. ---*/ +/*------------------------------------------------------------*/ + +static void ac_ACCESS4_SLOWLY ( Addr a, Bool isWrite ); +static void ac_ACCESS2_SLOWLY ( Addr a, Bool isWrite ); +static void ac_ACCESS1_SLOWLY ( Addr a, Bool isWrite ); +static void ac_fpu_ACCESS_check_SLOWLY ( Addr addr, Int size, Bool isWrite ); + +/*------------------------------------------------------------*/ +/*--- Data defns. ---*/ +/*------------------------------------------------------------*/ + +typedef + struct { + UChar abits[8192]; + } + AcSecMap; + +static AcSecMap* primary_map[ /*65536*/ 262144 ]; +static AcSecMap distinguished_secondary_map; + +static void init_shadow_memory ( void ) +{ + Int i; + + for (i = 0; i < 8192; i++) /* Invalid address */ + distinguished_secondary_map.abits[i] = VGM_BYTE_INVALID; + + /* These entries gradually get overwritten as the used address + space expands. */ + for (i = 0; i < 65536; i++) + primary_map[i] = &distinguished_secondary_map; + + /* These ones should never change; it's a bug in Valgrind if they do. */ + for (i = 65536; i < 262144; i++) + primary_map[i] = &distinguished_secondary_map; +} + +/*------------------------------------------------------------*/ +/*--- Basic bitmap management, reading and writing. ---*/ +/*------------------------------------------------------------*/ + +/* Allocate and initialise a secondary map. */ + +static AcSecMap* alloc_secondary_map ( __attribute__ ((unused)) + Char* caller ) +{ + AcSecMap* map; + UInt i; + PROF_EVENT(10); + + /* Mark all bytes as invalid access and invalid value. */ + map = (AcSecMap *)VG_(shadow_alloc)(sizeof(AcSecMap)); + for (i = 0; i < 8192; i++) + map->abits[i] = VGM_BYTE_INVALID; /* Invalid address */ + + /* VG_(printf)("ALLOC_2MAP(%s)\n", caller ); */ + return map; +} + + +/* Basic reading/writing of the bitmaps, for byte-sized accesses. */ + +static __inline__ UChar get_abit ( Addr a ) +{ + AcSecMap* sm = primary_map[a >> 16]; + UInt sm_off = a & 0xFFFF; + PROF_EVENT(20); +# if 0 + if (IS_DISTINGUISHED_SM(sm)) + VG_(message)(Vg_DebugMsg, + "accessed distinguished 2ndary (A)map! 0x%x\n", a); +# endif + return BITARR_TEST(sm->abits, sm_off) + ? VGM_BIT_INVALID : VGM_BIT_VALID; +} + +static /* __inline__ */ void set_abit ( Addr a, UChar abit ) +{ + AcSecMap* sm; + UInt sm_off; + PROF_EVENT(22); + ENSURE_MAPPABLE(a, "set_abit"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + if (abit) + BITARR_SET(sm->abits, sm_off); + else + BITARR_CLEAR(sm->abits, sm_off); +} + + +/* Reading/writing of the bitmaps, for aligned word-sized accesses. */ + +static __inline__ UChar get_abits4_ALIGNED ( Addr a ) +{ + AcSecMap* sm; + UInt sm_off; + UChar abits8; + PROF_EVENT(24); +# ifdef VG_DEBUG_MEMORY + sk_assert(IS_ALIGNED4_ADDR(a)); +# endif + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + abits8 = sm->abits[sm_off >> 3]; + abits8 >>= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */ + abits8 &= 0x0F; + return abits8; +} + + + +/*------------------------------------------------------------*/ +/*--- Setting permissions over address ranges. ---*/ +/*------------------------------------------------------------*/ + +static /* __inline__ */ +void set_address_range_perms ( Addr a, UInt len, + UInt example_a_bit ) +{ + UChar abyte8; + UInt sm_off; + AcSecMap* sm; + + PROF_EVENT(30); + + if (len == 0) + return; + + if (len > 100 * 1000 * 1000) { + VG_(message)(Vg_UserMsg, + "Warning: set address range perms: " + "large range %u, a %d", + len, example_a_bit ); + } + + VGP_PUSHCC(VgpSetMem); + + /* Requests to change permissions of huge address ranges may + indicate bugs in our machinery. 30,000,000 is arbitrary, but so + far all legitimate requests have fallen beneath that size. */ + /* 4 Mar 02: this is just stupid; get rid of it. */ + /* sk_assert(len < 30000000); */ + + /* Check the permissions make sense. */ + sk_assert(example_a_bit == VGM_BIT_VALID + || example_a_bit == VGM_BIT_INVALID); + + /* In order that we can charge through the address space at 8 + bytes/main-loop iteration, make up some perms. */ + abyte8 = (example_a_bit << 7) + | (example_a_bit << 6) + | (example_a_bit << 5) + | (example_a_bit << 4) + | (example_a_bit << 3) + | (example_a_bit << 2) + | (example_a_bit << 1) + | (example_a_bit << 0); + +# ifdef VG_DEBUG_MEMORY + /* Do it ... */ + while (True) { + PROF_EVENT(31); + if (len == 0) break; + set_abit ( a, example_a_bit ); + set_vbyte ( a, vbyte ); + a++; + len--; + } + +# else + /* Slowly do parts preceding 8-byte alignment. */ + while (True) { + PROF_EVENT(31); + if (len == 0) break; + if ((a % 8) == 0) break; + set_abit ( a, example_a_bit ); + a++; + len--; + } + + if (len == 0) { + VGP_POPCC(VgpSetMem); + return; + } + sk_assert((a % 8) == 0 && len > 0); + + /* Once aligned, go fast. */ + while (True) { + PROF_EVENT(32); + if (len < 8) break; + ENSURE_MAPPABLE(a, "set_address_range_perms(fast)"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + sm->abits[sm_off >> 3] = abyte8; + a += 8; + len -= 8; + } + + if (len == 0) { + VGP_POPCC(VgpSetMem); + return; + } + sk_assert((a % 8) == 0 && len > 0 && len < 8); + + /* Finish the upper fragment. */ + while (True) { + PROF_EVENT(33); + if (len == 0) break; + set_abit ( a, example_a_bit ); + a++; + len--; + } +# endif + + /* Check that zero page and highest page have not been written to + -- this could happen with buggy syscall wrappers. Today + (2001-04-26) had precisely such a problem with __NR_setitimer. */ + sk_assert(SK_(cheap_sanity_check)()); + VGP_POPCC(VgpSetMem); +} + +/* Set permissions for address ranges ... */ + +static void ac_make_noaccess ( Addr a, UInt len ) +{ + PROF_EVENT(35); + DEBUG("ac_make_noaccess(%p, %x)\n", a, len); + set_address_range_perms ( a, len, VGM_BIT_INVALID ); +} + +static void ac_make_accessible ( Addr a, UInt len ) +{ + PROF_EVENT(38); + DEBUG("ac_make_accessible(%p, %x)\n", a, len); + set_address_range_perms ( a, len, VGM_BIT_VALID ); +} + +static __inline__ +void make_aligned_word_noaccess(Addr a) +{ + AcSecMap* sm; + UInt sm_off; + UChar mask; + + VGP_PUSHCC(VgpESPAdj); + ENSURE_MAPPABLE(a, "make_aligned_word_noaccess"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + mask = 0x0F; + mask <<= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */ + /* mask now contains 1s where we wish to make address bits invalid (1s). */ + sm->abits[sm_off >> 3] |= mask; + VGP_POPCC(VgpESPAdj); +} + +static __inline__ +void make_aligned_word_accessible(Addr a) +{ + AcSecMap* sm; + UInt sm_off; + UChar mask; + + VGP_PUSHCC(VgpESPAdj); + ENSURE_MAPPABLE(a, "make_aligned_word_accessible"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + mask = 0x0F; + mask <<= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */ + /* mask now contains 1s where we wish to make address bits + invalid (0s). */ + sm->abits[sm_off >> 3] &= ~mask; + VGP_POPCC(VgpESPAdj); +} + +/* Nb: by "aligned" here we mean 8-byte aligned */ +static __inline__ +void make_aligned_doubleword_accessible(Addr a) +{ + AcSecMap* sm; + UInt sm_off; + + VGP_PUSHCC(VgpESPAdj); + ENSURE_MAPPABLE(a, "make_aligned_doubleword_accessible"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + sm->abits[sm_off >> 3] = VGM_BYTE_VALID; + VGP_POPCC(VgpESPAdj); +} + +static __inline__ +void make_aligned_doubleword_noaccess(Addr a) +{ + AcSecMap* sm; + UInt sm_off; + + VGP_PUSHCC(VgpESPAdj); + ENSURE_MAPPABLE(a, "make_aligned_doubleword_noaccess"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + sm->abits[sm_off >> 3] = VGM_BYTE_INVALID; + VGP_POPCC(VgpESPAdj); +} + +/* The %esp update handling functions */ +ESP_UPDATE_HANDLERS ( make_aligned_word_accessible, + make_aligned_word_noaccess, + make_aligned_doubleword_accessible, + make_aligned_doubleword_noaccess, + ac_make_accessible, + ac_make_noaccess + ); + + +/* Block-copy permissions (needed for implementing realloc()). */ + +static void ac_copy_address_range_state ( Addr src, Addr dst, UInt len ) +{ + UInt i; + + DEBUG("ac_copy_address_range_state\n"); + + PROF_EVENT(40); + for (i = 0; i < len; i++) { + UChar abit = get_abit ( src+i ); + PROF_EVENT(41); + set_abit ( dst+i, abit ); + } +} + + +/* Check permissions for address range. If inadequate permissions + exist, *bad_addr is set to the offending address, so the caller can + know what it is. */ + +static __inline__ +Bool ac_check_accessible ( Addr a, UInt len, Addr* bad_addr ) +{ + UInt i; + UChar abit; + PROF_EVENT(48); + for (i = 0; i < len; i++) { + PROF_EVENT(49); + abit = get_abit(a); + if (abit == VGM_BIT_INVALID) { + if (bad_addr != NULL) *bad_addr = a; + return False; + } + a++; + } + return True; +} + +/* The opposite; check that an address range is inaccessible. */ +static +Bool ac_check_noaccess ( Addr a, UInt len, Addr* bad_addr ) +{ + UInt i; + UChar abit; + PROF_EVENT(48); + for (i = 0; i < len; i++) { + PROF_EVENT(49); + abit = get_abit(a); + if (abit == VGM_BIT_VALID) { + if (bad_addr != NULL) *bad_addr = a; + return False; + } + a++; + } + return True; +} + +/* Check a zero-terminated ascii string. Tricky -- don't want to + examine the actual bytes, to find the end, until we're sure it is + safe to do so. */ + +static __inline__ +Bool ac_check_readable_asciiz ( Addr a, Addr* bad_addr ) +{ + UChar abit; + PROF_EVENT(46); + DEBUG("ac_check_readable_asciiz\n"); + while (True) { + PROF_EVENT(47); + abit = get_abit(a); + if (abit != VGM_BIT_VALID) { + if (bad_addr != NULL) *bad_addr = a; + return False; + } + /* Ok, a is safe to read. */ + if (* ((UChar*)a) == 0) return True; + a++; + } +} + + +/*------------------------------------------------------------*/ +/*--- Memory event handlers ---*/ +/*------------------------------------------------------------*/ + +static __inline__ +void ac_check_is_accessible ( CorePart part, ThreadId tid, + Char* s, Addr base, UInt size, Bool isWrite ) +{ + Bool ok; + Addr bad_addr; + + VGP_PUSHCC(VgpCheckMem); + + ok = ac_check_accessible ( base, size, &bad_addr ); + if (!ok) { + switch (part) { + case Vg_CoreSysCall: + MAC_(record_param_error) ( tid, bad_addr, isWrite, s ); + break; + + case Vg_CoreSignal: + sk_assert(isWrite); /* Should only happen with isWrite case */ + /* fall through */ + case Vg_CorePThread: + MAC_(record_core_mem_error)( tid, isWrite, s ); + break; + + /* If we're being asked to jump to a silly address, record an error + message before potentially crashing the entire system. */ + case Vg_CoreTranslate: + sk_assert(!isWrite); /* Should only happen with !isWrite case */ + MAC_(record_jump_error)( tid, bad_addr ); + break; + + default: + VG_(skin_panic)("ac_check_is_accessible: unexpected CorePart"); + } + } + + VGP_POPCC(VgpCheckMem); +} + +static +void ac_check_is_writable ( CorePart part, ThreadId tid, + Char* s, Addr base, UInt size ) +{ + ac_check_is_accessible ( part, tid, s, base, size, /*isWrite*/True ); +} + +static +void ac_check_is_readable ( CorePart part, ThreadId tid, + Char* s, Addr base, UInt size ) +{ + ac_check_is_accessible ( part, tid, s, base, size, /*isWrite*/False ); +} + +static +void ac_check_is_readable_asciiz ( CorePart part, ThreadId tid, + Char* s, Addr str ) +{ + Bool ok = True; + Addr bad_addr; + /* VG_(message)(Vg_DebugMsg,"check is readable asciiz: 0x%x",str); */ + + VGP_PUSHCC(VgpCheckMem); + + sk_assert(part == Vg_CoreSysCall); + ok = ac_check_readable_asciiz ( (Addr)str, &bad_addr ); + if (!ok) { + MAC_(record_param_error) ( tid, bad_addr, /*is_writable =*/False, s ); + } + + VGP_POPCC(VgpCheckMem); +} + +static +void ac_new_mem_startup( Addr a, UInt len, Bool rr, Bool ww, Bool xx ) +{ + /* Ignore the permissions, just make it readable. Seems to work... */ + DEBUG("new_mem_startup(%p, %u, rr=%u, ww=%u, xx=%u)\n", a,len,rr,ww,xx); + ac_make_accessible(a, len); +} + +static +void ac_new_mem_heap ( Addr a, UInt len, Bool is_inited ) +{ + ac_make_accessible(a, len); +} + +static +void ac_set_perms (Addr a, UInt len, Bool rr, Bool ww, Bool xx) +{ + DEBUG("ac_set_perms(%p, %u, rr=%u ww=%u, xx=%u)\n", + a, len, rr, ww, xx); + if (rr || ww || xx) { + ac_make_accessible(a, len); + } else { + ac_make_noaccess(a, len); + } +} + + +/*------------------------------------------------------------*/ +/*--- Functions called directly from generated code. ---*/ +/*------------------------------------------------------------*/ + +static __inline__ UInt rotateRight16 ( UInt x ) +{ + /* Amazingly, gcc turns this into a single rotate insn. */ + return (x >> 16) | (x << 16); +} + +static __inline__ UInt shiftRight16 ( UInt x ) +{ + return x >> 16; +} + + +/* Read/write 1/2/4 sized V bytes, and emit an address error if + needed. */ + +/* ac_helperc_ACCESS{1,2,4} handle the common case fast. + Under all other circumstances, it defers to the relevant _SLOWLY + function, which can handle all situations. +*/ +static __inline__ void ac_helperc_ACCESS4 ( Addr a, Bool isWrite ) +{ +# ifdef VG_DEBUG_MEMORY + return ac_ACCESS4_SLOWLY(a, isWrite); +# else + UInt sec_no = rotateRight16(a) & 0x3FFFF; + AcSecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + UChar abits = sm->abits[a_off]; + abits >>= (a & 4); + abits &= 15; + PROF_EVENT(66); + if (abits == VGM_NIBBLE_VALID) { + /* Handle common case quickly: a is suitably aligned, is mapped, + and is addressible. So just return. */ + return; + } else { + /* Slow but general case. */ + ac_ACCESS4_SLOWLY(a, isWrite); + } +# endif +} + +static __inline__ void ac_helperc_ACCESS2 ( Addr a, Bool isWrite ) +{ +# ifdef VG_DEBUG_MEMORY + return ac_ACCESS2_SLOWLY(a, isWrite); +# else + UInt sec_no = rotateRight16(a) & 0x1FFFF; + AcSecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + PROF_EVENT(67); + if (sm->abits[a_off] == VGM_BYTE_VALID) { + /* Handle common case quickly. */ + return; + } else { + /* Slow but general case. */ + ac_ACCESS2_SLOWLY(a, isWrite); + } +# endif +} + +static __inline__ void ac_helperc_ACCESS1 ( Addr a, Bool isWrite ) +{ +# ifdef VG_DEBUG_MEMORY + return ac_ACCESS1_SLOWLY(a, isWrite); +# else + UInt sec_no = shiftRight16(a); + AcSecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + PROF_EVENT(68); + if (sm->abits[a_off] == VGM_BYTE_VALID) { + /* Handle common case quickly. */ + return; + } else { + /* Slow but general case. */ + ac_ACCESS1_SLOWLY(a, isWrite); + } +# endif +} + +REGPARM(1) +static void ac_helperc_LOAD4 ( Addr a ) +{ + ac_helperc_ACCESS4 ( a, /*isWrite*/False ); +} +REGPARM(1) +static void ac_helperc_STORE4 ( Addr a ) +{ + ac_helperc_ACCESS4 ( a, /*isWrite*/True ); +} + +REGPARM(1) +static void ac_helperc_LOAD2 ( Addr a ) +{ + ac_helperc_ACCESS2 ( a, /*isWrite*/False ); +} +REGPARM(1) +static void ac_helperc_STORE2 ( Addr a ) +{ + ac_helperc_ACCESS2 ( a, /*isWrite*/True ); +} + +REGPARM(1) +static void ac_helperc_LOAD1 ( Addr a ) +{ + ac_helperc_ACCESS1 ( a, /*isWrite*/False ); +} +REGPARM(1) +static void ac_helperc_STORE1 ( Addr a ) +{ + ac_helperc_ACCESS1 ( a, /*isWrite*/True ); +} + + +/*------------------------------------------------------------*/ +/*--- Fallback functions to handle cases that the above ---*/ +/*--- ac_helperc_ACCESS{1,2,4} can't manage. ---*/ +/*------------------------------------------------------------*/ + +static void ac_ACCESS4_SLOWLY ( Addr a, Bool isWrite ) +{ + Bool a0ok, a1ok, a2ok, a3ok; + + PROF_EVENT(76); + + /* First establish independently the addressibility of the 4 bytes + involved. */ + a0ok = get_abit(a+0) == VGM_BIT_VALID; + a1ok = get_abit(a+1) == VGM_BIT_VALID; + a2ok = get_abit(a+2) == VGM_BIT_VALID; + a3ok = get_abit(a+3) == VGM_BIT_VALID; + + /* Now distinguish 3 cases */ + + /* Case 1: the address is completely valid, so: + - no addressing error + */ + if (a0ok && a1ok && a2ok && a3ok) { + return; + } + + /* Case 2: the address is completely invalid. + - emit addressing error + */ + /* VG_(printf)("%p (%d %d %d %d)\n", a, a0ok, a1ok, a2ok, a3ok); */ + if (!MAC_(clo_partial_loads_ok) + || ((a & 3) != 0) + || (!a0ok && !a1ok && !a2ok && !a3ok)) { + MAC_(record_address_error)( VG_(get_current_tid)(), a, 4, isWrite ); + return; + } + + /* Case 3: the address is partially valid. + - no addressing error + Case 3 is only allowed if MAC_(clo_partial_loads_ok) is True + (which is the default), and the address is 4-aligned. + If not, Case 2 will have applied. + */ + sk_assert(MAC_(clo_partial_loads_ok)); + { + return; + } +} + +static void ac_ACCESS2_SLOWLY ( Addr a, Bool isWrite ) +{ + /* Check the address for validity. */ + Bool aerr = False; + PROF_EVENT(77); + + if (get_abit(a+0) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+1) != VGM_BIT_VALID) aerr = True; + + /* If an address error has happened, report it. */ + if (aerr) { + MAC_(record_address_error)( VG_(get_current_tid)(), a, 2, isWrite ); + } +} + +static void ac_ACCESS1_SLOWLY ( Addr a, Bool isWrite) +{ + /* Check the address for validity. */ + Bool aerr = False; + PROF_EVENT(78); + + if (get_abit(a+0) != VGM_BIT_VALID) aerr = True; + + /* If an address error has happened, report it. */ + if (aerr) { + MAC_(record_address_error)( VG_(get_current_tid)(), a, 1, isWrite ); + } +} + + +/* --------------------------------------------------------------------- + FPU load and store checks, called from generated code. + ------------------------------------------------------------------ */ + +static +void ac_fpu_ACCESS_check ( Addr addr, Int size, Bool isWrite ) +{ + /* Ensure the read area is both addressible and valid (ie, + readable). If there's an address error, don't report a value + error too; but if there isn't an address error, check for a + value error. + + Try to be reasonably fast on the common case; wimp out and defer + to ac_fpu_ACCESS_check_SLOWLY for everything else. */ + + AcSecMap* sm; + UInt sm_off, a_off; + Addr addr4; + + PROF_EVENT(90); + +# ifdef VG_DEBUG_MEMORY + ac_fpu_ACCESS_check_SLOWLY ( addr, size, isWrite ); +# else + + if (size == 4) { + if (!IS_ALIGNED4_ADDR(addr)) goto slow4; + PROF_EVENT(91); + /* Properly aligned. */ + sm = primary_map[addr >> 16]; + sm_off = addr & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow4; + /* Properly aligned and addressible. */ + return; + slow4: + ac_fpu_ACCESS_check_SLOWLY ( addr, 4, isWrite ); + return; + } + + if (size == 8) { + if (!IS_ALIGNED4_ADDR(addr)) goto slow8; + PROF_EVENT(92); + /* Properly aligned. Do it in two halves. */ + addr4 = addr + 4; + /* First half. */ + sm = primary_map[addr >> 16]; + sm_off = addr & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8; + /* First half properly aligned and addressible. */ + /* Second half. */ + sm = primary_map[addr4 >> 16]; + sm_off = addr4 & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8; + /* Second half properly aligned and addressible. */ + /* Both halves properly aligned and addressible. */ + return; + slow8: + ac_fpu_ACCESS_check_SLOWLY ( addr, 8, isWrite ); + return; + } + + /* Can't be bothered to huff'n'puff to make these (allegedly) rare + cases go quickly. */ + if (size == 2) { + PROF_EVENT(93); + ac_fpu_ACCESS_check_SLOWLY ( addr, 2, isWrite ); + return; + } + + if (size == 16 || size == 10 || size == 28 || size == 108 || size == 512) { + PROF_EVENT(94); + ac_fpu_ACCESS_check_SLOWLY ( addr, size, isWrite ); + return; + } + + VG_(printf)("size is %d\n", size); + VG_(skin_panic)("fpu_ACCESS_check: unhandled size"); +# endif +} + +REGPARM(2) +static void ac_fpu_READ_check ( Addr addr, Int size ) +{ + ac_fpu_ACCESS_check ( addr, size, /*isWrite*/False ); +} + +REGPARM(2) +static void ac_fpu_WRITE_check ( Addr addr, Int size ) +{ + ac_fpu_ACCESS_check ( addr, size, /*isWrite*/True ); +} + +/* --------------------------------------------------------------------- + Slow, general cases for FPU access checks. + ------------------------------------------------------------------ */ + +void ac_fpu_ACCESS_check_SLOWLY ( Addr addr, Int size, Bool isWrite ) +{ + Int i; + Bool aerr = False; + PROF_EVENT(100); + for (i = 0; i < size; i++) { + PROF_EVENT(101); + if (get_abit(addr+i) != VGM_BIT_VALID) + aerr = True; + } + + if (aerr) { + MAC_(record_address_error)( VG_(get_current_tid)(), addr, size, isWrite ); + } +} + + +/*------------------------------------------------------------*/ +/*--- Our instrumenter ---*/ +/*------------------------------------------------------------*/ + +UCodeBlock* SK_(instrument)(UCodeBlock* cb_in, Addr orig_addr) +{ +/* Use this rather than eg. -1 because it's a UInt. */ +#define INVALID_DATA_SIZE 999999 + + UCodeBlock* cb; + Int i; + UInstr* u_in; + Int t_addr, t_size; + Addr helper; + + cb = VG_(setup_UCodeBlock)(cb_in); + + for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { + + t_addr = t_size = INVALID_TEMPREG; + u_in = VG_(get_instr)(cb_in, i); + + switch (u_in->opcode) { + case NOP: case LOCK: case CALLM_E: case CALLM_S: + break; + + /* For memory-ref instrs, copy the data_addr into a temporary + * to be passed to the helper at the end of the instruction. + */ + case LOAD: + switch (u_in->size) { + case 4: helper = (Addr)ac_helperc_LOAD4; break; + case 2: helper = (Addr)ac_helperc_LOAD2; break; + case 1: helper = (Addr)ac_helperc_LOAD1; break; + default: VG_(skin_panic) + ("addrcheck::SK_(instrument):LOAD"); + } + uInstr1(cb, CCALL, 0, TempReg, u_in->val1); + uCCall (cb, helper, 1, 1, False ); + VG_(copy_UInstr)(cb, u_in); + break; + + case STORE: + switch (u_in->size) { + case 4: helper = (Addr)ac_helperc_STORE4; break; + case 2: helper = (Addr)ac_helperc_STORE2; break; + case 1: helper = (Addr)ac_helperc_STORE1; break; + default: VG_(skin_panic) + ("addrcheck::SK_(instrument):STORE"); + } + uInstr1(cb, CCALL, 0, TempReg, u_in->val2); + uCCall (cb, helper, 1, 1, False ); + VG_(copy_UInstr)(cb, u_in); + break; + + case SSE3ag_MemRd_RegWr: + sk_assert(u_in->size == 4 || u_in->size == 8); + helper = (Addr)ac_fpu_READ_check; + goto do_Access_ARG1; + do_Access_ARG1: + sk_assert(u_in->tag1 == TempReg); + t_addr = u_in->val1; + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, u_in->size); + uInstr2(cb, CCALL, 0, TempReg, t_addr, TempReg, t_size); + uCCall(cb, helper, 2, 2, False ); + VG_(copy_UInstr)(cb, u_in); + break; + + case MMX2_MemRd: + sk_assert(u_in->size == 4 || u_in->size == 8); + helper = (Addr)ac_fpu_READ_check; + goto do_Access_ARG2; + case MMX2_MemWr: + sk_assert(u_in->size == 4 || u_in->size == 8); + helper = (Addr)ac_fpu_WRITE_check; + goto do_Access_ARG2; + case FPU_R: + helper = (Addr)ac_fpu_READ_check; + goto do_Access_ARG2; + case FPU_W: + helper = (Addr)ac_fpu_WRITE_check; + goto do_Access_ARG2; + do_Access_ARG2: + sk_assert(u_in->tag2 == TempReg); + t_addr = u_in->val2; + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, u_in->size); + uInstr2(cb, CCALL, 0, TempReg, t_addr, TempReg, t_size); + uCCall(cb, helper, 2, 2, False ); + VG_(copy_UInstr)(cb, u_in); + break; + + case MMX2a1_MemRd: + case SSE3a_MemRd: + case SSE2a_MemRd: + case SSE3a1_MemRd: + case SSE2a1_MemRd: + helper = (Addr)ac_fpu_READ_check; + goto do_Access_ARG3; + case SSE2a_MemWr: + case SSE3a_MemWr: + helper = (Addr)ac_fpu_WRITE_check; + goto do_Access_ARG3; + do_Access_ARG3: + sk_assert(u_in->size == 4 || u_in->size == 8 + || u_in->size == 16 || u_in->size == 512); + sk_assert(u_in->tag3 == TempReg); + t_addr = u_in->val3; + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, u_in->size); + uInstr2(cb, CCALL, 0, TempReg, t_addr, TempReg, t_size); + uCCall(cb, helper, 2, 2, False ); + VG_(copy_UInstr)(cb, u_in); + break; + + case SSE3e1_RegRd: + case SSE3e_RegWr: + case SSE3g1_RegWr: + case SSE5: + case SSE3g_RegWr: + case SSE3e_RegRd: + case SSE4: + case SSE3: + default: + VG_(copy_UInstr)(cb, u_in); + break; + } + } + + VG_(free_UCodeBlock)(cb_in); + return cb; +} + + +/*------------------------------------------------------------*/ +/*--- Detecting leaked (unreachable) malloc'd blocks. ---*/ +/*------------------------------------------------------------*/ + +/* For the memory leak detector, say whether an entire 64k chunk of + address space is possibly in use, or not. If in doubt return + True. +*/ +static +Bool ac_is_valid_64k_chunk ( UInt chunk_number ) +{ + sk_assert(chunk_number >= 0 && chunk_number < 65536); + if (IS_DISTINGUISHED_SM(primary_map[chunk_number])) { + /* Definitely not in use. */ + return False; + } else { + return True; + } +} + + +/* For the memory leak detector, say whether or not a given word + address is to be regarded as valid. */ +static +Bool ac_is_valid_address ( Addr a ) +{ + UChar abits; + sk_assert(IS_ALIGNED4_ADDR(a)); + abits = get_abits4_ALIGNED(a); + if (abits == VGM_NIBBLE_VALID) { + return True; + } else { + return False; + } +} + + +/* Leak detector for this tool. We don't actually do anything, merely + run the generic leak detector with suitable parameters for this + tool. */ +static void ac_detect_memory_leaks ( void ) +{ + MAC_(do_detect_memory_leaks) ( ac_is_valid_64k_chunk, ac_is_valid_address ); +} + + +/* --------------------------------------------------------------------- + Sanity check machinery (permanently engaged). + ------------------------------------------------------------------ */ + +Bool SK_(cheap_sanity_check) ( void ) +{ + /* nothing useful we can rapidly check */ + return True; +} + +Bool SK_(expensive_sanity_check) ( void ) +{ + Int i; + + /* Make sure nobody changed the distinguished secondary. */ + for (i = 0; i < 8192; i++) + if (distinguished_secondary_map.abits[i] != VGM_BYTE_INVALID) + return False; + + /* Make sure that the upper 3/4 of the primary map hasn't + been messed with. */ + for (i = 65536; i < 262144; i++) + if (primary_map[i] != & distinguished_secondary_map) + return False; + + return True; +} + +/*------------------------------------------------------------*/ +/*--- Client requests ---*/ +/*------------------------------------------------------------*/ + +Bool SK_(handle_client_request) ( ThreadId tid, UInt* arg_block, UInt *ret ) +{ +#define IGNORE(what) \ + do { \ + if (moans-- > 0) { \ + VG_(message)(Vg_UserMsg, \ + "Warning: Addrcheck: ignoring `%s' request.", what); \ + VG_(message)(Vg_UserMsg, \ + " To honour this request, rerun with --tool=memcheck."); \ + } \ + } while (0) + + UInt* arg = arg_block; + static Int moans = 3; + + /* Overload memcheck client reqs */ + if (!VG_IS_SKIN_USERREQ('M','C',arg[0]) + && VG_USERREQ__MALLOCLIKE_BLOCK != arg[0] + && VG_USERREQ__FREELIKE_BLOCK != arg[0] + && VG_USERREQ__CREATE_MEMPOOL != arg[0] + && VG_USERREQ__DESTROY_MEMPOOL != arg[0] + && VG_USERREQ__MEMPOOL_ALLOC != arg[0] + && VG_USERREQ__MEMPOOL_FREE != arg[0]) + return False; + + switch (arg[0]) { + case VG_USERREQ__DO_LEAK_CHECK: + ac_detect_memory_leaks(); + *ret = 0; /* return value is meaningless */ + break; + + /* Ignore these */ + case VG_USERREQ__CHECK_WRITABLE: /* check writable */ + IGNORE("VALGRIND_CHECK_WRITABLE"); + return False; + case VG_USERREQ__CHECK_READABLE: /* check readable */ + IGNORE("VALGRIND_CHECK_READABLE"); + return False; + case VG_USERREQ__MAKE_NOACCESS: /* make no access */ + IGNORE("VALGRIND_MAKE_NOACCESS"); + return False; + case VG_USERREQ__MAKE_WRITABLE: /* make writable */ + IGNORE("VALGRIND_MAKE_WRITABLE"); + return False; + case VG_USERREQ__MAKE_READABLE: /* make readable */ + IGNORE("VALGRIND_MAKE_READABLE"); + return False; + case VG_USERREQ__DISCARD: /* discard */ + IGNORE("VALGRIND_CHECK_DISCARD"); + return False; + + default: + if (MAC_(handle_common_client_requests)(tid, arg_block, ret )) { + return True; + } else { + VG_(message)(Vg_UserMsg, + "Warning: unknown addrcheck client request code %d", + arg[0]); + return False; + } + } + return True; + +#undef IGNORE +} + +/*------------------------------------------------------------*/ +/*--- Setup ---*/ +/*------------------------------------------------------------*/ + +Bool SK_(process_cmd_line_option)(Char* arg) +{ + return MAC_(process_common_cmd_line_option)(arg); +} + +void SK_(print_usage)(void) +{ + MAC_(print_common_usage)(); +} + +void SK_(print_debug_usage)(void) +{ + MAC_(print_common_debug_usage)(); +} + + +/*------------------------------------------------------------*/ +/*--- Setup ---*/ +/*------------------------------------------------------------*/ + +void SK_(pre_clo_init)(void) +{ + VG_(details_name) ("Addrcheck"); + VG_(details_version) (NULL); + VG_(details_description) ("a fine-grained address checker"); + VG_(details_copyright_author)( + "Copyright (C) 2002-2004, and GNU GPL'd, by Julian Seward et al."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + VG_(details_avg_translation_sizeB) ( 135 ); + + VG_(needs_core_errors) (); + VG_(needs_skin_errors) (); + VG_(needs_libc_freeres) (); + VG_(needs_command_line_options)(); + VG_(needs_client_requests) (); + VG_(needs_syscall_wrapper) (); + VG_(needs_sanity_checks) (); + VG_(needs_shadow_memory) (); + + MAC_( new_mem_heap) = & ac_new_mem_heap; + MAC_( ban_mem_heap) = & ac_make_noaccess; + MAC_(copy_mem_heap) = & ac_copy_address_range_state; + MAC_( die_mem_heap) = & ac_make_noaccess; + MAC_(check_noaccess) = & ac_check_noaccess; + + VG_(init_new_mem_startup) ( & ac_new_mem_startup ); + VG_(init_new_mem_stack_signal) ( & ac_make_accessible ); + VG_(init_new_mem_brk) ( & ac_make_accessible ); + VG_(init_new_mem_mmap) ( & ac_set_perms ); + + VG_(init_copy_mem_remap) ( & ac_copy_address_range_state ); + VG_(init_change_mem_mprotect) ( & ac_set_perms ); + + VG_(init_die_mem_stack_signal) ( & ac_make_noaccess ); + VG_(init_die_mem_brk) ( & ac_make_noaccess ); + VG_(init_die_mem_munmap) ( & ac_make_noaccess ); + + VG_(init_new_mem_stack_4) ( & MAC_(new_mem_stack_4) ); + VG_(init_new_mem_stack_8) ( & MAC_(new_mem_stack_8) ); + VG_(init_new_mem_stack_12) ( & MAC_(new_mem_stack_12) ); + VG_(init_new_mem_stack_16) ( & MAC_(new_mem_stack_16) ); + VG_(init_new_mem_stack_32) ( & MAC_(new_mem_stack_32) ); + VG_(init_new_mem_stack) ( & MAC_(new_mem_stack) ); + + VG_(init_die_mem_stack_4) ( & MAC_(die_mem_stack_4) ); + VG_(init_die_mem_stack_8) ( & MAC_(die_mem_stack_8) ); + VG_(init_die_mem_stack_12) ( & MAC_(die_mem_stack_12) ); + VG_(init_die_mem_stack_16) ( & MAC_(die_mem_stack_16) ); + VG_(init_die_mem_stack_32) ( & MAC_(die_mem_stack_32) ); + VG_(init_die_mem_stack) ( & MAC_(die_mem_stack) ); + + VG_(init_ban_mem_stack) ( & ac_make_noaccess ); + + VG_(init_pre_mem_read) ( & ac_check_is_readable ); + VG_(init_pre_mem_read_asciiz) ( & ac_check_is_readable_asciiz ); + VG_(init_pre_mem_write) ( & ac_check_is_writable ); + VG_(init_post_mem_write) ( & ac_make_accessible ); + + VG_(register_compact_helper)((Addr) & ac_helperc_LOAD4); + VG_(register_compact_helper)((Addr) & ac_helperc_LOAD2); + VG_(register_compact_helper)((Addr) & ac_helperc_LOAD1); + VG_(register_compact_helper)((Addr) & ac_helperc_STORE4); + VG_(register_compact_helper)((Addr) & ac_helperc_STORE2); + VG_(register_compact_helper)((Addr) & ac_helperc_STORE1); + VG_(register_noncompact_helper)((Addr) & ac_fpu_READ_check); + VG_(register_noncompact_helper)((Addr) & ac_fpu_WRITE_check); + + VGP_(register_profile_event) ( VgpSetMem, "set-mem-perms" ); + VGP_(register_profile_event) ( VgpCheckMem, "check-mem-perms" ); + VGP_(register_profile_event) ( VgpESPAdj, "adjust-ESP" ); + + init_shadow_memory(); + MAC_(common_pre_clo_init)(); +} + +void SK_(post_clo_init) ( void ) +{ +} + +void SK_(fini) ( Int exitcode ) +{ + MAC_(common_fini)( ac_detect_memory_leaks ); +} + +VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 1./8) + + +/*--------------------------------------------------------------------*/ +/*--- end ac_main.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/addrcheck/docs/.cvsignore b/VEX/head20041019/addrcheck/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/addrcheck/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/addrcheck/docs/CVS/Entries b/VEX/head20041019/addrcheck/docs/CVS/Entries new file mode 100644 index 0000000000..abb809579c --- /dev/null +++ b/VEX/head20041019/addrcheck/docs/CVS/Entries @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Thu Oct 3 10:38:40 2002// +/Makefile.am/1.3/Wed Aug 25 11:40:04 2004// +/ac_main.html/1.4/Wed Dec 3 21:44:45 2003// +D diff --git a/VEX/head20041019/addrcheck/docs/CVS/Repository b/VEX/head20041019/addrcheck/docs/CVS/Repository new file mode 100644 index 0000000000..225f3caa27 --- /dev/null +++ b/VEX/head20041019/addrcheck/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/addrcheck/docs diff --git a/VEX/head20041019/addrcheck/docs/CVS/Root b/VEX/head20041019/addrcheck/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/addrcheck/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/addrcheck/docs/CVS/Template b/VEX/head20041019/addrcheck/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/docs/Makefile.am b/VEX/head20041019/addrcheck/docs/Makefile.am new file mode 100644 index 0000000000..6e049abc78 --- /dev/null +++ b/VEX/head20041019/addrcheck/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = ac_main.html diff --git a/VEX/head20041019/addrcheck/docs/ac_main.html b/VEX/head20041019/addrcheck/docs/ac_main.html new file mode 100644 index 0000000000..d540fc0051 --- /dev/null +++ b/VEX/head20041019/addrcheck/docs/ac_main.html @@ -0,0 +1,103 @@ + + + Addrcheck: a lightweight memory checker + + + + +

Addrcheck: a lightweight memory checker

+ +To use this tool, you must specify --tool=addrcheck +on the Valgrind command line. + +

5.1  Kinds of bugs that Addrcheck can find

+ +Addrcheck is a simplified version of the Memcheck tool described +in Section 3. It is identical in every way to Memcheck, except for +one important detail: it does not do the undefined-value checks that +Memcheck does. This means Addrcheck is about twice as fast as +Memcheck, and uses less memory. Addrcheck can detect the following +errors: +
    +
  • Reading/writing memory after it has been free'd
  • +
  • Reading/writing off the end of malloc'd blocks
  • +
  • Reading/writing inappropriate areas on the stack
  • +
  • Memory leaks -- where pointers to malloc'd blocks are lost + forever
  • +
  • Mismatched use of malloc/new/new [] vs free/delete/delete []
  • +
  • Overlapping src and dst pointers in + memcpy() and related functions
  • +
  • Some misuses of the POSIX pthreads API
  • +
+

+ +

+Rather than duplicate much of the Memcheck docs here (a.k.a. since I +am a lazy b'stard), users of Addrcheck are advised to read +the section on Memcheck. Some important points: +

    +
  • Addrcheck is exactly like Memcheck, except that all the + value-definedness tracking machinery has been removed. Therefore, + the Memcheck documentation which discusses definedess ("V-bits") is + irrelevant. The stuff on addressibility ("A-bits") is still + relevant. +

    +

  • Addrcheck accepts the same command-line flags as Memcheck, with + the exception of ... (to be filled in). +

    +

  • Like Memcheck, Addrcheck will do memory leak checking (internally, + the same code does leak checking for both tools). The only + difference is how the two tools decide which memory locations + to consider when searching for pointers to blocks. Memcheck will + only consider 4-byte aligned locations which are validly + addressible and which hold defined values. Addrcheck does not + track definedness and so cannot apply the last, "defined value", + criteria. +

    + The result is that Addrcheck's leak checker may "discover" + pointers to blocks that Memcheck would not. So it is possible + that Memcheck could (correctly) conclude that a block is leaked, + yet Addrcheck would not conclude that. +

    + Whether or not this has any effect in practice is unknown. I + suspect not, but that is mere speculation at this stage. +

+ +

+Addrcheck is, therefore, a fine-grained address checker. All it +really does is check each memory reference to say whether or not that +location may validly be addressed. Addrcheck has a memory overhead of +one bit per byte of used address space. In contrast, Memcheck has an +overhead of nine bits per byte. + +

+Due to lazyness on the part of the implementor (Julian), error +messages from Addrcheck do not distinguish reads from writes. So it +will say, for example, "Invalid memory access of size 4", whereas +Memcheck would have said whether the access is a read or a write. +This could easily be remedied, if anyone is particularly bothered. + +

+Addrcheck is quite pleasant to use. It's faster than Memcheck, and +the lack of valid-value checks has another side effect: the errors it +does report are relatively easy to track down, compared to the +tedious and often confusing search sometimes needed to find the +cause of uninitialised-value errors reported by Memcheck. + +

+Because it is faster and lighter than Memcheck, our hope is that +Addrcheck is more suitable for less-intrusive, larger scale testing +than is viable with Memcheck. As of mid-November 2002, we have +experimented with running the KDE-3.1 desktop on Addrcheck (the entire +process tree, starting from startkde). Running on a +512MB, 1.7 GHz P4, the result is nearly usable. The ultimate aim is +that is fast and unintrusive enough that (eg) KDE sessions may be +unintrusively monitored for addressing errors whilst people do real +work with their KDE desktop. + +

+Addrcheck is a new experiment in the Valgrind world. We'd be +interested to hear your feedback on it. + + + diff --git a/VEX/head20041019/addrcheck/tests/.cvsignore b/VEX/head20041019/addrcheck/tests/.cvsignore new file mode 100644 index 0000000000..eda0907169 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +*.stdout.diff +*.stderr.diff +*.stdout.out +*.stderr.out diff --git a/VEX/head20041019/addrcheck/tests/CVS/Entries b/VEX/head20041019/addrcheck/tests/CVS/Entries new file mode 100644 index 0000000000..e95a65bf15 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/CVS/Entries @@ -0,0 +1,34 @@ +/.cvsignore/1.3/Fri Jun 25 23:25:10 2004// +/Makefile.am/1.9/Sat Jul 10 14:56:25 2004// +/badrw.stderr.exp/1.4/Wed Jan 7 08:47:03 2004// +/badrw.vgtest/1.1/Fri Sep 5 23:29:33 2003// +/filter_stderr/1.1/Fri Oct 4 11:35:47 2002// +/fprw.stderr.exp/1.4/Tue Apr 13 08:36:35 2004// +/fprw.vgtest/1.1/Fri Sep 5 23:29:33 2003// +/insn_basic.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_basic.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_basic.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_fpu.stderr.exp/1.1/Sat Mar 27 18:02:36 2004// +/insn_fpu.stdout.exp/1.4/Wed Mar 31 22:47:52 2004// +/insn_fpu.vgtest/1.1/Sat Mar 27 18:02:36 2004// +/insn_mmx.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmx.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmx.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmxext.stderr.exp/1.2/Tue Mar 9 08:50:02 2004// +/insn_mmxext.stdout.exp/1.2/Sun Jul 25 15:18:20 2004// +/insn_mmxext.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse.stdout.exp/1.2/Sun Jul 25 15:18:20 2004// +/insn_sse.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse2.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse2.stdout.exp/1.2/Sun Jul 25 15:18:20 2004// +/insn_sse2.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/overlap.stderr.exp/1.1/Wed May 5 10:46:21 2004// +/overlap.stdout.exp/1.1/Wed May 5 10:46:21 2004// +/overlap.vgtest/1.1/Wed May 5 10:46:22 2004// +/toobig-allocs.stderr.exp/1.1/Sat Jul 10 14:56:25 2004// +/toobig-allocs.vgtest/1.1/Sat Jul 10 14:56:25 2004// +D diff --git a/VEX/head20041019/addrcheck/tests/CVS/Repository b/VEX/head20041019/addrcheck/tests/CVS/Repository new file mode 100644 index 0000000000..a6a3a58b5c --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/CVS/Repository @@ -0,0 +1 @@ +valgrind/addrcheck/tests diff --git a/VEX/head20041019/addrcheck/tests/CVS/Root b/VEX/head20041019/addrcheck/tests/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/addrcheck/tests/CVS/Template b/VEX/head20041019/addrcheck/tests/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/tests/Makefile.am b/VEX/head20041019/addrcheck/tests/Makefile.am new file mode 100644 index 0000000000..7efee7dd60 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/Makefile.am @@ -0,0 +1,14 @@ +noinst_SCRIPTS = filter_stderr + +INSN_TESTS=insn_basic insn_fpu insn_cmov insn_mmx insn_mmxext insn_sse insn_sse2 + +EXTRA_DIST = $(noinst_SCRIPTS) \ + badrw.stderr.exp badrw.vgtest \ + fprw.stderr.exp fprw.vgtest \ + insn_basic.vgtest insn_cmov.vgtest insn_mmx.vgtest \ + $(addsuffix .stderr.exp,$(INSN_TESTS)) \ + $(addsuffix .stdout.exp,$(INSN_TESTS)) \ + $(addsuffix .vgtest,$(INSN_TESTS)) \ + overlap.stderr.exp overlap.stdout.exp overlap.vgtest \ + toobig-allocs.stderr.exp toobig-allocs.vgtest + diff --git a/VEX/head20041019/addrcheck/tests/badrw.stderr.exp b/VEX/head20041019/addrcheck/tests/badrw.stderr.exp new file mode 100644 index 0000000000..77c3f4a21c --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/badrw.stderr.exp @@ -0,0 +1,35 @@ +Invalid read of size 4 + at 0x........: main (badrw.c:19) + Address 0x........ is 4 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid write of size 4 + at 0x........: main (badrw.c:20) + Address 0x........ is 4 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid read of size 2 + at 0x........: main (badrw.c:22) + Address 0x........ is 4 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid write of size 2 + at 0x........: main (badrw.c:23) + Address 0x........ is 4 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid read of size 1 + at 0x........: main (badrw.c:25) + Address 0x........ is 1 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid write of size 1 + at 0x........: main (badrw.c:26) + Address 0x........ is 1 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) diff --git a/VEX/head20041019/addrcheck/tests/badrw.vgtest b/VEX/head20041019/addrcheck/tests/badrw.vgtest new file mode 100644 index 0000000000..6f68301891 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/badrw.vgtest @@ -0,0 +1,2 @@ +vgopts: -q +prog: ../../memcheck/tests/badrw diff --git a/VEX/head20041019/addrcheck/tests/filter_stderr b/VEX/head20041019/addrcheck/tests/filter_stderr new file mode 100755 index 0000000000..e7668baff1 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/filter_stderr @@ -0,0 +1,8 @@ +#! /bin/sh + +# Same as for MemCheck + +dir=`dirname $0` + +$dir/../../memcheck/tests/filter_stderr + diff --git a/VEX/head20041019/addrcheck/tests/fprw.stderr.exp b/VEX/head20041019/addrcheck/tests/fprw.stderr.exp new file mode 100644 index 0000000000..fdd523734b --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/fprw.stderr.exp @@ -0,0 +1,34 @@ +Invalid read of size 8 + at 0x........: main (fprw.c:20) + Address 0x........ is 0 bytes inside a block of size 8 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:18) + +Invalid write of size 8 + at 0x........: main (fprw.c:20) + Address 0x........ is 0 bytes inside a block of size 8 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:18) + +Invalid read of size 4 + at 0x........: main (fprw.c:21) + Address 0x........ is 0 bytes inside a block of size 4 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:19) + +Invalid write of size 4 + at 0x........: main (fprw.c:21) + Address 0x........ is 0 bytes inside a block of size 4 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:19) + +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:22) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Invalid write of size 8 + at 0x........: main (fprw.c:24) + Address 0x........ is 0 bytes inside a block of size 4 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:23) diff --git a/VEX/head20041019/addrcheck/tests/fprw.vgtest b/VEX/head20041019/addrcheck/tests/fprw.vgtest new file mode 100644 index 0000000000..9db266e4e8 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/fprw.vgtest @@ -0,0 +1,2 @@ +vgopts: --single-step=yes -q +prog: ../../memcheck/tests/fprw diff --git a/VEX/head20041019/addrcheck/tests/insn_basic.stderr.exp b/VEX/head20041019/addrcheck/tests/insn_basic.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/tests/insn_basic.stdout.exp b/VEX/head20041019/addrcheck/tests/insn_basic.stdout.exp new file mode 100644 index 0000000000..40cabbcd02 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_basic.stdout.exp @@ -0,0 +1,1083 @@ +aaa_1 ... ok +aaa_2 ... ok +aaa_3 ... ok +aaa_4 ... ok +aaa_5 ... ok +aaa_6 ... ok +aaa_7 ... ok +aaa_8 ... ok +aad_1 ... ok +aad_2 ... ok +aam_1 ... ok +aam_2 ... ok +aas_1 ... ok +aas_2 ... ok +aas_3 ... ok +aas_4 ... ok +aas_5 ... ok +aas_6 ... ok +aas_7 ... ok +aas_8 ... ok +adcb_1 ... ok +adcb_2 ... ok +adcb_3 ... ok +adcb_4 ... ok +adcb_5 ... ok +adcb_6 ... ok +adcb_7 ... ok +adcb_8 ... ok +adcb_9 ... ok +adcb_10 ... ok +adcb_11 ... ok +adcb_12 ... ok +adcw_1 ... ok +adcw_2 ... ok +adcw_3 ... ok +adcw_4 ... ok +adcw_5 ... ok +adcw_6 ... ok +adcw_7 ... ok +adcw_8 ... ok +adcw_9 ... ok +adcw_10 ... ok +adcw_11 ... ok +adcw_12 ... ok +adcw_13 ... ok +adcw_14 ... ok +adcl_1 ... ok +adcl_2 ... ok +adcl_3 ... ok +adcl_4 ... ok +adcl_5 ... ok +adcl_6 ... ok +adcl_7 ... ok +adcl_8 ... ok +adcl_9 ... ok +adcl_10 ... ok +adcl_11 ... ok +adcl_12 ... ok +adcl_13 ... ok +adcl_14 ... ok +addb_1 ... ok +addb_2 ... ok +addb_3 ... ok +addb_4 ... ok +addb_5 ... ok +addb_6 ... ok +addw_1 ... ok +addw_2 ... ok +addw_3 ... ok +addw_4 ... ok +addw_5 ... ok +addw_6 ... ok +addw_7 ... ok +addl_1 ... ok +addl_2 ... ok +addl_3 ... ok +addl_4 ... ok +addl_5 ... ok +addl_6 ... ok +addl_7 ... ok +andb_1 ... ok +andb_2 ... ok +andb_3 ... ok +andb_4 ... ok +andb_5 ... ok +andb_6 ... ok +andw_1 ... ok +andw_2 ... ok +andw_3 ... ok +andw_4 ... ok +andw_5 ... ok +andw_6 ... ok +andw_7 ... ok +andl_1 ... ok +andl_2 ... ok +andl_3 ... ok +andl_4 ... ok +andl_5 ... ok +andl_6 ... ok +andl_7 ... ok +bsfw_1 ... ok +bsfw_2 ... ok +bsfl_1 ... ok +bsfl_2 ... ok +bsrw_1 ... ok +bsrw_2 ... ok +bsrl_1 ... ok +bsrl_2 ... ok +bswapl_1 ... ok +btw_1 ... ok +btw_2 ... ok +btw_3 ... ok +btw_4 ... ok +btw_5 ... ok +btw_6 ... ok +btw_7 ... ok +btw_8 ... ok +btl_1 ... ok +btl_2 ... ok +btl_3 ... ok +btl_4 ... ok +btl_5 ... ok +btl_6 ... ok +btl_7 ... ok +btl_8 ... ok +btcw_1 ... ok +btcw_2 ... ok +btcw_3 ... ok +btcw_4 ... ok +btcw_5 ... ok +btcw_6 ... ok +btcw_7 ... ok +btcw_8 ... ok +btcl_1 ... ok +btcl_2 ... ok +btcl_3 ... ok +btcl_4 ... ok +btcl_5 ... ok +btcl_6 ... ok +btcl_7 ... ok +btcl_8 ... ok +btrw_1 ... ok +btrw_2 ... ok +btrw_3 ... ok +btrw_4 ... ok +btrw_5 ... ok +btrw_6 ... ok +btrw_7 ... ok +btrw_8 ... ok +btrl_1 ... ok +btrl_2 ... ok +btrl_3 ... ok +btrl_4 ... ok +btrl_5 ... ok +btrl_6 ... ok +btrl_7 ... ok +btrl_8 ... ok +btsw_1 ... ok +btsw_2 ... ok +btsw_3 ... ok +btsw_4 ... ok +btsw_5 ... ok +btsw_6 ... ok +btsw_7 ... ok +btsw_8 ... ok +btsl_1 ... ok +btsl_2 ... ok +btsl_3 ... ok +btsl_4 ... ok +btsl_5 ... ok +btsl_6 ... ok +btsl_7 ... ok +btsl_8 ... ok +cbw_1 ... ok +cbw_2 ... ok +cdq_1 ... ok +cdq_2 ... ok +clc_1 ... ok +clc_2 ... ok +cld_1 ... ok +cld_2 ... ok +cmc_1 ... ok +cmc_2 ... ok +cmpb_1 ... ok +cmpb_2 ... ok +cmpb_3 ... ok +cmpb_4 ... ok +cmpb_5 ... ok +cmpb_6 ... ok +cmpb_7 ... ok +cmpb_8 ... ok +cmpb_9 ... ok +cmpb_10 ... ok +cmpb_11 ... ok +cmpb_12 ... ok +cmpb_13 ... ok +cmpb_14 ... ok +cmpb_15 ... ok +cmpb_16 ... ok +cmpb_17 ... ok +cmpb_18 ... ok +cmpb_19 ... ok +cmpb_20 ... ok +cmpb_21 ... ok +cmpb_22 ... ok +cmpb_23 ... ok +cmpb_24 ... ok +cmpb_25 ... ok +cmpb_26 ... ok +cmpb_27 ... ok +cmpb_28 ... ok +cmpb_29 ... ok +cmpb_30 ... ok +cmpb_31 ... ok +cmpb_32 ... ok +cmpb_33 ... ok +cmpb_34 ... ok +cmpb_35 ... ok +cmpb_36 ... ok +cmpb_37 ... ok +cmpb_38 ... ok +cmpb_39 ... ok +cmpb_40 ... ok +cmpb_41 ... ok +cmpb_42 ... ok +cmpb_43 ... ok +cmpb_44 ... ok +cmpb_45 ... ok +cmpb_46 ... ok +cmpb_47 ... ok +cmpb_48 ... ok +cmpb_49 ... ok +cmpb_50 ... ok +cmpb_51 ... ok +cmpb_52 ... ok +cmpb_53 ... ok +cmpb_54 ... ok +cmpb_55 ... ok +cmpb_56 ... ok +cmpb_57 ... ok +cmpb_58 ... ok +cmpb_59 ... ok +cmpb_60 ... ok +cmpw_1 ... ok +cmpw_2 ... ok +cmpw_3 ... ok +cmpw_4 ... ok +cmpw_5 ... ok +cmpw_6 ... ok +cmpw_7 ... ok +cmpw_8 ... ok +cmpw_9 ... ok +cmpw_10 ... ok +cmpw_11 ... ok +cmpw_12 ... ok +cmpw_13 ... ok +cmpw_14 ... ok +cmpw_15 ... ok +cmpw_16 ... ok +cmpw_17 ... ok +cmpw_18 ... ok +cmpw_19 ... ok +cmpw_20 ... ok +cmpw_21 ... ok +cmpw_22 ... ok +cmpw_23 ... ok +cmpw_24 ... ok +cmpw_25 ... ok +cmpw_26 ... ok +cmpw_27 ... ok +cmpw_28 ... ok +cmpw_29 ... ok +cmpw_30 ... ok +cmpw_31 ... ok +cmpw_32 ... ok +cmpw_33 ... ok +cmpw_34 ... ok +cmpw_35 ... ok +cmpw_36 ... ok +cmpw_37 ... ok +cmpw_38 ... ok +cmpw_39 ... ok +cmpw_40 ... ok +cmpw_41 ... ok +cmpw_42 ... ok +cmpw_43 ... ok +cmpw_44 ... ok +cmpw_45 ... ok +cmpw_46 ... ok +cmpw_47 ... ok +cmpw_48 ... ok +cmpw_49 ... ok +cmpw_50 ... ok +cmpw_51 ... ok +cmpw_52 ... ok +cmpw_53 ... ok +cmpw_54 ... ok +cmpw_55 ... ok +cmpw_56 ... ok +cmpw_57 ... ok +cmpw_58 ... ok +cmpw_59 ... ok +cmpw_60 ... ok +cmpw_61 ... ok +cmpw_62 ... ok +cmpw_63 ... ok +cmpw_64 ... ok +cmpw_65 ... ok +cmpw_66 ... ok +cmpw_67 ... ok +cmpw_68 ... ok +cmpw_69 ... ok +cmpw_70 ... ok +cmpw_71 ... ok +cmpw_72 ... ok +cmpw_73 ... ok +cmpw_74 ... ok +cmpw_75 ... ok +cmpw_76 ... ok +cmpw_77 ... ok +cmpw_78 ... ok +cmpw_79 ... ok +cmpw_80 ... ok +cmpl_1 ... ok +cmpl_2 ... ok +cmpl_3 ... ok +cmpl_4 ... ok +cmpl_5 ... ok +cmpl_6 ... ok +cmpl_7 ... ok +cmpl_8 ... ok +cmpl_9 ... ok +cmpl_10 ... ok +cmpl_11 ... ok +cmpl_12 ... ok +cmpl_13 ... ok +cmpl_14 ... ok +cmpl_15 ... ok +cmpl_16 ... ok +cmpl_17 ... ok +cmpl_18 ... ok +cmpl_19 ... ok +cmpl_20 ... ok +cmpl_21 ... ok +cmpl_22 ... ok +cmpl_23 ... ok +cmpl_24 ... ok +cmpl_25 ... ok +cmpl_26 ... ok +cmpl_27 ... ok +cmpl_28 ... ok +cmpl_29 ... ok +cmpl_30 ... ok +cmpl_31 ... ok +cmpl_32 ... ok +cmpl_33 ... ok +cmpl_34 ... ok +cmpl_35 ... ok +cmpl_36 ... ok +cmpl_37 ... ok +cmpl_38 ... ok +cmpl_39 ... ok +cmpl_40 ... ok +cmpl_41 ... ok +cmpl_42 ... ok +cmpl_43 ... ok +cmpl_44 ... ok +cmpl_45 ... ok +cmpl_46 ... ok +cmpl_47 ... ok +cmpl_48 ... ok +cmpl_49 ... ok +cmpl_50 ... ok +cmpl_51 ... ok +cmpl_52 ... ok +cmpl_53 ... ok +cmpl_54 ... ok +cmpl_55 ... ok +cmpl_56 ... ok +cmpl_57 ... ok +cmpl_58 ... ok +cmpl_59 ... ok +cmpl_60 ... ok +cmpl_61 ... ok +cmpl_62 ... ok +cmpl_63 ... ok +cmpl_64 ... ok +cmpl_65 ... ok +cmpl_66 ... ok +cmpl_67 ... ok +cmpl_68 ... ok +cmpl_69 ... ok +cmpl_70 ... ok +cmpl_71 ... ok +cmpl_72 ... ok +cmpl_73 ... ok +cmpl_74 ... ok +cmpl_75 ... ok +cmpl_76 ... ok +cmpl_77 ... ok +cmpl_78 ... ok +cmpl_79 ... ok +cmpl_80 ... ok +cmpxchgb_1 ... ok +cmpxchgb_2 ... ok +cmpxchgb_3 ... ok +cmpxchgb_4 ... ok +cmpxchgw_1 ... ok +cmpxchgw_2 ... ok +cmpxchgw_3 ... ok +cmpxchgw_4 ... ok +cmpxchgl_1 ... ok +cmpxchgl_2 ... ok +cmpxchgl_3 ... ok +cmpxchgl_4 ... ok +cwd_1 ... ok +cwd_2 ... ok +cwde_1 ... ok +cwde_2 ... ok +daa_1 ... ok +daa_2 ... ok +das_1 ... ok +decb_1 ... ok +decb_2 ... ok +decw_1 ... ok +decw_2 ... ok +decl_1 ... ok +decl_2 ... ok +divb_1 ... ok +divb_2 ... ok +divw_1 ... ok +divw_2 ... ok +divl_1 ... ok +divl_2 ... ok +idivb_1 ... ok +idivb_2 ... ok +idivw_1 ... ok +idivw_2 ... ok +idivl_1 ... ok +idivl_2 ... ok +imulb_1 ... ok +imulb_2 ... ok +imulw_1 ... ok +imulw_2 ... ok +imull_1 ... ok +imull_2 ... ok +imulw_3 ... ok +imulw_4 ... ok +imulw_5 ... ok +imulw_6 ... ok +imulw_7 ... ok +imulw_8 ... ok +imulw_9 ... ok +imulw_10 ... ok +imull_3 ... ok +imull_4 ... ok +imull_5 ... ok +imull_6 ... ok +imull_7 ... ok +imull_8 ... ok +imull_9 ... ok +imull_10 ... ok +incb_1 ... ok +incb_2 ... ok +incw_1 ... ok +incw_2 ... ok +incl_1 ... ok +incl_2 ... ok +lahf_1 ... ok +lahf_2 ... ok +movb_1 ... ok +movb_2 ... ok +movb_3 ... ok +movb_4 ... ok +movb_5 ... ok +movw_1 ... ok +movw_2 ... ok +movw_3 ... ok +movw_4 ... ok +movw_5 ... ok +movl_1 ... ok +movl_2 ... ok +movl_3 ... ok +movl_4 ... ok +movl_5 ... ok +movsbw_1 ... ok +movsbw_2 ... ok +movsbl_1 ... ok +movsbl_2 ... ok +movswl_1 ... ok +movswl_2 ... ok +movzbw_1 ... ok +movzbw_2 ... ok +movzbl_1 ... ok +movzbl_2 ... ok +movzwl_1 ... ok +movzwl_2 ... ok +mulb_1 ... ok +mulb_2 ... ok +mulw_1 ... ok +mulw_2 ... ok +mull_1 ... ok +mull_2 ... ok +negb_1 ... ok +negb_2 ... ok +negw_1 ... ok +negw_2 ... ok +negl_1 ... ok +negl_2 ... ok +notb_1 ... ok +notb_2 ... ok +notw_1 ... ok +notw_2 ... ok +notl_1 ... ok +notl_2 ... ok +orb_1 ... ok +orb_2 ... ok +orb_3 ... ok +orb_4 ... ok +orb_5 ... ok +orb_6 ... ok +orw_1 ... ok +orw_2 ... ok +orw_3 ... ok +orw_4 ... ok +orw_5 ... ok +orw_6 ... ok +orw_7 ... ok +orl_1 ... ok +orl_2 ... ok +orl_3 ... ok +orl_4 ... ok +orl_5 ... ok +orl_6 ... ok +orl_7 ... ok +rclb_1 ... ok +rclb_2 ... ok +rclb_3 ... ok +rclb_4 ... ok +rclb_5 ... ok +rclb_6 ... ok +rclw_1 ... ok +rclw_2 ... ok +rclw_3 ... ok +rclw_4 ... ok +rclw_5 ... ok +rclw_6 ... ok +rcll_1 ... ok +rcll_2 ... ok +rcll_3 ... ok +rcll_4 ... ok +rcll_5 ... ok +rcll_6 ... ok +rcrb_1 ... ok +rcrb_2 ... ok +rcrb_3 ... ok +rcrb_4 ... ok +rcrb_5 ... ok +rcrb_6 ... ok +rcrw_1 ... ok +rcrw_2 ... ok +rcrw_3 ... ok +rcrw_4 ... ok +rcrw_5 ... ok +rcrw_6 ... ok +rcrl_1 ... ok +rcrl_2 ... ok +rcrl_3 ... ok +rcrl_4 ... ok +rcrl_5 ... ok +rcrl_6 ... ok +rolb_1 ... ok +rolb_2 ... ok +rolb_3 ... ok +rolb_4 ... ok +rolb_5 ... ok +rolb_6 ... ok +rolw_1 ... ok +rolw_2 ... ok +rolw_3 ... ok +rolw_4 ... ok +rolw_5 ... ok +rolw_6 ... ok +roll_1 ... ok +roll_2 ... ok +roll_3 ... ok +roll_4 ... ok +roll_5 ... ok +roll_6 ... ok +rorb_1 ... ok +rorb_2 ... ok +rorb_3 ... ok +rorb_4 ... ok +rorb_5 ... ok +rorb_6 ... ok +rorw_1 ... ok +rorw_2 ... ok +rorw_3 ... ok +rorw_4 ... ok +rorw_5 ... ok +rorw_6 ... ok +rorl_1 ... ok +rorl_2 ... ok +rorl_3 ... ok +rorl_4 ... ok +rorl_5 ... ok +rorl_6 ... ok +sahf_1 ... ok +sahf_2 ... ok +salb_1 ... ok +salb_2 ... ok +salb_3 ... ok +salb_4 ... ok +salb_5 ... ok +salb_6 ... ok +salw_1 ... ok +salw_2 ... ok +salw_3 ... ok +salw_4 ... ok +salw_5 ... ok +salw_6 ... ok +sall_1 ... ok +sall_2 ... ok +sall_3 ... ok +sall_4 ... ok +sall_5 ... ok +sall_6 ... ok +sarb_1 ... ok +sarb_2 ... ok +sarb_3 ... ok +sarb_4 ... ok +sarb_5 ... ok +sarb_6 ... ok +sarw_1 ... ok +sarw_2 ... ok +sarw_3 ... ok +sarw_4 ... ok +sarw_5 ... ok +sarw_6 ... ok +sarl_1 ... ok +sarl_2 ... ok +sarl_3 ... ok +sarl_4 ... ok +sarl_5 ... ok +sarl_6 ... ok +sbbb_1 ... ok +sbbb_2 ... ok +sbbb_3 ... ok +sbbb_4 ... ok +sbbb_5 ... ok +sbbb_6 ... ok +sbbb_7 ... ok +sbbb_8 ... ok +sbbb_9 ... ok +sbbb_10 ... ok +sbbb_11 ... ok +sbbb_12 ... ok +sbbw_1 ... ok +sbbw_2 ... ok +sbbw_3 ... ok +sbbw_4 ... ok +sbbw_5 ... ok +sbbw_6 ... ok +sbbw_7 ... ok +sbbw_8 ... ok +sbbw_9 ... ok +sbbw_10 ... ok +sbbw_11 ... ok +sbbw_12 ... ok +sbbw_13 ... ok +sbbw_14 ... ok +sbbl_1 ... ok +sbbl_2 ... ok +sbbl_3 ... ok +sbbl_4 ... ok +sbbl_5 ... ok +sbbl_6 ... ok +sbbl_7 ... ok +sbbl_8 ... ok +sbbl_9 ... ok +sbbl_10 ... ok +sbbl_11 ... ok +sbbl_12 ... ok +sbbl_13 ... ok +sbbl_14 ... ok +seta_1 ... ok +seta_2 ... ok +seta_3 ... ok +seta_4 ... ok +seta_5 ... ok +seta_6 ... ok +seta_7 ... ok +seta_8 ... ok +setae_1 ... ok +setae_2 ... ok +setae_3 ... ok +setae_4 ... ok +setb_1 ... ok +setb_2 ... ok +setb_3 ... ok +setb_4 ... ok +setbe_1 ... ok +setbe_2 ... ok +setbe_3 ... ok +setbe_4 ... ok +setbe_5 ... ok +setbe_6 ... ok +setbe_7 ... ok +setbe_8 ... ok +setc_1 ... ok +setc_2 ... ok +setc_3 ... ok +setc_4 ... ok +sete_1 ... ok +sete_2 ... ok +sete_3 ... ok +sete_4 ... ok +setg_1 ... ok +setg_2 ... ok +setg_3 ... ok +setg_4 ... ok +setg_5 ... ok +setg_6 ... ok +setg_7 ... ok +setg_8 ... ok +setg_9 ... ok +setg_10 ... ok +setg_11 ... ok +setg_12 ... ok +setg_13 ... ok +setg_14 ... ok +setg_15 ... ok +setg_16 ... ok +setge_1 ... ok +setge_2 ... ok +setge_3 ... ok +setge_4 ... ok +setge_5 ... ok +setge_6 ... ok +setge_7 ... ok +setge_8 ... ok +setl_1 ... ok +setl_2 ... ok +setl_3 ... ok +setl_4 ... ok +setl_5 ... ok +setl_6 ... ok +setl_7 ... ok +setl_8 ... ok +setle_1 ... ok +setle_2 ... ok +setle_3 ... ok +setle_4 ... ok +setle_5 ... ok +setle_6 ... ok +setle_7 ... ok +setle_8 ... ok +setle_9 ... ok +setle_10 ... ok +setle_11 ... ok +setle_12 ... ok +setle_13 ... ok +setle_14 ... ok +setle_15 ... ok +setle_16 ... ok +setna_1 ... ok +setna_2 ... ok +setna_3 ... ok +setna_4 ... ok +setna_5 ... ok +setna_6 ... ok +setna_7 ... ok +setna_8 ... ok +setnae_1 ... ok +setnae_2 ... ok +setnae_3 ... ok +setnae_4 ... ok +setnb_1 ... ok +setnb_2 ... ok +setnb_3 ... ok +setnb_4 ... ok +setnbe_1 ... ok +setnbe_2 ... ok +setnbe_3 ... ok +setnbe_4 ... ok +setnbe_5 ... ok +setnbe_6 ... ok +setnbe_7 ... ok +setnbe_8 ... ok +setnc_1 ... ok +setnc_2 ... ok +setnc_3 ... ok +setnc_4 ... ok +setne_1 ... ok +setne_2 ... ok +setne_3 ... ok +setne_4 ... ok +setng_1 ... ok +setng_2 ... ok +setng_3 ... ok +setng_4 ... ok +setng_5 ... ok +setng_6 ... ok +setng_7 ... ok +setng_8 ... ok +setng_9 ... ok +setng_10 ... ok +setng_11 ... ok +setng_12 ... ok +setng_13 ... ok +setng_14 ... ok +setng_15 ... ok +setng_16 ... ok +setnge_1 ... ok +setnge_2 ... ok +setnge_3 ... ok +setnge_4 ... ok +setnge_5 ... ok +setnge_6 ... ok +setnge_7 ... ok +setnge_8 ... ok +setnl_1 ... ok +setnl_2 ... ok +setnl_3 ... ok +setnl_4 ... ok +setnl_5 ... ok +setnl_6 ... ok +setnl_7 ... ok +setnl_8 ... ok +setnle_1 ... ok +setnle_2 ... ok +setnle_3 ... ok +setnle_4 ... ok +setnle_5 ... ok +setnle_6 ... ok +setnle_7 ... ok +setnle_8 ... ok +setnle_9 ... ok +setnle_10 ... ok +setnle_11 ... ok +setnle_12 ... ok +setnle_13 ... ok +setnle_14 ... ok +setnle_15 ... ok +setnle_16 ... ok +setno_1 ... ok +setno_2 ... ok +setno_3 ... ok +setno_4 ... ok +setnp_1 ... ok +setnp_2 ... ok +setnp_3 ... ok +setnp_4 ... ok +setns_1 ... ok +setns_2 ... ok +setns_3 ... ok +setns_4 ... ok +setnz_1 ... ok +setnz_2 ... ok +setnz_3 ... ok +setnz_4 ... ok +seto_1 ... ok +seto_2 ... ok +seto_3 ... ok +seto_4 ... ok +setp_1 ... ok +setp_2 ... ok +setp_3 ... ok +setp_4 ... ok +sets_1 ... ok +sets_2 ... ok +sets_3 ... ok +sets_4 ... ok +setz_1 ... ok +setz_2 ... ok +setz_3 ... ok +setz_4 ... ok +shlb_1 ... ok +shlb_2 ... ok +shlb_3 ... ok +shlb_4 ... ok +shlb_5 ... ok +shlb_6 ... ok +shlw_1 ... ok +shlw_2 ... ok +shlw_3 ... ok +shlw_4 ... ok +shlw_5 ... ok +shlw_6 ... ok +shll_1 ... ok +shll_2 ... ok +shll_3 ... ok +shll_4 ... ok +shll_5 ... ok +shll_6 ... ok +shrb_1 ... ok +shrb_2 ... ok +shrb_3 ... ok +shrb_4 ... ok +shrb_5 ... ok +shrb_6 ... ok +shrw_1 ... ok +shrw_2 ... ok +shrw_3 ... ok +shrw_4 ... ok +shrw_5 ... ok +shrw_6 ... ok +shrl_1 ... ok +shrl_2 ... ok +shrl_3 ... ok +shrl_4 ... ok +shrl_5 ... ok +shrl_6 ... ok +shldw_1 ... ok +shldw_2 ... ok +shldw_3 ... ok +shldw_4 ... ok +shldw_5 ... ok +shldw_6 ... ok +shldw_7 ... ok +shldw_8 ... ok +shldl_1 ... ok +shldl_2 ... ok +shldl_3 ... ok +shldl_4 ... ok +shldl_5 ... ok +shldl_6 ... ok +shldl_7 ... ok +shldl_8 ... ok +shrdw_1 ... ok +shrdw_2 ... ok +shrdw_3 ... ok +shrdw_4 ... ok +shrdw_5 ... ok +shrdw_6 ... ok +shrdw_7 ... ok +shrdw_8 ... ok +shrdl_1 ... ok +shrdl_2 ... ok +shrdl_3 ... ok +shrdl_4 ... ok +shrdl_5 ... ok +shrdl_6 ... ok +shrdl_7 ... ok +shrdl_8 ... ok +stc_1 ... ok +stc_2 ... ok +std_1 ... ok +std_2 ... ok +subb_1 ... ok +subb_2 ... ok +subb_3 ... ok +subb_4 ... ok +subb_5 ... ok +subb_6 ... ok +subw_1 ... ok +subw_2 ... ok +subw_3 ... ok +subw_4 ... ok +subw_5 ... ok +subw_6 ... ok +subw_7 ... ok +subl_1 ... ok +subl_2 ... ok +subl_3 ... ok +subl_4 ... ok +subl_5 ... ok +subl_6 ... ok +subl_7 ... ok +testb_1 ... ok +testb_2 ... ok +testb_3 ... ok +testb_4 ... ok +testb_5 ... ok +testb_6 ... ok +testb_7 ... ok +testb_8 ... ok +testb_9 ... ok +testb_10 ... ok +testb_11 ... ok +testb_12 ... ok +testb_13 ... ok +testb_14 ... ok +testb_15 ... ok +testb_16 ... ok +testb_17 ... ok +testb_18 ... ok +testb_19 ... ok +testb_20 ... ok +testb_21 ... ok +testb_22 ... ok +testb_23 ... ok +testb_24 ... ok +testb_25 ... ok +testw_1 ... ok +testw_2 ... ok +testw_3 ... ok +testw_4 ... ok +testw_5 ... ok +testw_6 ... ok +testw_7 ... ok +testw_8 ... ok +testw_9 ... ok +testw_10 ... ok +testw_11 ... ok +testw_12 ... ok +testw_13 ... ok +testw_14 ... ok +testw_15 ... ok +testw_16 ... ok +testw_17 ... ok +testw_18 ... ok +testw_19 ... ok +testw_20 ... ok +testw_21 ... ok +testw_22 ... ok +testw_23 ... ok +testw_24 ... ok +testw_25 ... ok +testl_1 ... ok +testl_2 ... ok +testl_3 ... ok +testl_4 ... ok +testl_5 ... ok +testl_6 ... ok +testl_7 ... ok +testl_8 ... ok +testl_9 ... ok +testl_10 ... ok +testl_11 ... ok +testl_12 ... ok +testl_13 ... ok +testl_14 ... ok +testl_15 ... ok +testl_16 ... ok +testl_17 ... ok +testl_18 ... ok +testl_19 ... ok +testl_20 ... ok +testl_21 ... ok +testl_22 ... ok +testl_23 ... ok +testl_24 ... ok +testl_25 ... ok +xaddb_1 ... ok +xaddb_2 ... ok +xaddw_1 ... ok +xaddw_2 ... ok +xaddl_1 ... ok +xaddl_2 ... ok +xchgb_1 ... ok +xchgb_2 ... ok +xchgb_3 ... ok +xchgw_1 ... ok +xchgw_2 ... ok +xchgw_3 ... ok +xchgw_4 ... ok +xchgw_5 ... ok +xchgl_1 ... ok +xchgl_2 ... ok +xchgl_3 ... ok +xchgl_4 ... ok +xchgl_5 ... ok +xorb_1 ... ok +xorb_2 ... ok +xorb_3 ... ok +xorb_4 ... ok +xorb_5 ... ok +xorb_6 ... ok +xorw_1 ... ok +xorw_2 ... ok +xorw_3 ... ok +xorw_4 ... ok +xorw_5 ... ok +xorw_6 ... ok +xorw_7 ... ok +xorl_1 ... ok +xorl_2 ... ok +xorl_3 ... ok +xorl_4 ... ok +xorl_5 ... ok +xorl_6 ... ok +xorl_7 ... ok diff --git a/VEX/head20041019/addrcheck/tests/insn_basic.vgtest b/VEX/head20041019/addrcheck/tests/insn_basic.vgtest new file mode 100644 index 0000000000..f5329ea810 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_basic.vgtest @@ -0,0 +1,2 @@ +vgopts: -q +prog: ../../none/tests/insn_basic diff --git a/VEX/head20041019/addrcheck/tests/insn_cmov.stderr.exp b/VEX/head20041019/addrcheck/tests/insn_cmov.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/tests/insn_cmov.stdout.exp b/VEX/head20041019/addrcheck/tests/insn_cmov.stdout.exp new file mode 100644 index 0000000000..31ac17204d --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_cmov.stdout.exp @@ -0,0 +1,384 @@ +cmova_1 ... ok +cmova_2 ... ok +cmova_3 ... ok +cmova_4 ... ok +cmova_5 ... ok +cmova_6 ... ok +cmova_7 ... ok +cmova_8 ... ok +cmovae_1 ... ok +cmovae_2 ... ok +cmovae_3 ... ok +cmovae_4 ... ok +cmovb_1 ... ok +cmovb_2 ... ok +cmovb_3 ... ok +cmovb_4 ... ok +cmovbe_1 ... ok +cmovbe_2 ... ok +cmovbe_3 ... ok +cmovbe_4 ... ok +cmovbe_5 ... ok +cmovbe_6 ... ok +cmovbe_7 ... ok +cmovbe_8 ... ok +cmovc_1 ... ok +cmovc_2 ... ok +cmovc_3 ... ok +cmovc_4 ... ok +cmove_1 ... ok +cmove_2 ... ok +cmove_3 ... ok +cmove_4 ... ok +cmovg_1 ... ok +cmovg_2 ... ok +cmovg_3 ... ok +cmovg_4 ... ok +cmovg_5 ... ok +cmovg_6 ... ok +cmovg_7 ... ok +cmovg_8 ... ok +cmovg_9 ... ok +cmovg_10 ... ok +cmovg_11 ... ok +cmovg_12 ... ok +cmovg_13 ... ok +cmovg_14 ... ok +cmovg_15 ... ok +cmovg_16 ... ok +cmovge_1 ... ok +cmovge_2 ... ok +cmovge_3 ... ok +cmovge_4 ... ok +cmovge_5 ... ok +cmovge_6 ... ok +cmovge_7 ... ok +cmovge_8 ... ok +cmovl_1 ... ok +cmovl_2 ... ok +cmovl_3 ... ok +cmovl_4 ... ok +cmovl_5 ... ok +cmovl_6 ... ok +cmovl_7 ... ok +cmovl_8 ... ok +cmovle_1 ... ok +cmovle_2 ... ok +cmovle_3 ... ok +cmovle_4 ... ok +cmovle_5 ... ok +cmovle_6 ... ok +cmovle_7 ... ok +cmovle_8 ... ok +cmovle_9 ... ok +cmovle_10 ... ok +cmovle_11 ... ok +cmovle_12 ... ok +cmovle_13 ... ok +cmovle_14 ... ok +cmovle_15 ... ok +cmovle_16 ... ok +cmovna_1 ... ok +cmovna_2 ... ok +cmovna_3 ... ok +cmovna_4 ... ok +cmovna_5 ... ok +cmovna_6 ... ok +cmovna_7 ... ok +cmovna_8 ... ok +cmovnae_1 ... ok +cmovnae_2 ... ok +cmovnae_3 ... ok +cmovnae_4 ... ok +cmovnb_1 ... ok +cmovnb_2 ... ok +cmovnb_3 ... ok +cmovnb_4 ... ok +cmovnbe_1 ... ok +cmovnbe_2 ... ok +cmovnbe_3 ... ok +cmovnbe_4 ... ok +cmovnbe_5 ... ok +cmovnbe_6 ... ok +cmovnbe_7 ... ok +cmovnbe_8 ... ok +cmovnc_1 ... ok +cmovnc_2 ... ok +cmovnc_3 ... ok +cmovnc_4 ... ok +cmovne_1 ... ok +cmovne_2 ... ok +cmovne_3 ... ok +cmovne_4 ... ok +cmovng_1 ... ok +cmovng_2 ... ok +cmovng_3 ... ok +cmovng_4 ... ok +cmovng_5 ... ok +cmovng_6 ... ok +cmovng_7 ... ok +cmovng_8 ... ok +cmovng_9 ... ok +cmovng_10 ... ok +cmovng_11 ... ok +cmovng_12 ... ok +cmovng_13 ... ok +cmovng_14 ... ok +cmovng_15 ... ok +cmovng_16 ... ok +cmovnge_1 ... ok +cmovnge_2 ... ok +cmovnge_3 ... ok +cmovnge_4 ... ok +cmovnge_5 ... ok +cmovnge_6 ... ok +cmovnge_7 ... ok +cmovnge_8 ... ok +cmovnl_1 ... ok +cmovnl_2 ... ok +cmovnl_3 ... ok +cmovnl_4 ... ok +cmovnl_5 ... ok +cmovnl_6 ... ok +cmovnl_7 ... ok +cmovnl_8 ... ok +cmovnle_1 ... ok +cmovnle_2 ... ok +cmovnle_3 ... ok +cmovnle_4 ... ok +cmovnle_5 ... ok +cmovnle_6 ... ok +cmovnle_7 ... ok +cmovnle_8 ... ok +cmovnle_9 ... ok +cmovnle_10 ... ok +cmovnle_11 ... ok +cmovnle_12 ... ok +cmovnle_13 ... ok +cmovnle_14 ... ok +cmovnle_15 ... ok +cmovnle_16 ... ok +cmovno_1 ... ok +cmovno_2 ... ok +cmovno_3 ... ok +cmovno_4 ... ok +cmovnp_1 ... ok +cmovnp_2 ... ok +cmovnp_3 ... ok +cmovnp_4 ... ok +cmovns_1 ... ok +cmovns_2 ... ok +cmovns_3 ... ok +cmovns_4 ... ok +cmovnz_1 ... ok +cmovnz_2 ... ok +cmovnz_3 ... ok +cmovnz_4 ... ok +cmovo_1 ... ok +cmovo_2 ... ok +cmovo_3 ... ok +cmovo_4 ... ok +cmovp_1 ... ok +cmovp_2 ... ok +cmovp_3 ... ok +cmovp_4 ... ok +cmovs_1 ... ok +cmovs_2 ... ok +cmovs_3 ... ok +cmovs_4 ... ok +cmovz_1 ... ok +cmovz_2 ... ok +cmovz_3 ... ok +cmovz_4 ... ok +cmova_9 ... ok +cmova_10 ... ok +cmova_11 ... ok +cmova_12 ... ok +cmova_13 ... ok +cmova_14 ... ok +cmova_15 ... ok +cmova_16 ... ok +cmovae_5 ... ok +cmovae_6 ... ok +cmovae_7 ... ok +cmovae_8 ... ok +cmovb_5 ... ok +cmovb_6 ... ok +cmovb_7 ... ok +cmovb_8 ... ok +cmovbe_9 ... ok +cmovbe_10 ... ok +cmovbe_11 ... ok +cmovbe_12 ... ok +cmovbe_13 ... ok +cmovbe_14 ... ok +cmovbe_15 ... ok +cmovbe_16 ... ok +cmovc_5 ... ok +cmovc_6 ... ok +cmovc_7 ... ok +cmovc_8 ... ok +cmove_5 ... ok +cmove_6 ... ok +cmove_7 ... ok +cmove_8 ... ok +cmovg_17 ... ok +cmovg_18 ... ok +cmovg_19 ... ok +cmovg_20 ... ok +cmovg_21 ... ok +cmovg_22 ... ok +cmovg_23 ... ok +cmovg_24 ... ok +cmovg_25 ... ok +cmovg_26 ... ok +cmovg_27 ... ok +cmovg_28 ... ok +cmovg_29 ... ok +cmovg_30 ... ok +cmovg_31 ... ok +cmovg_32 ... ok +cmovge_9 ... ok +cmovge_10 ... ok +cmovge_11 ... ok +cmovge_12 ... ok +cmovge_13 ... ok +cmovge_14 ... ok +cmovge_15 ... ok +cmovge_16 ... ok +cmovl_9 ... ok +cmovl_10 ... ok +cmovl_11 ... ok +cmovl_12 ... ok +cmovl_13 ... ok +cmovl_14 ... ok +cmovl_15 ... ok +cmovl_16 ... ok +cmovle_17 ... ok +cmovle_18 ... ok +cmovle_19 ... ok +cmovle_20 ... ok +cmovle_21 ... ok +cmovle_22 ... ok +cmovle_23 ... ok +cmovle_24 ... ok +cmovle_25 ... ok +cmovle_26 ... ok +cmovle_27 ... ok +cmovle_28 ... ok +cmovle_29 ... ok +cmovle_30 ... ok +cmovle_31 ... ok +cmovle_32 ... ok +cmovna_9 ... ok +cmovna_10 ... ok +cmovna_11 ... ok +cmovna_12 ... ok +cmovna_13 ... ok +cmovna_14 ... ok +cmovna_15 ... ok +cmovna_16 ... ok +cmovnae_5 ... ok +cmovnae_6 ... ok +cmovnae_7 ... ok +cmovnae_8 ... ok +cmovnb_5 ... ok +cmovnb_6 ... ok +cmovnb_7 ... ok +cmovnb_8 ... ok +cmovnbe_9 ... ok +cmovnbe_10 ... ok +cmovnbe_11 ... ok +cmovnbe_12 ... ok +cmovnbe_13 ... ok +cmovnbe_14 ... ok +cmovnbe_15 ... ok +cmovnbe_16 ... ok +cmovnc_5 ... ok +cmovnc_6 ... ok +cmovnc_7 ... ok +cmovnc_8 ... ok +cmovne_5 ... ok +cmovne_6 ... ok +cmovne_7 ... ok +cmovne_8 ... ok +cmovng_17 ... ok +cmovng_18 ... ok +cmovng_19 ... ok +cmovng_20 ... ok +cmovng_21 ... ok +cmovng_22 ... ok +cmovng_23 ... ok +cmovng_24 ... ok +cmovng_25 ... ok +cmovng_26 ... ok +cmovng_27 ... ok +cmovng_28 ... ok +cmovng_29 ... ok +cmovng_30 ... ok +cmovng_31 ... ok +cmovng_32 ... ok +cmovnge_9 ... ok +cmovnge_10 ... ok +cmovnge_11 ... ok +cmovnge_12 ... ok +cmovnge_13 ... ok +cmovnge_14 ... ok +cmovnge_15 ... ok +cmovnge_16 ... ok +cmovnl_9 ... ok +cmovnl_10 ... ok +cmovnl_11 ... ok +cmovnl_12 ... ok +cmovnl_13 ... ok +cmovnl_14 ... ok +cmovnl_15 ... ok +cmovnl_16 ... ok +cmovnle_17 ... ok +cmovnle_18 ... ok +cmovnle_19 ... ok +cmovnle_20 ... ok +cmovnle_21 ... ok +cmovnle_22 ... ok +cmovnle_23 ... ok +cmovnle_24 ... ok +cmovnle_25 ... ok +cmovnle_26 ... ok +cmovnle_27 ... ok +cmovnle_28 ... ok +cmovnle_29 ... ok +cmovnle_30 ... ok +cmovnle_31 ... ok +cmovnle_32 ... ok +cmovno_5 ... ok +cmovno_6 ... ok +cmovno_7 ... ok +cmovno_8 ... ok +cmovnp_5 ... ok +cmovnp_6 ... ok +cmovnp_7 ... ok +cmovnp_8 ... ok +cmovns_5 ... ok +cmovns_6 ... ok +cmovns_7 ... ok +cmovns_8 ... ok +cmovnz_5 ... ok +cmovnz_6 ... ok +cmovnz_7 ... ok +cmovnz_8 ... ok +cmovo_5 ... ok +cmovo_6 ... ok +cmovo_7 ... ok +cmovo_8 ... ok +cmovp_5 ... ok +cmovp_6 ... ok +cmovp_7 ... ok +cmovp_8 ... ok +cmovs_5 ... ok +cmovs_6 ... ok +cmovs_7 ... ok +cmovs_8 ... ok +cmovz_5 ... ok +cmovz_6 ... ok +cmovz_7 ... ok +cmovz_8 ... ok diff --git a/VEX/head20041019/addrcheck/tests/insn_cmov.vgtest b/VEX/head20041019/addrcheck/tests/insn_cmov.vgtest new file mode 100644 index 0000000000..0321a3ca84 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_cmov.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_cmov +cpu_test: cmov diff --git a/VEX/head20041019/addrcheck/tests/insn_fpu.stderr.exp b/VEX/head20041019/addrcheck/tests/insn_fpu.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/tests/insn_fpu.stdout.exp b/VEX/head20041019/addrcheck/tests/insn_fpu.stdout.exp new file mode 100644 index 0000000000..2dbaa07ce0 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_fpu.stdout.exp @@ -0,0 +1,452 @@ +fabs_1 ... ok +fabs_2 ... ok +fabs_3 ... ok +fabs_4 ... ok +fadds_1 ... ok +fadds_2 ... ok +fadds_3 ... ok +fadds_4 ... ok +faddl_1 ... ok +faddl_2 ... ok +faddl_3 ... ok +faddl_4 ... ok +fadd_1 ... ok +fadd_2 ... ok +fadd_3 ... ok +fadd_4 ... ok +fadd_5 ... ok +fadd_6 ... ok +fadd_7 ... ok +fadd_8 ... ok +fadd_9 ... ok +fadd_10 ... ok +fadd_11 ... ok +fadd_12 ... ok +fadd_13 ... ok +fadd_14 ... ok +fadd_15 ... ok +fadd_16 ... ok +faddp_1 ... ok +faddp_2 ... ok +faddp_3 ... ok +faddp_4 ... ok +faddp_5 ... ok +faddp_6 ... ok +faddp_7 ... ok +faddp_8 ... ok +faddp_9 ... ok +faddp_10 ... ok +faddp_11 ... ok +faddp_12 ... ok +faddp_13 ... ok +faddp_14 ... ok +faddp_15 ... ok +faddp_16 ... ok +fiadds_1 ... ok +fiadds_2 ... ok +fiadds_3 ... ok +fiadds_4 ... ok +fiadds_5 ... ok +fiadds_6 ... ok +fiadds_7 ... ok +fiadds_8 ... ok +fiaddl_1 ... ok +fiaddl_2 ... ok +fiaddl_3 ... ok +fiaddl_4 ... ok +fiaddl_5 ... ok +fiaddl_6 ... ok +fiaddl_7 ... ok +fiaddl_8 ... ok +fcomi_1 ... ok +fcomi_2 ... ok +fcomi_3 ... ok +fcomi_4 ... ok +fcomi_5 ... ok +fcomi_6 ... ok +fcomip_1 ... ok +fcomip_2 ... ok +fcomip_3 ... ok +fcomip_4 ... ok +fcomip_5 ... ok +fcomip_6 ... ok +fucomi_1 ... ok +fucomi_2 ... ok +fucomi_3 ... ok +fucomi_4 ... ok +fucomi_5 ... ok +fucomi_6 ... ok +fucomip_1 ... ok +fucomip_2 ... ok +fucomip_3 ... ok +fucomip_4 ... ok +fucomip_5 ... ok +fucomip_6 ... ok +fchs_1 ... ok +fchs_2 ... ok +fchs_3 ... ok +fchs_4 ... ok +fdivs_1 ... ok +fdivs_2 ... ok +fdivs_3 ... ok +fdivs_4 ... ok +fdivl_1 ... ok +fdivl_2 ... ok +fdivl_3 ... ok +fdivl_4 ... ok +fdiv_1 ... ok +fdiv_2 ... ok +fdiv_3 ... ok +fdiv_4 ... ok +fdiv_5 ... ok +fdiv_6 ... ok +fdiv_7 ... ok +fdiv_8 ... ok +fdiv_9 ... ok +fdiv_10 ... ok +fdiv_11 ... ok +fdiv_12 ... ok +fdiv_13 ... ok +fdiv_14 ... ok +fdiv_15 ... ok +fdiv_16 ... ok +fdivp_1 ... ok +fdivp_2 ... ok +fdivp_3 ... ok +fdivp_4 ... ok +fdivp_5 ... ok +fdivp_6 ... ok +fdivp_7 ... ok +fdivp_8 ... ok +fdivp_9 ... ok +fdivp_10 ... ok +fdivp_11 ... ok +fdivp_12 ... ok +fdivp_13 ... ok +fdivp_14 ... ok +fdivp_15 ... ok +fdivp_16 ... ok +fidivs_1 ... ok +fidivs_2 ... ok +fidivs_3 ... ok +fidivs_4 ... ok +fidivs_5 ... ok +fidivs_6 ... ok +fidivs_7 ... ok +fidivs_8 ... ok +fidivl_1 ... ok +fidivl_2 ... ok +fidivl_3 ... ok +fidivl_4 ... ok +fidivl_5 ... ok +fidivl_6 ... ok +fidivl_7 ... ok +fidivl_8 ... ok +fdivrs_1 ... ok +fdivrs_2 ... ok +fdivrs_3 ... ok +fdivrs_4 ... ok +fdivrl_1 ... ok +fdivrl_2 ... ok +fdivrl_3 ... ok +fdivrl_4 ... ok +fdivr_1 ... ok +fdivr_2 ... ok +fdivr_3 ... ok +fdivr_4 ... ok +fdivr_5 ... ok +fdivr_6 ... ok +fdivr_7 ... ok +fdivr_8 ... ok +fdivr_9 ... ok +fdivr_10 ... ok +fdivr_11 ... ok +fdivr_12 ... ok +fdivr_13 ... ok +fdivr_14 ... ok +fdivr_15 ... ok +fdivr_16 ... ok +fdivrp_1 ... ok +fdivrp_2 ... ok +fdivrp_3 ... ok +fdivrp_4 ... ok +fdivrp_5 ... ok +fdivrp_6 ... ok +fdivrp_7 ... ok +fdivrp_8 ... ok +fdivrp_9 ... ok +fdivrp_10 ... ok +fdivrp_11 ... ok +fdivrp_12 ... ok +fdivrp_13 ... ok +fdivrp_14 ... ok +fdivrp_15 ... ok +fdivrp_16 ... ok +fidivrs_1 ... ok +fidivrs_2 ... ok +fidivrs_3 ... ok +fidivrs_4 ... ok +fidivrs_5 ... ok +fidivrs_6 ... ok +fidivrs_7 ... ok +fidivrs_8 ... ok +fidivrl_1 ... ok +fidivrl_2 ... ok +fidivrl_3 ... ok +fidivrl_4 ... ok +fidivrl_5 ... ok +fidivrl_6 ... ok +fidivrl_7 ... ok +fidivrl_8 ... ok +filds_1 ... ok +filds_2 ... ok +filds_3 ... ok +filds_4 ... ok +fildl_1 ... ok +fildl_2 ... ok +fildl_3 ... ok +fildl_4 ... ok +fildq_1 ... ok +fildq_2 ... ok +fildq_3 ... ok +fildq_4 ... ok +fists_1 ... ok +fists_2 ... ok +fists_3 ... ok +fists_4 ... ok +fists_5 ... ok +fists_6 ... ok +fists_7 ... ok +fists_8 ... ok +fistl_1 ... ok +fistl_2 ... ok +fistl_3 ... ok +fistl_4 ... ok +fistl_5 ... ok +fistl_6 ... ok +fistl_7 ... ok +fistl_8 ... ok +fistps_1 ... ok +fistps_2 ... ok +fistps_3 ... ok +fistps_4 ... ok +fistps_5 ... ok +fistps_6 ... ok +fistps_7 ... ok +fistps_8 ... ok +fistpl_1 ... ok +fistpl_2 ... ok +fistpl_3 ... ok +fistpl_4 ... ok +fistpl_5 ... ok +fistpl_6 ... ok +fistpl_7 ... ok +fistpl_8 ... ok +fistpq_1 ... ok +fistpq_2 ... ok +fistpq_3 ... ok +fistpq_4 ... ok +fistpq_5 ... ok +fistpq_6 ... ok +fistpq_7 ... ok +fistpq_8 ... ok +flds_1 ... ok +flds_2 ... ok +fldl_1 ... ok +fldl_2 ... ok +fld_1 ... ok +fld_2 ... ok +fld_3 ... ok +fld1_1 ... ok +fldl2t_1 ... ok +fldl2e_1 ... ok +fldpi_1 ... ok +fldlg2_1 ... ok +fldln2_1 ... ok +fldz_1 ... ok +fmuls_1 ... ok +fmuls_2 ... ok +fmuls_3 ... ok +fmuls_4 ... ok +fmull_1 ... ok +fmull_2 ... ok +fmull_3 ... ok +fmull_4 ... ok +fmul_1 ... ok +fmul_2 ... ok +fmul_3 ... ok +fmul_4 ... ok +fmul_5 ... ok +fmul_6 ... ok +fmul_7 ... ok +fmul_8 ... ok +fmul_9 ... ok +fmul_10 ... ok +fmul_11 ... ok +fmul_12 ... ok +fmul_13 ... ok +fmul_14 ... ok +fmul_15 ... ok +fmul_16 ... ok +fmulp_1 ... ok +fmulp_2 ... ok +fmulp_3 ... ok +fmulp_4 ... ok +fmulp_5 ... ok +fmulp_6 ... ok +fmulp_7 ... ok +fmulp_8 ... ok +fmulp_9 ... ok +fmulp_10 ... ok +fmulp_11 ... ok +fmulp_12 ... ok +fmulp_13 ... ok +fmulp_14 ... ok +fmulp_15 ... ok +fmulp_16 ... ok +fimuls_1 ... ok +fimuls_2 ... ok +fimuls_3 ... ok +fimuls_4 ... ok +fimuls_5 ... ok +fimuls_6 ... ok +fimuls_7 ... ok +fimuls_8 ... ok +fimull_1 ... ok +fimull_2 ... ok +fimull_3 ... ok +fimull_4 ... ok +fimull_5 ... ok +fimull_6 ... ok +fimull_7 ... ok +fimull_8 ... ok +frndint_1 ... ok +frndint_2 ... ok +frndint_3 ... ok +frndint_4 ... ok +frndint_5 ... ok +frndint_6 ... ok +frndint_7 ... ok +frndint_8 ... ok +frndint_9 ... ok +frndint_10 ... ok +frndint_11 ... ok +frndint_12 ... ok +frndint_13 ... ok +frndint_14 ... ok +frndint_15 ... ok +frndint_16 ... ok +fsubs_1 ... ok +fsubs_2 ... ok +fsubs_3 ... ok +fsubs_4 ... ok +fsubl_1 ... ok +fsubl_2 ... ok +fsubl_3 ... ok +fsubl_4 ... ok +fsub_1 ... ok +fsub_2 ... ok +fsub_3 ... ok +fsub_4 ... ok +fsub_5 ... ok +fsub_6 ... ok +fsub_7 ... ok +fsub_8 ... ok +fsub_9 ... ok +fsub_10 ... ok +fsub_11 ... ok +fsub_12 ... ok +fsub_13 ... ok +fsub_14 ... ok +fsub_15 ... ok +fsub_16 ... ok +fsubp_1 ... ok +fsubp_2 ... ok +fsubp_3 ... ok +fsubp_4 ... ok +fsubp_5 ... ok +fsubp_6 ... ok +fsubp_7 ... ok +fsubp_8 ... ok +fsubp_9 ... ok +fsubp_10 ... ok +fsubp_11 ... ok +fsubp_12 ... ok +fsubp_13 ... ok +fsubp_14 ... ok +fsubp_15 ... ok +fsubp_16 ... ok +fisubs_1 ... ok +fisubs_2 ... ok +fisubs_3 ... ok +fisubs_4 ... ok +fisubs_5 ... ok +fisubs_6 ... ok +fisubs_7 ... ok +fisubs_8 ... ok +fisubl_1 ... ok +fisubl_2 ... ok +fisubl_3 ... ok +fisubl_4 ... ok +fisubl_5 ... ok +fisubl_6 ... ok +fisubl_7 ... ok +fisubl_8 ... ok +fsubrs_1 ... ok +fsubrs_2 ... ok +fsubrs_3 ... ok +fsubrs_4 ... ok +fsubrl_1 ... ok +fsubrl_2 ... ok +fsubrl_3 ... ok +fsubrl_4 ... ok +fsubr_1 ... ok +fsubr_2 ... ok +fsubr_3 ... ok +fsubr_4 ... ok +fsubr_5 ... ok +fsubr_6 ... ok +fsubr_7 ... ok +fsubr_8 ... ok +fsubr_9 ... ok +fsubr_10 ... ok +fsubr_11 ... ok +fsubr_12 ... ok +fsubr_13 ... ok +fsubr_14 ... ok +fsubr_15 ... ok +fsubr_16 ... ok +fsubrp_1 ... ok +fsubrp_2 ... ok +fsubrp_3 ... ok +fsubrp_4 ... ok +fsubrp_5 ... ok +fsubrp_6 ... ok +fsubrp_7 ... ok +fsubrp_8 ... ok +fsubrp_9 ... ok +fsubrp_10 ... ok +fsubrp_11 ... ok +fsubrp_12 ... ok +fsubrp_13 ... ok +fsubrp_14 ... ok +fsubrp_15 ... ok +fsubrp_16 ... ok +fisubrs_1 ... ok +fisubrs_2 ... ok +fisubrs_3 ... ok +fisubrs_4 ... ok +fisubrs_5 ... ok +fisubrs_6 ... ok +fisubrs_7 ... ok +fisubrs_8 ... ok +fisubrl_1 ... ok +fisubrl_2 ... ok +fisubrl_3 ... ok +fisubrl_4 ... ok +fisubrl_5 ... ok +fisubrl_6 ... ok +fisubrl_7 ... ok +fisubrl_8 ... ok +fxch_1 ... ok +fxch_2 ... ok diff --git a/VEX/head20041019/addrcheck/tests/insn_fpu.vgtest b/VEX/head20041019/addrcheck/tests/insn_fpu.vgtest new file mode 100644 index 0000000000..1b9546f54a --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_fpu.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_fpu +cpu_test: fpu diff --git a/VEX/head20041019/addrcheck/tests/insn_mmx.stderr.exp b/VEX/head20041019/addrcheck/tests/insn_mmx.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/tests/insn_mmx.stdout.exp b/VEX/head20041019/addrcheck/tests/insn_mmx.stdout.exp new file mode 100644 index 0000000000..95cbae1603 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_mmx.stdout.exp @@ -0,0 +1,103 @@ +movd_1 ... ok +movd_2 ... ok +movd_3 ... ok +movd_4 ... ok +movq_1 ... ok +movq_2 ... ok +movq_3 ... ok +packssdw_1 ... ok +packssdw_2 ... ok +packsswb_1 ... ok +packsswb_2 ... ok +packuswb_1 ... ok +packuswb_2 ... ok +paddb_1 ... ok +paddb_2 ... ok +paddd_1 ... ok +paddd_2 ... ok +paddsb_1 ... ok +paddsb_2 ... ok +paddsw_1 ... ok +paddsw_2 ... ok +paddusb_1 ... ok +paddusb_2 ... ok +paddusw_1 ... ok +paddusw_2 ... ok +paddw_1 ... ok +paddw_2 ... ok +pand_1 ... ok +pand_2 ... ok +pandn_1 ... ok +pandn_2 ... ok +pcmpeqb_1 ... ok +pcmpeqb_2 ... ok +pcmpeqd_1 ... ok +pcmpeqd_2 ... ok +pcmpeqw_1 ... ok +pcmpeqw_2 ... ok +pcmpgtb_1 ... ok +pcmpgtb_2 ... ok +pcmpgtd_1 ... ok +pcmpgtd_2 ... ok +pcmpgtw_1 ... ok +pcmpgtw_2 ... ok +pmaddwd_1 ... ok +pmaddwd_2 ... ok +pmulhw_1 ... ok +pmulhw_2 ... ok +pmullw_1 ... ok +pmullw_2 ... ok +por_1 ... ok +por_2 ... ok +pslld_1 ... ok +pslld_2 ... ok +pslld_3 ... ok +psllq_1 ... ok +psllq_2 ... ok +psllq_3 ... ok +psllw_1 ... ok +psllw_2 ... ok +psllw_3 ... ok +psrad_1 ... ok +psrad_2 ... ok +psrad_3 ... ok +psraw_1 ... ok +psraw_2 ... ok +psraw_3 ... ok +psrld_1 ... ok +psrld_2 ... ok +psrld_3 ... ok +psrlq_1 ... ok +psrlq_2 ... ok +psrlq_3 ... ok +psrlw_1 ... ok +psrlw_2 ... ok +psrlw_3 ... ok +psubb_1 ... ok +psubb_2 ... ok +psubd_1 ... ok +psubd_2 ... ok +psubsb_1 ... ok +psubsb_2 ... ok +psubsw_1 ... ok +psubsw_2 ... ok +psubusb_1 ... ok +psubusb_2 ... ok +psubusw_1 ... ok +psubusw_2 ... ok +psubw_1 ... ok +psubw_2 ... ok +punpckhbw_1 ... ok +punpckhbw_2 ... ok +punpckhdq_1 ... ok +punpckhdq_2 ... ok +punpckhwd_1 ... ok +punpckhwd_2 ... ok +punpcklbw_1 ... ok +punpcklbw_2 ... ok +punpckldq_1 ... ok +punpckldq_2 ... ok +punpcklwd_1 ... ok +punpcklwd_2 ... ok +pxor_1 ... ok +pxor_2 ... ok diff --git a/VEX/head20041019/addrcheck/tests/insn_mmx.vgtest b/VEX/head20041019/addrcheck/tests/insn_mmx.vgtest new file mode 100644 index 0000000000..ddbb977263 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_mmx.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_mmx +cpu_test: mmx diff --git a/VEX/head20041019/addrcheck/tests/insn_mmxext.stderr.exp b/VEX/head20041019/addrcheck/tests/insn_mmxext.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/tests/insn_mmxext.stdout.exp b/VEX/head20041019/addrcheck/tests/insn_mmxext.stdout.exp new file mode 100644 index 0000000000..23b2e55abd --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_mmxext.stdout.exp @@ -0,0 +1,29 @@ +movntq_1 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufw_1 ... ok +pshufw_2 ... ok +sfence_1 ... ok diff --git a/VEX/head20041019/addrcheck/tests/insn_mmxext.vgtest b/VEX/head20041019/addrcheck/tests/insn_mmxext.vgtest new file mode 100644 index 0000000000..bb667097fb --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_mmxext.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_mmxext +cpu_test: mmxext diff --git a/VEX/head20041019/addrcheck/tests/insn_sse.stderr.exp b/VEX/head20041019/addrcheck/tests/insn_sse.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/tests/insn_sse.stdout.exp b/VEX/head20041019/addrcheck/tests/insn_sse.stdout.exp new file mode 100644 index 0000000000..f15bd81f0b --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_sse.stdout.exp @@ -0,0 +1,142 @@ +addps_1 ... ok +addps_2 ... ok +addss_1 ... ok +addss_2 ... ok +andnps_1 ... ok +andnps_2 ... ok +andps_1 ... ok +andps_2 ... ok +cmpeqps_1 ... ok +cmpeqps_2 ... ok +cmpeqss_1 ... ok +cmpeqss_2 ... ok +cmpleps_1 ... ok +cmpleps_2 ... ok +cmpless_1 ... ok +cmpless_2 ... ok +cmpltps_1 ... ok +cmpltps_2 ... ok +cmpltss_1 ... ok +cmpltss_2 ... ok +cmpneqps_1 ... ok +cmpneqps_2 ... ok +cmpneqss_1 ... ok +cmpneqss_2 ... ok +cmpnleps_1 ... ok +cmpnleps_2 ... ok +cmpnless_1 ... ok +cmpnless_2 ... ok +cmpnltps_1 ... ok +cmpnltps_2 ... ok +cmpnltss_1 ... ok +cmpnltss_2 ... ok +comiss_1 ... ok +comiss_2 ... ok +comiss_3 ... ok +comiss_4 ... ok +comiss_5 ... ok +comiss_6 ... ok +cvtpi2ps_1 ... ok +cvtpi2ps_2 ... ok +cvtps2pi_1 ... ok +cvtps2pi_2 ... ok +cvtsi2ss_1 ... ok +cvtsi2ss_2 ... ok +cvtss2si_1 ... ok +cvtss2si_2 ... ok +cvttps2pi_1 ... ok +cvttps2pi_2 ... ok +cvttss2si_1 ... ok +cvttss2si_2 ... ok +divps_1 ... ok +divps_2 ... ok +divss_1 ... ok +divss_2 ... ok +maxps_1 ... ok +maxps_2 ... ok +maxss_1 ... ok +maxss_2 ... ok +minps_1 ... ok +minps_2 ... ok +minss_1 ... ok +minss_2 ... ok +movaps_1 ... ok +movaps_2 ... ok +movhlps_1 ... ok +movhps_1 ... ok +movhps_2 ... ok +movlhps_1 ... ok +movlps_1 ... ok +movlps_2 ... ok +movmskps_1 ... ok +movntps_1 ... ok +movntq_1 ... ok +movss_1 ... ok +movss_2 ... ok +movss_3 ... ok +movups_1 ... ok +movups_2 ... ok +mulps_1 ... ok +mulps_2 ... ok +mulss_1 ... ok +mulss_2 ... ok +orps_1 ... ok +orps_2 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufw_1 ... ok +pshufw_2 ... ok +rcpps_1 ... ok +rcpps_2 ... ok +rcpss_1 ... ok +rcpss_2 ... ok +rsqrtps_1 ... ok +rsqrtps_2 ... ok +rsqrtss_1 ... ok +rsqrtss_2 ... ok +sfence_1 ... ok +shufps_1 ... ok +shufps_2 ... ok +sqrtps_1 ... ok +sqrtps_2 ... ok +sqrtss_1 ... ok +sqrtss_2 ... ok +subps_1 ... ok +subps_2 ... ok +subss_1 ... ok +subss_2 ... ok +ucomiss_1 ... ok +ucomiss_2 ... ok +ucomiss_3 ... ok +ucomiss_4 ... ok +ucomiss_5 ... ok +ucomiss_6 ... ok +unpckhps_1 ... ok +unpckhps_2 ... ok +unpcklps_1 ... ok +unpcklps_2 ... ok +xorps_1 ... ok +xorps_2 ... ok diff --git a/VEX/head20041019/addrcheck/tests/insn_sse.vgtest b/VEX/head20041019/addrcheck/tests/insn_sse.vgtest new file mode 100644 index 0000000000..167c8e290b --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_sse.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_sse +cpu_test: sse diff --git a/VEX/head20041019/addrcheck/tests/insn_sse2.stderr.exp b/VEX/head20041019/addrcheck/tests/insn_sse2.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/addrcheck/tests/insn_sse2.stdout.exp b/VEX/head20041019/addrcheck/tests/insn_sse2.stdout.exp new file mode 100644 index 0000000000..9c24f72621 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_sse2.stdout.exp @@ -0,0 +1,294 @@ +addpd_1 ... ok +addpd_2 ... ok +addsd_1 ... ok +addsd_2 ... ok +andpd_1 ... ok +andpd_2 ... ok +andnpd_1 ... ok +andnpd_2 ... ok +cmpeqpd_1 ... ok +cmpeqpd_2 ... ok +cmpltpd_1 ... ok +cmpltpd_2 ... ok +cmplepd_1 ... ok +cmplepd_2 ... ok +cmpneqpd_1 ... ok +cmpneqpd_2 ... ok +cmpnltpd_1 ... ok +cmpnltpd_2 ... ok +cmpnlepd_1 ... ok +cmpnlepd_2 ... ok +cmpeqsd_1 ... ok +cmpeqsd_2 ... ok +cmpltsd_1 ... ok +cmpltsd_2 ... ok +cmplesd_1 ... ok +cmplesd_2 ... ok +cmpneqsd_1 ... ok +cmpneqsd_2 ... ok +cmpnltsd_1 ... ok +cmpnltsd_2 ... ok +cmpnlesd_1 ... ok +cmpnlesd_2 ... ok +comisd_1 ... ok +comisd_2 ... ok +comisd_3 ... ok +comisd_4 ... ok +comisd_5 ... ok +comisd_6 ... ok +cvtdq2pd_1 ... ok +cvtdq2pd_2 ... ok +cvtdq2ps_1 ... ok +cvtdq2ps_2 ... ok +cvtpd2dq_1 ... ok +cvtpd2dq_2 ... ok +cvtpd2pi_1 ... ok +cvtpd2pi_2 ... ok +cvtpd2ps_1 ... ok +cvtpd2ps_2 ... ok +cvtpi2pd_1 ... ok +cvtpi2pd_2 ... ok +cvtps2dq_1 ... ok +cvtps2dq_2 ... ok +cvtps2pd_1 ... ok +cvtps2pd_2 ... ok +cvtsd2si_1 ... ok +cvtsd2si_2 ... ok +cvtsd2ss_1 ... ok +cvtsd2ss_2 ... ok +cvtsi2sd_1 ... ok +cvtsi2sd_2 ... ok +cvtss2sd_1 ... ok +cvtss2sd_2 ... ok +cvttpd2pi_1 ... ok +cvttpd2pi_2 ... ok +cvttpd2dq_1 ... ok +cvttpd2dq_2 ... ok +cvttps2dq_1 ... ok +cvttps2dq_2 ... ok +cvttsd2si_1 ... ok +cvttsd2si_2 ... ok +divpd_1 ... ok +divpd_2 ... ok +divsd_1 ... ok +divsd_2 ... ok +lfence_1 ... ok +maxpd_1 ... ok +maxpd_2 ... ok +maxsd_1 ... ok +maxsd_2 ... ok +mfence_1 ... ok +minpd_1 ... ok +minpd_2 ... ok +minsd_1 ... ok +minsd_2 ... ok +movapd_1 ... ok +movapd_2 ... ok +movd_1 ... ok +movd_2 ... ok +movd_3 ... ok +movd_4 ... ok +movdqa_1 ... ok +movdqa_2 ... ok +movdqa_3 ... ok +movdqu_1 ... ok +movdqu_2 ... ok +movdqu_3 ... ok +movdq2q_1 ... ok +movhpd_1 ... ok +movhpd_2 ... ok +movlpd_1 ... ok +movlpd_2 ... ok +movmskpd_1 ... ok +movntdq_1 ... ok +movnti_1 ... ok +movntpd_1 ... ok +movq2dq_1 ... ok +movsd_1 ... ok +movsd_2 ... ok +movsd_3 ... ok +movupd_1 ... ok +movupd_2 ... ok +mulpd_1 ... ok +mulpd_2 ... ok +mulsd_1 ... ok +mulsd_2 ... ok +orpd_1 ... ok +orpd_2 ... ok +packssdw_1 ... ok +packssdw_2 ... ok +packsswb_1 ... ok +packsswb_2 ... ok +packuswb_1 ... ok +packuswb_2 ... ok +paddb_1 ... ok +paddb_2 ... ok +paddd_1 ... ok +paddd_2 ... ok +paddq_1 ... ok +paddq_2 ... ok +paddq_3 ... ok +paddq_4 ... ok +paddsb_1 ... ok +paddsb_2 ... ok +paddsw_1 ... ok +paddsw_2 ... ok +paddusb_1 ... ok +paddusb_2 ... ok +paddusw_1 ... ok +paddusw_2 ... ok +paddw_1 ... ok +paddw_2 ... ok +pand_1 ... ok +pand_2 ... ok +pandn_1 ... ok +pandn_2 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pcmpeqb_1 ... ok +pcmpeqb_2 ... ok +pcmpeqd_1 ... ok +pcmpeqd_2 ... ok +pcmpeqw_1 ... ok +pcmpeqw_2 ... ok +pcmpgtb_1 ... ok +pcmpgtb_2 ... ok +pcmpgtd_1 ... ok +pcmpgtd_2 ... ok +pcmpgtw_1 ... ok +pcmpgtw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pextrw_5 ... ok +pextrw_6 ... ok +pextrw_7 ... ok +pextrw_8 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pinsrw_5 ... ok +pinsrw_6 ... ok +pinsrw_7 ... ok +pinsrw_8 ... ok +pmaddwd_1 ... ok +pmaddwd_2 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +pmulhw_1 ... ok +pmulhw_2 ... ok +pmullw_1 ... ok +pmullw_2 ... ok +pmuludq_1 ... ok +pmuludq_2 ... ok +pmuludq_3 ... ok +pmuludq_4 ... ok +por_1 ... ok +por_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufd_1 ... ok +pshufd_2 ... ok +pshufhw_1 ... ok +pshufhw_2 ... ok +pshuflw_1 ... ok +pshuflw_2 ... ok +pslld_1 ... ok +pslld_2 ... ok +pslld_3 ... ok +pslldq_1 ... ok +pslldq_2 ... ok +psllq_1 ... ok +psllq_2 ... ok +psllq_3 ... ok +psllw_1 ... ok +psllw_2 ... ok +psllw_3 ... ok +psrad_1 ... ok +psrad_2 ... ok +psrad_3 ... ok +psraw_1 ... ok +psraw_2 ... ok +psraw_3 ... ok +psrld_1 ... ok +psrld_2 ... ok +psrld_3 ... ok +psrldq_1 ... ok +psrldq_2 ... ok +psrlq_1 ... ok +psrlq_2 ... ok +psrlq_3 ... ok +psrlw_1 ... ok +psrlw_2 ... ok +psrlw_3 ... ok +psubb_1 ... ok +psubb_2 ... ok +psubd_1 ... ok +psubd_2 ... ok +psubq_1 ... ok +psubq_2 ... ok +psubq_3 ... ok +psubq_4 ... ok +psubsb_1 ... ok +psubsb_2 ... ok +psubsw_1 ... ok +psubsw_2 ... ok +psubusb_1 ... ok +psubusb_2 ... ok +psubusw_1 ... ok +psubusw_2 ... ok +psubw_1 ... ok +psubw_2 ... ok +punpckhbw_1 ... ok +punpckhbw_2 ... ok +punpckhdq_1 ... ok +punpckhdq_2 ... ok +punpckhqdq_1 ... ok +punpckhqdq_2 ... ok +punpckhwd_1 ... ok +punpckhwd_2 ... ok +punpcklbw_1 ... ok +punpcklbw_2 ... ok +punpckldq_1 ... ok +punpckldq_2 ... ok +punpcklqdq_1 ... ok +punpcklqdq_2 ... ok +punpcklwd_1 ... ok +punpcklwd_2 ... ok +pxor_1 ... ok +pxor_2 ... ok +shufpd_1 ... ok +shufpd_2 ... ok +sqrtpd_1 ... ok +sqrtpd_2 ... ok +sqrtsd_1 ... ok +sqrtsd_2 ... ok +subpd_1 ... ok +subpd_2 ... ok +subsd_1 ... ok +subsd_2 ... ok +ucomisd_1 ... ok +ucomisd_2 ... ok +ucomisd_3 ... ok +ucomisd_4 ... ok +ucomisd_5 ... ok +ucomisd_6 ... ok +unpckhpd_1 ... ok +unpckhpd_2 ... ok +unpcklpd_1 ... ok +unpcklpd_2 ... ok +xorpd_1 ... ok +xorpd_2 ... ok diff --git a/VEX/head20041019/addrcheck/tests/insn_sse2.vgtest b/VEX/head20041019/addrcheck/tests/insn_sse2.vgtest new file mode 100644 index 0000000000..42e82f38d3 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/insn_sse2.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_sse2 +cpu_test: sse2 diff --git a/VEX/head20041019/addrcheck/tests/overlap.stderr.exp b/VEX/head20041019/addrcheck/tests/overlap.stderr.exp new file mode 100644 index 0000000000..6fcbcd1ca5 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/overlap.stderr.exp @@ -0,0 +1,27 @@ +Source and destination overlap in memcpy(0x........, 0x........, 21) + at 0x........: memcpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:40) + +Source and destination overlap in memcpy(0x........, 0x........, 21) + at 0x........: memcpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:42) + +Source and destination overlap in strncpy(0x........, 0x........, 21) + at 0x........: strncpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:45) + +Source and destination overlap in strncpy(0x........, 0x........, 21) + at 0x........: strncpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:47) + +Source and destination overlap in strcpy(0x........, 0x........) + at 0x........: strcpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:54) + +Source and destination overlap in strncat(0x........, 0x........, 21) + at 0x........: strncat (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:112) + +Source and destination overlap in strncat(0x........, 0x........, 21) + at 0x........: strncat (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:113) diff --git a/VEX/head20041019/addrcheck/tests/overlap.stdout.exp b/VEX/head20041019/addrcheck/tests/overlap.stdout.exp new file mode 100644 index 0000000000..12cb02e548 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/overlap.stdout.exp @@ -0,0 +1,11 @@ +`_________________________________________________' +`abcdefghijklmnopqrstuvwxyz' +`abcdefghijklmnopqrstuvwxy________________________' +`abcdefghijklmnopqrstuvwxyz_______________________' +`abcdefghijklmnopqrstuvwxyz' + +`ABCDEFG' +`ABCDEFGabcdefghijklmnopqrstuvwxyz' +`ABCDEFGabcdefghijklmnopqrstuvwxy' +`ABCDEFGabcdefghijklmnopqrstuvwxyz' +`ABCDEFGabcdefghijklmnopqrstuvwxyz' diff --git a/VEX/head20041019/addrcheck/tests/overlap.vgtest b/VEX/head20041019/addrcheck/tests/overlap.vgtest new file mode 100644 index 0000000000..da96655baf --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/overlap.vgtest @@ -0,0 +1,2 @@ +vgopts: -q +prog: ../../memcheck/tests/overlap diff --git a/VEX/head20041019/addrcheck/tests/toobig-allocs.stderr.exp b/VEX/head20041019/addrcheck/tests/toobig-allocs.stderr.exp new file mode 100644 index 0000000000..a5ba60a368 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/toobig-allocs.stderr.exp @@ -0,0 +1,9 @@ + +Attempting too-big malloc()... +Attempting too-big mmap()... + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +malloc/free: in use at exit: 0 bytes in 0 blocks. +malloc/free: 1 allocs, 0 frees, 2145386496 bytes allocated. +For a detailed leak analysis, rerun with: --leak-check=yes +For counts of detected errors, rerun with: -v diff --git a/VEX/head20041019/addrcheck/tests/toobig-allocs.vgtest b/VEX/head20041019/addrcheck/tests/toobig-allocs.vgtest new file mode 100644 index 0000000000..186cf5f901 --- /dev/null +++ b/VEX/head20041019/addrcheck/tests/toobig-allocs.vgtest @@ -0,0 +1 @@ +prog: ../../tests/toobig-allocs diff --git a/VEX/head20041019/autogen.sh b/VEX/head20041019/autogen.sh new file mode 100755 index 0000000000..117462c7ff --- /dev/null +++ b/VEX/head20041019/autogen.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +run () +{ + echo "running: $*" + eval $* + + if test $? != 0 ; then + echo "error: while running '$*'" + exit 1 + fi +} + +run aclocal +run autoheader +run automake -a +run autoconf diff --git a/VEX/head20041019/auxprogs/.cvsignore b/VEX/head20041019/auxprogs/.cvsignore new file mode 100644 index 0000000000..786212505d --- /dev/null +++ b/VEX/head20041019/auxprogs/.cvsignore @@ -0,0 +1,3 @@ +Makefile.in +Makefile +valgrind-listener diff --git a/VEX/head20041019/auxprogs/CVS/Entries b/VEX/head20041019/auxprogs/CVS/Entries new file mode 100644 index 0000000000..0f34eaefb0 --- /dev/null +++ b/VEX/head20041019/auxprogs/CVS/Entries @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Tue Feb 25 01:48:11 2003// +/Makefile.am/1.9/Wed Sep 1 23:20:46 2004// +/valgrind-listener.c/1.13/Wed Sep 1 23:58:13 2004// +D diff --git a/VEX/head20041019/auxprogs/CVS/Repository b/VEX/head20041019/auxprogs/CVS/Repository new file mode 100644 index 0000000000..9f5d9c1b09 --- /dev/null +++ b/VEX/head20041019/auxprogs/CVS/Repository @@ -0,0 +1 @@ +valgrind/auxprogs diff --git a/VEX/head20041019/auxprogs/CVS/Root b/VEX/head20041019/auxprogs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/auxprogs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/auxprogs/CVS/Template b/VEX/head20041019/auxprogs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/auxprogs/Makefile.am b/VEX/head20041019/auxprogs/Makefile.am new file mode 100644 index 0000000000..1e31292cab --- /dev/null +++ b/VEX/head20041019/auxprogs/Makefile.am @@ -0,0 +1,9 @@ +include $(top_srcdir)/Makefile.all.am +include $(top_srcdir)/Makefile.core-AM_CPPFLAGS.am + +AM_CFLAGS = $(WERROR) -Winline -Wall -O -g + +bin_PROGRAMS = valgrind-listener + +valgrind_listener_SOURCES = valgrind-listener.c + diff --git a/VEX/head20041019/auxprogs/valgrind-listener.c b/VEX/head20041019/auxprogs/valgrind-listener.c new file mode 100644 index 0000000000..1ab139cfe3 --- /dev/null +++ b/VEX/head20041019/auxprogs/valgrind-listener.c @@ -0,0 +1,400 @@ + +/*--------------------------------------------------------------------*/ +/*--- A simple program to listen for valgrind logfile data. ---*/ +/*--- valgrind-listener.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + + +/*---------------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* For VG_CLO_DEFAULT_LOGPORT and VG_BUGS_TO. */ +#include "core.h" + + +/*---------------------------------------------------------------*/ + +/* The maximum allowable number concurrent connections. */ +#define M_CONNECTIONS 50 + + +/*---------------------------------------------------------------*/ + +__attribute__ ((noreturn)) +static void panic ( Char* str ) +{ + fprintf(stderr, + "\nvalgrind-listener: the " + "`impossible' happened:\n %s\n", str); + fprintf(stderr, + "Please report this bug at: %s\n\n", VG_BUGS_TO); + exit(1); +} + +__attribute__ ((noreturn)) +static void my_assert_fail ( const Char* expr, const Char* file, Int line, const Char* fn ) +{ + fprintf(stderr, + "\nvalgrind-listener: %s:%d (%s): Assertion `%s' failed.\n", + file, line, fn, expr ); + fprintf(stderr, + "Please report this bug at: %s\n\n", VG_BUGS_TO); + exit(1); +} + +#undef assert +#undef VG__STRING + +#define VG__STRING(__str) #__str +#define assert(expr) \ + ((void) ((expr) ? 0 : \ + (my_assert_fail (VG__STRING(expr), \ + __FILE__, __LINE__, \ + __PRETTY_FUNCTION__), 0))) + + +/*---------------------------------------------------------------*/ + +/* holds the fds for connections; zero if slot not in use. */ +int conn_count = 0; +int conn_fd[M_CONNECTIONS]; +struct pollfd conn_pollfd[M_CONNECTIONS]; + + +void set_nonblocking ( int sd ) +{ + int res; + res = fcntl(sd, F_GETFL); + res = fcntl(sd, F_SETFL, res | O_NONBLOCK); + if (res != 0) { + perror("fcntl failed"); + panic("set_nonblocking"); + } +} + +void set_blocking ( int sd ) +{ + int res; + res = fcntl(sd, F_GETFL); + res = fcntl(sd, F_SETFL, res & ~O_NONBLOCK); + if (res != 0) { + perror("fcntl failed"); + panic("set_blocking"); + } +} + + +void copyout ( char* buf, int nbuf ) +{ + int i; + for (i = 0; i < nbuf; i++) { + if (buf[i] == '\n') { + fprintf(stdout, "\n(%d) ", conn_count); + } else { + fwrite(&buf[i], 1, 1, stdout); + } + } + fflush(stdout); +} + +int read_from_sd ( int sd ) +{ + char buf[100]; + int n; + + set_blocking(sd); + n = read(sd, buf, 99); + if (n <= 0) return 0; /* closed */ + copyout(buf, n); + + set_nonblocking(sd); + while (1) { + n = read(sd, buf, 100); + if (n <= 0) return 1; /* not closed */ + copyout(buf, n); + } +} + + +void snooze ( void ) +{ + struct timespec req; + req.tv_sec = 0; + req.tv_nsec = 200 * 1000 * 1000; + nanosleep(&req,NULL); +} + + +/* returns 0 if invalid, else port # */ +int atoi_portno ( char* str ) +{ + int n = 0; + while (1) { + if (*str == 0) + break; + if (*str < '0' || *str > '9') + return 0; + n = 10*n + (int)(*str - '0'); + str++; + if (n >= 65536) + return 0; + } + if (n < 1024) + return 0; + return n; +} + + +void usage ( void ) +{ + fprintf(stderr, + "\n" + "usage is:\n" + "\n" + " valgrind-listener [--exit-at-zero|-e] [port-number]\n" + "\n" + " where --exit-at-zero or -e causes the listener to exit\n" + " when the number of connections falls back to zero\n" + " (the default is to keep listening forever)\n" + "\n" + " port-number is the default port on which to listen for\n" + " connections. It must be between 1024 and 65535.\n" + " Current default is %d.\n" + "\n" + , + VG_CLO_DEFAULT_LOGPORT + ); + exit(1); +} + + +void banner ( char* str ) +{ + time_t t; + t = time(NULL); + printf("valgrind-listener %s at %s", str, ctime(&t)); + fflush(stdout); +} + + +void exit_routine ( void ) +{ + banner("exited"); + exit(0); +} + + +void sigint_handler ( int signo ) +{ + exit_routine(); +} + + +int main (int argc, char** argv) +{ + int i, j, k, res, one; + int main_sd, new_sd, client_len; + struct sockaddr_in client_addr, server_addr; + + char /*bool*/ exit_when_zero = 0; + int port = VG_CLO_DEFAULT_LOGPORT; + + for (i = 1; i < argc; i++) { + if (0==strcmp(argv[i], "--exit-at-zero") + || 0==strcmp(argv[i], "-e")) { + exit_when_zero = 1; + } + else + if (atoi_portno(argv[i]) > 0) { + port = atoi_portno(argv[i]); + } + else + usage(); + } + + banner("started"); + signal(SIGINT, sigint_handler); + + conn_count = 0; + for (i = 0; i < M_CONNECTIONS; i++) + conn_fd[i] = 0; + + /* create socket */ + main_sd = socket(AF_INET, SOCK_STREAM, 0); + if (main_sd < 0) { + perror("cannot open socket "); + panic("main -- create socket"); + } + + /* allow address reuse to avoid "address already in use" errors */ + + one = 1; + if (setsockopt(main_sd, SOL_SOCKET, SO_REUSEADDR, + &one, sizeof(int)) < 0) { + perror("cannot enable address reuse "); + panic("main -- enable address reuse"); + } + + /* bind server port */ + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + server_addr.sin_port = htons(port); + + if (bind(main_sd, (struct sockaddr *) &server_addr, + sizeof(server_addr) ) < 0) { + perror("cannot bind port "); + panic("main -- bind port"); + } + + res = listen(main_sd,M_CONNECTIONS); + if (res != 0) { + perror("listen failed "); + panic("main -- listen"); + } + + while (1) { + + snooze(); + + /* enquire, using poll, whether there is any activity available on + the main socket descriptor. If so, someone is trying to + connect; get the fd and add it to our table thereof. */ + { struct pollfd ufd; + while (1) { + ufd.fd = main_sd; + ufd.events = POLLIN; + ufd.revents = 0; + res = poll(&ufd, 1, 0); + if (res == 0) break; + + /* ok, we have someone waiting to connect. Get the sd. */ + client_len = sizeof(client_addr); + new_sd = accept(main_sd, (struct sockaddr *) &client_addr, + &client_len); + if (new_sd < 0) { + perror("cannot accept connection "); + panic("main -- accept connection"); + } + + /* find a place to put it. */ + assert(new_sd > 0); + for (i = 0; i < M_CONNECTIONS; i++) + if (conn_fd[i] == 0) + break; + + if (i >= M_CONNECTIONS) { + fprintf(stderr, "Too many concurrent connections. " + "Increase M_CONNECTIONS and recompile.\n"); + panic("main -- too many concurrent connections"); + } + + conn_fd[i] = new_sd; + conn_count++; + printf("\n(%d) -------------------- CONNECT " + "--------------------\n(%d)\n(%d) ", + conn_count, conn_count, conn_count); + fflush(stdout); + } /* while (1) */ + } + + /* We've processed all new connect requests. Listen for changes + to the current set of fds. */ + j = 0; + for (i = 0; i < M_CONNECTIONS; i++) { + if (conn_fd[i] == 0) + continue; + conn_pollfd[j].fd = conn_fd[i]; + conn_pollfd[j].events = POLLIN /* | POLLHUP | POLLNVAL */; + conn_pollfd[j].revents = 0; + j++; + } + + res = poll(conn_pollfd, j, 0 /* return immediately. */ ); + if (res < 0) { + perror("poll(main) failed"); + panic("poll(main) failed"); + } + + /* nothing happened. go round again. */ + if (res == 0) { + continue; + } + + /* inspect the fds. */ + for (i = 0; i < j; i++) { + + if (conn_pollfd[i].revents & POLLIN) { + /* data is available on this fd */ + res = read_from_sd(conn_pollfd[i].fd); + + if (res == 0) { + /* the connection has been closed. */ + close(conn_pollfd[i].fd); + /* this fd has been closed or otherwise gone bad; forget + about it. */ + for (k = 0; k < M_CONNECTIONS; k++) + if (conn_fd[k] == conn_pollfd[i].fd) + break; + assert(k < M_CONNECTIONS); + conn_fd[k] = 0; + conn_count--; + printf("\n(%d) ------------------- DISCONNECT " + "-------------------\n(%d)\n(%d) ", + conn_count, conn_count, conn_count); + fflush(stdout); + if (conn_count == 0 && exit_when_zero) { + printf("\n"); + fflush(stdout); + exit_routine(); + } + } + } + + } /* for (i = 0; i < j; i++) */ + + } /* while (1) */ + + /* NOTREACHED */ +} + + +/*--------------------------------------------------------------------*/ +/*--- end valgrind-listener.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/cachegrind/.cvsignore b/VEX/head20041019/cachegrind/.cvsignore new file mode 100644 index 0000000000..bc25b8a888 --- /dev/null +++ b/VEX/head20041019/cachegrind/.cvsignore @@ -0,0 +1,3 @@ +Makefile.in +Makefile +cg_annotate diff --git a/VEX/head20041019/cachegrind/CVS/Entries b/VEX/head20041019/cachegrind/CVS/Entries new file mode 100644 index 0000000000..0a0fb430bd --- /dev/null +++ b/VEX/head20041019/cachegrind/CVS/Entries @@ -0,0 +1,9 @@ +/.cvsignore/1.2/Mon Sep 23 11:37:05 2002// +/Makefile.am/1.48/Sat Sep 11 18:27:43 2004// +/cg_annotate.in/1.20/Tue Jul 6 21:54:20 2004// +/cg_arch.h/1.1/Sat Sep 11 16:45:25 2004// +/cg_main.c/1.81/Wed Oct 6 13:50:12 2004// +/cg_sim.c/1.1/Sun Jan 4 16:56:57 2004// +D/docs//// +D/tests//// +D/x86//// diff --git a/VEX/head20041019/cachegrind/CVS/Repository b/VEX/head20041019/cachegrind/CVS/Repository new file mode 100644 index 0000000000..2c241ce6c5 --- /dev/null +++ b/VEX/head20041019/cachegrind/CVS/Repository @@ -0,0 +1 @@ +valgrind/cachegrind diff --git a/VEX/head20041019/cachegrind/CVS/Root b/VEX/head20041019/cachegrind/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/cachegrind/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/cachegrind/CVS/Template b/VEX/head20041019/cachegrind/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/Makefile.am b/VEX/head20041019/cachegrind/Makefile.am new file mode 100644 index 0000000000..9d658d54e2 --- /dev/null +++ b/VEX/head20041019/cachegrind/Makefile.am @@ -0,0 +1,21 @@ +##include $(top_srcdir)/Makefile.tool.am +include $(top_srcdir)/Makefile.all.am +include $(top_srcdir)/Makefile.tool-flags.am +include $(top_srcdir)/Makefile.tool-inplace.am + +SUBDIRS = $(VG_ARCH) . tests docs + +AM_CPPFLAGS += -I$(top_srcdir)/cachegrind/$(VG_ARCH) + +bin_SCRIPTS = cg_annotate + +EXTRA_DIST = cg_sim.c + +noinst_HEADERS = cg_arch.h + +val_PROGRAMS = vgskin_cachegrind.so + +vgskin_cachegrind_so_SOURCES = cg_main.c +vgskin_cachegrind_so_LDFLAGS = -shared +vgskin_cachegrind_so_LDADD = ${VG_ARCH}/libcgarch.a + diff --git a/VEX/head20041019/cachegrind/cg_annotate.in b/VEX/head20041019/cachegrind/cg_annotate.in new file mode 100644 index 0000000000..17d19eea6d --- /dev/null +++ b/VEX/head20041019/cachegrind/cg_annotate.in @@ -0,0 +1,891 @@ +#! @PERL@ -w + +##--------------------------------------------------------------------## +##--- The cache simulation framework: instrumentation, recording ---## +##--- and results printing. ---## +##--- cg_annotate.in ---## +##--------------------------------------------------------------------## + +# This file is part of Cachegrind, a Valgrind tool for cache +# profiling programs. +# +# Copyright (C) 2002-2004 Nicholas Nethercote +# njn25@cam.ac.uk +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA. +# +# The GNU General Public License is contained in the file COPYING. + +#---------------------------------------------------------------------------- +# Annotator for cachegrind. +# +# File format is described in /docs/techdocs.html. +# +# Performance improvements record, using cachegrind.out for cacheprof, doing no +# source annotation (irrelevant ones removed): +# user time +# 1. turned off warnings in add_hash_a_to_b() 3.81 --> 3.48s +# [now add_array_a_to_b()] +# 6. make line_to_CC() return a ref instead of a hash 3.01 --> 2.77s +# +#10. changed file format to avoid file/fn name repetition 2.40s +# (not sure why higher; maybe due to new '.' entries?) +#11. changed file format to drop unnecessary end-line "."s 2.36s +# (shrunk file by about 37%) +#12. switched from hash CCs to array CCs 1.61s +#13. only adding b[i] to a[i] if b[i] defined (was doing it if +# either a[i] or b[i] was defined, but if b[i] was undefined +# it just added 0) 1.48s +#14. Stopped converting "." entries to undef and then back 1.16s +#15. Using foreach $i (x..y) instead of for ($i = 0...) in +# add_array_a_to_b() 1.11s +# +# Auto-annotating primes: +#16. Finding count lengths by int((length-1)/3), not by +# commifying (halves the number of commify calls) 1.68s --> 1.47s + +use strict; + +#---------------------------------------------------------------------------- +# Overview: the running example in the comments is for: +# - events = A,B,C,D +# - --show=C,A,D +# - --sort=D,C +#---------------------------------------------------------------------------- + +#---------------------------------------------------------------------------- +# Global variables, main data structures +#---------------------------------------------------------------------------- +# CCs are arrays, the counts corresponding to @events, with 'undef' +# representing '.'. This makes things fast (faster than using hashes for CCs) +# but we have to use @sort_order and @show_order below to handle the --sort and +# --show options, which is a bit tricky. +#---------------------------------------------------------------------------- + +# Total counts for summary (an array reference). +my $summary_CC; + +# Totals for each function, for overall summary. +# hash(filename:fn_name => CC array) +my %fn_totals; + +# Individual CCs, organised by filename and line_num for easy annotation. +# hash(filename => hash(line_num => CC array)) +my %all_ind_CCs; + +# Files chosen for annotation on the command line. +# key = basename (trimmed of any directory), value = full filename +my %user_ann_files; + +# Generic description string. +my $desc = ""; + +# Command line of profiled program. +my $cmd; + +# Events in input file, eg. (A,B,C,D) +my @events; + +# Events to show, from command line, eg. (C,A,D) +my @show_events; + +# Map from @show_events indices to @events indices, eg. (2,0,3). Gives the +# order in which we must traverse @events in order to show the @show_events, +# eg. (@events[$show_order[1]], @events[$show_order[2]]...) = @show_events. +# (Might help to think of it like a hash (0 => 2, 1 => 0, 2 => 3).) +my @show_order; + +# Print out the function totals sorted by these events, eg. (D,C). +my @sort_events; + +# Map from @sort_events indices to @events indices, eg. (3,2). Same idea as +# for @show_order. +my @sort_order; + +# Thresholds, one for each sort event (or default to 1 if no sort events +# specified). We print out functions and do auto-annotations until we've +# handled this proportion of all the events thresholded. +my @thresholds; + +my $default_threshold = 99; + +my $single_threshold = $default_threshold; + +# If on, automatically annotates all files that are involved in getting over +# all the threshold counts. +my $auto_annotate = 0; + +# Number of lines to show around each annotated line. +my $context = 8; + +# Directories in which to look for annotation files. +my @include_dirs = (""); + +# Input file name +my $input_file = undef; + +# Version number +my $version = "@VERSION@"; + +# Usage message. +my $usage = < [source-files] + + options for the user, with defaults in [ ], are: + -h --help show this message + -v --version show version + --show=A,B,C only show figures for events A,B,C [all] + --sort=A,B,C sort columns by events A,B,C [event column order] + --threshold=<0--100> percentage of counts (of primary sort event) we + are interested in [$default_threshold%] + --auto=yes|no annotate all source files containing functions + that helped reach the event count threshold [no] + --context=N print N lines of context before and after + annotated lines [8] + -I --include=

add to list of directories to search for + source files + + Cachegrind is Copyright (C) 2002-2004 Nicholas Nethercote. + Both are licensed under the GNU General Public License, version 2. + Bug reports, feedback, admiration, abuse, etc, to: njn25\@cam.ac.uk. + +END +; + +# Used in various places of output. +my $fancy = '-' x 80 . "\n"; + +#----------------------------------------------------------------------------- +# Argument and option handling +#----------------------------------------------------------------------------- +sub process_cmd_line() +{ + for my $arg (@ARGV) { + + # Option handling + if ($arg =~ /^-/) { + + # --version + if ($arg =~ /^-v$|^--version$/) { + die("cg_annotate-$version\n"); + + # --show=A,B,C + } elsif ($arg =~ /^--show=(.*)$/) { + @show_events = split(/,/, $1); + + # --sort=A,B,C + } elsif ($arg =~ /^--sort=(.*)$/) { + @sort_events = split(/,/, $1); + foreach my $i (0 .. scalar @sort_events - 1) { + if ($sort_events[$i] =~#/.*:(\d+)$/) { + /.*:([\d\.]+)%?$/) { + my $th = $1; + ($th >= 0 && $th <= 100) or die($usage); + $sort_events[$i] =~ s/:.*//; + $thresholds[$i] = $th; + } else { + $thresholds[$i] = 0; + } + } + + # --threshold=X (tolerates a trailing '%') + } elsif ($arg =~ /^--threshold=([\d\.]+)%?$/) { + $single_threshold = $1; + ($1 >= 0 && $1 <= 100) or die($usage); + + # --auto=yes|no + } elsif ($arg =~ /^--auto=(yes|no)$/) { + $auto_annotate = 1 if ($1 eq "yes"); + $auto_annotate = 0 if ($1 eq "no"); + + # --context=N + } elsif ($arg =~ /^--context=([\d\.]+)$/) { + $context = $1; + if ($context < 0) { + die($usage); + } + + # --include=A,B,C + } elsif ($arg =~ /^(-I|--include)=(.*)$/) { + my $inc = $2; + $inc =~ s|/$||; # trim trailing '/' + push(@include_dirs, "$inc/"); + + } elsif ($arg =~ /^--(\d+)$/) { + my $pid = $1; + if (not defined $input_file) { + $input_file = "cachegrind.out.$pid"; + } else { + die("One cachegrind.out. file at a time, please\n"); + } + + } else { # -h and --help fall under this case + die($usage); + } + + # Argument handling -- annotation file checking and selection. + # Stick filenames into a hash for quick 'n easy lookup throughout. + } else { + my $readable = 0; + foreach my $include_dir (@include_dirs) { + if (-r $include_dir . $arg) { + $readable = 1; + } + } + $readable or die("File $arg not found in any of: @include_dirs\n"); + $user_ann_files{$arg} = 1; + } + } + + # Must have chosen an input file + if (not defined $input_file) { + die($usage); + } +} + +#----------------------------------------------------------------------------- +# Reading of input file +#----------------------------------------------------------------------------- +sub max ($$) +{ + my ($x, $y) = @_; + return ($x > $y ? $x : $y); +} + +# Add the two arrays; any '.' entries are ignored. Two tricky things: +# 1. If $a2->[$i] is undefined, it defaults to 0 which is what we want; we turn +# off warnings to allow this. This makes things about 10% faster than +# checking for definedness ourselves. +# 2. We don't add an undefined count or a ".", even though it's value is 0, +# because we don't want to make an $a2->[$i] that is undef become 0 +# unnecessarily. +sub add_array_a_to_b ($$) +{ + my ($a1, $a2) = @_; + + my $n = max(scalar @$a1, scalar @$a2); + $^W = 0; + foreach my $i (0 .. $n-1) { + $a2->[$i] += $a1->[$i] if (defined $a1->[$i] && "." ne $a1->[$i]); + } + $^W = 1; +} + +# Add each event count to the CC array. '.' counts become undef, as do +# missing entries (implicitly). +sub line_to_CC ($) +{ + my @CC = (split /\s+/, $_[0]); + (@CC <= @events) or die("Line $.: too many event counts\n"); + return \@CC; +} + +sub read_input_file() +{ + open(INPUTFILE, "< $input_file") || die "File $input_file not opened\n"; + + # Read "desc:" lines. + my $line; + while ($line = ) { + if ($line =~ s/desc:\s+//) { + $desc .= $line; + } else { + last; + } + } + + # Read "cmd:" line (Nb: will already be in $line from "desc:" loop above). + ($line =~ s/cmd:\s+//) or die("Line $.: missing command line\n"); + $cmd = $line; + chomp($cmd); # Remove newline + + # Read "events:" line. We make a temporary hash in which the Nth event's + # value is N, which is useful for handling --show/--sort options below. + $line = ; + (defined $line && $line =~ s/events:\s+//) + or die("Line $.: missing events line\n"); + @events = split(/\s+/, $line); + my %events; + my $n = 0; + foreach my $event (@events) { + $events{$event} = $n; + $n++ + } + + # If no --show arg give, default to showing all events in the file. + # If --show option is used, check all specified events appeared in the + # "events:" line. Then initialise @show_order. + if (@show_events) { + foreach my $show_event (@show_events) { + (defined $events{$show_event}) or + die("--show event `$show_event' did not appear in input\n"); + } + } else { + @show_events = @events; + } + foreach my $show_event (@show_events) { + push(@show_order, $events{$show_event}); + } + + # Do as for --show, but if no --sort arg given, default to sorting by + # column order (ie. first column event is primary sort key, 2nd column is + # 2ndary key, etc). + if (@sort_events) { + foreach my $sort_event (@sort_events) { + (defined $events{$sort_event}) or + die("--sort event `$sort_event' did not appear in input\n"); + } + } else { + @sort_events = @events; + } + foreach my $sort_event (@sort_events) { + push(@sort_order, $events{$sort_event}); + } + + # If multiple threshold args weren't given via --sort, stick in the single + # threshold (either from --threshold if used, or the default otherwise) for + # the primary sort event, and 0% for the rest. + if (not @thresholds) { + foreach my $e (@sort_order) { + push(@thresholds, 0); + } + $thresholds[0] = $single_threshold; + } + + my $curr_file; + my $curr_fn; + my $curr_name; + + my $curr_fn_CC = []; + my $curr_file_ind_CCs = {}; # hash(line_num => CC) + + # Read body of input file. + while () { + s/#.*$//; # remove comments + if (s/^(\d+)\s+//) { + my $line_num = $1; + my $CC = line_to_CC($_); + add_array_a_to_b($CC, $curr_fn_CC); + + # If curr_file is selected, add CC to curr_file list. We look for + # full filename matches; or, if auto-annotating, we have to + # remember everything -- we won't know until the end what's needed. + if ($auto_annotate || defined $user_ann_files{$curr_file}) { + my $tmp = $curr_file_ind_CCs->{$line_num}; + $tmp = [] unless defined $tmp; + add_array_a_to_b($CC, $tmp); + $curr_file_ind_CCs->{$line_num} = $tmp; + } + + } elsif (s/^fn=(.*)$//) { + # Commit result from previous function + $fn_totals{$curr_name} = $curr_fn_CC if (defined $curr_name); + + # Setup new one + $curr_fn = $1; + $curr_name = "$curr_file:$curr_fn"; + $curr_fn_CC = $fn_totals{$curr_name}; + $curr_fn_CC = [] unless (defined $curr_fn_CC); + + } elsif (s/^fl=(.*)$//) { + $all_ind_CCs{$curr_file} = $curr_file_ind_CCs + if (defined $curr_file); + + $curr_file = $1; + $curr_file_ind_CCs = $all_ind_CCs{$curr_file}; + $curr_file_ind_CCs = {} unless (defined $curr_file_ind_CCs); + + } elsif (s/^\s*$//) { + # blank, do nothing + + } elsif (s/^summary:\s+//) { + # Finish up handling final filename/fn_name counts + $fn_totals{"$curr_file:$curr_fn"} = $curr_fn_CC + if (defined $curr_file && defined $curr_fn); + $all_ind_CCs{$curr_file} = + $curr_file_ind_CCs if (defined $curr_file); + + $summary_CC = line_to_CC($_); + (scalar(@$summary_CC) == @events) + or die("Line $.: summary event and total event mismatch\n"); + + } else { + warn("WARNING: line $. malformed, ignoring\n"); + } + } + + # Check if summary line was present + if (not defined $summary_CC) { + die("missing final summary line, aborting\n"); + } + + close(INPUTFILE); +} + +#----------------------------------------------------------------------------- +# Print options used +#----------------------------------------------------------------------------- +sub print_options () +{ + print($fancy); + print($desc); + print("Command: $cmd\n"); + print("Events recorded: @events\n"); + print("Events shown: @show_events\n"); + print("Event sort order: @sort_events\n"); + print("Thresholds: @thresholds\n"); + + my @include_dirs2 = @include_dirs; # copy @include_dirs + shift(@include_dirs2); # remove "" entry, which is always the first + unshift(@include_dirs2, "") if (0 == @include_dirs2); + my $include_dir = shift(@include_dirs2); + print("Include dirs: $include_dir\n"); + foreach my $include_dir (@include_dirs2) { + print(" $include_dir\n"); + } + + my @user_ann_files = keys %user_ann_files; + unshift(@user_ann_files, "") if (0 == @user_ann_files); + my $user_ann_file = shift(@user_ann_files); + print("User annotated: $user_ann_file\n"); + foreach $user_ann_file (@user_ann_files) { + print(" $user_ann_file\n"); + } + + my $is_on = ($auto_annotate ? "on" : "off"); + print("Auto-annotation: $is_on\n"); + print("\n"); +} + +#----------------------------------------------------------------------------- +# Print summary and sorted function totals +#----------------------------------------------------------------------------- +sub mycmp ($$) +{ + my ($c, $d) = @_; + + # Iterate through sort events (eg. 3,2); return result if two are different + foreach my $i (@sort_order) { + my ($x, $y); + $x = $c->[$i]; + $y = $d->[$i]; + $x = -1 unless defined $x; + $y = -1 unless defined $y; + + my $cmp = $y <=> $x; # reverse sort + if (0 != $cmp) { + return $cmp; + } + } + # Exhausted events, equal + return 0; +} + +sub commify ($) { + my ($val) = @_; + 1 while ($val =~ s/^(\d+)(\d{3})/$1,$2/); + return $val; +} + +# Because the counts can get very big, and we don't want to waste screen space +# and make lines too long, we compute exactly how wide each column needs to be +# by finding the widest entry for each one. +sub compute_CC_col_widths (@) +{ + my @CCs = @_; + my $CC_col_widths = []; + + # Initialise with minimum widths (from event names) + foreach my $event (@events) { + push(@$CC_col_widths, length($event)); + } + + # Find maximum width count for each column. @CC_col_width positions + # correspond to @CC positions. + foreach my $CC (@CCs) { + foreach my $i (0 .. scalar(@$CC)-1) { + if (defined $CC->[$i]) { + # Find length, accounting for commas that will be added + my $length = length $CC->[$i]; + my $clength = $length + int(($length - 1) / 3); + $CC_col_widths->[$i] = max($CC_col_widths->[$i], $clength); + } + } + } + return $CC_col_widths; +} + +# Print the CC with each column's size dictated by $CC_col_widths. +sub print_CC ($$) +{ + my ($CC, $CC_col_widths) = @_; + + foreach my $i (@show_order) { + my $count = (defined $CC->[$i] ? commify($CC->[$i]) : "."); + my $space = ' ' x ($CC_col_widths->[$i] - length($count)); + print("$space$count "); + } +} + +sub print_events ($) +{ + my ($CC_col_widths) = @_; + + foreach my $i (@show_order) { + my $event = $events[$i]; + my $event_width = length($event); + my $col_width = $CC_col_widths->[$i]; + my $space = ' ' x ($col_width - $event_width); + print("$space$event "); + } +} + +# Prints summary and function totals (with separate column widths, so that +# function names aren't pushed over unnecessarily by huge summary figures). +# Also returns a hash containing all the files that are involved in getting the +# events count above the thresholds (ie. all the interesting ones). +sub print_summary_and_fn_totals () +{ + my @fn_fullnames = keys %fn_totals; + + # Work out the size of each column for printing (summary and functions + # separately). + my $summary_CC_col_widths = compute_CC_col_widths($summary_CC); + my $fn_CC_col_widths = compute_CC_col_widths(values %fn_totals); + + # Header and counts for summary + print($fancy); + print_events($summary_CC_col_widths); + print("\n"); + print($fancy); + print_CC($summary_CC, $summary_CC_col_widths); + print(" PROGRAM TOTALS\n"); + print("\n"); + + # Header for functions + print($fancy); + print_events($fn_CC_col_widths); + print(" file:function\n"); + print($fancy); + + # Sort function names into order dictated by --sort option. + @fn_fullnames = sort { + mycmp($fn_totals{$a}, $fn_totals{$b}) + } @fn_fullnames; + + + # Assertion + (scalar @sort_order == scalar @thresholds) or + die("sort_order length != thresholds length:\n", + " @sort_order\n @thresholds\n"); + + my $threshold_files = {}; + # @curr_totals has the same shape as @sort_order and @thresholds + my @curr_totals = (); + foreach my $e (@thresholds) { + push(@curr_totals, 0); + } + + # Print functions, stopping when the threshold has been reached. + foreach my $fn_name (@fn_fullnames) { + + # Stop when we've reached all the thresholds + my $reached_all_thresholds = 1; + foreach my $i (0 .. scalar @thresholds - 1) { + my $prop = $curr_totals[$i] * 100 / $summary_CC->[$sort_order[$i]]; + $reached_all_thresholds &&= ($prop >= $thresholds[$i]); + } + last if $reached_all_thresholds; + + # Print function results + my $fn_CC = $fn_totals{$fn_name}; + print_CC($fn_CC, $fn_CC_col_widths); + print(" $fn_name\n"); + + # Update the threshold counts + my $filename = $fn_name; + $filename =~ s/:.+$//; # remove function name + $threshold_files->{$filename} = 1; + foreach my $i (0 .. scalar @sort_order - 1) { + $curr_totals[$i] += $fn_CC->[$sort_order[$i]] + if (defined $fn_CC->[$sort_order[$i]]); + } + } + print("\n"); + + return $threshold_files; +} + +#----------------------------------------------------------------------------- +# Annotate selected files +#----------------------------------------------------------------------------- + +# Issue a warning that the source file is more recent than the input file. +sub warning_on_src_more_recent_than_inputfile ($) +{ + my $src_file = $_[0]; + + my $warning = <{"???"}; + %all_ann_files = (%user_ann_files, %$threshold_files) + } else { + %all_ann_files = %user_ann_files; + } + + # Track if we did any annotations. + my $did_annotations = 0; + + LOOP: + foreach my $src_file (keys %all_ann_files) { + + my $opened_file = ""; + my $full_file_name = ""; + foreach my $include_dir (@include_dirs) { + my $try_name = $include_dir . $src_file; + if (open(INPUTFILE, "< $try_name")) { + $opened_file = $try_name; + $full_file_name = ($include_dir eq "" + ? $src_file + : "$include_dir + $src_file"); + last; + } + } + + if (not $opened_file) { + # Failed to open the file. If chosen on the command line, die. + # If arose from auto-annotation, print a little message. + if (defined $user_ann_files{$src_file}) { + die("File $src_file not opened in any of: @include_dirs\n"); + + } else { + push(@unfound_auto_annotate_files, $src_file); + } + + } else { + # File header (distinguish between user- and auto-selected files). + print("$fancy"); + my $ann_type = + (defined $user_ann_files{$src_file} ? "User" : "Auto"); + print("-- $ann_type-annotated source: $full_file_name\n"); + print("$fancy"); + + # Get file's CCs + my $src_file_CCs = $all_ind_CCs{$src_file}; + if (!defined $src_file_CCs) { + print(" No information has been collected for $src_file\n\n"); + next LOOP; + } + + $did_annotations = 1; + + # Numeric, not lexicographic sort! + my @line_nums = sort {$a <=> $b} keys %$src_file_CCs; + + # If $src_file more recent than cachegrind.out, issue warning + my $src_more_recent_than_inputfile = 0; + if ((stat $opened_file)[9] > (stat $input_file)[9]) { + $src_more_recent_than_inputfile = 1; + warning_on_src_more_recent_than_inputfile($src_file); + } + + # Work out the size of each column for printing + my $CC_col_widths = compute_CC_col_widths(values %$src_file_CCs); + + # Events header + print_events($CC_col_widths); + print("\n\n"); + + # Shift out 0 if it's in the line numbers (from unknown entries, + # likely due to bugs in Valgrind's stabs debug info reader) + shift(@line_nums) if (0 == $line_nums[0]); + + # Finds interesting line ranges -- all lines with a CC, and all + # lines within $context lines of a line with a CC. + my $n = @line_nums; + my @pairs; + for (my $i = 0; $i < $n; $i++) { + push(@pairs, $line_nums[$i] - $context); # lower marker + while ($i < $n-1 && + $line_nums[$i] + 2*$context >= $line_nums[$i+1]) { + $i++; + } + push(@pairs, $line_nums[$i] + $context); # upper marker + } + + # Annotate chosen lines, tracking total counts of lines printed + $pairs[0] = 1 if ($pairs[0] < 1); + while (@pairs) { + my $low = shift @pairs; + my $high = shift @pairs; + while ($. < $low-1) { + my $tmp = ; + last unless (defined $tmp); # hack to detect EOF + } + my $src_line; + # Print line number, unless start of file + print("-- line $low " . '-' x 40 . "\n") if ($low != 1); + while (($. < $high) && ($src_line = )) { + if (defined $line_nums[0] && $. == $line_nums[0]) { + print_CC($src_file_CCs->{$.}, $CC_col_widths); + add_array_a_to_b($src_file_CCs->{$.}, + $printed_totals_CC); + shift(@line_nums); + + } else { + print_CC( [], $CC_col_widths); + } + + print(" $src_line"); + } + # Print line number, unless EOF + if ($src_line) { + print("-- line $high " . '-' x 40 . "\n"); + } else { + last; + } + } + + # If there was info on lines past the end of the file... + if (@line_nums) { + foreach my $line_num (@line_nums) { + print_CC($src_file_CCs->{$line_num}, $CC_col_widths); + print(" \n"); + } + print("\n"); + warning_on_nonexistent_lines($src_more_recent_than_inputfile, + $src_file, \@line_nums); + } + print("\n"); + + # Print summary of counts attributed to file but not to any + # particular line (due to incomplete debug info). + if ($src_file_CCs->{0}) { + print_CC($src_file_CCs->{0}, $CC_col_widths); + print(" \n\n"); + } + + close(INPUTFILE); + } + } + + # Print list of unfound auto-annotate selected files. + if (@unfound_auto_annotate_files) { + print("$fancy"); + print("The following files chosen for auto-annotation could not be found:\n"); + print($fancy); + foreach my $f (@unfound_auto_annotate_files) { + print(" $f\n"); + } + print("\n"); + } + + # If we did any annotating, print what proportion of events were covered by + # annotated lines above. + if ($did_annotations) { + my $percent_printed_CC; + foreach (my $i = 0; $i < @$summary_CC; $i++) { + $percent_printed_CC->[$i] = + sprintf("%.0f", + $printed_totals_CC->[$i] / $summary_CC->[$i] * 100); + } + my $pp_CC_col_widths = compute_CC_col_widths($percent_printed_CC); + print($fancy); + print_events($pp_CC_col_widths); + print("\n"); + print($fancy); + print_CC($percent_printed_CC, $pp_CC_col_widths); + print(" percentage of events annotated\n\n"); + } +} + +#---------------------------------------------------------------------------- +# "main()" +#---------------------------------------------------------------------------- +process_cmd_line(); +read_input_file(); +print_options(); +my $threshold_files = print_summary_and_fn_totals(); +annotate_ann_files($threshold_files); + +##--------------------------------------------------------------------## +##--- end cg_annotate.in ---## +##--------------------------------------------------------------------## + + diff --git a/VEX/head20041019/cachegrind/cg_arch.h b/VEX/head20041019/cachegrind/cg_arch.h new file mode 100644 index 0000000000..71ed2bd842 --- /dev/null +++ b/VEX/head20041019/cachegrind/cg_arch.h @@ -0,0 +1,49 @@ + +/*--------------------------------------------------------------------*/ +/*--- Arch-specific declarations. cg_arch.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Cachegrind, a Valgrind tool for cache + profiling programs. + + Copyright (C) 2002-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __CG_ARCH_H +#define __CG_ARCH_H + +// For cache simulation +typedef struct { + int size; // bytes + int assoc; + int line_size; // bytes +} cache_t; + +void VGA_(configure_caches)(cache_t* I1c, cache_t* D1c, cache_t* L2c, + cache_t* I1_dflt, cache_t* D1_dflt, cache_t* L2_dflt, + Bool all_caches_clo_defined); + +#endif // __CG_ARCH_H + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/cachegrind/cg_main.c b/VEX/head20041019/cachegrind/cg_main.c new file mode 100644 index 0000000000..67be6bb097 --- /dev/null +++ b/VEX/head20041019/cachegrind/cg_main.c @@ -0,0 +1,1163 @@ + +/*--------------------------------------------------------------------*/ +/*--- Cachegrind: every but the simulation itself. ---*/ +/*--- cg_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Cachegrind, a Valgrind tool for cache + profiling programs. + + Copyright (C) 2002-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "tool.h" +//#include "vg_profile.c" + +#include "cg_arch.h" +#include "cg_sim.c" + +/*------------------------------------------------------------*/ +/*--- Constants ---*/ +/*------------------------------------------------------------*/ + +#define MIN_LINE_SIZE 16 +#define FILE_LEN 256 +#define FN_LEN 256 + +/*------------------------------------------------------------*/ +/*--- Profiling events ---*/ +/*------------------------------------------------------------*/ + +typedef + enum { + VgpGetLineCC = VgpFini+1, + VgpCacheSimulate, + VgpCacheResults + } + VgpToolCC; + +/*------------------------------------------------------------*/ +/*--- Types and Data Structures ---*/ +/*------------------------------------------------------------*/ + +typedef struct _CC CC; +struct _CC { + ULong a; + ULong m1; + ULong m2; +}; + +//------------------------------------------------------------ +// Primary data structure #1: CC table +// - Holds the per-source-line hit/miss stats, grouped by file/function/line. +// - hash(file, hash(fn, hash(line+CC))) +// - Each hash table is separately chained. +// - The array sizes below work fairly well for Konqueror. +// - Lookups done by instr_addr, which is converted immediately to a source +// location. +// - Traversed for dumping stats at end in file/func/line hierarchy. + +#define N_FILE_ENTRIES 251 +#define N_FN_ENTRIES 53 +#define N_LINE_ENTRIES 37 + +typedef struct _lineCC lineCC; +struct _lineCC { + Int line; + CC Ir; + CC Dr; + CC Dw; + lineCC* next; +}; + +typedef struct _fnCC fnCC; +struct _fnCC { + Char* fn; + fnCC* next; + lineCC* lines[N_LINE_ENTRIES]; +}; + +typedef struct _fileCC fileCC; +struct _fileCC { + Char* file; + fileCC* next; + fnCC* fns[N_FN_ENTRIES]; +}; + +// Top level of CC table. Auto-zeroed. +static fileCC *CC_table[N_FILE_ENTRIES]; + +//------------------------------------------------------------ +// Primary data structre #2: Instr-info table +// - Holds the cached info about each instr that is used for simulation. +// - table(BB_start_addr, list(instr_info)) +// - For each BB, each instr_info in the list holds info about the +// instruction (instr_size, instr_addr, etc), plus a pointer to its line +// CC. This node is what's passed to the simulation function. +// - When BBs are discarded the relevant list(instr_details) is freed. + +typedef struct _instr_info instr_info; +struct _instr_info { + Addr instr_addr; + UChar instr_size; + UChar data_size; + lineCC* parent; // parent line-CC +}; + +typedef struct _BB_info BB_info; +struct _BB_info { + BB_info* next; // next field + Addr BB_addr; // key + Int n_instrs; + instr_info instrs[0]; +}; + +VgHashTable instr_info_table; // hash(Addr, BB_info) + +//------------------------------------------------------------ +// Stats +static Int distinct_files = 0; +static Int distinct_fns = 0; +static Int distinct_lines = 0; +static Int distinct_instrs = 0; + +static Int full_debug_BBs = 0; +static Int file_line_debug_BBs = 0; +static Int fn_debug_BBs = 0; +static Int no_debug_BBs = 0; + +static Int BB_retranslations = 0; + +/*------------------------------------------------------------*/ +/*--- CC table operations ---*/ +/*------------------------------------------------------------*/ + +static void get_debug_info(Addr instr_addr, Char file[FILE_LEN], + Char fn[FN_LEN], Int* line) +{ + Bool found_file_line = VG_(get_filename_linenum)(instr_addr, file, + FILE_LEN, line); + Bool found_fn = VG_(get_fnname)(instr_addr, fn, FN_LEN); + + if (!found_file_line) { + VG_(strcpy)(file, "???"); + *line = 0; + } + if (!found_fn) { + VG_(strcpy)(fn, "???"); + } + if (found_file_line) { + if (found_fn) full_debug_BBs++; + else file_line_debug_BBs++; + } else { + if (found_fn) fn_debug_BBs++; + else no_debug_BBs++; + } +} + +static UInt hash(Char *s, UInt table_size) +{ + const int hash_constant = 256; + int hash_value = 0; + for ( ; *s; s++) + hash_value = (hash_constant * hash_value + *s) % table_size; + return hash_value; +} + +static __inline__ +fileCC* new_fileCC(Char filename[], fileCC* next) +{ + // Using calloc() zeroes the fns[] array + fileCC* cc = VG_(calloc)(1, sizeof(fileCC)); + cc->file = VG_(strdup)(filename); + cc->next = next; + return cc; +} + +static __inline__ +fnCC* new_fnCC(Char fn[], fnCC* next) +{ + // Using calloc() zeroes the lines[] array + fnCC* cc = VG_(calloc)(1, sizeof(fnCC)); + cc->fn = VG_(strdup)(fn); + cc->next = next; + return cc; +} + +static __inline__ +lineCC* new_lineCC(Int line, lineCC* next) +{ + // Using calloc() zeroes the Ir/Dr/Dw CCs and the instrs[] array + lineCC* cc = VG_(calloc)(1, sizeof(lineCC)); + cc->line = line; + cc->next = next; + return cc; +} + +static __inline__ +instr_info* new_instr_info(Addr instr_addr, lineCC* parent, instr_info* next) +{ + // Using calloc() zeroes instr_size and data_size + instr_info* ii = VG_(calloc)(1, sizeof(instr_info)); + ii->instr_addr = instr_addr; + ii->parent = parent; + return ii; +} + +// Do a three step traversal: by file, then fn, then line. +// In all cases prepends new nodes to their chain. Returns a pointer to the +// line node, creates a new one if necessary. +static lineCC* get_lineCC(Addr orig_addr) +{ + fileCC *curr_fileCC; + fnCC *curr_fnCC; + lineCC *curr_lineCC; + Char file[FILE_LEN], fn[FN_LEN]; + Int line; + UInt file_hash, fn_hash, line_hash; + + get_debug_info(orig_addr, file, fn, &line); + + VGP_PUSHCC(VgpGetLineCC); + + // level 1 + file_hash = hash(file, N_FILE_ENTRIES); + curr_fileCC = CC_table[file_hash]; + while (NULL != curr_fileCC && !VG_STREQ(file, curr_fileCC->file)) { + curr_fileCC = curr_fileCC->next; + } + if (NULL == curr_fileCC) { + CC_table[file_hash] = curr_fileCC = + new_fileCC(file, CC_table[file_hash]); + distinct_files++; + } + + // level 2 + fn_hash = hash(fn, N_FN_ENTRIES); + curr_fnCC = curr_fileCC->fns[fn_hash]; + while (NULL != curr_fnCC && !VG_STREQ(fn, curr_fnCC->fn)) { + curr_fnCC = curr_fnCC->next; + } + if (NULL == curr_fnCC) { + curr_fileCC->fns[fn_hash] = curr_fnCC = + new_fnCC(fn, curr_fileCC->fns[fn_hash]); + distinct_fns++; + } + + // level 3 + line_hash = line % N_LINE_ENTRIES; + curr_lineCC = curr_fnCC->lines[line_hash]; + while (NULL != curr_lineCC && line != curr_lineCC->line) { + curr_lineCC = curr_lineCC->next; + } + if (NULL == curr_lineCC) { + curr_fnCC->lines[line_hash] = curr_lineCC = + new_lineCC(line, curr_fnCC->lines[line_hash]); + distinct_lines++; + } + + VGP_POPCC(VgpGetLineCC); + return curr_lineCC; +} + +/*------------------------------------------------------------*/ +/*--- Cache simulation functions ---*/ +/*------------------------------------------------------------*/ + +static REGPARM(1) +void log_1I_0D_cache_access(instr_info* n) +{ + //VG_(printf)("1I_0D: CCaddr=0x%x, iaddr=0x%x, isize=%u\n", + // n, n->instr_addr, n->instr_size) + VGP_PUSHCC(VgpCacheSimulate); + cachesim_I1_doref(n->instr_addr, n->instr_size, + &n->parent->Ir.m1, &n->parent->Ir.m2); + n->parent->Ir.a++; + VGP_POPCC(VgpCacheSimulate); +} + +static REGPARM(2) +void log_1I_1Dr_cache_access(instr_info* n, Addr data_addr) +{ + //VG_(printf)("1I_1Dr: CCaddr=%p, iaddr=%p, isize=%u, daddr=%p, dsize=%u\n", + // n, n->instr_addr, n->instr_size, data_addr, n->data_size) + VGP_PUSHCC(VgpCacheSimulate); + cachesim_I1_doref(n->instr_addr, n->instr_size, + &n->parent->Ir.m1, &n->parent->Ir.m2); + n->parent->Ir.a++; + + cachesim_D1_doref(data_addr, n->data_size, + &n->parent->Dr.m1, &n->parent->Dr.m2); + n->parent->Dr.a++; + VGP_POPCC(VgpCacheSimulate); +} + +static REGPARM(2) +void log_1I_1Dw_cache_access(instr_info* n, Addr data_addr) +{ + //VG_(printf)("1I_1Dw: CCaddr=%p, iaddr=%p, isize=%u, daddr=%p, dsize=%u\n", + // n, n->instr_addr, n->instr_size, data_addr, n->data_size) + VGP_PUSHCC(VgpCacheSimulate); + cachesim_I1_doref(n->instr_addr, n->instr_size, + &n->parent->Ir.m1, &n->parent->Ir.m2); + n->parent->Ir.a++; + + cachesim_D1_doref(data_addr, n->data_size, + &n->parent->Dw.m1, &n->parent->Dw.m2); + n->parent->Dw.a++; + VGP_POPCC(VgpCacheSimulate); +} + +static REGPARM(3) +void log_1I_2D_cache_access(instr_info* n, Addr data_addr1, Addr data_addr2) +{ + //VG_(printf)("1I_2D: CCaddr=%p, iaddr=%p, isize=%u, daddr1=%p, daddr2=%p, dsize=%u\n", + // n, n->instr_addr, n->instr_size, data_addr1, data_addr2, n->data_size) + VGP_PUSHCC(VgpCacheSimulate); + cachesim_I1_doref(n->instr_addr, n->instr_size, + &n->parent->Ir.m1, &n->parent->Ir.m2); + n->parent->Ir.a++; + + cachesim_D1_doref(data_addr1, n->data_size, + &n->parent->Dr.m1, &n->parent->Dr.m2); + n->parent->Dr.a++; + cachesim_D1_doref(data_addr2, n->data_size, + &n->parent->Dw.m1, &n->parent->Dw.m2); + n->parent->Dw.a++; + VGP_POPCC(VgpCacheSimulate); +} + +/*------------------------------------------------------------*/ +/*--- Instrumentation ---*/ +/*------------------------------------------------------------*/ + +static +BB_info* get_BB_info(UCodeBlock* cb_in, Addr orig_addr, Bool* bb_seen_before) +{ + Int i, n_instrs; + UInstr* u_in; + BB_info* bb_info; + VgHashNode** dummy; + + // Count number of x86 instrs in BB + n_instrs = 1; // start at 1 because last x86 instr has no INCEIP + for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { + u_in = VG_(get_instr)(cb_in, i); + if (INCEIP == u_in->opcode) n_instrs++; + } + + // Get the BB_info + bb_info = (BB_info*)VG_(HT_get_node)(instr_info_table, orig_addr, &dummy); + *bb_seen_before = ( NULL == bb_info ? False : True ); + if (*bb_seen_before) { + // BB must have been translated before, but flushed from the TT + sk_assert(bb_info->n_instrs == n_instrs ); + BB_retranslations++; + } else { + // BB never translated before (at this address, at least; could have + // been unloaded and then reloaded elsewhere in memory) + bb_info = + VG_(calloc)(1, sizeof(BB_info) + n_instrs*sizeof(instr_info)); + bb_info->BB_addr = orig_addr; + bb_info->n_instrs = n_instrs; + VG_(HT_add_node)( instr_info_table, (VgHashNode*)bb_info ); + distinct_instrs++; + } + return bb_info; +} + +static +void do_details( instr_info* n, Bool bb_seen_before, + Addr instr_addr, Int instr_size, Int data_size ) +{ + lineCC* parent = get_lineCC(instr_addr); + if (bb_seen_before) { + sk_assert( n->instr_addr == instr_addr ); + sk_assert( n->instr_size == instr_size ); + sk_assert( n->data_size == data_size ); + // Don't assert that (n->parent == parent)... it's conceivable that + // the debug info might change; the other asserts should be enough to + // detect anything strange. + } else { + n->instr_addr = instr_addr; + n->instr_size = instr_size; + n->data_size = data_size; + n->parent = parent; + } +} + +static Bool is_valid_data_size(Int data_size) +{ + return (4 == data_size || 2 == data_size || 1 == data_size || + 8 == data_size || 10 == data_size || MIN_LINE_SIZE == data_size); +} + +// Instrumentation for the end of each x86 instruction. +static +void end_of_x86_instr(UCodeBlock* cb, instr_info* i_node, Bool bb_seen_before, + UInt instr_addr, UInt instr_size, UInt data_size, + Int t_read, Int t_read_addr, + Int t_write, Int t_write_addr) +{ + Addr helper; + Int argc; + Int t_CC_addr, + t_data_addr1 = INVALID_TEMPREG, + t_data_addr2 = INVALID_TEMPREG; + + sk_assert(instr_size >= MIN_INSTR_SIZE && + instr_size <= MAX_INSTR_SIZE); + +#define IS_(X) (INVALID_TEMPREG != t_##X##_addr) +#define INV(qqt) (INVALID_TEMPREG == (qqt)) + + // Work out what kind of x86 instruction it is + if (!IS_(read) && !IS_(write)) { + sk_assert( 0 == data_size ); + sk_assert(INV(t_read) && INV(t_write)); + helper = (Addr) & log_1I_0D_cache_access; + argc = 1; + + } else if (IS_(read) && !IS_(write)) { + sk_assert( is_valid_data_size(data_size) ); + sk_assert(!INV(t_read) && INV(t_write)); + helper = (Addr) & log_1I_1Dr_cache_access; + argc = 2; + t_data_addr1 = t_read_addr; + + } else if (!IS_(read) && IS_(write)) { + sk_assert( is_valid_data_size(data_size) ); + sk_assert(INV(t_read) && !INV(t_write)); + helper = (Addr) & log_1I_1Dw_cache_access; + argc = 2; + t_data_addr1 = t_write_addr; + + } else { + sk_assert(IS_(read) && IS_(write)); + sk_assert( is_valid_data_size(data_size) ); + sk_assert(!INV(t_read) && !INV(t_write)); + if (t_read == t_write) { + helper = (Addr) & log_1I_1Dr_cache_access; + argc = 2; + t_data_addr1 = t_read_addr; + } else { + helper = (Addr) & log_1I_2D_cache_access; + argc = 3; + t_data_addr1 = t_read_addr; + t_data_addr2 = t_write_addr; + } + } +#undef IS_ +#undef INV + + // Setup 1st arg: CC addr + do_details( i_node, bb_seen_before, instr_addr, instr_size, data_size ); + t_CC_addr = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_CC_addr); + uLiteral(cb, (Addr)i_node); + + // Call the helper + if (1 == argc) + uInstr1(cb, CCALL, 0, TempReg, t_CC_addr); + else if (2 == argc) + uInstr2(cb, CCALL, 0, TempReg, t_CC_addr, + TempReg, t_data_addr1); + else if (3 == argc) + uInstr3(cb, CCALL, 0, TempReg, t_CC_addr, + TempReg, t_data_addr1, + TempReg, t_data_addr2); + else + VG_(skin_panic)("argc... not 1 or 2 or 3?"); + + uCCall(cb, helper, argc, argc, False); +} + +UCodeBlock* SK_(instrument)(UCodeBlock* cb_in, Addr orig_addr) +{ + UCodeBlock* cb; + UInstr* u_in; + Int i, bb_info_i; + BB_info* bb_info; + Bool bb_seen_before = False; + Int t_read_addr, t_write_addr, t_read, t_write; + Addr x86_instr_addr = orig_addr; + UInt x86_instr_size, data_size = 0; + Bool instrumented_Jcc = False; + + bb_info = get_BB_info(cb_in, orig_addr, &bb_seen_before); + bb_info_i = 0; + + cb = VG_(setup_UCodeBlock)(cb_in); + + t_read_addr = t_write_addr = t_read = t_write = INVALID_TEMPREG; + + for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { + u_in = VG_(get_instr)(cb_in, i); + + // We want to instrument each x86 instruction with a call to the + // appropriate simulation function, which depends on whether the + // instruction does memory data reads/writes. x86 instructions can + // end in three ways, and this is how they are instrumented: + // + // 1. UCode, INCEIP --> UCode, Instrumentation, INCEIP + // 2. UCode, JMP --> UCode, Instrumentation, JMP + // 3. UCode, Jcc, JMP --> UCode, Instrumentation, Jcc, JMP + // + // The last UInstr in a BB is always a JMP. Jccs, when they appear, + // are always second last. This is checked with assertions. + // Instrumentation must go before any jumps. (JIFZ is the exception; + // if a JIFZ succeeds, no simulation is done for the instruction.) + // + // x86 instruction sizes are obtained from INCEIPs (for case 1) or + // from .extra4b field of the final JMP (for case 2 & 3). + + if (instrumented_Jcc) sk_assert(u_in->opcode == JMP); + + switch (u_in->opcode) { + + // For memory-ref instrs, copy the data_addr into a temporary to be + // passed to the cachesim_* helper at the end of the instruction. + case LOAD: + case SSE3ag_MemRd_RegWr: + t_read = u_in->val1; + t_read_addr = newTemp(cb); + uInstr2(cb, MOV, 4, TempReg, u_in->val1, TempReg, t_read_addr); + data_size = u_in->size; + VG_(copy_UInstr)(cb, u_in); + break; + + case FPU_R: + case MMX2_MemRd: + t_read = u_in->val2; + t_read_addr = newTemp(cb); + uInstr2(cb, MOV, 4, TempReg, u_in->val2, TempReg, t_read_addr); + data_size = u_in->size; + VG_(copy_UInstr)(cb, u_in); + break; + break; + + case MMX2a1_MemRd: + case SSE2a_MemRd: + case SSE2a1_MemRd: + case SSE3a_MemRd: + case SSE3a1_MemRd: + t_read = u_in->val3; + t_read_addr = newTemp(cb); + uInstr2(cb, MOV, 4, TempReg, u_in->val3, TempReg, t_read_addr); + data_size = u_in->size; + VG_(copy_UInstr)(cb, u_in); + break; + + // Note that we must set t_write_addr even for mod instructions; + // That's how the code above determines whether it does a write. + // Without it, it would think a mod instruction is a read. + // As for the MOV, if it's a mod instruction it's redundant, but it's + // not expensive and mod instructions are rare anyway. */ + case STORE: + case FPU_W: + case MMX2_MemWr: + t_write = u_in->val2; + t_write_addr = newTemp(cb); + uInstr2(cb, MOV, 4, TempReg, u_in->val2, TempReg, t_write_addr); + data_size = u_in->size; + VG_(copy_UInstr)(cb, u_in); + break; + + case SSE2a_MemWr: + case SSE3a_MemWr: + t_write = u_in->val3; + t_write_addr = newTemp(cb); + uInstr2(cb, MOV, 4, TempReg, u_in->val3, TempReg, t_write_addr); + data_size = u_in->size; + VG_(copy_UInstr)(cb, u_in); + break; + + // INCEIP: insert instrumentation + case INCEIP: + x86_instr_size = u_in->val1; + goto instrument_x86_instr; + + // JMP: insert instrumentation if the first JMP + case JMP: + if (instrumented_Jcc) { + sk_assert(CondAlways == u_in->cond); + sk_assert(i+1 == VG_(get_num_instrs)(cb_in)); + VG_(copy_UInstr)(cb, u_in); + instrumented_Jcc = False; // rest + break; + } else { + // The first JMP... instrument. + if (CondAlways != u_in->cond) { + sk_assert(i+2 == VG_(get_num_instrs)(cb_in)); + instrumented_Jcc = True; + } else { + sk_assert(i+1 == VG_(get_num_instrs)(cb_in)); + } + // Get x86 instr size from final JMP. + x86_instr_size = VG_(get_last_instr)(cb_in)->extra4b; + goto instrument_x86_instr; + } + + // Code executed at the end of each x86 instruction. + instrument_x86_instr: + // Large (eg. 28B, 108B, 512B) data-sized instructions will be + // done inaccurately but they're very rare and this avoids + // errors from hitting more than two cache lines in the + // simulation. + if (data_size > MIN_LINE_SIZE) data_size = MIN_LINE_SIZE; + + end_of_x86_instr(cb, &bb_info->instrs[ bb_info_i ], bb_seen_before, + x86_instr_addr, x86_instr_size, data_size, + t_read, t_read_addr, t_write, t_write_addr); + + // Copy original UInstr (INCEIP or JMP) + VG_(copy_UInstr)(cb, u_in); + + // Update loop state for next x86 instr + bb_info_i++; + x86_instr_addr += x86_instr_size; + t_read_addr = t_write_addr = t_read = t_write = INVALID_TEMPREG; + data_size = 0; + break; + + default: + VG_(copy_UInstr)(cb, u_in); + break; + } + } + + // BB address should be the same as the first instruction's address. + sk_assert(bb_info->BB_addr == bb_info->instrs[0].instr_addr ); + sk_assert(bb_info_i == bb_info->n_instrs); + + VG_(free_UCodeBlock)(cb_in); + return cb; + +#undef INVALID_DATA_SIZE +} + +/*------------------------------------------------------------*/ +/*--- Cache configuration ---*/ +/*------------------------------------------------------------*/ + +#define UNDEFINED_CACHE ((cache_t) { -1, -1, -1 }) + +static cache_t clo_I1_cache = UNDEFINED_CACHE; +static cache_t clo_D1_cache = UNDEFINED_CACHE; +static cache_t clo_L2_cache = UNDEFINED_CACHE; + +/* Checks cache config is ok; makes it so if not. */ +static +void check_cache(cache_t* cache, cache_t* dflt, Char *name) +{ + /* First check they're all powers of two */ + if (-1 == VG_(log2)(cache->size)) { + VG_(message)(Vg_UserMsg, + "warning: %s size of %dB not a power of two; " + "defaulting to %dB", name, cache->size, dflt->size); + cache->size = dflt->size; + } + + if (-1 == VG_(log2)(cache->assoc)) { + VG_(message)(Vg_UserMsg, + "warning: %s associativity of %d not a power of two; " + "defaulting to %d-way", name, cache->assoc, dflt->assoc); + cache->assoc = dflt->assoc; + } + + if (-1 == VG_(log2)(cache->line_size)) { + VG_(message)(Vg_UserMsg, + "warning: %s line size of %dB not a power of two; " + "defaulting to %dB", + name, cache->line_size, dflt->line_size); + cache->line_size = dflt->line_size; + } + + /* Then check line size >= 16 -- any smaller and a single instruction could + * straddle three cache lines, which breaks a simulation assertion and is + * stupid anyway. */ + if (cache->line_size < MIN_LINE_SIZE) { + VG_(message)(Vg_UserMsg, + "warning: %s line size of %dB too small; " + "increasing to %dB", name, cache->line_size, MIN_LINE_SIZE); + cache->line_size = MIN_LINE_SIZE; + } + + /* Then check cache size > line size (causes seg faults if not). */ + if (cache->size <= cache->line_size) { + VG_(message)(Vg_UserMsg, + "warning: %s cache size of %dB <= line size of %dB; " + "increasing to %dB", name, cache->size, cache->line_size, + cache->line_size * 2); + cache->size = cache->line_size * 2; + } + + /* Then check assoc <= (size / line size) (seg faults otherwise). */ + if (cache->assoc > (cache->size / cache->line_size)) { + VG_(message)(Vg_UserMsg, + "warning: %s associativity > (size / line size); " + "increasing size to %dB", + name, cache->assoc * cache->line_size); + cache->size = cache->assoc * cache->line_size; + } +} + +static +void configure_caches(cache_t* I1c, cache_t* D1c, cache_t* L2c) +{ +#define DEFINED(L) (-1 != L.size || -1 != L.assoc || -1 != L.line_size) + + Int n_clos = 0; + cache_t I1_dflt, D1_dflt, L2_dflt; + + // Count how many were defined on the command line. + if (DEFINED(clo_I1_cache)) { n_clos++; } + if (DEFINED(clo_D1_cache)) { n_clos++; } + if (DEFINED(clo_L2_cache)) { n_clos++; } + + // Set the default cache config (using auto-detection, if supported by + // current arch) + VGA_(configure_caches)( I1c, D1c, L2c, &I1_dflt, &D1_dflt, &L2_dflt, + (3 == n_clos) ); + + // Then replace with any defined on the command line. + if (DEFINED(clo_I1_cache)) { *I1c = clo_I1_cache; } + if (DEFINED(clo_D1_cache)) { *D1c = clo_D1_cache; } + if (DEFINED(clo_L2_cache)) { *L2c = clo_L2_cache; } + + // Then check values and fix if not acceptable. + check_cache(I1c, &I1_dflt, "I1"); + check_cache(D1c, &D1_dflt, "D1"); + check_cache(L2c, &L2_dflt, "L2"); + + if (VG_(clo_verbosity) > 1) { + VG_(message)(Vg_UserMsg, "Cache configuration used:"); + VG_(message)(Vg_UserMsg, " I1: %dB, %d-way, %dB lines", + I1c->size, I1c->assoc, I1c->line_size); + VG_(message)(Vg_UserMsg, " D1: %dB, %d-way, %dB lines", + D1c->size, D1c->assoc, D1c->line_size); + VG_(message)(Vg_UserMsg, " L2: %dB, %d-way, %dB lines", + L2c->size, L2c->assoc, L2c->line_size); + } +#undef CMD_LINE_DEFINED +} + +/*------------------------------------------------------------*/ +/*--- SK_(fini)() and related function ---*/ +/*------------------------------------------------------------*/ + +// Total reads/writes/misses. Calculated during CC traversal at the end. +// All auto-zeroed. +static CC Ir_total; +static CC Dr_total; +static CC Dw_total; + +static Char* cachegrind_out_file; + +static void file_err ( void ) +{ + VG_(message)(Vg_UserMsg, + "error: can't open cache simulation output file `%s'", + cachegrind_out_file ); + VG_(message)(Vg_UserMsg, + " ... so simulation results will be missing."); +} + +static void fprint_lineCC(Int fd, lineCC* n) +{ + Char buf[512]; + VG_(sprintf)(buf, "%u %llu %llu %llu %llu %llu %llu %llu %llu %llu\n", + n->line, + n->Ir.a, n->Ir.m1, n->Ir.m2, + n->Dr.a, n->Dr.m1, n->Dr.m2, + n->Dw.a, n->Dw.m1, n->Dw.m2); + VG_(write)(fd, (void*)buf, VG_(strlen)(buf)); + + Ir_total.a += n->Ir.a; Ir_total.m1 += n->Ir.m1; Ir_total.m2 += n->Ir.m2; + Dr_total.a += n->Dr.a; Dr_total.m1 += n->Dr.m1; Dr_total.m2 += n->Dr.m2; + Dw_total.a += n->Dw.a; Dw_total.m1 += n->Dw.m1; Dw_total.m2 += n->Dw.m2; +} + +static void fprint_CC_table_and_calc_totals(void) +{ + Int fd; + Char buf[512]; + fileCC *curr_fileCC; + fnCC *curr_fnCC; + lineCC *curr_lineCC; + Int i, j, k; + + VGP_PUSHCC(VgpCacheResults); + + fd = VG_(open)(cachegrind_out_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY, + VKI_S_IRUSR|VKI_S_IWUSR); + if (fd < 0) { + // If the file can't be opened for whatever reason (conflict + // between multiple cachegrinded processes?), give up now. + file_err(); + return; + } + + // "desc:" lines (giving I1/D1/L2 cache configuration). The spaces after + // the 2nd colon makes cg_annotate's output look nicer. + VG_(sprintf)(buf, "desc: I1 cache: %s\n" + "desc: D1 cache: %s\n" + "desc: L2 cache: %s\n", + I1.desc_line, D1.desc_line, L2.desc_line); + VG_(write)(fd, (void*)buf, VG_(strlen)(buf)); + + // "cmd:" line + VG_(strcpy)(buf, "cmd:"); + VG_(write)(fd, (void*)buf, VG_(strlen)(buf)); + for (i = 0; i < VG_(client_argc); i++) { + VG_(write)(fd, " ", 1); + VG_(write)(fd, VG_(client_argv)[i], VG_(strlen)(VG_(client_argv)[i])); + } + // "events:" line + VG_(sprintf)(buf, "\nevents: Ir I1mr I2mr Dr D1mr D2mr Dw D1mw D2mw\n"); + VG_(write)(fd, (void*)buf, VG_(strlen)(buf)); + + // Six loops here: three for the hash table arrays, and three for the + // chains hanging off the hash table arrays. + for (i = 0; i < N_FILE_ENTRIES; i++) { + curr_fileCC = CC_table[i]; + while (curr_fileCC != NULL) { + VG_(sprintf)(buf, "fl=%s\n", curr_fileCC->file); + VG_(write)(fd, (void*)buf, VG_(strlen)(buf)); + + for (j = 0; j < N_FN_ENTRIES; j++) { + curr_fnCC = curr_fileCC->fns[j]; + while (curr_fnCC != NULL) { + VG_(sprintf)(buf, "fn=%s\n", curr_fnCC->fn); + VG_(write)(fd, (void*)buf, VG_(strlen)(buf)); + + for (k = 0; k < N_LINE_ENTRIES; k++) { + curr_lineCC = curr_fnCC->lines[k]; + while (curr_lineCC != NULL) { + fprint_lineCC(fd, curr_lineCC); + curr_lineCC = curr_lineCC->next; + } + } + curr_fnCC = curr_fnCC->next; + } + } + curr_fileCC = curr_fileCC->next; + } + } + + // Summary stats must come after rest of table, since we calculate them + // during traversal. */ + VG_(sprintf)(buf, "summary: " + "%llu %llu %llu %llu %llu %llu %llu %llu %llu\n", + Ir_total.a, Ir_total.m1, Ir_total.m2, + Dr_total.a, Dr_total.m1, Dr_total.m2, + Dw_total.a, Dw_total.m1, Dw_total.m2); + VG_(write)(fd, (void*)buf, VG_(strlen)(buf)); + VG_(close)(fd); +} + +static UInt ULong_width(ULong n) +{ + UInt w = 0; + while (n > 0) { + n = n / 10; + w++; + } + return w + (w-1)/3; // add space for commas +} + +static +void percentify(Int n, Int ex, Int field_width, char buf[]) +{ + int i, len, space; + + VG_(sprintf)(buf, "%d.%d%%", n / ex, n % ex); + len = VG_(strlen)(buf); + space = field_width - len; + if (space < 0) space = 0; /* Allow for v. small field_width */ + i = len; + + /* Right justify in field */ + for ( ; i >= 0; i--) buf[i + space] = buf[i]; + for (i = 0; i < space; i++) buf[i] = ' '; +} + +void SK_(fini)(Int exitcode) +{ + static char buf1[128], buf2[128], buf3[128], fmt [128]; + + CC D_total; + ULong L2_total_m, L2_total_mr, L2_total_mw, + L2_total, L2_total_r, L2_total_w; + Int l1, l2, l3; + Int p; + + fprint_CC_table_and_calc_totals(); + + if (VG_(clo_verbosity) == 0) + return; + + /* I cache results. Use the I_refs value to determine the first column + * width. */ + l1 = ULong_width(Ir_total.a); + l2 = ULong_width(Dr_total.a); + l3 = ULong_width(Dw_total.a); + + /* Make format string, getting width right for numbers */ + VG_(sprintf)(fmt, "%%s %%,%dld", l1); + + VG_(message)(Vg_UserMsg, fmt, "I refs: ", Ir_total.a); + VG_(message)(Vg_UserMsg, fmt, "I1 misses: ", Ir_total.m1); + VG_(message)(Vg_UserMsg, fmt, "L2i misses: ", Ir_total.m2); + + p = 100; + + if (0 == Ir_total.a) Ir_total.a = 1; + percentify(Ir_total.m1 * 100 * p / Ir_total.a, p, l1+1, buf1); + VG_(message)(Vg_UserMsg, "I1 miss rate: %s", buf1); + + percentify(Ir_total.m2 * 100 * p / Ir_total.a, p, l1+1, buf1); + VG_(message)(Vg_UserMsg, "L2i miss rate: %s", buf1); + VG_(message)(Vg_UserMsg, ""); + + /* D cache results. Use the D_refs.rd and D_refs.wr values to determine the + * width of columns 2 & 3. */ + D_total.a = Dr_total.a + Dw_total.a; + D_total.m1 = Dr_total.m1 + Dw_total.m1; + D_total.m2 = Dr_total.m2 + Dw_total.m2; + + /* Make format string, getting width right for numbers */ + VG_(sprintf)(fmt, "%%s %%,%dld (%%,%dld rd + %%,%dld wr)", l1, l2, l3); + + VG_(message)(Vg_UserMsg, fmt, "D refs: ", + D_total.a, Dr_total.a, Dw_total.a); + VG_(message)(Vg_UserMsg, fmt, "D1 misses: ", + D_total.m1, Dr_total.m1, Dw_total.m1); + VG_(message)(Vg_UserMsg, fmt, "L2d misses: ", + D_total.m2, Dr_total.m2, Dw_total.m2); + + p = 10; + + if (0 == D_total.a) D_total.a = 1; + if (0 == Dr_total.a) Dr_total.a = 1; + if (0 == Dw_total.a) Dw_total.a = 1; + percentify( D_total.m1 * 100 * p / D_total.a, p, l1+1, buf1); + percentify(Dr_total.m1 * 100 * p / Dr_total.a, p, l2+1, buf2); + percentify(Dw_total.m1 * 100 * p / Dw_total.a, p, l3+1, buf3); + VG_(message)(Vg_UserMsg, "D1 miss rate: %s (%s + %s )", buf1, buf2,buf3); + + percentify( D_total.m2 * 100 * p / D_total.a, p, l1+1, buf1); + percentify(Dr_total.m2 * 100 * p / Dr_total.a, p, l2+1, buf2); + percentify(Dw_total.m2 * 100 * p / Dw_total.a, p, l3+1, buf3); + VG_(message)(Vg_UserMsg, "L2d miss rate: %s (%s + %s )", buf1, buf2,buf3); + VG_(message)(Vg_UserMsg, ""); + + /* L2 overall results */ + + L2_total = Dr_total.m1 + Dw_total.m1 + Ir_total.m1; + L2_total_r = Dr_total.m1 + Ir_total.m1; + L2_total_w = Dw_total.m1; + VG_(message)(Vg_UserMsg, fmt, "L2 refs: ", + L2_total, L2_total_r, L2_total_w); + + L2_total_m = Dr_total.m2 + Dw_total.m2 + Ir_total.m2; + L2_total_mr = Dr_total.m2 + Ir_total.m2; + L2_total_mw = Dw_total.m2; + VG_(message)(Vg_UserMsg, fmt, "L2 misses: ", + L2_total_m, L2_total_mr, L2_total_mw); + + percentify(L2_total_m * 100 * p / (Ir_total.a + D_total.a), p, l1+1, buf1); + percentify(L2_total_mr * 100 * p / (Ir_total.a + Dr_total.a), p, l2+1, buf2); + percentify(L2_total_mw * 100 * p / Dw_total.a, p, l3+1, buf3); + VG_(message)(Vg_UserMsg, "L2 miss rate: %s (%s + %s )", buf1, buf2,buf3); + + + // Various stats + if (VG_(clo_verbosity) > 1) { + int BB_lookups = full_debug_BBs + fn_debug_BBs + + file_line_debug_BBs + no_debug_BBs; + + VG_(message)(Vg_DebugMsg, ""); + VG_(message)(Vg_DebugMsg, "Distinct files: %d", distinct_files); + VG_(message)(Vg_DebugMsg, "Distinct fns: %d", distinct_fns); + VG_(message)(Vg_DebugMsg, "Distinct lines: %d", distinct_lines); + VG_(message)(Vg_DebugMsg, "Distinct instrs: %d", distinct_instrs); + VG_(message)(Vg_DebugMsg, "BB lookups: %d", BB_lookups); + VG_(message)(Vg_DebugMsg, "With full debug info:%3d%% (%d)", + full_debug_BBs * 100 / BB_lookups, + full_debug_BBs); + VG_(message)(Vg_DebugMsg, "With file/line debug info:%3d%% (%d)", + file_line_debug_BBs * 100 / BB_lookups, + file_line_debug_BBs); + VG_(message)(Vg_DebugMsg, "With fn name debug info:%3d%% (%d)", + fn_debug_BBs * 100 / BB_lookups, + fn_debug_BBs); + VG_(message)(Vg_DebugMsg, "With no debug info:%3d%% (%d)", + no_debug_BBs * 100 / BB_lookups, + no_debug_BBs); + VG_(message)(Vg_DebugMsg, "BBs Retranslated: %d", BB_retranslations); + } + VGP_POPCC(VgpCacheResults); +} + +/*--------------------------------------------------------------------*/ +/*--- Discarding BB info ---*/ +/*--------------------------------------------------------------------*/ + +// Called when a translation is invalidated due to code unloading. +void SK_(discard_basic_block_info) ( Addr a, UInt size ) +{ + VgHashNode** prev_next_ptr; + VgHashNode* bb_info; + + if (0) VG_(printf)( "discard_basic_block_info: %p, %u\n", a, size); + + // Get BB info, remove from table, free BB info. Simple! + bb_info = VG_(HT_get_node)(instr_info_table, a, &prev_next_ptr); + sk_assert(NULL != bb_info); + *prev_next_ptr = bb_info->next; + VG_(free)(bb_info); +} + +/*--------------------------------------------------------------------*/ +/*--- Command line processing ---*/ +/*--------------------------------------------------------------------*/ + +static void parse_cache_opt ( cache_t* cache, char* opt ) +{ + int i = 0, i2, i3; + + // Option argument looks like "65536,2,64". + // Find commas, replace with NULs to make three independent + // strings, then extract numbers, put NULs back. Yuck. + while (VG_(isdigit)(opt[i])) i++; + if (',' == opt[i]) { + opt[i++] = '\0'; + i2 = i; + } else goto bad; + while (VG_(isdigit)(opt[i])) i++; + if (',' == opt[i]) { + opt[i++] = '\0'; + i3 = i; + } else goto bad; + while (VG_(isdigit)(opt[i])) i++; + if ('\0' != opt[i]) goto bad; + + cache->size = (Int)VG_(atoll)(opt); + cache->assoc = (Int)VG_(atoll)(opt + i2); + cache->line_size = (Int)VG_(atoll)(opt + i3); + + opt[i2-1] = ','; + opt[i3-1] = ','; + return; + + bad: + VG_(bad_option)(opt); +} + +Bool SK_(process_cmd_line_option)(Char* arg) +{ + // 5 is length of "--I1=" + if (VG_CLO_STREQN(5, arg, "--I1=")) + parse_cache_opt(&clo_I1_cache, &arg[5]); + else if (VG_CLO_STREQN(5, arg, "--D1=")) + parse_cache_opt(&clo_D1_cache, &arg[5]); + else if (VG_CLO_STREQN(5, arg, "--L2=")) + parse_cache_opt(&clo_L2_cache, &arg[5]); + else + return False; + + return True; +} + +void SK_(print_usage)(void) +{ + VG_(printf)( +" --I1=,, set I1 cache manually\n" +" --D1=,, set D1 cache manually\n" +" --L2=,, set L2 cache manually\n" + ); +} + +void SK_(print_debug_usage)(void) +{ + VG_(printf)( +" (none)\n" + ); +} + +/*--------------------------------------------------------------------*/ +/*--- Setup ---*/ +/*--------------------------------------------------------------------*/ + +void SK_(pre_clo_init)(void) +{ + Char* base_dir = NULL; + + VG_(details_name) ("Cachegrind"); + VG_(details_version) (NULL); + VG_(details_description) ("an I1/D1/L2 cache profiler"); + VG_(details_copyright_author)( + "Copyright (C) 2002-2004, and GNU GPL'd, by Nicholas Nethercote et al."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + VG_(details_avg_translation_sizeB) ( 155 ); + + VG_(needs_basic_block_discards)(); + VG_(needs_command_line_options)(); + + VG_(register_compact_helper)((Addr) & log_1I_0D_cache_access); + VG_(register_compact_helper)((Addr) & log_1I_1Dr_cache_access); + VG_(register_compact_helper)((Addr) & log_1I_1Dw_cache_access); + VG_(register_compact_helper)((Addr) & log_1I_2D_cache_access); + + /* Get working directory */ + sk_assert( VG_(getcwd_alloc)(&base_dir) ); + + /* Block is big enough for dir name + cachegrind.out. */ + cachegrind_out_file = VG_(malloc)((VG_(strlen)(base_dir) + 32)*sizeof(Char)); + VG_(sprintf)(cachegrind_out_file, "%s/cachegrind.out.%d", + base_dir, VG_(getpid)()); + VG_(free)(base_dir); + + instr_info_table = VG_(HT_construct)(); +} + +void SK_(post_clo_init)(void) +{ + cache_t I1c, D1c, L2c; + + configure_caches(&I1c, &D1c, &L2c); + + cachesim_I1_initcache(I1c); + cachesim_D1_initcache(D1c); + cachesim_L2_initcache(L2c); + + VGP_(register_profile_event)(VgpGetLineCC, "get-lineCC"); + VGP_(register_profile_event)(VgpCacheSimulate, "cache-simulate"); + VGP_(register_profile_event)(VgpCacheResults, "cache-results"); +} + +VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 0) + +/*--------------------------------------------------------------------*/ +/*--- end cg_main.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/cachegrind/cg_sim.c b/VEX/head20041019/cachegrind/cg_sim.c new file mode 100644 index 0000000000..5d42c1ba4a --- /dev/null +++ b/VEX/head20041019/cachegrind/cg_sim.c @@ -0,0 +1,210 @@ + +/*--------------------------------------------------------------------*/ +/*--- Cache simulation cg_sim.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Cachegrind, a Valgrind tool for cache + profiling programs. + + Copyright (C) 2002-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* Notes: + - simulates a write-allocate cache + - (block --> set) hash function uses simple bit selection + - handling of references straddling two cache blocks: + - counts as only one cache access (not two) + - both blocks hit --> one hit + - one block hits, the other misses --> one miss + - both blocks miss --> one miss (not two) +*/ + +typedef struct { + int size; /* bytes */ + int assoc; + int line_size; /* bytes */ + int sets; + int sets_min_1; + int assoc_bits; + int line_size_bits; + int tag_shift; + char desc_line[128]; + int* tags; +} cache_t2; + +/* By this point, the size/assoc/line_size has been checked. */ +static void cachesim_initcache(cache_t config, cache_t2* c) +{ + int i; + + c->size = config.size; + c->assoc = config.assoc; + c->line_size = config.line_size; + + c->sets = (c->size / c->line_size) / c->assoc; + c->sets_min_1 = c->sets - 1; + c->assoc_bits = VG_(log2)(c->assoc); + c->line_size_bits = VG_(log2)(c->line_size); + c->tag_shift = c->line_size_bits + VG_(log2)(c->sets); + + if (c->assoc == 1) { + VG_(sprintf)(c->desc_line, "%d B, %d B, direct-mapped", + c->size, c->line_size); + } else { + VG_(sprintf)(c->desc_line, "%d B, %d B, %d-way associative", + c->size, c->line_size, c->assoc); + } + + c->tags = VG_(malloc)(sizeof(UInt) * c->sets * c->assoc); + + for (i = 0; i < c->sets * c->assoc; i++) + c->tags[i] = 0; +} + +#if 0 +static void print_cache(cache_t2* c) +{ + UInt set, way, i; + + /* Note initialisation and update of 'i'. */ + for (i = 0, set = 0; set < c->sets; set++) { + for (way = 0; way < c->assoc; way++, i++) { + VG_(printf)("%8x ", c->tags[i]); + } + VG_(printf)("\n"); + } +} +#endif + +/* This is done as a macro rather than by passing in the cache_t2 as an + * arg because it slows things down by a small amount (3-5%) due to all + * that extra indirection. */ + +#define CACHESIM(L, MISS_TREATMENT) \ +/* The cache and associated bits and pieces. */ \ +static cache_t2 L; \ + \ +static void cachesim_##L##_initcache(cache_t config) \ +{ \ + cachesim_initcache(config, &L); \ +} \ + \ +static /* __inline__ */ \ +void cachesim_##L##_doref(Addr a, UChar size, ULong* m1, ULong *m2) \ +{ \ + register UInt set1 = ( a >> L.line_size_bits) & (L.sets_min_1); \ + register UInt set2 = ((a+size-1) >> L.line_size_bits) & (L.sets_min_1); \ + register UInt tag = a >> L.tag_shift; \ + int i, j; \ + Bool is_miss = False; \ + int* set; \ + \ + /* First case: word entirely within line. */ \ + if (set1 == set2) { \ + \ + /* Shifting is a bit faster than multiplying */ \ + set = &(L.tags[set1 << L.assoc_bits]); \ + \ + /* This loop is unrolled for just the first case, which is the most */\ + /* common. We can't unroll any further because it would screw up */\ + /* if we have a direct-mapped (1-way) cache. */\ + if (tag == set[0]) { \ + return; \ + } \ + /* If the tag is one other than the MRU, move it into the MRU spot */\ + /* and shuffle the rest down. */\ + for (i = 1; i < L.assoc; i++) { \ + if (tag == set[i]) { \ + for (j = i; j > 0; j--) { \ + set[j] = set[j - 1]; \ + } \ + set[0] = tag; \ + return; \ + } \ + } \ + \ + /* A miss; install this tag as MRU, shuffle rest down. */ \ + for (j = L.assoc - 1; j > 0; j--) { \ + set[j] = set[j - 1]; \ + } \ + set[0] = tag; \ + MISS_TREATMENT; \ + return; \ + \ + /* Second case: word straddles two lines. */ \ + /* Nb: this is a fast way of doing ((set1+1) % L.sets) */ \ + } else if (((set1 + 1) & (L.sets-1)) == set2) { \ + set = &(L.tags[set1 << L.assoc_bits]); \ + if (tag == set[0]) { \ + goto block2; \ + } \ + for (i = 1; i < L.assoc; i++) { \ + if (tag == set[i]) { \ + for (j = i; j > 0; j--) { \ + set[j] = set[j - 1]; \ + } \ + set[0] = tag; \ + goto block2; \ + } \ + } \ + for (j = L.assoc - 1; j > 0; j--) { \ + set[j] = set[j - 1]; \ + } \ + set[0] = tag; \ + is_miss = True; \ +block2: \ + set = &(L.tags[set2 << L.assoc_bits]); \ + if (tag == set[0]) { \ + goto miss_treatment; \ + } \ + for (i = 1; i < L.assoc; i++) { \ + if (tag == set[i]) { \ + for (j = i; j > 0; j--) { \ + set[j] = set[j - 1]; \ + } \ + set[0] = tag; \ + goto miss_treatment; \ + } \ + } \ + for (j = L.assoc - 1; j > 0; j--) { \ + set[j] = set[j - 1]; \ + } \ + set[0] = tag; \ + is_miss = True; \ +miss_treatment: \ + if (is_miss) { MISS_TREATMENT; } \ + \ + } else { \ + VG_(printf)("addr: %x size: %u sets: %d %d", a, size, set1, set2); \ + VG_(skin_panic)("item straddles more than two cache sets"); \ + } \ + return; \ +} + +CACHESIM(L2, (*m2)++ ); +CACHESIM(I1, { (*m1)++; cachesim_L2_doref(a, size, m1, m2); } ); +CACHESIM(D1, { (*m1)++; cachesim_L2_doref(a, size, m1, m2); } ); + +/*--------------------------------------------------------------------*/ +/*--- end cg_sim.c ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/VEX/head20041019/cachegrind/docs/.cvsignore b/VEX/head20041019/cachegrind/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/cachegrind/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/cachegrind/docs/CVS/Entries b/VEX/head20041019/cachegrind/docs/CVS/Entries new file mode 100644 index 0000000000..05ac7ec696 --- /dev/null +++ b/VEX/head20041019/cachegrind/docs/CVS/Entries @@ -0,0 +1,5 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:22 2002// +/Makefile.am/1.3/Wed Aug 25 11:40:04 2004// +/cg_main.html/1.5/Fri Nov 14 17:47:52 2003// +/cg_techdocs.html/1.4/Thu Nov 20 16:20:54 2003// +D diff --git a/VEX/head20041019/cachegrind/docs/CVS/Repository b/VEX/head20041019/cachegrind/docs/CVS/Repository new file mode 100644 index 0000000000..a5e96cda3a --- /dev/null +++ b/VEX/head20041019/cachegrind/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/cachegrind/docs diff --git a/VEX/head20041019/cachegrind/docs/CVS/Root b/VEX/head20041019/cachegrind/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/cachegrind/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/cachegrind/docs/CVS/Template b/VEX/head20041019/cachegrind/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/docs/Makefile.am b/VEX/head20041019/cachegrind/docs/Makefile.am new file mode 100644 index 0000000000..9657fe59f0 --- /dev/null +++ b/VEX/head20041019/cachegrind/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = cg_main.html cg_techdocs.html diff --git a/VEX/head20041019/cachegrind/docs/cg_main.html b/VEX/head20041019/cachegrind/docs/cg_main.html new file mode 100644 index 0000000000..545748ae9c --- /dev/null +++ b/VEX/head20041019/cachegrind/docs/cg_main.html @@ -0,0 +1,714 @@ + + + Cachegrind: a cache-miss profiler + + + + +

Cachegrind: a cache-miss profiler

+ +To use this tool, you must specify --tool=cachegrind +on the Valgrind command line. + +

+Detailed technical documentation on how Cachegrind works is available +here. If you want to know how +to use it, you only need to read this page. + + + +

4.1  Cache profiling

+Cachegrind is a tool for doing cache simulations and annotating your source +line-by-line with the number of cache misses. In particular, it records: +
    +
  • L1 instruction cache reads and misses; +
  • L1 data cache reads and read misses, writes and write misses; +
  • L2 unified cache reads and read misses, writes and writes misses. +
+On a modern x86 machine, an L1 miss will typically cost around 10 cycles, +and an L2 miss can cost as much as 200 cycles. Detailed cache profiling can be +very useful for improving the performance of your program.

+ +Also, since one instruction cache read is performed per instruction executed, +you can find out how many instructions are executed per line, which can be +useful for traditional profiling and test coverage.

+ +Any feedback, bug-fixes, suggestions, etc, welcome. + + +

4.2  Overview

+First off, as for normal Valgrind use, you probably want to compile with +debugging info (the -g flag). But by contrast with normal +Valgrind use, you probably do want to turn optimisation on, since you +should profile your program as it will be normally run. + +The two steps are: +
    +
  1. Run your program with valgrind --tool=cachegrind in front of + the normal command line invocation. When the program finishes, + Cachegrind will print summary cache statistics. It also collects + line-by-line information in a file + cachegrind.out.pid, where pid + is the program's process id. +

    + This step should be done every time you want to collect + information about a new program, a changed program, or about the + same program with different input. +

  2. +

  3. Generate a function-by-function summary, and possibly annotate + source files, using the supplied + cg_annotate program. Source files to annotate can be + specified manually, or manually on the command line, or + "interesting" source files can be annotated automatically with + the --auto=yes option. You can annotate C/C++ + files or assembly language files equally easily. +

    + This step can be performed as many times as you like for each + Step 2. You may want to do multiple annotations showing + different information each time. +

  4. +

+ +The steps are described in detail in the following sections. + + +

4.3  Cache simulation specifics

+ +Cachegrind uses a simulation for a machine with a split L1 cache and a unified +L2 cache. This configuration is used for all (modern) x86-based machines we +are aware of. Old Cyrix CPUs had a unified I and D L1 cache, but they are +ancient history now.

+ +The more specific characteristics of the simulation are as follows. + +

    +
  • Write-allocate: when a write miss occurs, the block written to + is brought into the D1 cache. Most modern caches have this + property.

    +

  • +

    +

  • Bit-selection hash function: the line(s) in the cache to which a + memory block maps is chosen by the middle bits M--(M+N-1) of the + byte address, where: +
      +
    •  line size = 2^M bytes 
    • +
    • (cache size / line size) = 2^N bytes
    • +
    +
  • +

    +

  • Inclusive L2 cache: the L2 cache replicates all the entries of + the L1 cache. This is standard on Pentium chips, but AMD + Athlons use an exclusive L2 cache that only holds blocks evicted + from L1. Ditto AMD Durons and most modern VIAs.
  • +
+ +The cache configuration simulated (cache size, associativity and line size) is +determined automagically using the CPUID instruction. If you have an old +machine that (a) doesn't support the CPUID instruction, or (b) supports it in +an early incarnation that doesn't give any cache information, then Cachegrind +will fall back to using a default configuration (that of a model 3/4 Athlon). +Cachegrind will tell you if this happens. You can manually specify one, two or +all three levels (I1/D1/L2) of the cache from the command line using the +--I1, --D1 and --L2 options. + +

+Other noteworthy behaviour: + +

    +
  • References that straddle two cache lines are treated as follows: +
      +
    • If both blocks hit --> counted as one hit
    • +
    • If one block hits, the other misses --> counted as one miss
    • +
    • If both blocks miss --> counted as one miss (not two)
    • +
    +
  • + +
  • Instructions that modify a memory location (eg. inc and + dec) are counted as doing just a read, ie. a single data + reference. This may seem strange, but since the write can never cause a + miss (the read guarantees the block is in the cache) it's not very + interesting. +

    + Thus it measures not the number of times the data cache is accessed, but + the number of times a data cache miss could occur.

    +

  • +
+ +If you are interested in simulating a cache with different properties, it is +not particularly hard to write your own cache simulator, or to modify the +existing ones in vg_cachesim_I1.c, vg_cachesim_D1.c, +vg_cachesim_L2.c and vg_cachesim_gen.c. We'd be +interested to hear from anyone who does. + + + +

4.4  Profiling programs

+ +To gather cache profiling information about the program ls -l, +invoke Cachegrind like this: + +
valgrind --tool=cachegrind ls -l
+ +The program will execute (slowly). Upon completion, summary statistics +that look like this will be printed: + +
+==31751== I   refs:      27,742,716
+==31751== I1  misses:           276
+==31751== L2  misses:           275
+==31751== I1  miss rate:        0.0%
+==31751== L2i miss rate:        0.0%
+==31751== 
+==31751== D   refs:      15,430,290  (10,955,517 rd + 4,474,773 wr)
+==31751== D1  misses:        41,185  (    21,905 rd +    19,280 wr)
+==31751== L2  misses:        23,085  (     3,987 rd +    19,098 wr)
+==31751== D1  miss rate:        0.2% (       0.1%   +       0.4%)
+==31751== L2d miss rate:        0.1% (       0.0%   +       0.4%)
+==31751== 
+==31751== L2 misses:         23,360  (     4,262 rd +    19,098 wr)
+==31751== L2 miss rate:         0.0% (       0.0%   +       0.4%)
+
+ +Cache accesses for instruction fetches are summarised first, giving the +number of fetches made (this is the number of instructions executed, which +can be useful to know in its own right), the number of I1 misses, and the +number of L2 instruction (L2i) misses. +

+Cache accesses for data follow. The information is similar to that of the +instruction fetches, except that the values are also shown split between reads +and writes (note each row's rd and wr values add up +to the row's total). +

+Combined instruction and data figures for the L2 cache follow that. + + +

4.5  Output file

+ +As well as printing summary information, Cachegrind also writes +line-by-line cache profiling information to a file named +cachegrind.out.pid. This file is human-readable, but is +best interpreted by the accompanying program cg_annotate, +described in the next section. +

+Things to note about the cachegrind.out.pid file: +

    +
  • It is written every time Cachegrind + is run, and will overwrite any existing + cachegrind.out.pid in the current directory (but + that won't happen very often because it takes some time for process ids + to be recycled).
  • +

  • It can be huge: ls -l generates a file of about + 350KB. Browsing a few files and web pages with a Konqueror + built with full debugging information generates a file + of around 15 MB.
  • +
+ +Note that older versions of Cachegrind used a log file named +cachegrind.out (i.e. no .pid suffix). +The suffix serves two purposes. Firstly, it means you don't have to +rename old log files that you don't want to overwrite. Secondly, and +more importantly, it allows correct profiling with the +--trace-children=yes option of programs that spawn child +processes. + + + +

4.6  Cachegrind options

+ +Cache-simulation specific options are: + +
    +
  • --I1=<size>,<associativity>,<line_size>
    + --D1=<size>,<associativity>,<line_size>
    + --L2=<size>,<associativity>,<line_size>

    + [default: uses CPUID for automagic cache configuration]

    + + Manually specifies the I1/D1/L2 cache configuration, where + size and line_size are measured in bytes. The + three items must be comma-separated, but with no spaces, eg: + +

    + valgrind --tool=cachegrind --I1=65535,2,64 +
    + + You can specify one, two or three of the I1/D1/L2 caches. Any level not + manually specified will be simulated using the configuration found in the + normal way (via the CPUID instruction, or failing that, via defaults). +
+ + + +

4.7  Annotating C/C++ programs

+ +Before using cg_annotate, it is worth widening your +window to be at least 120-characters wide if possible, as the output +lines can be quite long. +

+To get a function-by-function summary, run cg_annotate +--pid in a directory containing a +cachegrind.out.pid file. The --pid +is required so that cg_annotate knows which log file to use when +several are present. +

+The output looks like this: + +

+--------------------------------------------------------------------------------
+I1 cache:              65536 B, 64 B, 2-way associative
+D1 cache:              65536 B, 64 B, 2-way associative
+L2 cache:              262144 B, 64 B, 8-way associative
+Command:               concord vg_to_ucode.c
+Events recorded:       Ir I1mr I2mr Dr D1mr D2mr Dw D1mw D2mw
+Events shown:          Ir I1mr I2mr Dr D1mr D2mr Dw D1mw D2mw
+Event sort order:      Ir I1mr I2mr Dr D1mr D2mr Dw D1mw D2mw
+Threshold:             99%
+Chosen for annotation:
+Auto-annotation:       on
+
+--------------------------------------------------------------------------------
+Ir         I1mr I2mr Dr         D1mr   D2mr  Dw        D1mw   D2mw
+--------------------------------------------------------------------------------
+27,742,716  276  275 10,955,517 21,905 3,987 4,474,773 19,280 19,098  PROGRAM TOTALS
+
+--------------------------------------------------------------------------------
+Ir        I1mr I2mr Dr        D1mr  D2mr  Dw        D1mw   D2mw    file:function
+--------------------------------------------------------------------------------
+8,821,482    5    5 2,242,702 1,621    73 1,794,230      0      0  getc.c:_IO_getc
+5,222,023    4    4 2,276,334    16    12   875,959      1      1  concord.c:get_word
+2,649,248    2    2 1,344,810 7,326 1,385         .      .      .  vg_main.c:strcmp
+2,521,927    2    2   591,215     0     0   179,398      0      0  concord.c:hash
+2,242,740    2    2 1,046,612   568    22   448,548      0      0  ctype.c:tolower
+1,496,937    4    4   630,874 9,000 1,400   279,388      0      0  concord.c:insert
+  897,991   51   51   897,831    95    30        62      1      1  ???:???
+  598,068    1    1   299,034     0     0   149,517      0      0  ../sysdeps/generic/lockfile.c:__flockfile
+  598,068    0    0   299,034     0     0   149,517      0      0  ../sysdeps/generic/lockfile.c:__funlockfile
+  598,024    4    4   213,580    35    16   149,506      0      0  vg_clientmalloc.c:malloc
+  446,587    1    1   215,973 2,167   430   129,948 14,057 13,957  concord.c:add_existing
+  341,760    2    2   128,160     0     0   128,160      0      0  vg_clientmalloc.c:vg_trap_here_WRAPPER
+  320,782    4    4   150,711   276     0    56,027     53     53  concord.c:init_hash_table
+  298,998    1    1   106,785     0     0    64,071      1      1  concord.c:create
+  149,518    0    0   149,516     0     0         1      0      0  ???:tolower@@GLIBC_2.0
+  149,518    0    0   149,516     0     0         1      0      0  ???:fgetc@@GLIBC_2.0
+   95,983    4    4    38,031     0     0    34,409  3,152  3,150  concord.c:new_word_node
+   85,440    0    0    42,720     0     0    21,360      0      0  vg_clientmalloc.c:vg_bogus_epilogue
+
+ +First up is a summary of the annotation options: + +
    +
  • I1 cache, D1 cache, L2 cache: cache configuration. So you know the + configuration with which these results were obtained.
  • + +

  • Command: the command line invocation of the program under + examination.
  • + +

  • Events recorded: event abbreviations are:

    +

      +
    • Ir : I cache reads (ie. instructions executed)
    • +
    • I1mr: I1 cache read misses
    • +
    • I2mr: L2 cache instruction read misses
    • +
    • Dr : D cache reads (ie. memory reads)
    • +
    • D1mr: D1 cache read misses
    • +
    • D2mr: L2 cache data read misses
    • +
    • Dw : D cache writes (ie. memory writes)
    • +
    • D1mw: D1 cache write misses
    • +
    • D2mw: L2 cache data write misses
    • +

    + Note that D1 total accesses is given by D1mr + + D1mw, and that L2 total accesses is given by + I2mr + D2mr + D2mw.

  • + +

  • Events shown: the events shown (a subset of events gathered). This can + be adjusted with the --show option.
  • + +

  • Event sort order: the sort order in which functions are shown. For + example, in this case the functions are sorted from highest + Ir counts to lowest. If two functions have identical + Ir counts, they will then be sorted by I1mr + counts, and so on. This order can be adjusted with the + --sort option.

    + + Note that this dictates the order the functions appear. It is not + the order in which the columns appear; that is dictated by the "events + shown" line (and can be changed with the --show option). +

  • + +

  • Threshold: cg_annotate by default omits functions + that cause very low numbers of misses to avoid drowning you in + information. In this case, cg_annotate shows summaries the + functions that account for 99% of the Ir counts; + Ir is chosen as the threshold event since it is the + primary sort event. The threshold can be adjusted with the + --threshold option.
  • + +

  • Chosen for annotation: names of files specified manually for annotation; + in this case none.
  • + +

  • Auto-annotation: whether auto-annotation was requested via the + --auto=yes option. In this case no.
  • +

+ +Then follows summary statistics for the whole program. These are similar +to the summary provided when running valgrind --tool=cachegrind.

+ +Then follows function-by-function statistics. Each function is +identified by a file_name:function_name pair. If a column +contains only a dot it means the function never performs +that event (eg. the third row shows that strcmp() +contains no instructions that write to memory). The name +??? is used if the the file name and/or function name +could not be determined from debugging information. If most of the +entries have the form ???:??? the program probably wasn't +compiled with -g. If any code was invalidated (either due to +self-modifying code or unloading of shared objects) its counts are aggregated +into a single cost centre written as (discarded):(discarded).

+ +It is worth noting that functions will come from three types of source files: +

    +
  1. From the profiled program (concord.c in this example).
  2. +
  3. From libraries (eg. getc.c)
  4. +
  5. From Valgrind's implementation of some libc functions (eg. + vg_clientmalloc.c:malloc). These are recognisable because + the filename begins with vg_, and is probably one of + vg_main.c, vg_clientmalloc.c or + vg_mylibc.c. +
  6. +
+ +There are two ways to annotate source files -- by choosing them +manually, or with the --auto=yes option. To do it +manually, just specify the filenames as arguments to +cg_annotate. For example, the output from running +cg_annotate concord.c for our example produces the same +output as above followed by an annotated version of +concord.c, a section of which looks like: + +
+--------------------------------------------------------------------------------
+-- User-annotated source: concord.c
+--------------------------------------------------------------------------------
+Ir        I1mr I2mr Dr      D1mr  D2mr  Dw      D1mw   D2mw
+
+[snip]
+
+        .    .    .       .     .     .       .      .      .  void init_hash_table(char *file_name, Word_Node *table[])
+        3    1    1       .     .     .       1      0      0  {
+        .    .    .       .     .     .       .      .      .      FILE *file_ptr;
+        .    .    .       .     .     .       .      .      .      Word_Info *data;
+        1    0    0       .     .     .       1      1      1      int line = 1, i;
+        .    .    .       .     .     .       .      .      .
+        5    0    0       .     .     .       3      0      0      data = (Word_Info *) create(sizeof(Word_Info));
+        .    .    .       .     .     .       .      .      .
+    4,991    0    0   1,995     0     0     998      0      0      for (i = 0; i < TABLE_SIZE; i++)
+    3,988    1    1   1,994     0     0     997     53     52          table[i] = NULL;
+        .    .    .       .     .     .       .      .      .
+        .    .    .       .     .     .       .      .      .      /* Open file, check it. */
+        6    0    0       1     0     0       4      0      0      file_ptr = fopen(file_name, "r");
+        2    0    0       1     0     0       .      .      .      if (!(file_ptr)) {
+        .    .    .       .     .     .       .      .      .          fprintf(stderr, "Couldn't open '%s'.\n", file_name);
+        1    1    1       .     .     .       .      .      .          exit(EXIT_FAILURE);
+        .    .    .       .     .     .       .      .      .      }
+        .    .    .       .     .     .       .      .      .
+  165,062    1    1  73,360     0     0  91,700      0      0      while ((line = get_word(data, line, file_ptr)) != EOF)
+  146,712    0    0  73,356     0     0  73,356      0      0          insert(data->;word, data->line, table);
+        .    .    .       .     .     .       .      .      .
+        4    0    0       1     0     0       2      0      0      free(data);
+        4    0    0       1     0     0       2      0      0      fclose(file_ptr);
+        3    0    0       2     0     0       .      .      .  }
+
+ +(Although column widths are automatically minimised, a wide terminal is clearly +useful.)

+ +Each source file is clearly marked (User-annotated source) as +having been chosen manually for annotation. If the file was found in one of +the directories specified with the -I/--include +option, the directory and file are both given.

+ +Each line is annotated with its event counts. Events not applicable for a line +are represented by a `.'; this is useful for distinguishing between an event +which cannot happen, and one which can but did not.

+ +Sometimes only a small section of a source file is executed. To minimise +uninteresting output, Valgrind only shows annotated lines and lines within a +small distance of annotated lines. Gaps are marked with the line numbers so +you know which part of a file the shown code comes from, eg: + +

+(figures and code for line 704)
+-- line 704 ----------------------------------------
+-- line 878 ----------------------------------------
+(figures and code for line 878)
+
+ +The amount of context to show around annotated lines is controlled by the +--context option.

+ +To get automatic annotation, run cg_annotate --auto=yes. +cg_annotate will automatically annotate every source file it can find that is +mentioned in the function-by-function summary. Therefore, the files chosen for +auto-annotation are affected by the --sort and +--threshold options. Each source file is clearly marked +(Auto-annotated source) as being chosen automatically. Any files +that could not be found are mentioned at the end of the output, eg: + +

+--------------------------------------------------------------------------------
+The following files chosen for auto-annotation could not be found:
+--------------------------------------------------------------------------------
+  getc.c
+  ctype.c
+  ../sysdeps/generic/lockfile.c
+
+ +This is quite common for library files, since libraries are usually compiled +with debugging information, but the source files are often not present on a +system. If a file is chosen for annotation both manually and +automatically, it is marked as User-annotated source. + +Use the -I/--include option to tell Valgrind where to look for +source files if the filenames found from the debugging information aren't +specific enough. + +Beware that cg_annotate can take some time to digest large +cachegrind.out.pid files, e.g. 30 seconds or more. Also +beware that auto-annotation can produce a lot of output if your program is +large! + + +

4.8  Annotating assembler programs

+ +Valgrind can annotate assembler programs too, or annotate the +assembler generated for your C program. Sometimes this is useful for +understanding what is really happening when an interesting line of C +code is translated into multiple instructions.

+ +To do this, you just need to assemble your .s files with +assembler-level debug information. gcc doesn't do this, but you can +use the GNU assembler with the --gstabs option to +generate object files with this information, eg: + +

as --gstabs foo.s
+ +You can then profile and annotate source files in the same way as for C/C++ +programs. + + +

4.9  cg_annotate options

+
    +
  • --pid
  • + + Indicates which cachegrind.out.pid file to read. + Not actually an option -- it is required. + +

  • -h, --help
  • +

  • -v, --version

    + + Help and version, as usual.

  • + +
  • --sort=A,B,C [default: order in + cachegrind.out.pid]

    + Specifies the events upon which the sorting of the function-by-function + entries will be based. Useful if you want to concentrate on eg. I cache + misses (--sort=I1mr,I2mr), or D cache misses + (--sort=D1mr,D2mr), or L2 misses + (--sort=D2mr,I2mr).

  • + +

  • --show=A,B,C [default: all, using order in + cachegrind.out.pid]

    + Specifies which events to show (and the column order). Default is to use + all present in the cachegrind.out.pid file (and use + the order in the file).

  • + +

  • --threshold=X [default: 99%]

    + Sets the threshold for the function-by-function summary. Functions are + shown that account for more than X% of the primary sort event. If + auto-annotating, also affects which files are annotated. + + Note: thresholds can be set for more than one of the events by appending + any events for the --sort option with a colon and a number + (no spaces, though). E.g. if you want to see the functions that cover + 99% of L2 read misses and 99% of L2 write misses, use this option: + +

    --sort=D2mr:99,D2mw:99
    +
  • + +

  • --auto=no [default]
    + --auto=yes

    + When enabled, automatically annotates every file that is mentioned in the + function-by-function summary that can be found. Also gives a list of + those that couldn't be found. + +

  • --context=N [default: 8]

    + Print N lines of context before and after each annotated line. Avoids + printing large sections of source files that were not executed. Use a + large number (eg. 10,000) to show all source lines. +

  • + +

  • -I=<dir>, --include=<dir> + [default: empty string]

    + Adds a directory to the list in which to search for files. Multiple + -I/--include options can be given to add multiple directories. +

+ + +

4.10  Warnings

+There are a couple of situations in which cg_annotate issues warnings. + +
    +
  • If a source file is more recent than the + cachegrind.out.pid file. This is because the + information in cachegrind.out.pid is only recorded + with line numbers, so if the line numbers change at all in the source + (eg. lines added, deleted, swapped), any annotations will be + incorrect.

    + +

  • If information is recorded about line numbers past the end of a file. + This can be caused by the above problem, ie. shortening the source file + while using an old cachegrind.out.pid file. If this + happens, the figures for the bogus lines are printed anyway (clearly + marked as bogus) in case they are important.
  • +

+ + +

4.11  Things to watch out for

+Some odd things that can occur during annotation: + +
    +
  • If annotating at the assembler level, you might see something like this: + +
    +      1    0    0  .    .    .  .    .    .          leal -12(%ebp),%eax
    +      1    0    0  .    .    .  1    0    0          movl %eax,84(%ebx)
    +      2    0    0  0    0    0  1    0    0          movl $1,-20(%ebp)
    +      .    .    .  .    .    .  .    .    .          .align 4,0x90
    +      1    0    0  .    .    .  .    .    .          movl $.LnrB,%eax
    +      1    0    0  .    .    .  1    0    0          movl %eax,-16(%ebp)
    +      
    + + How can the third instruction be executed twice when the others are + executed only once? As it turns out, it isn't. Here's a dump of the + executable, using objdump -d: + +
    +      8048f25:       8d 45 f4                lea    0xfffffff4(%ebp),%eax
    +      8048f28:       89 43 54                mov    %eax,0x54(%ebx)
    +      8048f2b:       c7 45 ec 01 00 00 00    movl   $0x1,0xffffffec(%ebp)
    +      8048f32:       89 f6                   mov    %esi,%esi
    +      8048f34:       b8 08 8b 07 08          mov    $0x8078b08,%eax
    +      8048f39:       89 45 f0                mov    %eax,0xfffffff0(%ebp)
    +      
    + + Notice the extra mov %esi,%esi instruction. Where did this + come from? The GNU assembler inserted it to serve as the two bytes of + padding needed to align the movl $.LnrB,%eax instruction on + a four-byte boundary, but pretended it didn't exist when adding debug + information. Thus when Valgrind reads the debug info it thinks that the + movl $0x1,0xffffffec(%ebp) instruction covers the address + range 0x8048f2b--0x804833 by itself, and attributes the counts for the + mov %esi,%esi to it.

    +

  • + +
  • Inlined functions can cause strange results in the function-by-function + summary. If a function inline_me() is defined in + foo.h and inlined in the functions f1(), + f2() and f3() in bar.c, there will + not be a foo.h:inline_me() function entry. Instead, there + will be separate function entries for each inlining site, ie. + foo.h:f1(), foo.h:f2() and + foo.h:f3(). To find the total counts for + foo.h:inline_me(), add up the counts from each entry.

    + + The reason for this is that although the debug info output by gcc + indicates the switch from bar.c to foo.h, it + doesn't indicate the name of the function in foo.h, so + Valgrind keeps using the old one.

    + +

  • Sometimes, the same filename might be represented with a relative name + and with an absolute name in different parts of the debug info, eg: + /home/user/proj/proj.h and ../proj.h. In this + case, if you use auto-annotation, the file will be annotated twice with + the counts split between the two.

    +

  • + +
  • Files with more than 65,535 lines cause difficulties for the stabs debug + info reader. This is because the line number in the struct + nlist defined in a.out.h under Linux is only a 16-bit + value. Valgrind can handle some files with more than 65,535 lines + correctly by making some guesses to identify line number overflows. But + some cases are beyond it, in which case you'll get a warning message + explaining that annotations for the file might be incorrect.

    +

  • + +
  • If you compile some files with -g and some without, some + events that take place in a file without debug info could be attributed + to the last line of a file with debug info (whichever one gets placed + before the non-debug-info file in the executable).

    +

  • +
+ +This list looks long, but these cases should be fairly rare.

+ +Note: stabs is not an easy format to read. If you come across bizarre +annotations that look like might be caused by a bug in the stabs reader, +please let us know.

+ + +

4.12  Accuracy

+Valgrind's cache profiling has a number of shortcomings: + +
    +
  • It doesn't account for kernel activity -- the effect of system calls on + the cache contents is ignored.
  • + +

  • It doesn't account for other process activity (although this is probably + desirable when considering a single program).
  • + +

  • It doesn't account for virtual-to-physical address mappings; hence the + entire simulation is not a true representation of what's happening in the + cache.
  • + +

  • It doesn't account for cache misses not visible at the instruction level, + eg. those arising from TLB misses, or speculative execution.
  • + +

  • Valgrind's custom threads implementation will schedule threads + differently to the standard one. This could warp the results for + threaded programs. +
  • + +

  • The instructions bts, btr and btc + will incorrectly be counted as doing a data read if both the arguments + are registers, eg: + +
    btsl %eax, %edx
    + + This should only happen rarely. +
  • + +

  • FPU instructions with data sizes of 28 and 108 bytes (e.g. + fsave) are treated as though they only access 16 bytes. + These instructions seem to be rare so hopefully this won't affect + accuracy much. +
  • +

+ +Another thing worth nothing is that results are very sensitive. Changing the +size of the valgrind.so file, the size of the program being +profiled, or even the length of its name can perturb the results. Variations +will be small, but don't expect perfectly repeatable results if your program +changes at all.

+ +While these factors mean you shouldn't trust the results to be super-accurate, +hopefully they should be close enough to be useful.

+ + +

4.13  Todo

+
    +
  • Program start-up/shut-down calls a lot of functions that aren't + interesting and just complicate the output. Would be nice to exclude + these somehow.
  • +

    +

+ + + diff --git a/VEX/head20041019/cachegrind/docs/cg_techdocs.html b/VEX/head20041019/cachegrind/docs/cg_techdocs.html new file mode 100644 index 0000000000..0ac5b67596 --- /dev/null +++ b/VEX/head20041019/cachegrind/docs/cg_techdocs.html @@ -0,0 +1,458 @@ + + + + How Cachegrind works + + + + +  +

How Cachegrind works

+ +
+Detailed technical notes for hackers, maintainers and the +overly-curious
+

+njn25@cam.ac.uk
+http://valgrind.kde.org
+

+Copyright © 2001-2003 Nick Nethercote +

+

+ +

+ + + + +


+ +

Cache profiling

+Valgrind is a very nice platform for doing cache profiling and other kinds of +simulation, because it converts horrible x86 instructions into nice clean +RISC-like UCode. For example, for cache profiling we are interested in +instructions that read and write memory; in UCode there are only four +instructions that do this: LOAD, STORE, +FPU_R and FPU_W. By contrast, because of the x86 +addressing modes, almost every instruction can read or write memory.

+ +Most of the cache profiling machinery is in the file +vg_cachesim.c.

+ +These notes are a somewhat haphazard guide to how Valgrind's cache profiling +works.

+ +

Cost centres

+Valgrind gathers cache profiling about every instruction executed, +individually. Each instruction has a cost centre associated with it. +There are two kinds of cost centre: one for instructions that don't reference +memory (iCC), and one for instructions that do +(idCC): + +
+typedef struct _CC {
+   ULong a;
+   ULong m1;
+   ULong m2;
+} CC;
+
+typedef struct _iCC {
+   /* word 1 */
+   UChar tag;
+   UChar instr_size;
+
+   /* words 2+ */
+   Addr instr_addr;
+   CC I;
+} iCC;
+   
+typedef struct _idCC {
+   /* word 1 */
+   UChar tag;
+   UChar instr_size;
+   UChar data_size;
+
+   /* words 2+ */
+   Addr instr_addr;
+   CC I; 
+   CC D; 
+} idCC; 
+
+ +Each CC has three fields a, m1, +m2 for recording references, level 1 misses and level 2 misses. +Each of these is a 64-bit ULong -- the numbers can get very large, +ie. greater than 4.2 billion allowed by a 32-bit unsigned int.

+ +A iCC has one CC for instruction cache accesses. A +idCC has two, one for instruction cache accesses, and one for data +cache accesses.

+ +The iCC and dCC structs also store unchanging +information about the instruction: +

    +
  • An instruction-type identification tag (explained below)
  • +

  • Instruction size
  • +

  • Data reference size (idCC only)
  • +

  • Instruction address
  • +

+ +Note that data address is not one of the fields for idCC. This is +because for many memory-referencing instructions the data address can change +each time it's executed (eg. if it uses register-offset addressing). We have +to give this item to the cache simulation in a different way (see +Instrumentation section below). Some memory-referencing instructions do always +reference the same address, but we don't try to treat them specialy in order to +keep things simple.

+ +Also note that there is only room for recording info about one data cache +access in an idCC. So what about instructions that do a read then +a write, such as: + +

inc %(esi)
+ +In a write-allocate cache, as simulated by Valgrind, the write cannot miss, +since it immediately follows the read which will drag the block into the cache +if it's not already there. So the write access isn't really interesting, and +Valgrind doesn't record it. This means that Valgrind doesn't measure +memory references, but rather memory references that could miss in the cache. +This behaviour is the same as that used by the AMD Athlon hardware counters. +It also has the benefit of simplifying the implementation -- instructions that +read and write memory can be treated like instructions that read memory.

+ +

Storing cost-centres

+Cost centres are stored in a way that makes them very cheap to lookup, which is +important since one is looked up for every original x86 instruction +executed.

+ +Valgrind does JIT translations at the basic block level, and cost centres are +also setup and stored at the basic block level. By doing things carefully, we +store all the cost centres for a basic block in a contiguous array, and lookup +comes almost for free.

+ +Consider this part of a basic block (for exposition purposes, pretend it's an +entire basic block): + +

+movl $0x0,%eax
+movl $0x99, -4(%ebp)
+
+ +The translation to UCode looks like this: + +
+MOVL      $0x0, t20
+PUTL      t20, %EAX
+INCEIPo   $5
+
+LEA1L     -4(t4), t14
+MOVL      $0x99, t18
+STL       t18, (t14)
+INCEIPo   $7
+
+ +The first step is to allocate the cost centres. This requires a preliminary +pass to count how many x86 instructions were in the basic block, and their +types (and thus sizes). UCode translations for single x86 instructions are +delimited by the INCEIPo instruction, the argument of which gives +the byte size of the instruction (note that lazy INCEIP updating is turned off +to allow this).

+ +We can tell if an x86 instruction references memory by looking for +LDL and STL UCode instructions, and thus what kind of +cost centre is required. From this we can determine how many cost centres we +need for the basic block, and their sizes. We can then allocate them in a +single array.

+ +Consider the example code above. After the preliminary pass, we know we need +two cost centres, one iCC and one dCC. So we +allocate an array to store these which looks like this: + +

+|(uninit)|      tag         (1 byte)
+|(uninit)|      instr_size  (1 bytes)
+|(uninit)|      (padding)   (2 bytes)
+|(uninit)|      instr_addr  (4 bytes)
+|(uninit)|      I.a         (8 bytes)
+|(uninit)|      I.m1        (8 bytes)
+|(uninit)|      I.m2        (8 bytes)
+
+|(uninit)|      tag         (1 byte)
+|(uninit)|      instr_size  (1 byte)
+|(uninit)|      data_size   (1 byte)
+|(uninit)|      (padding)   (1 byte)
+|(uninit)|      instr_addr  (4 bytes)
+|(uninit)|      I.a         (8 bytes)
+|(uninit)|      I.m1        (8 bytes)
+|(uninit)|      I.m2        (8 bytes)
+|(uninit)|      D.a         (8 bytes)
+|(uninit)|      D.m1        (8 bytes)
+|(uninit)|      D.m2        (8 bytes)
+
+ +(We can see now why we need tags to distinguish between the two types of cost +centres.)

+ +We also record the size of the array. We look up the debug info of the first +instruction in the basic block, and then stick the array into a table indexed +by filename and function name. This makes it easy to dump the information +quickly to file at the end.

+ +

Instrumentation

+The instrumentation pass has two main jobs: + +
    +
  1. Fill in the gaps in the allocated cost centres.
  2. +

  3. Add UCode to call the cache simulator for each instruction.
  4. +

+ +The instrumentation pass steps through the UCode and the cost centres in +tandem. As each original x86 instruction's UCode is processed, the appropriate +gaps in the instructions cost centre are filled in, for example: + +
+|INSTR_CC|      tag         (1 byte)
+|5       |      instr_size  (1 bytes)
+|(uninit)|      (padding)   (2 bytes)
+|i_addr1 |      instr_addr  (4 bytes)
+|0       |      I.a         (8 bytes)
+|0       |      I.m1        (8 bytes)
+|0       |      I.m2        (8 bytes)
+
+|WRITE_CC|      tag         (1 byte)
+|7       |      instr_size  (1 byte)
+|4       |      data_size   (1 byte)
+|(uninit)|      (padding)   (1 byte)
+|i_addr2 |      instr_addr  (4 bytes)
+|0       |      I.a         (8 bytes)
+|0       |      I.m1        (8 bytes)
+|0       |      I.m2        (8 bytes)
+|0       |      D.a         (8 bytes)
+|0       |      D.m1        (8 bytes)
+|0       |      D.m2        (8 bytes)
+
+ +(Note that this step is not performed if a basic block is re-translated; see +here for more information.)

+ +GCC inserts padding before the instr_size field so that it is word +aligned.

+ +The instrumentation added to call the cache simulation function looks like this +(instrumentation is indented to distinguish it from the original UCode): + +

+MOVL      $0x0, t20
+PUTL      t20, %EAX
+  PUSHL     %eax
+  PUSHL     %ecx
+  PUSHL     %edx
+  MOVL      $0x4091F8A4, t46  # address of 1st CC
+  PUSHL     t46
+  CALLMo    $0x12             # second cachesim function
+  CLEARo    $0x4
+  POPL      %edx
+  POPL      %ecx
+  POPL      %eax
+INCEIPo   $5
+
+LEA1L     -4(t4), t14
+MOVL      $0x99, t18
+  MOVL      t14, t42
+STL       t18, (t14)
+  PUSHL     %eax
+  PUSHL     %ecx
+  PUSHL     %edx
+  PUSHL     t42
+  MOVL      $0x4091F8C4, t44  # address of 2nd CC
+  PUSHL     t44
+  CALLMo    $0x13             # second cachesim function
+  CLEARo    $0x8
+  POPL      %edx
+  POPL      %ecx
+  POPL      %eax
+INCEIPo   $7
+
+ +Consider the first instruction's UCode. Each call is surrounded by three +PUSHL and POPL instructions to save and restore the +caller-save registers. Then the address of the instruction's cost centre is +pushed onto the stack, to be the first argument to the cache simulation +function. The address is known at this point because we are doing a +simultaneous pass through the cost centre array. This means the cost centre +lookup for each instruction is almost free (just the cost of pushing an +argument for a function call). Then the call to the cache simulation function +for non-memory-reference instructions is made (note that the +CALLMo UInstruction takes an offset into a table of predefined +functions; it is not an absolute address), and the single argument is +CLEARed from the stack.

+ +The second instruction's UCode is similar. The only difference is that, as +mentioned before, we have to pass the address of the data item referenced to +the cache simulation function too. This explains the MOVL t14, +t42 and PUSHL t42 UInstructions. (Note that the seemingly +redundant MOVing will probably be optimised away during register +allocation.)

+ +Note that instead of storing unchanging information about each instruction +(instruction size, data size, etc) in its cost centre, we could have passed in +these arguments to the simulation function. But this would slow the calls down +(two or three extra arguments pushed onto the stack). Also it would bloat the +UCode instrumentation by amounts similar to the space required for them in the +cost centre; bloated UCode would also fill the translation cache more quickly, +requiring more translations for large programs and slowing them down more.

+ + +

Handling basic block retranslations

+The above description ignores one complication. Valgrind has a limited size +cache for basic block translations; if it fills up, old translations are +discarded. If a discarded basic block is executed again, it must be +re-translated.

+ +However, we can't use this approach for profiling -- we can't throw away cost +centres for instructions in the middle of execution! So when a basic block is +translated, we first look for its cost centre array in the hash table. If +there is no cost centre array, it must be the first translation, so we proceed +as described above. But if there is a cost centre array already, it must be a +retranslation. In this case, we skip the cost centre allocation and +initialisation steps, but still do the UCode instrumentation step.

+ +

The cache simulation

+The cache simulation is fairly straightforward. It just tracks which memory +blocks are in the cache at the moment (it doesn't track the contents, since +that is irrelevant).

+ +The interface to the simulation is quite clean. The functions called from the +UCode contain calls to the simulation functions in the files +vg_cachesim_{I1,D1,L2}.c; these calls are inlined so that only +one function call is done per simulated x86 instruction. The file +vg_cachesim.c simply #includes the three files +containing the simulation, which makes plugging in new cache simulations is +very easy -- you just replace the three files and recompile.

+ +

Output

+Output is fairly straightforward, basically printing the cost centre for every +instruction, grouped by files and functions. Total counts (eg. total cache +accesses, total L1 misses) are calculated when traversing this structure rather +than during execution, to save time; the cache simulation functions are called +so often that even one or two extra adds can make a sizeable difference.

+ +Input file has the following format: + +

+file         ::= desc_line* cmd_line events_line data_line+ summary_line
+desc_line    ::= "desc:" ws? non_nl_string
+cmd_line     ::= "cmd:" ws? cmd
+events_line  ::= "events:" ws? (event ws)+
+data_line    ::= file_line | fn_line | count_line
+file_line    ::= ("fl=" | "fi=" | "fe=") filename
+fn_line      ::= "fn=" fn_name
+count_line   ::= line_num ws? (count ws)+
+summary_line ::= "summary:" ws? (count ws)+
+count        ::= num | "."
+
+ +Where: + +
    +
  • non_nl_string is any string not containing a newline.
  • +

  • cmd is a command line invocation.
  • +

  • filename and fn_name can be anything.
  • +

  • num and line_num are decimal numbers.
  • +

  • ws is whitespace.
  • +

  • nl is a newline.
  • +

+ +The contents of the "desc:" lines is printed out at the top of the summary. +This is a generic way of providing simulation specific information, eg. for +giving the cache configuration for cache simulation.

+ +Counts can be "." to represent "N/A", eg. the number of write misses for an +instruction that doesn't write to memory.

+ +The number of counts in each line and the +summary_line should not exceed the number of events in the +event_line. If the number in each line is less, +cg_annotate treats those missing as though they were a "." entry.

+ +A file_line changes the current file name. A fn_line +changes the current function name. A count_line contains counts +that pertain to the current filename/fn_name. A "fn=" file_line +and a fn_line must appear before any count_lines to +give the context of the first count_lines.

+ +Each file_line should be immediately followed by a +fn_line. "fi=" file_lines are used to switch +filenames for inlined functions; "fe=" file_lines are similar, but +are put at the end of a basic block in which the file name hasn't been switched +back to the original file name. (fi and fe lines behave the same, they are +only distinguished to help debugging.)

+ + +

Summary of performance features

+Quite a lot of work has gone into making the profiling as fast as possible. +This is a summary of the important features: + +
    +
  • The basic block-level cost centre storage allows almost free cost centre + lookup.
  • + +

  • Only one function call is made per instruction simulated; even this + accounts for a sizeable percentage of execution time, but it seems + unavoidable if we want flexibility in the cache simulator.
  • + +

  • Unchanging information about an instruction is stored in its cost centre, + avoiding unnecessary argument pushing, and minimising UCode + instrumentation bloat.
  • + +

  • Summary counts are calculated at the end, rather than during + execution.
  • + +

  • The cachegrind.out output files can contain huge amounts of + information; file format was carefully chosen to minimise file + sizes.
  • +

+ + +

Annotation

+Annotation is done by cg_annotate. It is a fairly straightforward Perl script +that slurps up all the cost centres, and then runs through all the chosen +source files, printing out cost centres with them. It too has been carefully +optimised. + + +

Similar work, extensions

+It would be relatively straightforward to do other simulations and obtain +line-by-line information about interesting events. A good example would be +branch prediction -- all branches could be instrumented to interact with a +branch prediction simulator, using very similar techniques to those described +above.

+ +In particular, cg_annotate would not need to change -- the file format is such +that it is not specific to the cache simulation, but could be used for any kind +of line-by-line information. The only part of cg_annotate that is specific to +the cache simulation is the name of the input file +(cachegrind.out), although it would be very simple to add an +option to control this.

+ + + diff --git a/VEX/head20041019/cachegrind/tests/.cvsignore b/VEX/head20041019/cachegrind/tests/.cvsignore new file mode 100644 index 0000000000..768ce5ccfd --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/.cvsignore @@ -0,0 +1,11 @@ +Makefile.in +Makefile +chdir +dlclose +fpu-28-108 +cachegrind.out +cachegrind.out.* +*.stdout.diff +*.stderr.diff +*.stdout.out +*.stderr.out diff --git a/VEX/head20041019/cachegrind/tests/CVS/Entries b/VEX/head20041019/cachegrind/tests/CVS/Entries new file mode 100644 index 0000000000..243e8762e0 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/CVS/Entries @@ -0,0 +1,37 @@ +/.cvsignore/1.2/Wed May 28 01:02:46 2003// +/Makefile.am/1.12/Sat Mar 27 18:02:36 2004// +/chdir.c/1.1/Wed Apr 30 20:23:56 2003// +/chdir.stderr.exp/1.1/Wed Apr 30 20:23:58 2003// +/chdir.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/dlclose.c/1.2/Mon Sep 23 09:36:24 2002// +/dlclose.stderr.exp/1.5/Wed Nov 19 20:09:53 2003// +/dlclose.stdout.exp/1.2/Mon Sep 23 09:36:24 2002// +/dlclose.vgtest/1.5/Sat Apr 17 17:25:08 2004// +/filter_cachesim_discards/1.4/Mon May 5 16:18:50 2003// +/filter_stderr/1.4/Mon May 5 16:18:51 2003// +/fpu-28-108.S/1.2/Mon Sep 23 09:36:24 2002// +/fpu-28-108.stderr.exp/1.2/Mon Sep 23 09:36:24 2002// +/fpu-28-108.vgtest/1.5/Sat Apr 17 17:25:08 2004// +/insn_basic.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_basic.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_basic.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/insn_cmov.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/insn_fpu.stderr.exp/1.1/Sat Mar 27 18:02:36 2004// +/insn_fpu.stdout.exp/1.4/Wed Mar 31 22:47:52 2004// +/insn_fpu.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/insn_mmx.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmx.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmx.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/insn_mmxext.stderr.exp/1.2/Tue Mar 9 08:50:02 2004// +/insn_mmxext.stdout.exp/1.2/Sun Jul 25 15:18:20 2004// +/insn_mmxext.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/insn_sse.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse.stdout.exp/1.2/Sun Jul 25 15:18:20 2004// +/insn_sse.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/insn_sse2.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse2.stdout.exp/1.2/Sun Jul 25 15:18:20 2004// +/insn_sse2.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/myprint.c/1.2/Mon Sep 23 09:36:24 2002// +D diff --git a/VEX/head20041019/cachegrind/tests/CVS/Repository b/VEX/head20041019/cachegrind/tests/CVS/Repository new file mode 100644 index 0000000000..ca89623416 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/CVS/Repository @@ -0,0 +1 @@ +valgrind/cachegrind/tests diff --git a/VEX/head20041019/cachegrind/tests/CVS/Root b/VEX/head20041019/cachegrind/tests/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/cachegrind/tests/CVS/Template b/VEX/head20041019/cachegrind/tests/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/tests/Makefile.am b/VEX/head20041019/cachegrind/tests/Makefile.am new file mode 100644 index 0000000000..cb3115d7e8 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/Makefile.am @@ -0,0 +1,27 @@ +noinst_SCRIPTS = filter_stderr filter_cachesim_discards + +INSN_TESTS=insn_basic insn_fpu insn_cmov insn_mmx insn_mmxext insn_sse insn_sse2 + +EXTRA_DIST = $(noinst_SCRIPTS) \ + chdir.vgtest chdir.stderr.exp \ + dlclose.vgtest dlclose.stderr.exp dlclose.stdout.exp \ + fpu-28-108.vgtest fpu-28-108.stderr.exp \ + $(addsuffix .stderr.exp,$(INSN_TESTS)) \ + $(addsuffix .stdout.exp,$(INSN_TESTS)) \ + $(addsuffix .vgtest,$(INSN_TESTS)) + +check_PROGRAMS = \ + chdir dlclose fpu-28-108 myprint.so + +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -g +AM_CXXFLAGS = $(AM_CFLAGS) +AM_CCASFLAGS = $(AM_CFLAGS) + +# C ones +chdir_SOURCES = chdir.c +dlclose_SOURCES = dlclose.c +dlclose_LDADD = -ldl +myprint_so_SOURCES = myprint.c +myprint_so_LDFLAGS = -shared + +fpu_28_108_SOURCES = fpu-28-108.S diff --git a/VEX/head20041019/cachegrind/tests/chdir.c b/VEX/head20041019/cachegrind/tests/chdir.c new file mode 100644 index 0000000000..9b681cfc4b --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/chdir.c @@ -0,0 +1,10 @@ +#include + +// Before the bug was fixed, if a program changed working directory, things +// would break and the cachegrind.out. file wouldn't get written. +int main(void) +{ + chdir(".."); + + return 0; +} diff --git a/VEX/head20041019/cachegrind/tests/chdir.stderr.exp b/VEX/head20041019/cachegrind/tests/chdir.stderr.exp new file mode 100644 index 0000000000..8eaf65446c --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/chdir.stderr.exp @@ -0,0 +1,17 @@ + + +I refs: +I1 misses: +L2i misses: +I1 miss rate: +L2i miss rate: + +D refs: +D1 misses: +L2d misses: +D1 miss rate: +L2d miss rate: + +L2 refs: +L2 misses: +L2 miss rate: diff --git a/VEX/head20041019/cachegrind/tests/chdir.vgtest b/VEX/head20041019/cachegrind/tests/chdir.vgtest new file mode 100644 index 0000000000..c91b4b673f --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/chdir.vgtest @@ -0,0 +1,2 @@ +prog: chdir +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/dlclose.c b/VEX/head20041019/cachegrind/tests/dlclose.c new file mode 100644 index 0000000000..9fee03088b --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/dlclose.c @@ -0,0 +1,38 @@ +/* This exercises the code that was causing this bug: + + valgrind: vg_cachesim.c:389 (get_BBCC): Assertion `((Bool)0) == remove' + failed. + + in Cachegrind 1.0.0 and 1.0.1, that was caused by unloading symbols before + invalidating translations. +*/ + +#include +#include +#include + +int main(int argc, char **argv) { + void *handle; + void (*myprint)(void); + char *error; + + handle = dlopen ("./myprint.so", RTLD_LAZY); + if (!handle) { + fputs (dlerror(), stderr); + exit(1); + } + + myprint = dlsym(handle, "myprint"); + if ((error = dlerror()) != NULL) { + fprintf (stderr, "%s\n", error); + exit(1); + } + + (*myprint)(); + + /* Assertion failure was happening here */ + dlclose(handle); + + return 0; +} + diff --git a/VEX/head20041019/cachegrind/tests/dlclose.stderr.exp b/VEX/head20041019/cachegrind/tests/dlclose.stderr.exp new file mode 100644 index 0000000000..8eaf65446c --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/dlclose.stderr.exp @@ -0,0 +1,17 @@ + + +I refs: +I1 misses: +L2i misses: +I1 miss rate: +L2i miss rate: + +D refs: +D1 misses: +L2d misses: +D1 miss rate: +L2d miss rate: + +L2 refs: +L2 misses: +L2 miss rate: diff --git a/VEX/head20041019/cachegrind/tests/dlclose.stdout.exp b/VEX/head20041019/cachegrind/tests/dlclose.stdout.exp new file mode 100644 index 0000000000..890082f481 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/dlclose.stdout.exp @@ -0,0 +1 @@ +This is myprint! diff --git a/VEX/head20041019/cachegrind/tests/dlclose.vgtest b/VEX/head20041019/cachegrind/tests/dlclose.vgtest new file mode 100644 index 0000000000..353a3243af --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/dlclose.vgtest @@ -0,0 +1,3 @@ +prog: dlclose +stderr_filter: filter_cachesim_discards +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/filter_cachesim_discards b/VEX/head20041019/cachegrind/tests/filter_cachesim_discards new file mode 100755 index 0000000000..d184c4f0bb --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/filter_cachesim_discards @@ -0,0 +1,8 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/filter_stderr | + +# Anonymise paths like "/local/foo/bar/tests/baz/quux" (note "tests" is there) +sed "s/\/.*\/tests\//\/...\/tests\//" diff --git a/VEX/head20041019/cachegrind/tests/filter_stderr b/VEX/head20041019/cachegrind/tests/filter_stderr new file mode 100755 index 0000000000..1dad41acf1 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/filter_stderr @@ -0,0 +1,16 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic | + +# Remove numbers from I/D/L2 "refs:" lines +sed "s/\(\(I\|D\|L2\) *refs:\)[ 0-9,()+rdw]*$/\1/" | + +# Remove numbers from I1/D1/L2/L2i/L2d "misses:" and "miss rates:" lines +sed "s/\(\(I1\|D1\|L2\|L2i\|L2d\) *\(misses\|miss rate\):\)[ 0-9,()+rdw%\.]*$/\1/" | + +# Remove warnings lines for P4s: +sed "/warning: Pentium with 12 K micro-op instruction trace cache/d" | +sed "/Simulating a 16 KB cache with 32 B lines/d" + diff --git a/VEX/head20041019/cachegrind/tests/fpu-28-108.S b/VEX/head20041019/cachegrind/tests/fpu-28-108.S new file mode 100644 index 0000000000..f655c00f79 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/fpu-28-108.S @@ -0,0 +1,24 @@ +/* Test 28 and 108 byte loads and stores. (Just make sure program + runs without any assertion failures from V.) */ + +/* Useful listing: + gcc -o tests/fpu_28_108 tests/fpu_28_108.S -Wa,-a */ + +.data +fooble: + .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +bar: + +.text +.globl main +main: + fstsw fooble + fsave fooble + frstor fooble + fstenv fooble + fldenv fooble + movl $0, %eax + ret + diff --git a/VEX/head20041019/cachegrind/tests/fpu-28-108.stderr.exp b/VEX/head20041019/cachegrind/tests/fpu-28-108.stderr.exp new file mode 100644 index 0000000000..8eaf65446c --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/fpu-28-108.stderr.exp @@ -0,0 +1,17 @@ + + +I refs: +I1 misses: +L2i misses: +I1 miss rate: +L2i miss rate: + +D refs: +D1 misses: +L2d misses: +D1 miss rate: +L2d miss rate: + +L2 refs: +L2 misses: +L2 miss rate: diff --git a/VEX/head20041019/cachegrind/tests/fpu-28-108.vgtest b/VEX/head20041019/cachegrind/tests/fpu-28-108.vgtest new file mode 100644 index 0000000000..48cc454c89 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/fpu-28-108.vgtest @@ -0,0 +1,2 @@ +prog: fpu-28-108 +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/insn_basic.stderr.exp b/VEX/head20041019/cachegrind/tests/insn_basic.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/tests/insn_basic.stdout.exp b/VEX/head20041019/cachegrind/tests/insn_basic.stdout.exp new file mode 100644 index 0000000000..40cabbcd02 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_basic.stdout.exp @@ -0,0 +1,1083 @@ +aaa_1 ... ok +aaa_2 ... ok +aaa_3 ... ok +aaa_4 ... ok +aaa_5 ... ok +aaa_6 ... ok +aaa_7 ... ok +aaa_8 ... ok +aad_1 ... ok +aad_2 ... ok +aam_1 ... ok +aam_2 ... ok +aas_1 ... ok +aas_2 ... ok +aas_3 ... ok +aas_4 ... ok +aas_5 ... ok +aas_6 ... ok +aas_7 ... ok +aas_8 ... ok +adcb_1 ... ok +adcb_2 ... ok +adcb_3 ... ok +adcb_4 ... ok +adcb_5 ... ok +adcb_6 ... ok +adcb_7 ... ok +adcb_8 ... ok +adcb_9 ... ok +adcb_10 ... ok +adcb_11 ... ok +adcb_12 ... ok +adcw_1 ... ok +adcw_2 ... ok +adcw_3 ... ok +adcw_4 ... ok +adcw_5 ... ok +adcw_6 ... ok +adcw_7 ... ok +adcw_8 ... ok +adcw_9 ... ok +adcw_10 ... ok +adcw_11 ... ok +adcw_12 ... ok +adcw_13 ... ok +adcw_14 ... ok +adcl_1 ... ok +adcl_2 ... ok +adcl_3 ... ok +adcl_4 ... ok +adcl_5 ... ok +adcl_6 ... ok +adcl_7 ... ok +adcl_8 ... ok +adcl_9 ... ok +adcl_10 ... ok +adcl_11 ... ok +adcl_12 ... ok +adcl_13 ... ok +adcl_14 ... ok +addb_1 ... ok +addb_2 ... ok +addb_3 ... ok +addb_4 ... ok +addb_5 ... ok +addb_6 ... ok +addw_1 ... ok +addw_2 ... ok +addw_3 ... ok +addw_4 ... ok +addw_5 ... ok +addw_6 ... ok +addw_7 ... ok +addl_1 ... ok +addl_2 ... ok +addl_3 ... ok +addl_4 ... ok +addl_5 ... ok +addl_6 ... ok +addl_7 ... ok +andb_1 ... ok +andb_2 ... ok +andb_3 ... ok +andb_4 ... ok +andb_5 ... ok +andb_6 ... ok +andw_1 ... ok +andw_2 ... ok +andw_3 ... ok +andw_4 ... ok +andw_5 ... ok +andw_6 ... ok +andw_7 ... ok +andl_1 ... ok +andl_2 ... ok +andl_3 ... ok +andl_4 ... ok +andl_5 ... ok +andl_6 ... ok +andl_7 ... ok +bsfw_1 ... ok +bsfw_2 ... ok +bsfl_1 ... ok +bsfl_2 ... ok +bsrw_1 ... ok +bsrw_2 ... ok +bsrl_1 ... ok +bsrl_2 ... ok +bswapl_1 ... ok +btw_1 ... ok +btw_2 ... ok +btw_3 ... ok +btw_4 ... ok +btw_5 ... ok +btw_6 ... ok +btw_7 ... ok +btw_8 ... ok +btl_1 ... ok +btl_2 ... ok +btl_3 ... ok +btl_4 ... ok +btl_5 ... ok +btl_6 ... ok +btl_7 ... ok +btl_8 ... ok +btcw_1 ... ok +btcw_2 ... ok +btcw_3 ... ok +btcw_4 ... ok +btcw_5 ... ok +btcw_6 ... ok +btcw_7 ... ok +btcw_8 ... ok +btcl_1 ... ok +btcl_2 ... ok +btcl_3 ... ok +btcl_4 ... ok +btcl_5 ... ok +btcl_6 ... ok +btcl_7 ... ok +btcl_8 ... ok +btrw_1 ... ok +btrw_2 ... ok +btrw_3 ... ok +btrw_4 ... ok +btrw_5 ... ok +btrw_6 ... ok +btrw_7 ... ok +btrw_8 ... ok +btrl_1 ... ok +btrl_2 ... ok +btrl_3 ... ok +btrl_4 ... ok +btrl_5 ... ok +btrl_6 ... ok +btrl_7 ... ok +btrl_8 ... ok +btsw_1 ... ok +btsw_2 ... ok +btsw_3 ... ok +btsw_4 ... ok +btsw_5 ... ok +btsw_6 ... ok +btsw_7 ... ok +btsw_8 ... ok +btsl_1 ... ok +btsl_2 ... ok +btsl_3 ... ok +btsl_4 ... ok +btsl_5 ... ok +btsl_6 ... ok +btsl_7 ... ok +btsl_8 ... ok +cbw_1 ... ok +cbw_2 ... ok +cdq_1 ... ok +cdq_2 ... ok +clc_1 ... ok +clc_2 ... ok +cld_1 ... ok +cld_2 ... ok +cmc_1 ... ok +cmc_2 ... ok +cmpb_1 ... ok +cmpb_2 ... ok +cmpb_3 ... ok +cmpb_4 ... ok +cmpb_5 ... ok +cmpb_6 ... ok +cmpb_7 ... ok +cmpb_8 ... ok +cmpb_9 ... ok +cmpb_10 ... ok +cmpb_11 ... ok +cmpb_12 ... ok +cmpb_13 ... ok +cmpb_14 ... ok +cmpb_15 ... ok +cmpb_16 ... ok +cmpb_17 ... ok +cmpb_18 ... ok +cmpb_19 ... ok +cmpb_20 ... ok +cmpb_21 ... ok +cmpb_22 ... ok +cmpb_23 ... ok +cmpb_24 ... ok +cmpb_25 ... ok +cmpb_26 ... ok +cmpb_27 ... ok +cmpb_28 ... ok +cmpb_29 ... ok +cmpb_30 ... ok +cmpb_31 ... ok +cmpb_32 ... ok +cmpb_33 ... ok +cmpb_34 ... ok +cmpb_35 ... ok +cmpb_36 ... ok +cmpb_37 ... ok +cmpb_38 ... ok +cmpb_39 ... ok +cmpb_40 ... ok +cmpb_41 ... ok +cmpb_42 ... ok +cmpb_43 ... ok +cmpb_44 ... ok +cmpb_45 ... ok +cmpb_46 ... ok +cmpb_47 ... ok +cmpb_48 ... ok +cmpb_49 ... ok +cmpb_50 ... ok +cmpb_51 ... ok +cmpb_52 ... ok +cmpb_53 ... ok +cmpb_54 ... ok +cmpb_55 ... ok +cmpb_56 ... ok +cmpb_57 ... ok +cmpb_58 ... ok +cmpb_59 ... ok +cmpb_60 ... ok +cmpw_1 ... ok +cmpw_2 ... ok +cmpw_3 ... ok +cmpw_4 ... ok +cmpw_5 ... ok +cmpw_6 ... ok +cmpw_7 ... ok +cmpw_8 ... ok +cmpw_9 ... ok +cmpw_10 ... ok +cmpw_11 ... ok +cmpw_12 ... ok +cmpw_13 ... ok +cmpw_14 ... ok +cmpw_15 ... ok +cmpw_16 ... ok +cmpw_17 ... ok +cmpw_18 ... ok +cmpw_19 ... ok +cmpw_20 ... ok +cmpw_21 ... ok +cmpw_22 ... ok +cmpw_23 ... ok +cmpw_24 ... ok +cmpw_25 ... ok +cmpw_26 ... ok +cmpw_27 ... ok +cmpw_28 ... ok +cmpw_29 ... ok +cmpw_30 ... ok +cmpw_31 ... ok +cmpw_32 ... ok +cmpw_33 ... ok +cmpw_34 ... ok +cmpw_35 ... ok +cmpw_36 ... ok +cmpw_37 ... ok +cmpw_38 ... ok +cmpw_39 ... ok +cmpw_40 ... ok +cmpw_41 ... ok +cmpw_42 ... ok +cmpw_43 ... ok +cmpw_44 ... ok +cmpw_45 ... ok +cmpw_46 ... ok +cmpw_47 ... ok +cmpw_48 ... ok +cmpw_49 ... ok +cmpw_50 ... ok +cmpw_51 ... ok +cmpw_52 ... ok +cmpw_53 ... ok +cmpw_54 ... ok +cmpw_55 ... ok +cmpw_56 ... ok +cmpw_57 ... ok +cmpw_58 ... ok +cmpw_59 ... ok +cmpw_60 ... ok +cmpw_61 ... ok +cmpw_62 ... ok +cmpw_63 ... ok +cmpw_64 ... ok +cmpw_65 ... ok +cmpw_66 ... ok +cmpw_67 ... ok +cmpw_68 ... ok +cmpw_69 ... ok +cmpw_70 ... ok +cmpw_71 ... ok +cmpw_72 ... ok +cmpw_73 ... ok +cmpw_74 ... ok +cmpw_75 ... ok +cmpw_76 ... ok +cmpw_77 ... ok +cmpw_78 ... ok +cmpw_79 ... ok +cmpw_80 ... ok +cmpl_1 ... ok +cmpl_2 ... ok +cmpl_3 ... ok +cmpl_4 ... ok +cmpl_5 ... ok +cmpl_6 ... ok +cmpl_7 ... ok +cmpl_8 ... ok +cmpl_9 ... ok +cmpl_10 ... ok +cmpl_11 ... ok +cmpl_12 ... ok +cmpl_13 ... ok +cmpl_14 ... ok +cmpl_15 ... ok +cmpl_16 ... ok +cmpl_17 ... ok +cmpl_18 ... ok +cmpl_19 ... ok +cmpl_20 ... ok +cmpl_21 ... ok +cmpl_22 ... ok +cmpl_23 ... ok +cmpl_24 ... ok +cmpl_25 ... ok +cmpl_26 ... ok +cmpl_27 ... ok +cmpl_28 ... ok +cmpl_29 ... ok +cmpl_30 ... ok +cmpl_31 ... ok +cmpl_32 ... ok +cmpl_33 ... ok +cmpl_34 ... ok +cmpl_35 ... ok +cmpl_36 ... ok +cmpl_37 ... ok +cmpl_38 ... ok +cmpl_39 ... ok +cmpl_40 ... ok +cmpl_41 ... ok +cmpl_42 ... ok +cmpl_43 ... ok +cmpl_44 ... ok +cmpl_45 ... ok +cmpl_46 ... ok +cmpl_47 ... ok +cmpl_48 ... ok +cmpl_49 ... ok +cmpl_50 ... ok +cmpl_51 ... ok +cmpl_52 ... ok +cmpl_53 ... ok +cmpl_54 ... ok +cmpl_55 ... ok +cmpl_56 ... ok +cmpl_57 ... ok +cmpl_58 ... ok +cmpl_59 ... ok +cmpl_60 ... ok +cmpl_61 ... ok +cmpl_62 ... ok +cmpl_63 ... ok +cmpl_64 ... ok +cmpl_65 ... ok +cmpl_66 ... ok +cmpl_67 ... ok +cmpl_68 ... ok +cmpl_69 ... ok +cmpl_70 ... ok +cmpl_71 ... ok +cmpl_72 ... ok +cmpl_73 ... ok +cmpl_74 ... ok +cmpl_75 ... ok +cmpl_76 ... ok +cmpl_77 ... ok +cmpl_78 ... ok +cmpl_79 ... ok +cmpl_80 ... ok +cmpxchgb_1 ... ok +cmpxchgb_2 ... ok +cmpxchgb_3 ... ok +cmpxchgb_4 ... ok +cmpxchgw_1 ... ok +cmpxchgw_2 ... ok +cmpxchgw_3 ... ok +cmpxchgw_4 ... ok +cmpxchgl_1 ... ok +cmpxchgl_2 ... ok +cmpxchgl_3 ... ok +cmpxchgl_4 ... ok +cwd_1 ... ok +cwd_2 ... ok +cwde_1 ... ok +cwde_2 ... ok +daa_1 ... ok +daa_2 ... ok +das_1 ... ok +decb_1 ... ok +decb_2 ... ok +decw_1 ... ok +decw_2 ... ok +decl_1 ... ok +decl_2 ... ok +divb_1 ... ok +divb_2 ... ok +divw_1 ... ok +divw_2 ... ok +divl_1 ... ok +divl_2 ... ok +idivb_1 ... ok +idivb_2 ... ok +idivw_1 ... ok +idivw_2 ... ok +idivl_1 ... ok +idivl_2 ... ok +imulb_1 ... ok +imulb_2 ... ok +imulw_1 ... ok +imulw_2 ... ok +imull_1 ... ok +imull_2 ... ok +imulw_3 ... ok +imulw_4 ... ok +imulw_5 ... ok +imulw_6 ... ok +imulw_7 ... ok +imulw_8 ... ok +imulw_9 ... ok +imulw_10 ... ok +imull_3 ... ok +imull_4 ... ok +imull_5 ... ok +imull_6 ... ok +imull_7 ... ok +imull_8 ... ok +imull_9 ... ok +imull_10 ... ok +incb_1 ... ok +incb_2 ... ok +incw_1 ... ok +incw_2 ... ok +incl_1 ... ok +incl_2 ... ok +lahf_1 ... ok +lahf_2 ... ok +movb_1 ... ok +movb_2 ... ok +movb_3 ... ok +movb_4 ... ok +movb_5 ... ok +movw_1 ... ok +movw_2 ... ok +movw_3 ... ok +movw_4 ... ok +movw_5 ... ok +movl_1 ... ok +movl_2 ... ok +movl_3 ... ok +movl_4 ... ok +movl_5 ... ok +movsbw_1 ... ok +movsbw_2 ... ok +movsbl_1 ... ok +movsbl_2 ... ok +movswl_1 ... ok +movswl_2 ... ok +movzbw_1 ... ok +movzbw_2 ... ok +movzbl_1 ... ok +movzbl_2 ... ok +movzwl_1 ... ok +movzwl_2 ... ok +mulb_1 ... ok +mulb_2 ... ok +mulw_1 ... ok +mulw_2 ... ok +mull_1 ... ok +mull_2 ... ok +negb_1 ... ok +negb_2 ... ok +negw_1 ... ok +negw_2 ... ok +negl_1 ... ok +negl_2 ... ok +notb_1 ... ok +notb_2 ... ok +notw_1 ... ok +notw_2 ... ok +notl_1 ... ok +notl_2 ... ok +orb_1 ... ok +orb_2 ... ok +orb_3 ... ok +orb_4 ... ok +orb_5 ... ok +orb_6 ... ok +orw_1 ... ok +orw_2 ... ok +orw_3 ... ok +orw_4 ... ok +orw_5 ... ok +orw_6 ... ok +orw_7 ... ok +orl_1 ... ok +orl_2 ... ok +orl_3 ... ok +orl_4 ... ok +orl_5 ... ok +orl_6 ... ok +orl_7 ... ok +rclb_1 ... ok +rclb_2 ... ok +rclb_3 ... ok +rclb_4 ... ok +rclb_5 ... ok +rclb_6 ... ok +rclw_1 ... ok +rclw_2 ... ok +rclw_3 ... ok +rclw_4 ... ok +rclw_5 ... ok +rclw_6 ... ok +rcll_1 ... ok +rcll_2 ... ok +rcll_3 ... ok +rcll_4 ... ok +rcll_5 ... ok +rcll_6 ... ok +rcrb_1 ... ok +rcrb_2 ... ok +rcrb_3 ... ok +rcrb_4 ... ok +rcrb_5 ... ok +rcrb_6 ... ok +rcrw_1 ... ok +rcrw_2 ... ok +rcrw_3 ... ok +rcrw_4 ... ok +rcrw_5 ... ok +rcrw_6 ... ok +rcrl_1 ... ok +rcrl_2 ... ok +rcrl_3 ... ok +rcrl_4 ... ok +rcrl_5 ... ok +rcrl_6 ... ok +rolb_1 ... ok +rolb_2 ... ok +rolb_3 ... ok +rolb_4 ... ok +rolb_5 ... ok +rolb_6 ... ok +rolw_1 ... ok +rolw_2 ... ok +rolw_3 ... ok +rolw_4 ... ok +rolw_5 ... ok +rolw_6 ... ok +roll_1 ... ok +roll_2 ... ok +roll_3 ... ok +roll_4 ... ok +roll_5 ... ok +roll_6 ... ok +rorb_1 ... ok +rorb_2 ... ok +rorb_3 ... ok +rorb_4 ... ok +rorb_5 ... ok +rorb_6 ... ok +rorw_1 ... ok +rorw_2 ... ok +rorw_3 ... ok +rorw_4 ... ok +rorw_5 ... ok +rorw_6 ... ok +rorl_1 ... ok +rorl_2 ... ok +rorl_3 ... ok +rorl_4 ... ok +rorl_5 ... ok +rorl_6 ... ok +sahf_1 ... ok +sahf_2 ... ok +salb_1 ... ok +salb_2 ... ok +salb_3 ... ok +salb_4 ... ok +salb_5 ... ok +salb_6 ... ok +salw_1 ... ok +salw_2 ... ok +salw_3 ... ok +salw_4 ... ok +salw_5 ... ok +salw_6 ... ok +sall_1 ... ok +sall_2 ... ok +sall_3 ... ok +sall_4 ... ok +sall_5 ... ok +sall_6 ... ok +sarb_1 ... ok +sarb_2 ... ok +sarb_3 ... ok +sarb_4 ... ok +sarb_5 ... ok +sarb_6 ... ok +sarw_1 ... ok +sarw_2 ... ok +sarw_3 ... ok +sarw_4 ... ok +sarw_5 ... ok +sarw_6 ... ok +sarl_1 ... ok +sarl_2 ... ok +sarl_3 ... ok +sarl_4 ... ok +sarl_5 ... ok +sarl_6 ... ok +sbbb_1 ... ok +sbbb_2 ... ok +sbbb_3 ... ok +sbbb_4 ... ok +sbbb_5 ... ok +sbbb_6 ... ok +sbbb_7 ... ok +sbbb_8 ... ok +sbbb_9 ... ok +sbbb_10 ... ok +sbbb_11 ... ok +sbbb_12 ... ok +sbbw_1 ... ok +sbbw_2 ... ok +sbbw_3 ... ok +sbbw_4 ... ok +sbbw_5 ... ok +sbbw_6 ... ok +sbbw_7 ... ok +sbbw_8 ... ok +sbbw_9 ... ok +sbbw_10 ... ok +sbbw_11 ... ok +sbbw_12 ... ok +sbbw_13 ... ok +sbbw_14 ... ok +sbbl_1 ... ok +sbbl_2 ... ok +sbbl_3 ... ok +sbbl_4 ... ok +sbbl_5 ... ok +sbbl_6 ... ok +sbbl_7 ... ok +sbbl_8 ... ok +sbbl_9 ... ok +sbbl_10 ... ok +sbbl_11 ... ok +sbbl_12 ... ok +sbbl_13 ... ok +sbbl_14 ... ok +seta_1 ... ok +seta_2 ... ok +seta_3 ... ok +seta_4 ... ok +seta_5 ... ok +seta_6 ... ok +seta_7 ... ok +seta_8 ... ok +setae_1 ... ok +setae_2 ... ok +setae_3 ... ok +setae_4 ... ok +setb_1 ... ok +setb_2 ... ok +setb_3 ... ok +setb_4 ... ok +setbe_1 ... ok +setbe_2 ... ok +setbe_3 ... ok +setbe_4 ... ok +setbe_5 ... ok +setbe_6 ... ok +setbe_7 ... ok +setbe_8 ... ok +setc_1 ... ok +setc_2 ... ok +setc_3 ... ok +setc_4 ... ok +sete_1 ... ok +sete_2 ... ok +sete_3 ... ok +sete_4 ... ok +setg_1 ... ok +setg_2 ... ok +setg_3 ... ok +setg_4 ... ok +setg_5 ... ok +setg_6 ... ok +setg_7 ... ok +setg_8 ... ok +setg_9 ... ok +setg_10 ... ok +setg_11 ... ok +setg_12 ... ok +setg_13 ... ok +setg_14 ... ok +setg_15 ... ok +setg_16 ... ok +setge_1 ... ok +setge_2 ... ok +setge_3 ... ok +setge_4 ... ok +setge_5 ... ok +setge_6 ... ok +setge_7 ... ok +setge_8 ... ok +setl_1 ... ok +setl_2 ... ok +setl_3 ... ok +setl_4 ... ok +setl_5 ... ok +setl_6 ... ok +setl_7 ... ok +setl_8 ... ok +setle_1 ... ok +setle_2 ... ok +setle_3 ... ok +setle_4 ... ok +setle_5 ... ok +setle_6 ... ok +setle_7 ... ok +setle_8 ... ok +setle_9 ... ok +setle_10 ... ok +setle_11 ... ok +setle_12 ... ok +setle_13 ... ok +setle_14 ... ok +setle_15 ... ok +setle_16 ... ok +setna_1 ... ok +setna_2 ... ok +setna_3 ... ok +setna_4 ... ok +setna_5 ... ok +setna_6 ... ok +setna_7 ... ok +setna_8 ... ok +setnae_1 ... ok +setnae_2 ... ok +setnae_3 ... ok +setnae_4 ... ok +setnb_1 ... ok +setnb_2 ... ok +setnb_3 ... ok +setnb_4 ... ok +setnbe_1 ... ok +setnbe_2 ... ok +setnbe_3 ... ok +setnbe_4 ... ok +setnbe_5 ... ok +setnbe_6 ... ok +setnbe_7 ... ok +setnbe_8 ... ok +setnc_1 ... ok +setnc_2 ... ok +setnc_3 ... ok +setnc_4 ... ok +setne_1 ... ok +setne_2 ... ok +setne_3 ... ok +setne_4 ... ok +setng_1 ... ok +setng_2 ... ok +setng_3 ... ok +setng_4 ... ok +setng_5 ... ok +setng_6 ... ok +setng_7 ... ok +setng_8 ... ok +setng_9 ... ok +setng_10 ... ok +setng_11 ... ok +setng_12 ... ok +setng_13 ... ok +setng_14 ... ok +setng_15 ... ok +setng_16 ... ok +setnge_1 ... ok +setnge_2 ... ok +setnge_3 ... ok +setnge_4 ... ok +setnge_5 ... ok +setnge_6 ... ok +setnge_7 ... ok +setnge_8 ... ok +setnl_1 ... ok +setnl_2 ... ok +setnl_3 ... ok +setnl_4 ... ok +setnl_5 ... ok +setnl_6 ... ok +setnl_7 ... ok +setnl_8 ... ok +setnle_1 ... ok +setnle_2 ... ok +setnle_3 ... ok +setnle_4 ... ok +setnle_5 ... ok +setnle_6 ... ok +setnle_7 ... ok +setnle_8 ... ok +setnle_9 ... ok +setnle_10 ... ok +setnle_11 ... ok +setnle_12 ... ok +setnle_13 ... ok +setnle_14 ... ok +setnle_15 ... ok +setnle_16 ... ok +setno_1 ... ok +setno_2 ... ok +setno_3 ... ok +setno_4 ... ok +setnp_1 ... ok +setnp_2 ... ok +setnp_3 ... ok +setnp_4 ... ok +setns_1 ... ok +setns_2 ... ok +setns_3 ... ok +setns_4 ... ok +setnz_1 ... ok +setnz_2 ... ok +setnz_3 ... ok +setnz_4 ... ok +seto_1 ... ok +seto_2 ... ok +seto_3 ... ok +seto_4 ... ok +setp_1 ... ok +setp_2 ... ok +setp_3 ... ok +setp_4 ... ok +sets_1 ... ok +sets_2 ... ok +sets_3 ... ok +sets_4 ... ok +setz_1 ... ok +setz_2 ... ok +setz_3 ... ok +setz_4 ... ok +shlb_1 ... ok +shlb_2 ... ok +shlb_3 ... ok +shlb_4 ... ok +shlb_5 ... ok +shlb_6 ... ok +shlw_1 ... ok +shlw_2 ... ok +shlw_3 ... ok +shlw_4 ... ok +shlw_5 ... ok +shlw_6 ... ok +shll_1 ... ok +shll_2 ... ok +shll_3 ... ok +shll_4 ... ok +shll_5 ... ok +shll_6 ... ok +shrb_1 ... ok +shrb_2 ... ok +shrb_3 ... ok +shrb_4 ... ok +shrb_5 ... ok +shrb_6 ... ok +shrw_1 ... ok +shrw_2 ... ok +shrw_3 ... ok +shrw_4 ... ok +shrw_5 ... ok +shrw_6 ... ok +shrl_1 ... ok +shrl_2 ... ok +shrl_3 ... ok +shrl_4 ... ok +shrl_5 ... ok +shrl_6 ... ok +shldw_1 ... ok +shldw_2 ... ok +shldw_3 ... ok +shldw_4 ... ok +shldw_5 ... ok +shldw_6 ... ok +shldw_7 ... ok +shldw_8 ... ok +shldl_1 ... ok +shldl_2 ... ok +shldl_3 ... ok +shldl_4 ... ok +shldl_5 ... ok +shldl_6 ... ok +shldl_7 ... ok +shldl_8 ... ok +shrdw_1 ... ok +shrdw_2 ... ok +shrdw_3 ... ok +shrdw_4 ... ok +shrdw_5 ... ok +shrdw_6 ... ok +shrdw_7 ... ok +shrdw_8 ... ok +shrdl_1 ... ok +shrdl_2 ... ok +shrdl_3 ... ok +shrdl_4 ... ok +shrdl_5 ... ok +shrdl_6 ... ok +shrdl_7 ... ok +shrdl_8 ... ok +stc_1 ... ok +stc_2 ... ok +std_1 ... ok +std_2 ... ok +subb_1 ... ok +subb_2 ... ok +subb_3 ... ok +subb_4 ... ok +subb_5 ... ok +subb_6 ... ok +subw_1 ... ok +subw_2 ... ok +subw_3 ... ok +subw_4 ... ok +subw_5 ... ok +subw_6 ... ok +subw_7 ... ok +subl_1 ... ok +subl_2 ... ok +subl_3 ... ok +subl_4 ... ok +subl_5 ... ok +subl_6 ... ok +subl_7 ... ok +testb_1 ... ok +testb_2 ... ok +testb_3 ... ok +testb_4 ... ok +testb_5 ... ok +testb_6 ... ok +testb_7 ... ok +testb_8 ... ok +testb_9 ... ok +testb_10 ... ok +testb_11 ... ok +testb_12 ... ok +testb_13 ... ok +testb_14 ... ok +testb_15 ... ok +testb_16 ... ok +testb_17 ... ok +testb_18 ... ok +testb_19 ... ok +testb_20 ... ok +testb_21 ... ok +testb_22 ... ok +testb_23 ... ok +testb_24 ... ok +testb_25 ... ok +testw_1 ... ok +testw_2 ... ok +testw_3 ... ok +testw_4 ... ok +testw_5 ... ok +testw_6 ... ok +testw_7 ... ok +testw_8 ... ok +testw_9 ... ok +testw_10 ... ok +testw_11 ... ok +testw_12 ... ok +testw_13 ... ok +testw_14 ... ok +testw_15 ... ok +testw_16 ... ok +testw_17 ... ok +testw_18 ... ok +testw_19 ... ok +testw_20 ... ok +testw_21 ... ok +testw_22 ... ok +testw_23 ... ok +testw_24 ... ok +testw_25 ... ok +testl_1 ... ok +testl_2 ... ok +testl_3 ... ok +testl_4 ... ok +testl_5 ... ok +testl_6 ... ok +testl_7 ... ok +testl_8 ... ok +testl_9 ... ok +testl_10 ... ok +testl_11 ... ok +testl_12 ... ok +testl_13 ... ok +testl_14 ... ok +testl_15 ... ok +testl_16 ... ok +testl_17 ... ok +testl_18 ... ok +testl_19 ... ok +testl_20 ... ok +testl_21 ... ok +testl_22 ... ok +testl_23 ... ok +testl_24 ... ok +testl_25 ... ok +xaddb_1 ... ok +xaddb_2 ... ok +xaddw_1 ... ok +xaddw_2 ... ok +xaddl_1 ... ok +xaddl_2 ... ok +xchgb_1 ... ok +xchgb_2 ... ok +xchgb_3 ... ok +xchgw_1 ... ok +xchgw_2 ... ok +xchgw_3 ... ok +xchgw_4 ... ok +xchgw_5 ... ok +xchgl_1 ... ok +xchgl_2 ... ok +xchgl_3 ... ok +xchgl_4 ... ok +xchgl_5 ... ok +xorb_1 ... ok +xorb_2 ... ok +xorb_3 ... ok +xorb_4 ... ok +xorb_5 ... ok +xorb_6 ... ok +xorw_1 ... ok +xorw_2 ... ok +xorw_3 ... ok +xorw_4 ... ok +xorw_5 ... ok +xorw_6 ... ok +xorw_7 ... ok +xorl_1 ... ok +xorl_2 ... ok +xorl_3 ... ok +xorl_4 ... ok +xorl_5 ... ok +xorl_6 ... ok +xorl_7 ... ok diff --git a/VEX/head20041019/cachegrind/tests/insn_basic.vgtest b/VEX/head20041019/cachegrind/tests/insn_basic.vgtest new file mode 100644 index 0000000000..a6efda61c1 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_basic.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_basic +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/insn_cmov.stderr.exp b/VEX/head20041019/cachegrind/tests/insn_cmov.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/tests/insn_cmov.stdout.exp b/VEX/head20041019/cachegrind/tests/insn_cmov.stdout.exp new file mode 100644 index 0000000000..31ac17204d --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_cmov.stdout.exp @@ -0,0 +1,384 @@ +cmova_1 ... ok +cmova_2 ... ok +cmova_3 ... ok +cmova_4 ... ok +cmova_5 ... ok +cmova_6 ... ok +cmova_7 ... ok +cmova_8 ... ok +cmovae_1 ... ok +cmovae_2 ... ok +cmovae_3 ... ok +cmovae_4 ... ok +cmovb_1 ... ok +cmovb_2 ... ok +cmovb_3 ... ok +cmovb_4 ... ok +cmovbe_1 ... ok +cmovbe_2 ... ok +cmovbe_3 ... ok +cmovbe_4 ... ok +cmovbe_5 ... ok +cmovbe_6 ... ok +cmovbe_7 ... ok +cmovbe_8 ... ok +cmovc_1 ... ok +cmovc_2 ... ok +cmovc_3 ... ok +cmovc_4 ... ok +cmove_1 ... ok +cmove_2 ... ok +cmove_3 ... ok +cmove_4 ... ok +cmovg_1 ... ok +cmovg_2 ... ok +cmovg_3 ... ok +cmovg_4 ... ok +cmovg_5 ... ok +cmovg_6 ... ok +cmovg_7 ... ok +cmovg_8 ... ok +cmovg_9 ... ok +cmovg_10 ... ok +cmovg_11 ... ok +cmovg_12 ... ok +cmovg_13 ... ok +cmovg_14 ... ok +cmovg_15 ... ok +cmovg_16 ... ok +cmovge_1 ... ok +cmovge_2 ... ok +cmovge_3 ... ok +cmovge_4 ... ok +cmovge_5 ... ok +cmovge_6 ... ok +cmovge_7 ... ok +cmovge_8 ... ok +cmovl_1 ... ok +cmovl_2 ... ok +cmovl_3 ... ok +cmovl_4 ... ok +cmovl_5 ... ok +cmovl_6 ... ok +cmovl_7 ... ok +cmovl_8 ... ok +cmovle_1 ... ok +cmovle_2 ... ok +cmovle_3 ... ok +cmovle_4 ... ok +cmovle_5 ... ok +cmovle_6 ... ok +cmovle_7 ... ok +cmovle_8 ... ok +cmovle_9 ... ok +cmovle_10 ... ok +cmovle_11 ... ok +cmovle_12 ... ok +cmovle_13 ... ok +cmovle_14 ... ok +cmovle_15 ... ok +cmovle_16 ... ok +cmovna_1 ... ok +cmovna_2 ... ok +cmovna_3 ... ok +cmovna_4 ... ok +cmovna_5 ... ok +cmovna_6 ... ok +cmovna_7 ... ok +cmovna_8 ... ok +cmovnae_1 ... ok +cmovnae_2 ... ok +cmovnae_3 ... ok +cmovnae_4 ... ok +cmovnb_1 ... ok +cmovnb_2 ... ok +cmovnb_3 ... ok +cmovnb_4 ... ok +cmovnbe_1 ... ok +cmovnbe_2 ... ok +cmovnbe_3 ... ok +cmovnbe_4 ... ok +cmovnbe_5 ... ok +cmovnbe_6 ... ok +cmovnbe_7 ... ok +cmovnbe_8 ... ok +cmovnc_1 ... ok +cmovnc_2 ... ok +cmovnc_3 ... ok +cmovnc_4 ... ok +cmovne_1 ... ok +cmovne_2 ... ok +cmovne_3 ... ok +cmovne_4 ... ok +cmovng_1 ... ok +cmovng_2 ... ok +cmovng_3 ... ok +cmovng_4 ... ok +cmovng_5 ... ok +cmovng_6 ... ok +cmovng_7 ... ok +cmovng_8 ... ok +cmovng_9 ... ok +cmovng_10 ... ok +cmovng_11 ... ok +cmovng_12 ... ok +cmovng_13 ... ok +cmovng_14 ... ok +cmovng_15 ... ok +cmovng_16 ... ok +cmovnge_1 ... ok +cmovnge_2 ... ok +cmovnge_3 ... ok +cmovnge_4 ... ok +cmovnge_5 ... ok +cmovnge_6 ... ok +cmovnge_7 ... ok +cmovnge_8 ... ok +cmovnl_1 ... ok +cmovnl_2 ... ok +cmovnl_3 ... ok +cmovnl_4 ... ok +cmovnl_5 ... ok +cmovnl_6 ... ok +cmovnl_7 ... ok +cmovnl_8 ... ok +cmovnle_1 ... ok +cmovnle_2 ... ok +cmovnle_3 ... ok +cmovnle_4 ... ok +cmovnle_5 ... ok +cmovnle_6 ... ok +cmovnle_7 ... ok +cmovnle_8 ... ok +cmovnle_9 ... ok +cmovnle_10 ... ok +cmovnle_11 ... ok +cmovnle_12 ... ok +cmovnle_13 ... ok +cmovnle_14 ... ok +cmovnle_15 ... ok +cmovnle_16 ... ok +cmovno_1 ... ok +cmovno_2 ... ok +cmovno_3 ... ok +cmovno_4 ... ok +cmovnp_1 ... ok +cmovnp_2 ... ok +cmovnp_3 ... ok +cmovnp_4 ... ok +cmovns_1 ... ok +cmovns_2 ... ok +cmovns_3 ... ok +cmovns_4 ... ok +cmovnz_1 ... ok +cmovnz_2 ... ok +cmovnz_3 ... ok +cmovnz_4 ... ok +cmovo_1 ... ok +cmovo_2 ... ok +cmovo_3 ... ok +cmovo_4 ... ok +cmovp_1 ... ok +cmovp_2 ... ok +cmovp_3 ... ok +cmovp_4 ... ok +cmovs_1 ... ok +cmovs_2 ... ok +cmovs_3 ... ok +cmovs_4 ... ok +cmovz_1 ... ok +cmovz_2 ... ok +cmovz_3 ... ok +cmovz_4 ... ok +cmova_9 ... ok +cmova_10 ... ok +cmova_11 ... ok +cmova_12 ... ok +cmova_13 ... ok +cmova_14 ... ok +cmova_15 ... ok +cmova_16 ... ok +cmovae_5 ... ok +cmovae_6 ... ok +cmovae_7 ... ok +cmovae_8 ... ok +cmovb_5 ... ok +cmovb_6 ... ok +cmovb_7 ... ok +cmovb_8 ... ok +cmovbe_9 ... ok +cmovbe_10 ... ok +cmovbe_11 ... ok +cmovbe_12 ... ok +cmovbe_13 ... ok +cmovbe_14 ... ok +cmovbe_15 ... ok +cmovbe_16 ... ok +cmovc_5 ... ok +cmovc_6 ... ok +cmovc_7 ... ok +cmovc_8 ... ok +cmove_5 ... ok +cmove_6 ... ok +cmove_7 ... ok +cmove_8 ... ok +cmovg_17 ... ok +cmovg_18 ... ok +cmovg_19 ... ok +cmovg_20 ... ok +cmovg_21 ... ok +cmovg_22 ... ok +cmovg_23 ... ok +cmovg_24 ... ok +cmovg_25 ... ok +cmovg_26 ... ok +cmovg_27 ... ok +cmovg_28 ... ok +cmovg_29 ... ok +cmovg_30 ... ok +cmovg_31 ... ok +cmovg_32 ... ok +cmovge_9 ... ok +cmovge_10 ... ok +cmovge_11 ... ok +cmovge_12 ... ok +cmovge_13 ... ok +cmovge_14 ... ok +cmovge_15 ... ok +cmovge_16 ... ok +cmovl_9 ... ok +cmovl_10 ... ok +cmovl_11 ... ok +cmovl_12 ... ok +cmovl_13 ... ok +cmovl_14 ... ok +cmovl_15 ... ok +cmovl_16 ... ok +cmovle_17 ... ok +cmovle_18 ... ok +cmovle_19 ... ok +cmovle_20 ... ok +cmovle_21 ... ok +cmovle_22 ... ok +cmovle_23 ... ok +cmovle_24 ... ok +cmovle_25 ... ok +cmovle_26 ... ok +cmovle_27 ... ok +cmovle_28 ... ok +cmovle_29 ... ok +cmovle_30 ... ok +cmovle_31 ... ok +cmovle_32 ... ok +cmovna_9 ... ok +cmovna_10 ... ok +cmovna_11 ... ok +cmovna_12 ... ok +cmovna_13 ... ok +cmovna_14 ... ok +cmovna_15 ... ok +cmovna_16 ... ok +cmovnae_5 ... ok +cmovnae_6 ... ok +cmovnae_7 ... ok +cmovnae_8 ... ok +cmovnb_5 ... ok +cmovnb_6 ... ok +cmovnb_7 ... ok +cmovnb_8 ... ok +cmovnbe_9 ... ok +cmovnbe_10 ... ok +cmovnbe_11 ... ok +cmovnbe_12 ... ok +cmovnbe_13 ... ok +cmovnbe_14 ... ok +cmovnbe_15 ... ok +cmovnbe_16 ... ok +cmovnc_5 ... ok +cmovnc_6 ... ok +cmovnc_7 ... ok +cmovnc_8 ... ok +cmovne_5 ... ok +cmovne_6 ... ok +cmovne_7 ... ok +cmovne_8 ... ok +cmovng_17 ... ok +cmovng_18 ... ok +cmovng_19 ... ok +cmovng_20 ... ok +cmovng_21 ... ok +cmovng_22 ... ok +cmovng_23 ... ok +cmovng_24 ... ok +cmovng_25 ... ok +cmovng_26 ... ok +cmovng_27 ... ok +cmovng_28 ... ok +cmovng_29 ... ok +cmovng_30 ... ok +cmovng_31 ... ok +cmovng_32 ... ok +cmovnge_9 ... ok +cmovnge_10 ... ok +cmovnge_11 ... ok +cmovnge_12 ... ok +cmovnge_13 ... ok +cmovnge_14 ... ok +cmovnge_15 ... ok +cmovnge_16 ... ok +cmovnl_9 ... ok +cmovnl_10 ... ok +cmovnl_11 ... ok +cmovnl_12 ... ok +cmovnl_13 ... ok +cmovnl_14 ... ok +cmovnl_15 ... ok +cmovnl_16 ... ok +cmovnle_17 ... ok +cmovnle_18 ... ok +cmovnle_19 ... ok +cmovnle_20 ... ok +cmovnle_21 ... ok +cmovnle_22 ... ok +cmovnle_23 ... ok +cmovnle_24 ... ok +cmovnle_25 ... ok +cmovnle_26 ... ok +cmovnle_27 ... ok +cmovnle_28 ... ok +cmovnle_29 ... ok +cmovnle_30 ... ok +cmovnle_31 ... ok +cmovnle_32 ... ok +cmovno_5 ... ok +cmovno_6 ... ok +cmovno_7 ... ok +cmovno_8 ... ok +cmovnp_5 ... ok +cmovnp_6 ... ok +cmovnp_7 ... ok +cmovnp_8 ... ok +cmovns_5 ... ok +cmovns_6 ... ok +cmovns_7 ... ok +cmovns_8 ... ok +cmovnz_5 ... ok +cmovnz_6 ... ok +cmovnz_7 ... ok +cmovnz_8 ... ok +cmovo_5 ... ok +cmovo_6 ... ok +cmovo_7 ... ok +cmovo_8 ... ok +cmovp_5 ... ok +cmovp_6 ... ok +cmovp_7 ... ok +cmovp_8 ... ok +cmovs_5 ... ok +cmovs_6 ... ok +cmovs_7 ... ok +cmovs_8 ... ok +cmovz_5 ... ok +cmovz_6 ... ok +cmovz_7 ... ok +cmovz_8 ... ok diff --git a/VEX/head20041019/cachegrind/tests/insn_cmov.vgtest b/VEX/head20041019/cachegrind/tests/insn_cmov.vgtest new file mode 100644 index 0000000000..c020cbf474 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_cmov.vgtest @@ -0,0 +1,4 @@ +vgopts: -q +prog: ../../none/tests/insn_cmov +cpu_test: cmov +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/insn_fpu.stderr.exp b/VEX/head20041019/cachegrind/tests/insn_fpu.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/tests/insn_fpu.stdout.exp b/VEX/head20041019/cachegrind/tests/insn_fpu.stdout.exp new file mode 100644 index 0000000000..2dbaa07ce0 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_fpu.stdout.exp @@ -0,0 +1,452 @@ +fabs_1 ... ok +fabs_2 ... ok +fabs_3 ... ok +fabs_4 ... ok +fadds_1 ... ok +fadds_2 ... ok +fadds_3 ... ok +fadds_4 ... ok +faddl_1 ... ok +faddl_2 ... ok +faddl_3 ... ok +faddl_4 ... ok +fadd_1 ... ok +fadd_2 ... ok +fadd_3 ... ok +fadd_4 ... ok +fadd_5 ... ok +fadd_6 ... ok +fadd_7 ... ok +fadd_8 ... ok +fadd_9 ... ok +fadd_10 ... ok +fadd_11 ... ok +fadd_12 ... ok +fadd_13 ... ok +fadd_14 ... ok +fadd_15 ... ok +fadd_16 ... ok +faddp_1 ... ok +faddp_2 ... ok +faddp_3 ... ok +faddp_4 ... ok +faddp_5 ... ok +faddp_6 ... ok +faddp_7 ... ok +faddp_8 ... ok +faddp_9 ... ok +faddp_10 ... ok +faddp_11 ... ok +faddp_12 ... ok +faddp_13 ... ok +faddp_14 ... ok +faddp_15 ... ok +faddp_16 ... ok +fiadds_1 ... ok +fiadds_2 ... ok +fiadds_3 ... ok +fiadds_4 ... ok +fiadds_5 ... ok +fiadds_6 ... ok +fiadds_7 ... ok +fiadds_8 ... ok +fiaddl_1 ... ok +fiaddl_2 ... ok +fiaddl_3 ... ok +fiaddl_4 ... ok +fiaddl_5 ... ok +fiaddl_6 ... ok +fiaddl_7 ... ok +fiaddl_8 ... ok +fcomi_1 ... ok +fcomi_2 ... ok +fcomi_3 ... ok +fcomi_4 ... ok +fcomi_5 ... ok +fcomi_6 ... ok +fcomip_1 ... ok +fcomip_2 ... ok +fcomip_3 ... ok +fcomip_4 ... ok +fcomip_5 ... ok +fcomip_6 ... ok +fucomi_1 ... ok +fucomi_2 ... ok +fucomi_3 ... ok +fucomi_4 ... ok +fucomi_5 ... ok +fucomi_6 ... ok +fucomip_1 ... ok +fucomip_2 ... ok +fucomip_3 ... ok +fucomip_4 ... ok +fucomip_5 ... ok +fucomip_6 ... ok +fchs_1 ... ok +fchs_2 ... ok +fchs_3 ... ok +fchs_4 ... ok +fdivs_1 ... ok +fdivs_2 ... ok +fdivs_3 ... ok +fdivs_4 ... ok +fdivl_1 ... ok +fdivl_2 ... ok +fdivl_3 ... ok +fdivl_4 ... ok +fdiv_1 ... ok +fdiv_2 ... ok +fdiv_3 ... ok +fdiv_4 ... ok +fdiv_5 ... ok +fdiv_6 ... ok +fdiv_7 ... ok +fdiv_8 ... ok +fdiv_9 ... ok +fdiv_10 ... ok +fdiv_11 ... ok +fdiv_12 ... ok +fdiv_13 ... ok +fdiv_14 ... ok +fdiv_15 ... ok +fdiv_16 ... ok +fdivp_1 ... ok +fdivp_2 ... ok +fdivp_3 ... ok +fdivp_4 ... ok +fdivp_5 ... ok +fdivp_6 ... ok +fdivp_7 ... ok +fdivp_8 ... ok +fdivp_9 ... ok +fdivp_10 ... ok +fdivp_11 ... ok +fdivp_12 ... ok +fdivp_13 ... ok +fdivp_14 ... ok +fdivp_15 ... ok +fdivp_16 ... ok +fidivs_1 ... ok +fidivs_2 ... ok +fidivs_3 ... ok +fidivs_4 ... ok +fidivs_5 ... ok +fidivs_6 ... ok +fidivs_7 ... ok +fidivs_8 ... ok +fidivl_1 ... ok +fidivl_2 ... ok +fidivl_3 ... ok +fidivl_4 ... ok +fidivl_5 ... ok +fidivl_6 ... ok +fidivl_7 ... ok +fidivl_8 ... ok +fdivrs_1 ... ok +fdivrs_2 ... ok +fdivrs_3 ... ok +fdivrs_4 ... ok +fdivrl_1 ... ok +fdivrl_2 ... ok +fdivrl_3 ... ok +fdivrl_4 ... ok +fdivr_1 ... ok +fdivr_2 ... ok +fdivr_3 ... ok +fdivr_4 ... ok +fdivr_5 ... ok +fdivr_6 ... ok +fdivr_7 ... ok +fdivr_8 ... ok +fdivr_9 ... ok +fdivr_10 ... ok +fdivr_11 ... ok +fdivr_12 ... ok +fdivr_13 ... ok +fdivr_14 ... ok +fdivr_15 ... ok +fdivr_16 ... ok +fdivrp_1 ... ok +fdivrp_2 ... ok +fdivrp_3 ... ok +fdivrp_4 ... ok +fdivrp_5 ... ok +fdivrp_6 ... ok +fdivrp_7 ... ok +fdivrp_8 ... ok +fdivrp_9 ... ok +fdivrp_10 ... ok +fdivrp_11 ... ok +fdivrp_12 ... ok +fdivrp_13 ... ok +fdivrp_14 ... ok +fdivrp_15 ... ok +fdivrp_16 ... ok +fidivrs_1 ... ok +fidivrs_2 ... ok +fidivrs_3 ... ok +fidivrs_4 ... ok +fidivrs_5 ... ok +fidivrs_6 ... ok +fidivrs_7 ... ok +fidivrs_8 ... ok +fidivrl_1 ... ok +fidivrl_2 ... ok +fidivrl_3 ... ok +fidivrl_4 ... ok +fidivrl_5 ... ok +fidivrl_6 ... ok +fidivrl_7 ... ok +fidivrl_8 ... ok +filds_1 ... ok +filds_2 ... ok +filds_3 ... ok +filds_4 ... ok +fildl_1 ... ok +fildl_2 ... ok +fildl_3 ... ok +fildl_4 ... ok +fildq_1 ... ok +fildq_2 ... ok +fildq_3 ... ok +fildq_4 ... ok +fists_1 ... ok +fists_2 ... ok +fists_3 ... ok +fists_4 ... ok +fists_5 ... ok +fists_6 ... ok +fists_7 ... ok +fists_8 ... ok +fistl_1 ... ok +fistl_2 ... ok +fistl_3 ... ok +fistl_4 ... ok +fistl_5 ... ok +fistl_6 ... ok +fistl_7 ... ok +fistl_8 ... ok +fistps_1 ... ok +fistps_2 ... ok +fistps_3 ... ok +fistps_4 ... ok +fistps_5 ... ok +fistps_6 ... ok +fistps_7 ... ok +fistps_8 ... ok +fistpl_1 ... ok +fistpl_2 ... ok +fistpl_3 ... ok +fistpl_4 ... ok +fistpl_5 ... ok +fistpl_6 ... ok +fistpl_7 ... ok +fistpl_8 ... ok +fistpq_1 ... ok +fistpq_2 ... ok +fistpq_3 ... ok +fistpq_4 ... ok +fistpq_5 ... ok +fistpq_6 ... ok +fistpq_7 ... ok +fistpq_8 ... ok +flds_1 ... ok +flds_2 ... ok +fldl_1 ... ok +fldl_2 ... ok +fld_1 ... ok +fld_2 ... ok +fld_3 ... ok +fld1_1 ... ok +fldl2t_1 ... ok +fldl2e_1 ... ok +fldpi_1 ... ok +fldlg2_1 ... ok +fldln2_1 ... ok +fldz_1 ... ok +fmuls_1 ... ok +fmuls_2 ... ok +fmuls_3 ... ok +fmuls_4 ... ok +fmull_1 ... ok +fmull_2 ... ok +fmull_3 ... ok +fmull_4 ... ok +fmul_1 ... ok +fmul_2 ... ok +fmul_3 ... ok +fmul_4 ... ok +fmul_5 ... ok +fmul_6 ... ok +fmul_7 ... ok +fmul_8 ... ok +fmul_9 ... ok +fmul_10 ... ok +fmul_11 ... ok +fmul_12 ... ok +fmul_13 ... ok +fmul_14 ... ok +fmul_15 ... ok +fmul_16 ... ok +fmulp_1 ... ok +fmulp_2 ... ok +fmulp_3 ... ok +fmulp_4 ... ok +fmulp_5 ... ok +fmulp_6 ... ok +fmulp_7 ... ok +fmulp_8 ... ok +fmulp_9 ... ok +fmulp_10 ... ok +fmulp_11 ... ok +fmulp_12 ... ok +fmulp_13 ... ok +fmulp_14 ... ok +fmulp_15 ... ok +fmulp_16 ... ok +fimuls_1 ... ok +fimuls_2 ... ok +fimuls_3 ... ok +fimuls_4 ... ok +fimuls_5 ... ok +fimuls_6 ... ok +fimuls_7 ... ok +fimuls_8 ... ok +fimull_1 ... ok +fimull_2 ... ok +fimull_3 ... ok +fimull_4 ... ok +fimull_5 ... ok +fimull_6 ... ok +fimull_7 ... ok +fimull_8 ... ok +frndint_1 ... ok +frndint_2 ... ok +frndint_3 ... ok +frndint_4 ... ok +frndint_5 ... ok +frndint_6 ... ok +frndint_7 ... ok +frndint_8 ... ok +frndint_9 ... ok +frndint_10 ... ok +frndint_11 ... ok +frndint_12 ... ok +frndint_13 ... ok +frndint_14 ... ok +frndint_15 ... ok +frndint_16 ... ok +fsubs_1 ... ok +fsubs_2 ... ok +fsubs_3 ... ok +fsubs_4 ... ok +fsubl_1 ... ok +fsubl_2 ... ok +fsubl_3 ... ok +fsubl_4 ... ok +fsub_1 ... ok +fsub_2 ... ok +fsub_3 ... ok +fsub_4 ... ok +fsub_5 ... ok +fsub_6 ... ok +fsub_7 ... ok +fsub_8 ... ok +fsub_9 ... ok +fsub_10 ... ok +fsub_11 ... ok +fsub_12 ... ok +fsub_13 ... ok +fsub_14 ... ok +fsub_15 ... ok +fsub_16 ... ok +fsubp_1 ... ok +fsubp_2 ... ok +fsubp_3 ... ok +fsubp_4 ... ok +fsubp_5 ... ok +fsubp_6 ... ok +fsubp_7 ... ok +fsubp_8 ... ok +fsubp_9 ... ok +fsubp_10 ... ok +fsubp_11 ... ok +fsubp_12 ... ok +fsubp_13 ... ok +fsubp_14 ... ok +fsubp_15 ... ok +fsubp_16 ... ok +fisubs_1 ... ok +fisubs_2 ... ok +fisubs_3 ... ok +fisubs_4 ... ok +fisubs_5 ... ok +fisubs_6 ... ok +fisubs_7 ... ok +fisubs_8 ... ok +fisubl_1 ... ok +fisubl_2 ... ok +fisubl_3 ... ok +fisubl_4 ... ok +fisubl_5 ... ok +fisubl_6 ... ok +fisubl_7 ... ok +fisubl_8 ... ok +fsubrs_1 ... ok +fsubrs_2 ... ok +fsubrs_3 ... ok +fsubrs_4 ... ok +fsubrl_1 ... ok +fsubrl_2 ... ok +fsubrl_3 ... ok +fsubrl_4 ... ok +fsubr_1 ... ok +fsubr_2 ... ok +fsubr_3 ... ok +fsubr_4 ... ok +fsubr_5 ... ok +fsubr_6 ... ok +fsubr_7 ... ok +fsubr_8 ... ok +fsubr_9 ... ok +fsubr_10 ... ok +fsubr_11 ... ok +fsubr_12 ... ok +fsubr_13 ... ok +fsubr_14 ... ok +fsubr_15 ... ok +fsubr_16 ... ok +fsubrp_1 ... ok +fsubrp_2 ... ok +fsubrp_3 ... ok +fsubrp_4 ... ok +fsubrp_5 ... ok +fsubrp_6 ... ok +fsubrp_7 ... ok +fsubrp_8 ... ok +fsubrp_9 ... ok +fsubrp_10 ... ok +fsubrp_11 ... ok +fsubrp_12 ... ok +fsubrp_13 ... ok +fsubrp_14 ... ok +fsubrp_15 ... ok +fsubrp_16 ... ok +fisubrs_1 ... ok +fisubrs_2 ... ok +fisubrs_3 ... ok +fisubrs_4 ... ok +fisubrs_5 ... ok +fisubrs_6 ... ok +fisubrs_7 ... ok +fisubrs_8 ... ok +fisubrl_1 ... ok +fisubrl_2 ... ok +fisubrl_3 ... ok +fisubrl_4 ... ok +fisubrl_5 ... ok +fisubrl_6 ... ok +fisubrl_7 ... ok +fisubrl_8 ... ok +fxch_1 ... ok +fxch_2 ... ok diff --git a/VEX/head20041019/cachegrind/tests/insn_fpu.vgtest b/VEX/head20041019/cachegrind/tests/insn_fpu.vgtest new file mode 100644 index 0000000000..eb7c9ac4fb --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_fpu.vgtest @@ -0,0 +1,4 @@ +vgopts: -q +prog: ../../none/tests/insn_fpu +cpu_test: fpu +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/insn_mmx.stderr.exp b/VEX/head20041019/cachegrind/tests/insn_mmx.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/tests/insn_mmx.stdout.exp b/VEX/head20041019/cachegrind/tests/insn_mmx.stdout.exp new file mode 100644 index 0000000000..95cbae1603 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_mmx.stdout.exp @@ -0,0 +1,103 @@ +movd_1 ... ok +movd_2 ... ok +movd_3 ... ok +movd_4 ... ok +movq_1 ... ok +movq_2 ... ok +movq_3 ... ok +packssdw_1 ... ok +packssdw_2 ... ok +packsswb_1 ... ok +packsswb_2 ... ok +packuswb_1 ... ok +packuswb_2 ... ok +paddb_1 ... ok +paddb_2 ... ok +paddd_1 ... ok +paddd_2 ... ok +paddsb_1 ... ok +paddsb_2 ... ok +paddsw_1 ... ok +paddsw_2 ... ok +paddusb_1 ... ok +paddusb_2 ... ok +paddusw_1 ... ok +paddusw_2 ... ok +paddw_1 ... ok +paddw_2 ... ok +pand_1 ... ok +pand_2 ... ok +pandn_1 ... ok +pandn_2 ... ok +pcmpeqb_1 ... ok +pcmpeqb_2 ... ok +pcmpeqd_1 ... ok +pcmpeqd_2 ... ok +pcmpeqw_1 ... ok +pcmpeqw_2 ... ok +pcmpgtb_1 ... ok +pcmpgtb_2 ... ok +pcmpgtd_1 ... ok +pcmpgtd_2 ... ok +pcmpgtw_1 ... ok +pcmpgtw_2 ... ok +pmaddwd_1 ... ok +pmaddwd_2 ... ok +pmulhw_1 ... ok +pmulhw_2 ... ok +pmullw_1 ... ok +pmullw_2 ... ok +por_1 ... ok +por_2 ... ok +pslld_1 ... ok +pslld_2 ... ok +pslld_3 ... ok +psllq_1 ... ok +psllq_2 ... ok +psllq_3 ... ok +psllw_1 ... ok +psllw_2 ... ok +psllw_3 ... ok +psrad_1 ... ok +psrad_2 ... ok +psrad_3 ... ok +psraw_1 ... ok +psraw_2 ... ok +psraw_3 ... ok +psrld_1 ... ok +psrld_2 ... ok +psrld_3 ... ok +psrlq_1 ... ok +psrlq_2 ... ok +psrlq_3 ... ok +psrlw_1 ... ok +psrlw_2 ... ok +psrlw_3 ... ok +psubb_1 ... ok +psubb_2 ... ok +psubd_1 ... ok +psubd_2 ... ok +psubsb_1 ... ok +psubsb_2 ... ok +psubsw_1 ... ok +psubsw_2 ... ok +psubusb_1 ... ok +psubusb_2 ... ok +psubusw_1 ... ok +psubusw_2 ... ok +psubw_1 ... ok +psubw_2 ... ok +punpckhbw_1 ... ok +punpckhbw_2 ... ok +punpckhdq_1 ... ok +punpckhdq_2 ... ok +punpckhwd_1 ... ok +punpckhwd_2 ... ok +punpcklbw_1 ... ok +punpcklbw_2 ... ok +punpckldq_1 ... ok +punpckldq_2 ... ok +punpcklwd_1 ... ok +punpcklwd_2 ... ok +pxor_1 ... ok +pxor_2 ... ok diff --git a/VEX/head20041019/cachegrind/tests/insn_mmx.vgtest b/VEX/head20041019/cachegrind/tests/insn_mmx.vgtest new file mode 100644 index 0000000000..504558cf63 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_mmx.vgtest @@ -0,0 +1,4 @@ +vgopts: -q +prog: ../../none/tests/insn_mmx +cpu_test: mmx +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/insn_mmxext.stderr.exp b/VEX/head20041019/cachegrind/tests/insn_mmxext.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/tests/insn_mmxext.stdout.exp b/VEX/head20041019/cachegrind/tests/insn_mmxext.stdout.exp new file mode 100644 index 0000000000..23b2e55abd --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_mmxext.stdout.exp @@ -0,0 +1,29 @@ +movntq_1 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufw_1 ... ok +pshufw_2 ... ok +sfence_1 ... ok diff --git a/VEX/head20041019/cachegrind/tests/insn_mmxext.vgtest b/VEX/head20041019/cachegrind/tests/insn_mmxext.vgtest new file mode 100644 index 0000000000..30a705f33a --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_mmxext.vgtest @@ -0,0 +1,4 @@ +vgopts: -q +prog: ../../none/tests/insn_mmxext +cpu_test: mmxext +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/insn_sse.stderr.exp b/VEX/head20041019/cachegrind/tests/insn_sse.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/tests/insn_sse.stdout.exp b/VEX/head20041019/cachegrind/tests/insn_sse.stdout.exp new file mode 100644 index 0000000000..f15bd81f0b --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_sse.stdout.exp @@ -0,0 +1,142 @@ +addps_1 ... ok +addps_2 ... ok +addss_1 ... ok +addss_2 ... ok +andnps_1 ... ok +andnps_2 ... ok +andps_1 ... ok +andps_2 ... ok +cmpeqps_1 ... ok +cmpeqps_2 ... ok +cmpeqss_1 ... ok +cmpeqss_2 ... ok +cmpleps_1 ... ok +cmpleps_2 ... ok +cmpless_1 ... ok +cmpless_2 ... ok +cmpltps_1 ... ok +cmpltps_2 ... ok +cmpltss_1 ... ok +cmpltss_2 ... ok +cmpneqps_1 ... ok +cmpneqps_2 ... ok +cmpneqss_1 ... ok +cmpneqss_2 ... ok +cmpnleps_1 ... ok +cmpnleps_2 ... ok +cmpnless_1 ... ok +cmpnless_2 ... ok +cmpnltps_1 ... ok +cmpnltps_2 ... ok +cmpnltss_1 ... ok +cmpnltss_2 ... ok +comiss_1 ... ok +comiss_2 ... ok +comiss_3 ... ok +comiss_4 ... ok +comiss_5 ... ok +comiss_6 ... ok +cvtpi2ps_1 ... ok +cvtpi2ps_2 ... ok +cvtps2pi_1 ... ok +cvtps2pi_2 ... ok +cvtsi2ss_1 ... ok +cvtsi2ss_2 ... ok +cvtss2si_1 ... ok +cvtss2si_2 ... ok +cvttps2pi_1 ... ok +cvttps2pi_2 ... ok +cvttss2si_1 ... ok +cvttss2si_2 ... ok +divps_1 ... ok +divps_2 ... ok +divss_1 ... ok +divss_2 ... ok +maxps_1 ... ok +maxps_2 ... ok +maxss_1 ... ok +maxss_2 ... ok +minps_1 ... ok +minps_2 ... ok +minss_1 ... ok +minss_2 ... ok +movaps_1 ... ok +movaps_2 ... ok +movhlps_1 ... ok +movhps_1 ... ok +movhps_2 ... ok +movlhps_1 ... ok +movlps_1 ... ok +movlps_2 ... ok +movmskps_1 ... ok +movntps_1 ... ok +movntq_1 ... ok +movss_1 ... ok +movss_2 ... ok +movss_3 ... ok +movups_1 ... ok +movups_2 ... ok +mulps_1 ... ok +mulps_2 ... ok +mulss_1 ... ok +mulss_2 ... ok +orps_1 ... ok +orps_2 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufw_1 ... ok +pshufw_2 ... ok +rcpps_1 ... ok +rcpps_2 ... ok +rcpss_1 ... ok +rcpss_2 ... ok +rsqrtps_1 ... ok +rsqrtps_2 ... ok +rsqrtss_1 ... ok +rsqrtss_2 ... ok +sfence_1 ... ok +shufps_1 ... ok +shufps_2 ... ok +sqrtps_1 ... ok +sqrtps_2 ... ok +sqrtss_1 ... ok +sqrtss_2 ... ok +subps_1 ... ok +subps_2 ... ok +subss_1 ... ok +subss_2 ... ok +ucomiss_1 ... ok +ucomiss_2 ... ok +ucomiss_3 ... ok +ucomiss_4 ... ok +ucomiss_5 ... ok +ucomiss_6 ... ok +unpckhps_1 ... ok +unpckhps_2 ... ok +unpcklps_1 ... ok +unpcklps_2 ... ok +xorps_1 ... ok +xorps_2 ... ok diff --git a/VEX/head20041019/cachegrind/tests/insn_sse.vgtest b/VEX/head20041019/cachegrind/tests/insn_sse.vgtest new file mode 100644 index 0000000000..8dded750a7 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_sse.vgtest @@ -0,0 +1,4 @@ +vgopts: -q +prog: ../../none/tests/insn_sse +cpu_test: sse +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/insn_sse2.stderr.exp b/VEX/head20041019/cachegrind/tests/insn_sse2.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/tests/insn_sse2.stdout.exp b/VEX/head20041019/cachegrind/tests/insn_sse2.stdout.exp new file mode 100644 index 0000000000..9c24f72621 --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_sse2.stdout.exp @@ -0,0 +1,294 @@ +addpd_1 ... ok +addpd_2 ... ok +addsd_1 ... ok +addsd_2 ... ok +andpd_1 ... ok +andpd_2 ... ok +andnpd_1 ... ok +andnpd_2 ... ok +cmpeqpd_1 ... ok +cmpeqpd_2 ... ok +cmpltpd_1 ... ok +cmpltpd_2 ... ok +cmplepd_1 ... ok +cmplepd_2 ... ok +cmpneqpd_1 ... ok +cmpneqpd_2 ... ok +cmpnltpd_1 ... ok +cmpnltpd_2 ... ok +cmpnlepd_1 ... ok +cmpnlepd_2 ... ok +cmpeqsd_1 ... ok +cmpeqsd_2 ... ok +cmpltsd_1 ... ok +cmpltsd_2 ... ok +cmplesd_1 ... ok +cmplesd_2 ... ok +cmpneqsd_1 ... ok +cmpneqsd_2 ... ok +cmpnltsd_1 ... ok +cmpnltsd_2 ... ok +cmpnlesd_1 ... ok +cmpnlesd_2 ... ok +comisd_1 ... ok +comisd_2 ... ok +comisd_3 ... ok +comisd_4 ... ok +comisd_5 ... ok +comisd_6 ... ok +cvtdq2pd_1 ... ok +cvtdq2pd_2 ... ok +cvtdq2ps_1 ... ok +cvtdq2ps_2 ... ok +cvtpd2dq_1 ... ok +cvtpd2dq_2 ... ok +cvtpd2pi_1 ... ok +cvtpd2pi_2 ... ok +cvtpd2ps_1 ... ok +cvtpd2ps_2 ... ok +cvtpi2pd_1 ... ok +cvtpi2pd_2 ... ok +cvtps2dq_1 ... ok +cvtps2dq_2 ... ok +cvtps2pd_1 ... ok +cvtps2pd_2 ... ok +cvtsd2si_1 ... ok +cvtsd2si_2 ... ok +cvtsd2ss_1 ... ok +cvtsd2ss_2 ... ok +cvtsi2sd_1 ... ok +cvtsi2sd_2 ... ok +cvtss2sd_1 ... ok +cvtss2sd_2 ... ok +cvttpd2pi_1 ... ok +cvttpd2pi_2 ... ok +cvttpd2dq_1 ... ok +cvttpd2dq_2 ... ok +cvttps2dq_1 ... ok +cvttps2dq_2 ... ok +cvttsd2si_1 ... ok +cvttsd2si_2 ... ok +divpd_1 ... ok +divpd_2 ... ok +divsd_1 ... ok +divsd_2 ... ok +lfence_1 ... ok +maxpd_1 ... ok +maxpd_2 ... ok +maxsd_1 ... ok +maxsd_2 ... ok +mfence_1 ... ok +minpd_1 ... ok +minpd_2 ... ok +minsd_1 ... ok +minsd_2 ... ok +movapd_1 ... ok +movapd_2 ... ok +movd_1 ... ok +movd_2 ... ok +movd_3 ... ok +movd_4 ... ok +movdqa_1 ... ok +movdqa_2 ... ok +movdqa_3 ... ok +movdqu_1 ... ok +movdqu_2 ... ok +movdqu_3 ... ok +movdq2q_1 ... ok +movhpd_1 ... ok +movhpd_2 ... ok +movlpd_1 ... ok +movlpd_2 ... ok +movmskpd_1 ... ok +movntdq_1 ... ok +movnti_1 ... ok +movntpd_1 ... ok +movq2dq_1 ... ok +movsd_1 ... ok +movsd_2 ... ok +movsd_3 ... ok +movupd_1 ... ok +movupd_2 ... ok +mulpd_1 ... ok +mulpd_2 ... ok +mulsd_1 ... ok +mulsd_2 ... ok +orpd_1 ... ok +orpd_2 ... ok +packssdw_1 ... ok +packssdw_2 ... ok +packsswb_1 ... ok +packsswb_2 ... ok +packuswb_1 ... ok +packuswb_2 ... ok +paddb_1 ... ok +paddb_2 ... ok +paddd_1 ... ok +paddd_2 ... ok +paddq_1 ... ok +paddq_2 ... ok +paddq_3 ... ok +paddq_4 ... ok +paddsb_1 ... ok +paddsb_2 ... ok +paddsw_1 ... ok +paddsw_2 ... ok +paddusb_1 ... ok +paddusb_2 ... ok +paddusw_1 ... ok +paddusw_2 ... ok +paddw_1 ... ok +paddw_2 ... ok +pand_1 ... ok +pand_2 ... ok +pandn_1 ... ok +pandn_2 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pcmpeqb_1 ... ok +pcmpeqb_2 ... ok +pcmpeqd_1 ... ok +pcmpeqd_2 ... ok +pcmpeqw_1 ... ok +pcmpeqw_2 ... ok +pcmpgtb_1 ... ok +pcmpgtb_2 ... ok +pcmpgtd_1 ... ok +pcmpgtd_2 ... ok +pcmpgtw_1 ... ok +pcmpgtw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pextrw_5 ... ok +pextrw_6 ... ok +pextrw_7 ... ok +pextrw_8 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pinsrw_5 ... ok +pinsrw_6 ... ok +pinsrw_7 ... ok +pinsrw_8 ... ok +pmaddwd_1 ... ok +pmaddwd_2 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +pmulhw_1 ... ok +pmulhw_2 ... ok +pmullw_1 ... ok +pmullw_2 ... ok +pmuludq_1 ... ok +pmuludq_2 ... ok +pmuludq_3 ... ok +pmuludq_4 ... ok +por_1 ... ok +por_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufd_1 ... ok +pshufd_2 ... ok +pshufhw_1 ... ok +pshufhw_2 ... ok +pshuflw_1 ... ok +pshuflw_2 ... ok +pslld_1 ... ok +pslld_2 ... ok +pslld_3 ... ok +pslldq_1 ... ok +pslldq_2 ... ok +psllq_1 ... ok +psllq_2 ... ok +psllq_3 ... ok +psllw_1 ... ok +psllw_2 ... ok +psllw_3 ... ok +psrad_1 ... ok +psrad_2 ... ok +psrad_3 ... ok +psraw_1 ... ok +psraw_2 ... ok +psraw_3 ... ok +psrld_1 ... ok +psrld_2 ... ok +psrld_3 ... ok +psrldq_1 ... ok +psrldq_2 ... ok +psrlq_1 ... ok +psrlq_2 ... ok +psrlq_3 ... ok +psrlw_1 ... ok +psrlw_2 ... ok +psrlw_3 ... ok +psubb_1 ... ok +psubb_2 ... ok +psubd_1 ... ok +psubd_2 ... ok +psubq_1 ... ok +psubq_2 ... ok +psubq_3 ... ok +psubq_4 ... ok +psubsb_1 ... ok +psubsb_2 ... ok +psubsw_1 ... ok +psubsw_2 ... ok +psubusb_1 ... ok +psubusb_2 ... ok +psubusw_1 ... ok +psubusw_2 ... ok +psubw_1 ... ok +psubw_2 ... ok +punpckhbw_1 ... ok +punpckhbw_2 ... ok +punpckhdq_1 ... ok +punpckhdq_2 ... ok +punpckhqdq_1 ... ok +punpckhqdq_2 ... ok +punpckhwd_1 ... ok +punpckhwd_2 ... ok +punpcklbw_1 ... ok +punpcklbw_2 ... ok +punpckldq_1 ... ok +punpckldq_2 ... ok +punpcklqdq_1 ... ok +punpcklqdq_2 ... ok +punpcklwd_1 ... ok +punpcklwd_2 ... ok +pxor_1 ... ok +pxor_2 ... ok +shufpd_1 ... ok +shufpd_2 ... ok +sqrtpd_1 ... ok +sqrtpd_2 ... ok +sqrtsd_1 ... ok +sqrtsd_2 ... ok +subpd_1 ... ok +subpd_2 ... ok +subsd_1 ... ok +subsd_2 ... ok +ucomisd_1 ... ok +ucomisd_2 ... ok +ucomisd_3 ... ok +ucomisd_4 ... ok +ucomisd_5 ... ok +ucomisd_6 ... ok +unpckhpd_1 ... ok +unpckhpd_2 ... ok +unpcklpd_1 ... ok +unpcklpd_2 ... ok +xorpd_1 ... ok +xorpd_2 ... ok diff --git a/VEX/head20041019/cachegrind/tests/insn_sse2.vgtest b/VEX/head20041019/cachegrind/tests/insn_sse2.vgtest new file mode 100644 index 0000000000..594a3fd2ab --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/insn_sse2.vgtest @@ -0,0 +1,4 @@ +vgopts: -q +prog: ../../none/tests/insn_sse2 +cpu_test: sse2 +delete: cachegrind.out.* diff --git a/VEX/head20041019/cachegrind/tests/myprint.c b/VEX/head20041019/cachegrind/tests/myprint.c new file mode 100644 index 0000000000..e22ae87a2f --- /dev/null +++ b/VEX/head20041019/cachegrind/tests/myprint.c @@ -0,0 +1,6 @@ +#include + +void myprint(void) +{ + puts("This is myprint!"); +} diff --git a/VEX/head20041019/cachegrind/x86/.cvsignore b/VEX/head20041019/cachegrind/x86/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/VEX/head20041019/cachegrind/x86/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/VEX/head20041019/cachegrind/x86/CVS/Entries b/VEX/head20041019/cachegrind/x86/CVS/Entries new file mode 100644 index 0000000000..1cdf18446c --- /dev/null +++ b/VEX/head20041019/cachegrind/x86/CVS/Entries @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Sat Sep 11 16:45:27 2004// +/Makefile.am/1.2/Sat Sep 11 18:27:43 2004// +/cg_arch.c/1.2/Wed Oct 13 11:30:14 2004// +D diff --git a/VEX/head20041019/cachegrind/x86/CVS/Repository b/VEX/head20041019/cachegrind/x86/CVS/Repository new file mode 100644 index 0000000000..0bd1c8fb4d --- /dev/null +++ b/VEX/head20041019/cachegrind/x86/CVS/Repository @@ -0,0 +1 @@ +valgrind/cachegrind/x86 diff --git a/VEX/head20041019/cachegrind/x86/CVS/Root b/VEX/head20041019/cachegrind/x86/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/cachegrind/x86/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/cachegrind/x86/CVS/Template b/VEX/head20041019/cachegrind/x86/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/cachegrind/x86/Makefile.am b/VEX/head20041019/cachegrind/x86/Makefile.am new file mode 100644 index 0000000000..5c07f39612 --- /dev/null +++ b/VEX/head20041019/cachegrind/x86/Makefile.am @@ -0,0 +1,8 @@ +include $(top_srcdir)/Makefile.tool-flags.am + +AM_CPPFLAGS += -I$(top_srcdir)/cachegrind + +noinst_LIBRARIES = libcgarch.a + +libcgarch_a_SOURCES = cg_arch.c + diff --git a/VEX/head20041019/cachegrind/x86/cg_arch.c b/VEX/head20041019/cachegrind/x86/cg_arch.c new file mode 100644 index 0000000000..29cc28380f --- /dev/null +++ b/VEX/head20041019/cachegrind/x86/cg_arch.c @@ -0,0 +1,360 @@ + +/*--------------------------------------------------------------------*/ +/*--- Arch-specific definitions. x86/cg_arch.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Cachegrind, a Valgrind tool for cache + profiling programs. + + Copyright (C) 2002-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "tool.h" +#include "cg_arch.h" + +// All CPUID info taken from sandpile.org/a32/cpuid.htm */ +// Probably only works for Intel and AMD chips, and probably only for some of +// them. + +static void micro_ops_warn(Int actual_size, Int used_size, Int line_size) +{ + VG_(message)(Vg_DebugMsg, + "warning: Pentium with %d K micro-op instruction trace cache", + actual_size); + VG_(message)(Vg_DebugMsg, + " Simulating a %d KB cache with %d B lines", + used_size, line_size); +} + +/* Intel method is truly wretched. We have to do an insane indexing into an + * array of pre-defined configurations for various parts of the memory + * hierarchy. + */ +static +Int Intel_cache_info(Int level, cache_t* I1c, cache_t* D1c, cache_t* L2c) +{ + UChar info[16]; + Int i, trials; + Bool L2_found = False; + + if (level < 2) { + VG_(message)(Vg_DebugMsg, + "warning: CPUID level < 2 for Intel processor (%d)", + level); + return -1; + } + + VG_(cpuid)(2, (Int*)&info[0], (Int*)&info[4], + (Int*)&info[8], (Int*)&info[12]); + trials = info[0] - 1; /* AL register - bits 0..7 of %eax */ + info[0] = 0x0; /* reset AL */ + + if (0 != trials) { + VG_(message)(Vg_DebugMsg, + "warning: non-zero CPUID trials for Intel processor (%d)", + trials); + return -1; + } + + for (i = 0; i < 16; i++) { + + switch (info[i]) { + + case 0x0: /* ignore zeros */ + break; + + /* TLB info, ignore */ + case 0x01: case 0x02: case 0x03: case 0x04: + case 0x50: case 0x51: case 0x52: case 0x5b: case 0x5c: case 0x5d: + case 0xb0: case 0xb3: + break; + + case 0x06: *I1c = (cache_t) { 8, 4, 32 }; break; + case 0x08: *I1c = (cache_t) { 16, 4, 32 }; break; + case 0x30: *I1c = (cache_t) { 32, 8, 64 }; break; + + case 0x0a: *D1c = (cache_t) { 8, 2, 32 }; break; + case 0x0c: *D1c = (cache_t) { 16, 4, 32 }; break; + case 0x2c: *D1c = (cache_t) { 32, 8, 64 }; break; + + /* IA-64 info -- panic! */ + case 0x10: case 0x15: case 0x1a: + case 0x88: case 0x89: case 0x8a: case 0x8d: + case 0x90: case 0x96: case 0x9b: + VG_(skin_panic)("IA-64 cache detected?!"); + + case 0x22: case 0x23: case 0x25: case 0x29: + VG_(message)(Vg_DebugMsg, + "warning: L3 cache detected but ignored\n"); + break; + + /* These are sectored, whatever that means */ + case 0x39: *L2c = (cache_t) { 128, 4, 64 }; L2_found = True; break; + case 0x3c: *L2c = (cache_t) { 256, 4, 64 }; L2_found = True; break; + + /* If a P6 core, this means "no L2 cache". + If a P4 core, this means "no L3 cache". + We don't know what core it is, so don't issue a warning. To detect + a missing L2 cache, we use 'L2_found'. */ + case 0x40: + break; + + case 0x41: *L2c = (cache_t) { 128, 4, 32 }; L2_found = True; break; + case 0x42: *L2c = (cache_t) { 256, 4, 32 }; L2_found = True; break; + case 0x43: *L2c = (cache_t) { 512, 4, 32 }; L2_found = True; break; + case 0x44: *L2c = (cache_t) { 1024, 4, 32 }; L2_found = True; break; + case 0x45: *L2c = (cache_t) { 2048, 4, 32 }; L2_found = True; break; + + /* These are sectored, whatever that means */ + case 0x60: *D1c = (cache_t) { 16, 8, 64 }; break; /* sectored */ + case 0x66: *D1c = (cache_t) { 8, 4, 64 }; break; /* sectored */ + case 0x67: *D1c = (cache_t) { 16, 4, 64 }; break; /* sectored */ + case 0x68: *D1c = (cache_t) { 32, 4, 64 }; break; /* sectored */ + + /* HACK ALERT: Instruction trace cache -- capacity is micro-ops based. + * conversion to byte size is a total guess; treat the 12K and 16K + * cases the same since the cache byte size must be a power of two for + * everything to work!. Also guessing 32 bytes for the line size... + */ + case 0x70: /* 12K micro-ops, 8-way */ + *I1c = (cache_t) { 16, 8, 32 }; + micro_ops_warn(12, 16, 32); + break; + case 0x71: /* 16K micro-ops, 8-way */ + *I1c = (cache_t) { 16, 8, 32 }; + micro_ops_warn(16, 16, 32); + break; + case 0x72: /* 32K micro-ops, 8-way */ + *I1c = (cache_t) { 32, 8, 32 }; + micro_ops_warn(32, 32, 32); + break; + + /* These are sectored, whatever that means */ + case 0x79: *L2c = (cache_t) { 128, 8, 64 }; L2_found = True; break; + case 0x7a: *L2c = (cache_t) { 256, 8, 64 }; L2_found = True; break; + case 0x7b: *L2c = (cache_t) { 512, 8, 64 }; L2_found = True; break; + case 0x7c: *L2c = (cache_t) { 1024, 8, 64 }; L2_found = True; break; + case 0x7e: *L2c = (cache_t) { 256, 8, 128 }; L2_found = True; break; + + case 0x81: *L2c = (cache_t) { 128, 8, 32 }; L2_found = True; break; + case 0x82: *L2c = (cache_t) { 256, 8, 32 }; L2_found = True; break; + case 0x83: *L2c = (cache_t) { 512, 8, 32 }; L2_found = True; break; + case 0x84: *L2c = (cache_t) { 1024, 8, 32 }; L2_found = True; break; + case 0x85: *L2c = (cache_t) { 2048, 8, 32 }; L2_found = True; break; + case 0x86: *L2c = (cache_t) { 512, 4, 64 }; L2_found = True; break; + case 0x87: *L2c = (cache_t) { 1024, 8, 64 }; L2_found = True; break; + + default: + VG_(message)(Vg_DebugMsg, + "warning: Unknown Intel cache config value " + "(0x%x), ignoring", info[i]); + break; + } + } + + if (!L2_found) + VG_(message)(Vg_DebugMsg, + "warning: L2 cache not installed, ignore L2 results."); + + return 0; +} + +/* AMD method is straightforward, just extract appropriate bits from the + * result registers. + * + * Bits, for D1 and I1: + * 31..24 data L1 cache size in KBs + * 23..16 data L1 cache associativity (FFh=full) + * 15.. 8 data L1 cache lines per tag + * 7.. 0 data L1 cache line size in bytes + * + * Bits, for L2: + * 31..16 unified L2 cache size in KBs + * 15..12 unified L2 cache associativity (0=off, FFh=full) + * 11.. 8 unified L2 cache lines per tag + * 7.. 0 unified L2 cache line size in bytes + * + * #3 The AMD K7 processor's L2 cache must be configured prior to relying + * upon this information. (Whatever that means -- njn) + * + * Also, according to Cyrille Chepelov, Duron stepping A0 processors (model + * 0x630) have a bug and misreport their L2 size as 1KB (it's really 64KB), + * so we detect that. + * + * Returns 0 on success, non-zero on failure. + */ +static +Int AMD_cache_info(cache_t* I1c, cache_t* D1c, cache_t* L2c) +{ + UInt ext_level; + UInt dummy, model; + UInt I1i, D1i, L2i; + + VG_(cpuid)(0x80000000, &ext_level, &dummy, &dummy, &dummy); + + if (0 == (ext_level & 0x80000000) || ext_level < 0x80000006) { + VG_(message)(Vg_UserMsg, + "warning: ext_level < 0x80000006 for AMD processor (0x%x)", + ext_level); + return -1; + } + + VG_(cpuid)(0x80000005, &dummy, &dummy, &D1i, &I1i); + VG_(cpuid)(0x80000006, &dummy, &dummy, &L2i, &dummy); + + VG_(cpuid)(0x1, &model, &dummy, &dummy, &dummy); + + /* Check for Duron bug */ + if (model == 0x630) { + VG_(message)(Vg_UserMsg, + "Buggy Duron stepping A0. Assuming L2 size=65536 bytes"); + L2i = (64 << 16) | (L2i & 0xffff); + } + + D1c->size = (D1i >> 24) & 0xff; + D1c->assoc = (D1i >> 16) & 0xff; + D1c->line_size = (D1i >> 0) & 0xff; + + I1c->size = (I1i >> 24) & 0xff; + I1c->assoc = (I1i >> 16) & 0xff; + I1c->line_size = (I1i >> 0) & 0xff; + + L2c->size = (L2i >> 16) & 0xffff; /* Nb: different bits used for L2 */ + L2c->assoc = (L2i >> 12) & 0xf; + L2c->line_size = (L2i >> 0) & 0xff; + + return 0; +} + +static jmp_buf cpuid_jmpbuf; + +static +void cpuid_SIGILL_handler(int signum) +{ + __builtin_longjmp(cpuid_jmpbuf, 1); +} + +static +Int get_caches_from_CPUID(cache_t* I1c, cache_t* D1c, cache_t* L2c) +{ + Int level, res, ret; + Char vendor_id[13]; + vki_ksigaction sigill_new, sigill_saved; + + /* Install own SIGILL handler */ + sigill_new.ksa_handler = cpuid_SIGILL_handler; + sigill_new.ksa_flags = 0; + sigill_new.ksa_restorer = NULL; + res = VG_(ksigemptyset)( &sigill_new.ksa_mask ); + sk_assert(res == 0); + + res = VG_(ksigaction)( VKI_SIGILL, &sigill_new, &sigill_saved ); + sk_assert(res == 0); + + /* Trap for illegal instruction, in case it's a really old processor that + * doesn't support CPUID. */ + if (__builtin_setjmp(cpuid_jmpbuf) == 0) { + VG_(cpuid)(0, &level, (int*)&vendor_id[0], + (int*)&vendor_id[8], (int*)&vendor_id[4]); + vendor_id[12] = '\0'; + + /* Restore old SIGILL handler */ + res = VG_(ksigaction)( VKI_SIGILL, &sigill_saved, NULL ); + sk_assert(res == 0); + + } else { + VG_(message)(Vg_DebugMsg, "CPUID instruction not supported"); + + /* Restore old SIGILL handler */ + res = VG_(ksigaction)( VKI_SIGILL, &sigill_saved, NULL ); + sk_assert(res == 0); + return -1; + } + + if (0 == level) { + VG_(message)(Vg_DebugMsg, "CPUID level is 0, early Pentium?\n"); + return -1; + } + + /* Only handling Intel and AMD chips... no Cyrix, Transmeta, etc */ + if (0 == VG_(strcmp)(vendor_id, "GenuineIntel")) { + ret = Intel_cache_info(level, I1c, D1c, L2c); + + } else if (0 == VG_(strcmp)(vendor_id, "AuthenticAMD")) { + ret = AMD_cache_info(I1c, D1c, L2c); + + } else if (0 == VG_(strcmp)(vendor_id, "CentaurHauls")) { + /* Total kludge. Pretend to be a VIA Nehemiah. */ + D1c->size = 64; + D1c->assoc = 16; + D1c->line_size = 16; + I1c->size = 64; + I1c->assoc = 4; + I1c->line_size = 16; + L2c->size = 64; + L2c->assoc = 16; + L2c->line_size = 16; + ret = 0; + + } else { + VG_(message)(Vg_DebugMsg, "CPU vendor ID not recognised (%s)", + vendor_id); + return -1; + } + + /* Successful! Convert sizes from KB to bytes */ + I1c->size *= 1024; + D1c->size *= 1024; + L2c->size *= 1024; + + return ret; +} + + +void VGA_(configure_caches)(cache_t* I1c, cache_t* D1c, cache_t* L2c, + cache_t* I1_dflt, cache_t* D1_dflt, cache_t* L2_dflt, + Bool all_caches_clo_defined) +{ + Int res; + + // Set caches to default. + *I1_dflt = (cache_t) { 65536, 2, 64 }; + *D1_dflt = (cache_t) { 65536, 2, 64 }; + *L2_dflt = (cache_t) { 262144, 8, 64 }; + *I1c = *I1_dflt; + *D1c = *D1_dflt; + *L2c = *L2_dflt; + + // Then replace with any info we can get from CPUID. + res = get_caches_from_CPUID(I1c, D1c, L2c); + + // Warn if CPUID failed and config not completely specified from cmd line. + if (res != 0 && !all_caches_clo_defined) { + VG_(message)(Vg_DebugMsg, + "Warning: Couldn't auto-detect cache config, using one " + "or more defaults "); + } +} + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/configure.in b/VEX/head20041019/configure.in new file mode 100644 index 0000000000..7ce44c5458 --- /dev/null +++ b/VEX/head20041019/configure.in @@ -0,0 +1,454 @@ +# Process this file with autoconf to produce a configure script. +AC_INIT(Valgrind, 2.3.0.CVS, valgrind-users@lists.sourceforge.net) +AC_CONFIG_SRCDIR(coregrind/vg_main.c) +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE + +AM_MAINTAINER_MODE + +# Checks for programs. +CFLAGS="" + +AC_PROG_LN_S +AC_PROG_CC +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_RANLIB + +# Check for the compiler support +if test "${GCC}" != "yes" ; then + AC_MSG_ERROR([Valgrind relies on GCC to be compiled]) +fi + +# figure out where perl lives +AC_PATH_PROG(PERL, perl) + +# figure out where gdb lives +AC_PATH_PROG(GDB, gdb) +# autoheader tries to execute the 3rd string or something; I get warnings when +# it's defined. So just leave it undefined. --njn 2002-Apr-18 +AC_DEFINE_UNQUOTED(GDB_PATH, "$GDB", "") + +# some older automake's don't have it so try something on our own +ifdef([AM_PROG_AS],[AM_PROG_AS], +[ +AS="${CC}" +AC_SUBST(AS) + +ASFLAGS="" +AC_SUBST(ASFLAGS) +]) + +# This variable will collect the individual suppression files +# depending on the results of autoconf + +DEFAULT_SUPP="" + + +# We don't want gcc 2.7 +AC_MSG_CHECKING([for a supported version of gcc]) + +gcc_version=`${CC} --version | head -n 1` + +case "${gcc_version}" in + gcc-2.7.*) + AC_MSG_RESULT([no (${gcc_version})]) + AC_MSG_ERROR([please use a recent (>= gcc-2.95) version of gcc]) + ;; + + *) + AC_MSG_RESULT([ok (${gcc_version})]) + ;; +esac + + +# Checks for the platform +AC_CANONICAL_HOST + +AC_MSG_CHECKING([for a supported CPU]) +AC_SUBST(VG_ARCH) + +case "${host_cpu}" in + i?86) + AC_MSG_RESULT([ok (${host_cpu})]) + VG_ARCH="x86" + ;; + + powerpc*) + AC_MSG_RESULT([no (${host_cpu})]) + VG_ARCH="powerpc" + AC_MSG_ERROR([PowerPC not supported. Sorry]) + ;; + + *) + AC_MSG_RESULT([no (${host_cpu})]) + AC_MSG_ERROR([Unsupported host architecture. Sorry]) + ;; +esac + +AC_MSG_CHECKING([for a supported OS]) +AC_SUBST(VG_OS) + +case "${host_os}" in + *linux*) + AC_MSG_RESULT([ok (${host_os})]) + VG_OS="linux" + + # Ok, this is linux. Check the kernel version + AC_MSG_CHECKING([for the kernel version]) + + kernel=`uname -r` + + case "${kernel}" in + 2.6.*) + AC_MSG_RESULT([2.6 family (${kernel})]) + AC_DEFINE([KERNEL_2_6], 1, [Define to 1 if you're using Linux 2.6.x]) + ;; + + 2.4.*) + AC_MSG_RESULT([2.4 family (${kernel})]) + AC_DEFINE([KERNEL_2_4], 1, [Define to 1 if you're using Linux 2.4.x]) + ;; + + 2.2.*) + AC_MSG_RESULT([2.2 family (${kernel})]) + AC_DEFINE([KERNEL_2_2], 1, [Define to 1 if you're using Linux 2.2.x]) + ;; + + *) + AC_MSG_RESULT([unsupported (${kernel})]) + AC_MSG_ERROR([Valgrind works on kernels 2.2, 2.4, 2.6]) + ;; + esac + + ;; + + *freebsd*) + AC_MSG_RESULT([ok (${host_os})]) + VG_OS="freebsd" + ;; + + *) + AC_MSG_RESULT([no (${host_os})]) + AC_MSG_ERROR([Valgrind is operating system specific. Sorry. Please consider doing a port.]) + ;; +esac + +AC_MSG_CHECKING([for a supported CPU/OS combination]) +AC_SUBST(VG_PLATFORM) + +VG_PLATFORM="$VG_ARCH-$VG_OS" + +case $VG_PLATFORM in + x86-linux) + AC_MSG_RESULT([ok (${host_cpu}-${host_os})]) + ;; + + *) + AC_MSG_RESULT([no (${host_cpu}-${host_os})]) + AC_MSG_ERROR([Valgrind is platform specific. Sorry. Please consider doin +g a port.]) + ;; +esac + +AC_SUBST(DEFAULT_SUPP) + +glibc="" + +AC_EGREP_CPP([GLIBC_21], [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 1) + GLIBC_21 + #endif +#endif +], +glibc="2.1") + +AC_EGREP_CPP([GLIBC_22], [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2) + GLIBC_22 + #endif +#endif +], +glibc="2.2") + +AC_EGREP_CPP([GLIBC_23], [ +#include +#ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ == 3) + GLIBC_23 + #endif +#endif +], +glibc="2.3") + +# Ok, this is linux. Check the kernel version +AC_MSG_CHECKING([the glibc version]) + +case "${glibc}" in + 2.1) + AC_MSG_RESULT(2.1 family) + AC_DEFINE([GLIBC_2_1], 1, [Define to 1 if you're using glibc 2.1.x]) + DEFAULT_SUPP="${DEFAULT_SUPP} glibc-2.1.supp" + ;; + + 2.2) + AC_MSG_RESULT(2.2 family) + AC_DEFINE([GLIBC_2_2], 1, [Define to 1 if you're using glibc 2.2.x]) + DEFAULT_SUPP="${DEFAULT_SUPP} glibc-2.2.supp" + ;; + + 2.3) + AC_MSG_RESULT(2.3 family) + AC_DEFINE([GLIBC_2_3], 1, [Define to 1 if you're using glibc 2.3.x]) + DEFAULT_SUPP="${DEFAULT_SUPP} glibc-2.3.supp" + ;; + + *) + AC_MSG_RESULT(unsupported version) + AC_MSG_ERROR([Valgrind requires the glibc version 2.1, 2.2 or 2.3]) + ;; +esac + +# APIs introduced in recent glibc versions + +AC_MSG_CHECKING([whether sched_param has a sched_priority member]) +AC_CACHE_VAL(vg_have_sched_priority, +[ +AC_TRY_COMPILE([#include ],[ +struct sched_param p; p.sched_priority=1;], +vg_have_sched_priority=yes, +vg_have_sched_priority=no) +]) +AC_MSG_RESULT($vg_have_sched_priority) +if test "$vg_have_sched_priority" = yes; then +AC_DEFINE([HAVE_SCHED_PRIORITY], 1, [pthread / sched_priority exists]) +fi + +AC_MSG_CHECKING([whether nfds_t is defined]) +AC_CACHE_VAL(vg_have_nfds_t, +[ +AC_TRY_COMPILE([#include ],[ +nfds_t i=0;], +vg_have_nfds_t=yes, +vg_have_nfds_t=no) +]) +AC_MSG_RESULT($vg_have_nfds_t) +if test "$vg_have_nfds_t" = yes; then +AC_DEFINE([HAVE_NFDS_T], 1, [nfds_t exists]) +fi + +# try to detect the XFree version +# JRS 2002-06-17: this is completely bogus because it +# detects the server version, whereas we need to know the +# client library version. So what follows is hacked to +# use all the X supp files regardless of what is detected. +# This is really stoooopid and should be fixed properly. + +AC_PATH_X + +if test "${no_x}" != 'yes' ; then + + AC_MSG_CHECKING([X version]) + + cat< conftest.c +#include + +int main (int argc, char * argv []) +{ + Display * display = XOpenDisplay (NULL); + + if (display) { + printf ("%s version=%d\n", ServerVendor (display), VendorRelease (display)); + } + + return 0; +} +EOF + + ${CC} -o conftest conftest.c -I${x_includes} -L${x_libraries} -lX11 >&5 2>&1 + + if test "$?" != 0 ; then + AC_MSG_RESULT([cannot compile test program]) + else + xfree=`./conftest` + + case "${xfree}" in + *XFree86*) + case "${xfree}" in + *version=4*) + AC_MSG_RESULT([XFree 4.x family]) + AC_DEFINE([XFREE_4], 1, [Define to 1 if you're using XFree 4.x]) + DEFAULT_SUPP="${DEFAULT_SUPP} xfree-4.supp" + # haaaaaaack! + DEFAULT_SUPP="${DEFAULT_SUPP} xfree-3.supp" + ;; + + *version=3*) + AC_MSG_RESULT([XFree 3.x family]) + AC_DEFINE([XFREE_3], 1, [Define to 1 if you're using XFree86 3.x]) + DEFAULT_SUPP="${DEFAULT_SUPP} xfree-3.supp" + # haaaaaaack! + DEFAULT_SUPP="${DEFAULT_SUPP} xfree-4.supp" + ;; + + *) AC_MSG_RESULT([unknown XFree86 server (${xfree})]) + # haaaaaaack! + DEFAULT_SUPP="${DEFAULT_SUPP} xfree-3.supp" + DEFAULT_SUPP="${DEFAULT_SUPP} xfree-4.supp" + ;; + esac + ;; + *X.Org*) + case "${xfree}" in + *version=6*) + AC_MSG_RESULT([X.Org 6.x family]) + AC_DEFINE([XFREE_4], 1, [Define to 1 if you're using XFree 4.x]) + DEFAULT_SUPP="${DEFAULT_SUPP} xfree-4.supp" + # haaaaaaack! + DEFAULT_SUPP="${DEFAULT_SUPP} xfree-3.supp" + ;; + esac + ;; + *) AC_MSG_RESULT([not a XFree86 server]) + ;; + esac + + fi + + rm -f conftest conftest.c +fi + + +# check if the GNU as supports CFI directives +AC_MSG_CHECKING([if gas accepts .cfi]) +AC_TRY_LINK(, [ + +__asm__ __volatile__ (".cfi_startproc\n" + ".cfi_adjust_cfa_offset 0x0\n" + ".cfi_endproc\n"); +], +[ + AC_DEFINE_UNQUOTED([HAVE_GAS_CFI], 1, [Define if your GNU as supports .cfi]) + AC_MSG_RESULT(yes) +], + AC_MSG_RESULT(no) +) + +# does this compiler support -mpreferred-stack-boundary=2 ? +AC_MSG_CHECKING([if gcc accepts -mpreferred-stack-boundary]) + +safe_CFLAGS=$CFLAGS +CFLAGS="-mpreferred-stack-boundary=2" + +AC_TRY_COMPILE(, [ + +int main () { return 0 ; } + +], +[ +PREFERRED_STACK_BOUNDARY="-mpreferred-stack-boundary=2" +AC_MSG_RESULT([yes]) +], [ +PREFERRED_STACK_BOUNDARY="" +AC_MSG_RESULT([no]) +]) +CFLAGS=$safe_CFLAGS + +AC_SUBST(PREFERRED_STACK_BOUNDARY) + + + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/socket.h sys/statfs.h sys/time.h sys/endian.h endian.h termios.h unistd.h utime.h linux/fb.h mqueue.h linux/compiler.h]) + +AH_TEMPLATE([HAVE_LINUX_MII_H], []) +AC_MSG_CHECKING([for linux/mii.h]) +AC_PREPROC_IFELSE([#include ], + [AC_DEFINE([HAVE_LINUX_MII_H]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_TYPE_UID_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_CHECK_TYPES(__pthread_unwind_buf_t,,,[#include ]) +AC_CHECK_TYPES(u16,,,[#include ]) + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_FUNC_MMAP +AC_TYPE_SIGNAL + +AC_CHECK_FUNCS([floor memchr memset mkdir strchr strdup strpbrk strrchr strstr semtimedop]) + +AC_OUTPUT( + Makefile + valgrind.spec + valgrind.pc + docs/Makefile + tests/Makefile + tests/vg_regtest + tests/unused/Makefile + include/valgrind.h + include/Makefile + include/x86/Makefile + auxprogs/Makefile + coregrind/Makefile + coregrind/demangle/Makefile + coregrind/docs/Makefile + coregrind/x86/Makefile + coregrind/x86-linux/Makefile + addrcheck/Makefile + addrcheck/tests/Makefile + addrcheck/docs/Makefile + memcheck/Makefile + memcheck/tests/Makefile + memcheck/docs/Makefile + cachegrind/Makefile + cachegrind/x86/Makefile + cachegrind/tests/Makefile + cachegrind/docs/Makefile + cachegrind/cg_annotate + helgrind/Makefile + helgrind/tests/Makefile + helgrind/docs/Makefile + massif/Makefile + massif/hp2ps/Makefile + massif/tests/Makefile + massif/docs/Makefile + corecheck/Makefile + corecheck/tests/Makefile + corecheck/docs/Makefile + lackey/Makefile + lackey/tests/Makefile + lackey/docs/Makefile + none/Makefile + none/tests/Makefile + none/docs/Makefile +) + +cat< default.supp +# This is a generated file, composed of the following suppression rules: +# +# ${DEFAULT_SUPP} +# + +EOF + +for file in ${DEFAULT_SUPP} ; do + cat ${srcdir}/$file >> default.supp +done diff --git a/VEX/head20041019/corecheck/.cvsignore b/VEX/head20041019/corecheck/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/corecheck/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/corecheck/CVS/Entries b/VEX/head20041019/corecheck/CVS/Entries new file mode 100644 index 0000000000..47bfa5675d --- /dev/null +++ b/VEX/head20041019/corecheck/CVS/Entries @@ -0,0 +1,5 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:30 2002// +/Makefile.am/1.45/Wed Sep 1 23:20:47 2004// +/cc_main.c/1.20/Thu Sep 2 08:51:40 2004// +D/docs//// +D/tests//// diff --git a/VEX/head20041019/corecheck/CVS/Repository b/VEX/head20041019/corecheck/CVS/Repository new file mode 100644 index 0000000000..d55fa87d12 --- /dev/null +++ b/VEX/head20041019/corecheck/CVS/Repository @@ -0,0 +1 @@ +valgrind/corecheck diff --git a/VEX/head20041019/corecheck/CVS/Root b/VEX/head20041019/corecheck/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/corecheck/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/corecheck/CVS/Template b/VEX/head20041019/corecheck/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/corecheck/Makefile.am b/VEX/head20041019/corecheck/Makefile.am new file mode 100644 index 0000000000..9e4c37b7d0 --- /dev/null +++ b/VEX/head20041019/corecheck/Makefile.am @@ -0,0 +1,7 @@ +include $(top_srcdir)/Makefile.tool.am + +val_PROGRAMS = vgskin_corecheck.so + +vgskin_corecheck_so_SOURCES = cc_main.c +vgskin_corecheck_so_LDFLAGS = -shared + diff --git a/VEX/head20041019/corecheck/cc_main.c b/VEX/head20041019/corecheck/cc_main.c new file mode 100644 index 0000000000..fc3e1c7d00 --- /dev/null +++ b/VEX/head20041019/corecheck/cc_main.c @@ -0,0 +1,65 @@ + +/*--------------------------------------------------------------------*/ +/*--- CoreCheck: a tool reporting errors detected in core. ---*/ +/*--- cc_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of CoreCheck, a rudimentary Valgrind tool for + detecting certain basic program errors. + + Copyright (C) 2002-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "tool.h" + +void SK_(pre_clo_init)(void) +{ + VG_(details_name) ("Coregrind"); + VG_(details_version) (NULL); + VG_(details_description) ("a rudimentary error detector"); + VG_(details_copyright_author)( + "Copyright (C) 2002-2004, and GNU GPL'd, by Nicholas Nethercote."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + + VG_(needs_core_errors)(); + + /* No core events to track */ +} + +VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 0) + +void SK_(post_clo_init)(void) +{ +} + +UCodeBlock* SK_(instrument)(UCodeBlock* cb, Addr a) +{ + return cb; +} + +void SK_(fini)(Int exitcode) +{ +} + +/*--------------------------------------------------------------------*/ +/*--- end cc_main.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/corecheck/docs/.cvsignore b/VEX/head20041019/corecheck/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/corecheck/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/corecheck/docs/CVS/Entries b/VEX/head20041019/corecheck/docs/CVS/Entries new file mode 100644 index 0000000000..0e2fffb41f --- /dev/null +++ b/VEX/head20041019/corecheck/docs/CVS/Entries @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Thu Oct 3 10:07:33 2002// +/Makefile.am/1.3/Wed Aug 25 11:40:05 2004// +/cc_main.html/1.4/Sun Jan 4 16:43:19 2004// +D diff --git a/VEX/head20041019/corecheck/docs/CVS/Repository b/VEX/head20041019/corecheck/docs/CVS/Repository new file mode 100644 index 0000000000..9af3e69a26 --- /dev/null +++ b/VEX/head20041019/corecheck/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/corecheck/docs diff --git a/VEX/head20041019/corecheck/docs/CVS/Root b/VEX/head20041019/corecheck/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/corecheck/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/corecheck/docs/CVS/Template b/VEX/head20041019/corecheck/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/corecheck/docs/Makefile.am b/VEX/head20041019/corecheck/docs/Makefile.am new file mode 100644 index 0000000000..4e4da803f0 --- /dev/null +++ b/VEX/head20041019/corecheck/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = cc_main.html diff --git a/VEX/head20041019/corecheck/docs/cc_main.html b/VEX/head20041019/corecheck/docs/cc_main.html new file mode 100644 index 0000000000..3a374a4039 --- /dev/null +++ b/VEX/head20041019/corecheck/docs/cc_main.html @@ -0,0 +1,66 @@ + + + + Cachegrind + + + + + +

CoreCheck

+
This manual was last updated on 2002-10-03
+

+ +

+njn25@cam.ac.uk
+Copyright © 2000-2004 Nicholas Nethercote +

+CoreCheck is licensed under the GNU General Public License, +version 2
+CoreCheck is a Valgrind tool that does very basic error checking. +

+ +

+ +

1  CoreCheck

+ +CoreCheck is a very simple tool for Valgrind. It adds no instrumentation to +the program's code, and only reports the few kinds of errors detected by +Valgrind's core. It is mainly of use for Valgrind's developers for debugging +and regression testing. +

+The errors detected are those found by the core when +VG_(needs).core_errors is set. These include: + +

    +
  • Pthread API errors (many; eg. unlocking a non-locked mutex)

    +

  • Silly arguments to malloc() et al (eg. negative size)

    +

  • Invalid file descriptors to blocking syscalls read() and + write()

    +

  • Bad signal numbers passed to sigaction()

    +

  • Attempts to install signal handler for SIGKILL or + SIGSTOP

    +

+ +
+ + + diff --git a/VEX/head20041019/corecheck/tests/.cvsignore b/VEX/head20041019/corecheck/tests/.cvsignore new file mode 100644 index 0000000000..bcb560cb6a --- /dev/null +++ b/VEX/head20041019/corecheck/tests/.cvsignore @@ -0,0 +1,33 @@ +Makefile.in +Makefile +erringfds +malloc3 +sigkill +pth_atfork1 +pth_cancel1 +pth_cancel2 +pth_cvsimple +pth_empty +pth_mutexspeed +pth_once +pth_rwlock +res_search +.ktemp +*.stdout.diff +*.stderr.diff* +*.stdout.out +*.stderr.out +fdleak_cmsg +fdleak_creat +fdleak_dup +fdleak_dup2 +fdleak_fcntl +fdleak_ipv4 +fdleak_open +fdleak_pipe +fdleak_socketpair +pth_exit +pth_exit2 +vgprintf +as_shm +as_mmap diff --git a/VEX/head20041019/corecheck/tests/CVS/Entries b/VEX/head20041019/corecheck/tests/CVS/Entries new file mode 100644 index 0000000000..5ea0f8344b --- /dev/null +++ b/VEX/head20041019/corecheck/tests/CVS/Entries @@ -0,0 +1,92 @@ +/.cvsignore/1.9/Sat Oct 16 14:49:53 2004// +/Makefile.am/1.22/Sat Oct 16 14:49:53 2004// +/as_mmap.c/1.1/Tue Dec 16 02:14:00 2003// +/as_mmap.stderr.exp/1.1/Tue Dec 16 02:14:00 2003// +/as_mmap.stderr.exp2/1.1/Wed Oct 13 16:48:20 2004// +/as_mmap.vgtest/1.1/Tue Dec 16 02:14:00 2003// +/as_shm.c/1.3/Fri Jan 9 16:15:06 2004// +/as_shm.stderr.exp/1.1/Tue Dec 16 02:14:00 2003// +/as_shm.stdout.exp/1.2/Fri Jan 9 16:15:06 2004// +/as_shm.vgtest/1.1/Tue Dec 16 02:14:00 2003// +/erringfds.c/1.2/Mon Sep 23 09:36:24 2002// +/erringfds.stderr.exp/1.4/Sun Dec 14 06:50:36 2003// +/erringfds.stdout.exp/1.2/Mon Sep 23 09:36:24 2002// +/erringfds.vgtest/1.2/Mon Sep 23 09:36:24 2002// +/fdleak_cmsg.c/1.1/Mon Nov 17 17:44:59 2003// +/fdleak_cmsg.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_cmsg.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/fdleak_creat.c/1.1/Mon Nov 17 17:44:59 2003// +/fdleak_creat.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_creat.stderr.exp2/1.1/Sun Mar 7 19:40:33 2004// +/fdleak_creat.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/fdleak_dup.c/1.1/Mon Nov 17 17:44:59 2003// +/fdleak_dup.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_dup.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/fdleak_dup2.c/1.1/Mon Nov 17 17:44:59 2003// +/fdleak_dup2.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_dup2.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/fdleak_fcntl.c/1.1/Mon Nov 17 17:44:59 2003// +/fdleak_fcntl.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_fcntl.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/fdleak_ipv4.c/1.1/Mon Nov 17 17:44:59 2003// +/fdleak_ipv4.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_ipv4.stdout.exp/1.1/Mon Nov 17 17:44:59 2003// +/fdleak_ipv4.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/fdleak_open.c/1.2/Thu Dec 4 16:12:21 2003// +/fdleak_open.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_open.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/fdleak_pipe.c/1.1/Mon Nov 17 17:44:59 2003// +/fdleak_pipe.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_pipe.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/fdleak_socketpair.c/1.2/Thu Dec 4 16:12:21 2003// +/fdleak_socketpair.stderr.exp/1.4/Sun Feb 29 12:02:32 2004// +/fdleak_socketpair.vgtest/1.2/Sun Feb 29 12:02:32 2004// +/filter_fdleak/1.3/Tue Mar 9 09:59:26 2004// +/filter_stderr/1.3/Mon Sep 23 11:21:45 2002// +/pth_atfork1.c/1.3/Mon May 5 11:04:37 2003// +/pth_atfork1.stderr.exp/1.3/Mon Nov 18 11:33:37 2002// +/pth_atfork1.stdout.exp/1.4/Mon May 5 11:04:37 2003// +/pth_atfork1.vgtest/1.2/Mon Sep 23 09:36:24 2002// +/pth_cancel1.c/1.1/Sat Jun 12 12:58:21 2004// +/pth_cancel1.stderr.exp/1.1/Sat Jun 12 12:58:21 2004// +/pth_cancel1.stdout.exp/1.1/Sat Jun 12 12:58:21 2004// +/pth_cancel1.vgtest/1.1/Sat Jun 12 12:58:21 2004// +/pth_cancel2.c/1.3/Sat Oct 5 15:28:29 2002// +/pth_cancel2.stderr.exp/1.3/Mon Nov 18 11:33:37 2002// +/pth_cancel2.vgtest/1.2/Mon Sep 23 09:36:24 2002// +/pth_cvsimple.c/1.2/Mon Sep 23 09:36:24 2002// +/pth_cvsimple.stderr.exp/1.3/Mon Nov 18 11:33:37 2002// +/pth_cvsimple.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/pth_cvsimple.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/pth_empty.c/1.2/Mon Sep 23 09:36:25 2002// +/pth_empty.stderr.exp/1.3/Mon Nov 18 11:33:37 2002// +/pth_empty.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/pth_exit.c/1.1/Mon Oct 13 22:26:54 2003// +/pth_exit.stderr.exp/1.1/Mon Oct 13 22:26:54 2003// +/pth_exit.vgtest/1.1/Mon Oct 13 22:26:54 2003// +/pth_exit2.c/1.1/Sat Aug 14 15:37:59 2004// +/pth_exit2.stderr.exp/1.1/Sat Aug 14 15:37:59 2004// +/pth_exit2.vgtest/1.1/Sat Aug 14 15:37:59 2004// +/pth_mutexspeed.c/1.2/Mon Sep 23 09:36:25 2002// +/pth_mutexspeed.stderr.exp/1.3/Mon Nov 18 11:33:37 2002// +/pth_mutexspeed.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/pth_mutexspeed.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/pth_once.c/1.2/Mon Sep 23 09:36:25 2002// +/pth_once.stderr.exp/1.3/Mon Nov 18 11:33:37 2002// +/pth_once.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/pth_once.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/pth_rwlock.c/1.1/Sat Oct 16 14:49:53 2004// +/pth_rwlock.stderr.exp/1.1/Sat Oct 16 14:49:53 2004// +/pth_rwlock.vgtest/1.1/Sat Oct 16 14:49:53 2004// +/res_search.c/1.3/Wed Jul 9 21:53:32 2003// +/res_search.stderr.exp/1.1/Fri Jul 4 16:16:51 2003// +/res_search.stdout.exp/1.1/Fri Jul 4 16:16:51 2003// +/res_search.vgtest/1.3/Fri Jul 11 00:17:54 2003// +/sigkill.c/1.4/Sun Feb 22 17:24:01 2004// +/sigkill.stderr.exp/1.5/Sun Feb 22 17:24:01 2004// +/sigkill.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/vgprintf.c/1.2/Sat Dec 13 03:07:40 2003// +/vgprintf.stderr.exp/1.1/Fri Oct 31 18:52:18 2003// +/vgprintf.stdout.exp/1.1/Fri Oct 31 18:52:18 2003// +/vgprintf.vgtest/1.1/Fri Oct 31 18:52:18 2003// +D diff --git a/VEX/head20041019/corecheck/tests/CVS/Repository b/VEX/head20041019/corecheck/tests/CVS/Repository new file mode 100644 index 0000000000..5604db7c52 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/CVS/Repository @@ -0,0 +1 @@ +valgrind/corecheck/tests diff --git a/VEX/head20041019/corecheck/tests/CVS/Root b/VEX/head20041019/corecheck/tests/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/corecheck/tests/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/corecheck/tests/CVS/Template b/VEX/head20041019/corecheck/tests/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/corecheck/tests/Makefile.am b/VEX/head20041019/corecheck/tests/Makefile.am new file mode 100644 index 0000000000..90658326f7 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/Makefile.am @@ -0,0 +1,90 @@ +##--------------------------------------------------------------------------- +## These test core error checking, eg. "silly values" for malloc/calloc, +## pthread errors (and suppressions), signal handling errors, invalid fds for +## blocking syscalls, etc. +##--------------------------------------------------------------------------- + +noinst_SCRIPTS = filter_stderr filter_fdleak + +EXTRA_DIST = $(noinst_SCRIPTS) \ + as_mmap.stderr.exp as_mmap.stderr.exp2 as_mmap.vgtest \ + erringfds.stderr.exp erringfds.stdout.exp erringfds.vgtest \ + fdleak_cmsg.stderr.exp fdleak_cmsg.vgtest \ + fdleak_creat.stderr.exp fdleak_creat.vgtest \ + fdleak_dup.stderr.exp fdleak_dup.vgtest \ + fdleak_dup2.stderr.exp fdleak_dup2.vgtest \ + fdleak_fcntl.stderr.exp fdleak_fcntl.vgtest \ + fdleak_ipv4.stderr.exp fdleak_ipv4.stdout.exp fdleak_ipv4.vgtest \ + fdleak_open.stderr.exp fdleak_open.vgtest \ + fdleak_pipe.stderr.exp fdleak_pipe.vgtest \ + fdleak_socketpair.stderr.exp fdleak_socketpair.vgtest \ + pth_atfork1.stderr.exp pth_atfork1.stdout.exp pth_atfork1.vgtest \ + pth_cancel1.stderr.exp pth_cancel1.stdout.exp pth_cancel1.vgtest \ + pth_cancel2.stderr.exp pth_cancel2.vgtest \ + pth_cvsimple.stderr.exp pth_cvsimple.stdout.exp pth_cvsimple.vgtest \ + pth_empty.stderr.exp pth_empty.vgtest \ + pth_exit.stderr.exp pth_exit.vgtest \ + pth_exit2.stderr.exp pth_exit2.vgtest \ + pth_mutexspeed.stderr.exp \ + pth_mutexspeed.stdout.exp pth_mutexspeed.vgtest \ + pth_once.stderr.exp pth_once.stdout.exp pth_once.vgtest \ + pth_rwlock.stderr.exp pth_rwlock.vgtest \ + sigkill.stderr.exp sigkill.vgtest \ + res_search.stderr.exp res_search.stdout.exp res_search.vgtest \ + vgprintf.stderr.exp vgprintf.stdout.exp vgprintf.vgtest + +check_PROGRAMS = \ + erringfds fdleak_cmsg fdleak_creat fdleak_dup fdleak_dup2 \ + fdleak_fcntl fdleak_ipv4 fdleak_open fdleak_pipe \ + fdleak_socketpair sigkill res_search \ + pth_atfork1 pth_cancel1 pth_cancel2 pth_cvsimple pth_empty \ + pth_exit pth_exit2 pth_mutexspeed pth_once pth_rwlock \ + as_mmap as_shm \ + vgprintf + +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -g -O0 -I$(top_srcdir)/include +AM_CXXFLAGS = $(AM_CFLAGS) + +vgprintf_SOURCES = vgprintf.c + +# Client address space checks +as_mmap_SOURCES = as_mmap.c +as_shm_SOURCES = as_shm.c + +# Leak tests +fdleak_cmsg_SOURCES = fdleak_cmsg.c +fdleak_creat_SOURCES = fdleak_creat.c +fdleak_dup_SOURCES = fdleak_dup.c +fdleak_dup2_SOURCES = fdleak_dup2.c +fdleak_fcntl_SOURCES = fdleak_fcntl.c +fdleak_ipv4_SOURCES = fdleak_ipv4.c +fdleak_open_SOURCES = fdleak_open.c +fdleak_pipe_SOURCES = fdleak_pipe.c +fdleak_socketpair_SOURCES = fdleak_socketpair.c +sigkill_SOURCES = sigkill.c + +# Pthread ones +pth_atfork1_SOURCES = pth_atfork1.c +pth_atfork1_LDADD = -lpthread +pth_cancel1_SOURCES = pth_cancel1.c +pth_cancel1_LDADD = -lpthread +pth_cancel2_SOURCES = pth_cancel2.c +pth_cancel2_LDADD = -lpthread +pth_cvsimple_SOURCES = pth_cvsimple.c +pth_cvsimple_LDADD = -lpthread +pth_empty_SOURCES = pth_empty.c +pth_empty_LDADD = -lpthread +pth_exit_SOURCES = pth_exit.c +pth_exit_LDADD = -lpthread +pth_exit2_SOURCES = pth_exit2.c +pth_exit2_LDADD = -lpthread +pth_mutexspeed_SOURCES = pth_mutexspeed.c +pth_mutexspeed_LDADD = -lpthread +pth_once_SOURCES = pth_once.c +pth_once_LDADD = -lpthread +pth_rwlock_SOURCES = pth_rwlock.c +pth_rwlock_LDADD = -lpthread +res_search_SOURCES = res_search.c +res_search_LDADD = -lresolv -lpthread + + diff --git a/VEX/head20041019/corecheck/tests/as_mmap.c b/VEX/head20041019/corecheck/tests/as_mmap.c new file mode 100644 index 0000000000..da40a8612a --- /dev/null +++ b/VEX/head20041019/corecheck/tests/as_mmap.c @@ -0,0 +1,26 @@ +#include +#include + +int main() +{ + char local; + char *top = (char *)(((unsigned long)&local + 0x0fffffff) & ~0x0fffffff); + + if (mmap((void *)0x00000000, 0x10000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0) == MAP_FAILED) + perror("mmap @ 0x00000000"); + if (mmap((void *)0x00010000, 0x10000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0) == MAP_FAILED) + perror("mmap @ 0x00010000"); + if (mmap((void *)0x50000000, 0x10000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0) == MAP_FAILED) + perror("mmap @ 0x50000000"); + if (mmap(top, 0x10000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0) == MAP_FAILED) + perror("mmap @ top"); + if (mmap(top+0x08000000, 0x10000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0) == MAP_FAILED) + perror("mmap @ top+.5G"); + + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/as_mmap.stderr.exp b/VEX/head20041019/corecheck/tests/as_mmap.stderr.exp new file mode 100644 index 0000000000..e5314c8ded --- /dev/null +++ b/VEX/head20041019/corecheck/tests/as_mmap.stderr.exp @@ -0,0 +1,9 @@ + +Warning: client syscall mmap2 tried to modify addresses 0x0-0x10000 +mmap @ 0x00000000: Cannot allocate memory +Warning: client syscall mmap2 tried to modify addresses 0xB0000000-0xB0010000 +mmap @ top: Cannot allocate memory +Warning: client syscall mmap2 tried to modify addresses 0xB8000000-0xB8010000 +mmap @ top+.5G: Cannot allocate memory + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/as_mmap.stderr.exp2 b/VEX/head20041019/corecheck/tests/as_mmap.stderr.exp2 new file mode 100644 index 0000000000..d400151966 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/as_mmap.stderr.exp2 @@ -0,0 +1,9 @@ + +Warning: client syscall mmap tried to modify addresses 0x0-0x10000 +mmap @ 0x00000000: Cannot allocate memory +Warning: client syscall mmap tried to modify addresses 0xB0000000-0xB0010000 +mmap @ top: Cannot allocate memory +Warning: client syscall mmap tried to modify addresses 0xB8000000-0xB8010000 +mmap @ top+.5G: Cannot allocate memory + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/as_mmap.vgtest b/VEX/head20041019/corecheck/tests/as_mmap.vgtest new file mode 100644 index 0000000000..13cb4513c1 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/as_mmap.vgtest @@ -0,0 +1 @@ +prog: as_mmap diff --git a/VEX/head20041019/corecheck/tests/as_shm.c b/VEX/head20041019/corecheck/tests/as_shm.c new file mode 100644 index 0000000000..005c725fa4 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/as_shm.c @@ -0,0 +1,33 @@ +#include +#include +#include +#include + +int main() +{ + int shmid = shmget(IPC_PRIVATE, 0x10000, IPC_CREAT|IPC_EXCL|0777); + void *addr; + char local; + char *top = (char *)(((unsigned long)&local + 0x0fffffff) & ~0x0fffffff); + + if (shmid == -1) + perror("shmget"); + + addr = shmat(shmid, 0, 0); + + if (addr == (void *)-1) + perror("shmat @ 0"); + else + printf("shmat 0: addr=...\n"); + + addr = shmat(shmid, top, 0); + + if (addr == (void *)-1) + perror("shmat @ top"); + else + printf("shmat 2: addr=...\n"); + + shmctl(shmid, IPC_RMID, NULL); + + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/as_shm.stderr.exp b/VEX/head20041019/corecheck/tests/as_shm.stderr.exp new file mode 100644 index 0000000000..ee51d43815 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/as_shm.stderr.exp @@ -0,0 +1,5 @@ + +Warning: client syscall shmat tried to modify addresses 0xB0000000-0xB0010000 +shmat @ top: Invalid argument + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/as_shm.stdout.exp b/VEX/head20041019/corecheck/tests/as_shm.stdout.exp new file mode 100644 index 0000000000..5c3a150d20 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/as_shm.stdout.exp @@ -0,0 +1 @@ +shmat 0: addr=... diff --git a/VEX/head20041019/corecheck/tests/as_shm.vgtest b/VEX/head20041019/corecheck/tests/as_shm.vgtest new file mode 100644 index 0000000000..1263cea7d2 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/as_shm.vgtest @@ -0,0 +1 @@ +prog: as_shm diff --git a/VEX/head20041019/corecheck/tests/erringfds.c b/VEX/head20041019/corecheck/tests/erringfds.c new file mode 100644 index 0000000000..0f1afe5dee --- /dev/null +++ b/VEX/head20041019/corecheck/tests/erringfds.c @@ -0,0 +1,17 @@ + +#include +#include +#include +#include +#include + +int main ( void ) +{ + int fd, n; + char buf[10]; + fd = open("foo/bar/xyzzy", O_RDONLY); /* fails */ + printf("fd = %d\n", fd); + n = read ( fd, buf, 10 ); + printf ("n = %d\n", n); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/erringfds.stderr.exp b/VEX/head20041019/corecheck/tests/erringfds.stderr.exp new file mode 100644 index 0000000000..b6a487d38b --- /dev/null +++ b/VEX/head20041019/corecheck/tests/erringfds.stderr.exp @@ -0,0 +1,4 @@ + +Warning: invalid file descriptor -1 in syscall read() + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/erringfds.stdout.exp b/VEX/head20041019/corecheck/tests/erringfds.stdout.exp new file mode 100644 index 0000000000..bcc1770f18 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/erringfds.stdout.exp @@ -0,0 +1,2 @@ +fd = -1 +n = -1 diff --git a/VEX/head20041019/corecheck/tests/erringfds.vgtest b/VEX/head20041019/corecheck/tests/erringfds.vgtest new file mode 100644 index 0000000000..5a8ede4c38 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/erringfds.vgtest @@ -0,0 +1 @@ +prog: erringfds diff --git a/VEX/head20041019/corecheck/tests/fdleak_cmsg.c b/VEX/head20041019/corecheck/tests/fdleak_cmsg.c new file mode 100644 index 0000000000..a6dccb1020 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_cmsg.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +char filea[24]; +char fileb[24]; +char sock[24]; + +void +server () +{ + int s, fd1, fd2; + struct sockaddr_un addr; + + fd1 = open(filea, O_RDWR | O_CREAT | O_TRUNC, 0750); + if(fd1 == -1) { + perror("open"); + exit(1); + } + + fd2 = open(fileb, O_RDWR | O_CREAT | O_TRUNC, 0750); + if(fd2 == -1) { + perror("open"); + exit(1); + } + + s = socket(PF_UNIX, SOCK_STREAM, 0); + if(s == -1) { + perror("socket"); + exit(1); + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + sprintf(addr.sun_path, sock); + + unlink(addr.sun_path); + if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("bind"); + exit(1); + } + + if(listen(s, 5) == -1) { + perror("listen"); + exit(1); + } + + { + int x; + int baddrsize = 0; + struct sockaddr_un baddr; + struct msghdr msg = {NULL, 0, NULL, 0, 0, 0, 0}; + struct cmsghdr *cmsg; + char buf[CMSG_SPACE(sizeof(int) * 2)]; + struct iovec iov[1]; + + memset(&baddr, 0, sizeof(baddr)); + x = accept(s, (struct sockaddr *)&baddr, &baddrsize); + if(x == -1) { + perror("accept"); + exit(1); + } + + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * 2); + ((int *)CMSG_DATA(cmsg))[0] = fd1; + ((int *)CMSG_DATA(cmsg))[1] = fd2; + + iov[0].iov_base = "hello"; + iov[0].iov_len = 6; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if(sendmsg(x, &msg, 0) == -1) { + perror("sendmsg"); + exit(1); + } + } +} + +void +client () +{ + int s, fd1 = -1, fd2 = -1, size, count = 0, ret; + struct sockaddr_un addr; + struct iovec iov[1]; + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int) * 2)]; + } control_un; + struct msghdr msg = { NULL, 0, iov, 1, control_un.control, + sizeof(control_un), 0 }; + struct cmsghdr *cmsg = &control_un.cm; + char buf[1024]; + + iov[0].iov_base = buf; + iov[0].iov_len = sizeof(buf); + + s = socket(PF_UNIX, SOCK_STREAM, 0); + if(s == -1) { + perror("socket"); + exit(1); + } + + addr.sun_family = AF_UNIX; + sprintf(addr.sun_path, sock); + + do { + count++; + ret = connect(s, (struct sockaddr *)&addr, sizeof(addr)); + if(ret == -1) sleep(1); + } while (count < 10 && ret == -1); + + if(ret == -1) { + perror("connect"); + exit(1); + } + + if((size = recvmsg(s, &msg, 0)) == -1) { + perror("recvmsg"); + exit(1); + } + + + cmsg = CMSG_FIRSTHDR(&msg); + while(cmsg) { + if(cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS && + cmsg->cmsg_len == CMSG_LEN(sizeof(int) * 2)) { + fd1 = ((int *)CMSG_DATA(cmsg))[0]; + fd2 = ((int *)CMSG_DATA(cmsg))[1]; + } + + cmsg = CMSG_NXTHDR(&msg, cmsg); + } + + if(fd1 != -1) write(fd1, "Yeah 1\n", 8); + if(fd2 != -1) write(fd2, "Yeah 2\n", 8); +} + + +int +main (int argc, char **argv) +{ + int pid, status; + + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + pid = getpid(); + sprintf(filea, "/tmp/data1.%d", pid); + sprintf(fileb, "/tmp/data2.%d", pid); + sprintf(sock, "/tmp/sock.%d", pid); + + if((pid = fork()) == 0) { + server(); + return 0; + } + + client(); + + wait(&status); + + unlink(filea); + unlink(fileb); + unlink(sock); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_cmsg.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_cmsg.stderr.exp new file mode 100644 index 0000000000..d4ad192098 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_cmsg.stderr.exp @@ -0,0 +1,55 @@ + + +FILE DESCRIPTORS: 7 open at exit. +Open AF_UNIX socket .: /tmp/sock + at 0x........: accept (in /...libc...) + by 0x........: main (fdleak_cmsg.c:170) + +Open AF_UNIX socket .: /tmp/sock + at 0x........: socket (in /...libc...) + by 0x........: main (fdleak_cmsg.c:170) + +Open file descriptor .: /tmp/data2 + at 0x........: open (in /...libc...) + by 0x........: main (fdleak_cmsg.c:170) + +Open file descriptor .: /tmp/data1 + at 0x........: open (in /...libc...) + by 0x........: main (fdleak_cmsg.c:170) + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) + +FILE DESCRIPTORS: 6 open at exit. +Open file descriptor .: /tmp/data2 + at 0x........: recvmsg (in /...libc...) + by 0x........: main (fdleak_cmsg.c:174) + +Open file descriptor .: /tmp/data1 + at 0x........: recvmsg (in /...libc...) + by 0x........: main (fdleak_cmsg.c:174) + +Open AF_UNIX socket .: + at 0x........: socket (in /...libc...) + by 0x........: main (fdleak_cmsg.c:174) + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_cmsg.vgtest b/VEX/head20041019/corecheck/tests/fdleak_cmsg.vgtest new file mode 100644 index 0000000000..5083320443 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_cmsg.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_cmsg +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/fdleak_creat.c b/VEX/head20041019/corecheck/tests/fdleak_creat.c new file mode 100644 index 0000000000..9a2aaf1e17 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_creat.c @@ -0,0 +1,21 @@ +#include +#include +#include + +int +main (int argc, char **argv) +{ + char filename[24]; + + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + sprintf(filename, "/tmp/file.%d\n", getpid()); + creat(filename, 0); + unlink(filename); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_creat.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_creat.stderr.exp new file mode 100644 index 0000000000..66e60362a0 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_creat.stderr.exp @@ -0,0 +1,20 @@ + + +FILE DESCRIPTORS: 4 open at exit. +Open file descriptor .: /tmp/file + + at 0x........: creat (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_creat.stderr.exp2 b/VEX/head20041019/corecheck/tests/fdleak_creat.stderr.exp2 new file mode 100644 index 0000000000..c29fd9699c --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_creat.stderr.exp2 @@ -0,0 +1,19 @@ + + +FILE DESCRIPTORS: 4 open at exit. +Open file descriptor .: /tmp/file + + at 0x........: open (in /...libc...) + by 0x........: main (fdleak_creat.c:18) + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_creat.vgtest b/VEX/head20041019/corecheck/tests/fdleak_creat.vgtest new file mode 100644 index 0000000000..ffa412a456 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_creat.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_creat +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/fdleak_dup.c b/VEX/head20041019/corecheck/tests/fdleak_dup.c new file mode 100644 index 0000000000..db8a287461 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_dup.c @@ -0,0 +1,19 @@ +#include +#include + +int +main (int argc, char **argv) +{ + int s; + + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + s = open("/dev/null", O_RDONLY); + dup(s); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_dup.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_dup.stderr.exp new file mode 100644 index 0000000000..0c54be6b06 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_dup.stderr.exp @@ -0,0 +1,24 @@ + + +FILE DESCRIPTORS: 5 open at exit. +Open file descriptor .: /dev/null + at 0x........: dup (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: /dev/null + at 0x........: open (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_dup.vgtest b/VEX/head20041019/corecheck/tests/fdleak_dup.vgtest new file mode 100644 index 0000000000..d150dfef0a --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_dup.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_dup +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/fdleak_dup2.c b/VEX/head20041019/corecheck/tests/fdleak_dup2.c new file mode 100644 index 0000000000..bcc8ff4a1b --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_dup2.c @@ -0,0 +1,23 @@ +#include +#include + +int +main (int argc, char **argv) +{ + int s1; + int s2; + + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + s1 = open("/dev/null", O_RDONLY); + s2 = open("/dev/null", O_RDONLY); + + dup2(s1, 20); + dup2(s1, s2); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_dup2.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_dup2.stderr.exp new file mode 100644 index 0000000000..612b352e03 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_dup2.stderr.exp @@ -0,0 +1,29 @@ + + +FILE DESCRIPTORS: 6 open at exit. +Open file descriptor .: /dev/null + at 0x........: dup2 (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: /dev/null + at 0x........: dup2 (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: /dev/null + at 0x........: open (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_dup2.vgtest b/VEX/head20041019/corecheck/tests/fdleak_dup2.vgtest new file mode 100644 index 0000000000..7b0d95eeac --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_dup2.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_dup2 +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/fdleak_fcntl.c b/VEX/head20041019/corecheck/tests/fdleak_fcntl.c new file mode 100644 index 0000000000..e2ea4f4a09 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_fcntl.c @@ -0,0 +1,20 @@ +#include +#include +#include + +int +main (int argc, char **argv) +{ + int s1; + + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + s1 = open("/dev/null", O_RDONLY); + if(fcntl(s1, F_DUPFD, s1) == -1) perror("fcntl"); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_fcntl.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_fcntl.stderr.exp new file mode 100644 index 0000000000..89f059e0e2 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_fcntl.stderr.exp @@ -0,0 +1,23 @@ + + +FILE DESCRIPTORS: 5 open at exit. +Open file descriptor .: /dev/null + at 0x........: fcntl (in /...libc...) + by 0x........: main (fdleak_fcntl.c:18) + +Open file descriptor .: /dev/null + at 0x........: open (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_fcntl.vgtest b/VEX/head20041019/corecheck/tests/fdleak_fcntl.vgtest new file mode 100644 index 0000000000..5b506eaafc --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_fcntl.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_fcntl +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/fdleak_ipv4.c b/VEX/head20041019/corecheck/tests/fdleak_ipv4.c new file mode 100644 index 0000000000..98d28aee20 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_ipv4.c @@ -0,0 +1,109 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void +server () +{ + int s, x; + struct sockaddr_in baddr; + struct sockaddr_in addr; + int baddrsize = sizeof(baddr); + int one = 1; + + s = socket(PF_INET, SOCK_STREAM, 0); + if(s == -1) { + perror("socket"); + exit(1); + } + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)); + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 12321; + + if(bind(s, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + perror("bind"); + exit(1); + } + + if(listen(s, 5) == -1) { + perror("listen"); + exit(1); + } + + memset(&baddr, 0, sizeof(baddr)); + x = accept(s, (struct sockaddr *)&baddr, &baddrsize); + if(x == -1) { + perror("accept"); + exit(1); + } + + write(x, "hello", 6); +} + +void +client () +{ + int s, count = 0, ret; + struct sockaddr_in addr; + char buf[1024]; + + s = socket(PF_INET, SOCK_STREAM, 0); + if(s == -1) { + perror("socket"); + exit(1); + } + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 12321; + + do { + count++; + ret = connect(s, (struct sockaddr *)&addr, sizeof(addr)); + if(ret == -1) sleep(1); + } while (count < 10 && ret == -1); + + if(ret == -1) { + perror("connect"); + exit(1); + } + + read(s, buf, sizeof(buf)); + + printf("%s\n", buf); +} + + +int +main (int argc, char **argv) +{ + int pid, status; + + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + if((pid = fork()) == 0) { + server(); + return 0; + } + + client(); + + wait(&status); + + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_ipv4.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_ipv4.stderr.exp new file mode 100644 index 0000000000..cfe029ed4c --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_ipv4.stderr.exp @@ -0,0 +1,39 @@ + + +FILE DESCRIPTORS: 5 open at exit. +Open AF_INET socket 4: 127.0.0.1:... <-> 127.0.0.1:... + at 0x........: accept (in /...libc...) + by 0x........: main (fdleak_ipv4.c:100) + +Open AF_INET socket 3: 127.0.0.1:... <-> unbound + at 0x........: socket (in /...libc...) + by 0x........: main (fdleak_ipv4.c:100) + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) + +FILE DESCRIPTORS: 4 open at exit. +Open AF_INET socket 3: 127.0.0.1:... <-> 127.0.0.1:... + at 0x........: socket (in /...libc...) + by 0x........: main (fdleak_ipv4.c:104) + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_ipv4.stdout.exp b/VEX/head20041019/corecheck/tests/fdleak_ipv4.stdout.exp new file mode 100644 index 0000000000..ce01362503 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_ipv4.stdout.exp @@ -0,0 +1 @@ +hello diff --git a/VEX/head20041019/corecheck/tests/fdleak_ipv4.vgtest b/VEX/head20041019/corecheck/tests/fdleak_ipv4.vgtest new file mode 100644 index 0000000000..7d664a1abb --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_ipv4.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_ipv4 +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/fdleak_open.c b/VEX/head20041019/corecheck/tests/fdleak_open.c new file mode 100644 index 0000000000..22b4228d84 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_open.c @@ -0,0 +1,15 @@ +#include +#include +int +main (int argc, char **argv) +{ + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + open("/dev/null", O_RDONLY); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_open.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_open.stderr.exp new file mode 100644 index 0000000000..6fda89512a --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_open.stderr.exp @@ -0,0 +1,19 @@ + + +FILE DESCRIPTORS: 4 open at exit. +Open file descriptor .: /dev/null + at 0x........: open (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_open.vgtest b/VEX/head20041019/corecheck/tests/fdleak_open.vgtest new file mode 100644 index 0000000000..8b9bef573a --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_open.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_open +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/fdleak_pipe.c b/VEX/head20041019/corecheck/tests/fdleak_pipe.c new file mode 100644 index 0000000000..6c2d566d18 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_pipe.c @@ -0,0 +1,17 @@ +#include + +int +main (int argc, char **argv) +{ + int fds[2]; + + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + pipe(fds); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_pipe.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_pipe.stderr.exp new file mode 100644 index 0000000000..0ba67334fb --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_pipe.stderr.exp @@ -0,0 +1,24 @@ + + +FILE DESCRIPTORS: 5 open at exit. +Open file descriptor .: + at 0x........: pipe (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: + at 0x........: pipe (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_pipe.vgtest b/VEX/head20041019/corecheck/tests/fdleak_pipe.vgtest new file mode 100644 index 0000000000..242c127537 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_pipe.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_pipe +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/fdleak_socketpair.c b/VEX/head20041019/corecheck/tests/fdleak_socketpair.c new file mode 100644 index 0000000000..da3e91204e --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_socketpair.c @@ -0,0 +1,17 @@ +#include +#include +int +main (int argc, char **argv) +{ + int fds[2]; + + /* + * Fedora Core 1's Perl opens /dev/pts/2 as fd 10. Let's close it + * now to get consistent results across different releases. + */ + + close(10); + + socketpair(AF_UNIX, SOCK_STREAM, PF_UNIX, fds); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/fdleak_socketpair.stderr.exp b/VEX/head20041019/corecheck/tests/fdleak_socketpair.stderr.exp new file mode 100644 index 0000000000..87192dad18 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_socketpair.stderr.exp @@ -0,0 +1,24 @@ + + +FILE DESCRIPTORS: 5 open at exit. +Open AF_UNIX socket .: + at 0x........: socketpair (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open AF_UNIX socket .: + at 0x........: socketpair (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +Open file descriptor .: . + + +Open file descriptor .: . + + +Open file descriptor .: /dev/null + + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/fdleak_socketpair.vgtest b/VEX/head20041019/corecheck/tests/fdleak_socketpair.vgtest new file mode 100644 index 0000000000..9903dc2f71 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/fdleak_socketpair.vgtest @@ -0,0 +1,4 @@ +prog: fdleak_socketpair +vgopts: --track-fds=yes +stderr_filter: filter_fdleak +args: < /dev/null diff --git a/VEX/head20041019/corecheck/tests/filter_fdleak b/VEX/head20041019/corecheck/tests/filter_fdleak new file mode 100755 index 0000000000..b33fb2d730 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/filter_fdleak @@ -0,0 +1,29 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic | + +# Anonymise addresses +$dir/../../tests/filter_addresses | + +# Anonymise line numbers in mac_replace_strmem.c +sed "s/mac_replace_strmem.c:[0-9]*/mac_replace_strmem.c:.../" | + +$dir/../../tests/filter_test_paths | + +# Anonymise paths like "(in /foo/bar/libc-baz.so)" +sed "s/(in \/.*libc.*)$/(in \/...libc...)/" | + +# Anonymise paths like "xxx (../sysdeps/unix/sysv/linux/quux.c:129)" +sed "s/(\.\.\/sysdeps\/unix\/sysv\/linux\/.*\.c:[0-9]*)$/(in \/...libc...)/" | + +# Anonymise paths like "__libc_start_main (../foo/bar/libc-quux.c:129)" +sed "s/__libc_\(.*\) (.*)$/__libc_\1 (...libc...)/" | + +sed s/"^Open AF_UNIX socket [0-9]*: /Open AF_UNIX socket .: /" | +sed s/"^Open \(AF_UNIX socket\|file descriptor\) [0-9]*: \/dev\/null/Open \\1 .: \/dev\/null/" | +sed s/"^Open \(AF_UNIX socket\|file descriptor\) [0-9]*: \/tmp\/\(sock\|data1\|data2\|file\)\.[0-9]*/Open \\1 .: \/tmp\/\\2/" | +sed s/"^Open file descriptor [0-9]*: .*/Open file descriptor .: ./" | +sed s/"^Open file descriptor [0-9]*:$/Open file descriptor .:/" | +sed s/"127.0.0.1:[0-9]*/127.0.0.1:.../g" diff --git a/VEX/head20041019/corecheck/tests/filter_stderr b/VEX/head20041019/corecheck/tests/filter_stderr new file mode 100755 index 0000000000..76189819f2 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/filter_stderr @@ -0,0 +1,5 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic diff --git a/VEX/head20041019/corecheck/tests/pth_atfork1.c b/VEX/head20041019/corecheck/tests/pth_atfork1.c new file mode 100644 index 0000000000..b9305808c8 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_atfork1.c @@ -0,0 +1,106 @@ +/* Tests for fork in multi-threaded environment. + Copyright (C) 2000 Free Software Foundation, Inc. + Contributed by Ulrich Drepper , 2000. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include +#include +#include +#include +#include +#include +#include + +enum +{ + PREPARE_BIT = 1, + PARENT_BIT = 2, + CHILD_BIT = 4 +}; + +static int var; + +static void +prepare (void) +{ + var |= PREPARE_BIT; +} + +static void +parent (void) +{ + var |= PARENT_BIT; +} + +static void +child (void) +{ + var |= CHILD_BIT; +} + + +static void *thread (void *arg); + + +int +main (void) +{ + pthread_t th; + void *res; + + pthread_atfork (prepare, parent, child); + + if (pthread_create (&th, NULL, thread, NULL) != 0) + error (EXIT_FAILURE, 0, "cannot create thread"); + + pthread_join (th, &res); + + if ( ( int ) ( long int ) res != 0 ) + error(EXIT_FAILURE, 0, "pthread_join res != 0" ); + + printf ( "all ok\n" ); + return 0; +} + + +static void * +thread (void *arg) +{ + int status; + pid_t pid; + + pid = fork (); + if (pid == 0) + { + /* We check whether the `prepare' and `child' function ran. */ + exit (var != (PREPARE_BIT | CHILD_BIT)); + } + else if (pid == (pid_t) -1) + error (EXIT_FAILURE, errno, "cannot fork"); + + if (waitpid (pid, &status, 0) != pid) + error (EXIT_FAILURE, errno, "wrong child"); + + if (WTERMSIG (status) != 0) + error (EXIT_FAILURE, 0, "Child terminated incorrectly"); + status = WEXITSTATUS (status); + + if (status == 0) + status = var != (PREPARE_BIT | PARENT_BIT); + + return (void *) (long int) status; +} diff --git a/VEX/head20041019/corecheck/tests/pth_atfork1.stderr.exp b/VEX/head20041019/corecheck/tests/pth_atfork1.stderr.exp new file mode 100644 index 0000000000..4201442c27 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_atfork1.stderr.exp @@ -0,0 +1,5 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/pth_atfork1.stdout.exp b/VEX/head20041019/corecheck/tests/pth_atfork1.stdout.exp new file mode 100644 index 0000000000..d48ce72996 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_atfork1.stdout.exp @@ -0,0 +1 @@ +all ok diff --git a/VEX/head20041019/corecheck/tests/pth_atfork1.vgtest b/VEX/head20041019/corecheck/tests/pth_atfork1.vgtest new file mode 100644 index 0000000000..237ff87d75 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_atfork1.vgtest @@ -0,0 +1 @@ +prog: pth_atfork1 diff --git a/VEX/head20041019/corecheck/tests/pth_cancel1.c b/VEX/head20041019/corecheck/tests/pth_cancel1.c new file mode 100644 index 0000000000..d1b93e34b8 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_cancel1.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +static void thread_cleanup(void *arg) +{ + printf("cleaning up %p\n", arg); + + return; +} + +static void *thread_main(void *arg) +{ + pthread_cleanup_push(thread_cleanup, (void *)0x1234); + pthread_cleanup_push(thread_cleanup, (void *)0x5678); + + if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) != 0) + { + perror("pthread_setcanceltype"); + return NULL; + } + + if (pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) != 0) + { + perror("pthread_setcancelstate"); + return NULL; + } + + pause(); + + pthread_cleanup_pop(0); + pthread_cleanup_pop(0); + + return NULL; +} + +int main(int argc, char **argv) +{ + pthread_t tid; + void *result; + + if (pthread_create(&tid, NULL, thread_main, NULL) != 0) + { + perror("pthread_create"); + exit(1); + } + + sleep(1); + + if (pthread_cancel(tid) != 0) + { + perror("pthread_cancel"); + exit(1); + } + + if (pthread_join(tid, &result) != 0) + { + perror("pthread_join"); + exit(1); + } + + printf("result = %p\n", result); + + exit(0); +} diff --git a/VEX/head20041019/corecheck/tests/pth_cancel1.stderr.exp b/VEX/head20041019/corecheck/tests/pth_cancel1.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_cancel1.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/pth_cancel1.stdout.exp b/VEX/head20041019/corecheck/tests/pth_cancel1.stdout.exp new file mode 100644 index 0000000000..f0e1e4e3a7 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_cancel1.stdout.exp @@ -0,0 +1,3 @@ +cleaning up 0x5678 +cleaning up 0x1234 +result = 0xffffffff diff --git a/VEX/head20041019/corecheck/tests/pth_cancel1.vgtest b/VEX/head20041019/corecheck/tests/pth_cancel1.vgtest new file mode 100644 index 0000000000..f224af4270 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_cancel1.vgtest @@ -0,0 +1 @@ +prog: pth_cancel1 diff --git a/VEX/head20041019/corecheck/tests/pth_cancel2.c b/VEX/head20041019/corecheck/tests/pth_cancel2.c new file mode 100644 index 0000000000..63fd75a3f3 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_cancel2.c @@ -0,0 +1,101 @@ +/******************************************************** + * An example source module to accompany... + * + * "Using POSIX Threads: Programming with Pthreads" + * by Brad nichols, Dick Buttlar, Jackie Farrell + * O'Reilly & Associates, Inc. + * + ******************************************************** + * async_safe -- + * + * Example showing macro wrappers for calling non-async + * safe routines when the caller has asynchronous + * cancellation turned on + */ + +#include +#include +#include +#include +#include +#include + +#include + + +#define async_cancel_safe_read(fd,buf,amt) \ + { \ + int oldtype; \ + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); \ + if (read(fd,buf,amt) < 0) \ + perror("read"),exit(1); \ + pthread_setcanceltype(oldtype,NULL); \ + pthread_testcancel(); \ + } + + +#define async_cancel_safe_write(fd,buf,amt) \ + { \ + int oldtype; \ + pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); \ + if (write(fd,buf,amt) < 0) \ + perror("write"), exit(1); \ + pthread_setcanceltype(oldtype,NULL); \ + pthread_testcancel(); \ + } + + +static int fd; + +void *io(void *arg) +{ + int *fd2=(int *)arg; + char buf[20]="String"; + int amt=20; + + for (;;) { + async_cancel_safe_write(*fd2,buf,amt); + async_cancel_safe_read(*fd2,buf,amt); + } + return(NULL); +} + +void *killer(void *arg) +{ + pthread_t * target = (pthread_t *)arg; + sleep(1); + pthread_cancel(*target); + return(NULL); +} + +extern int +main(void) +{ + pthread_t io_thread, killer_thread; + + // extern void *io(void *); + // extern void *killer(void *); + + if ((fd = open(".ktemp",O_CREAT | O_RDWR, 0666)) < 0) + perror("open"), exit(1); + + pthread_create(&io_thread, + NULL, + io, + (void *)&fd); + pthread_create(&killer_thread, + NULL, + killer, + (void *)&io_thread); + + pthread_join(io_thread, NULL); + + pthread_join(killer_thread,NULL); + + if ((close(fd)) < 0) + perror("close"),exit(1); + if ((unlink(".ktemp")) < 0) + perror("unlink"),exit(1); + + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/pth_cancel2.stderr.exp b/VEX/head20041019/corecheck/tests/pth_cancel2.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_cancel2.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/pth_cancel2.vgtest b/VEX/head20041019/corecheck/tests/pth_cancel2.vgtest new file mode 100644 index 0000000000..7bb8e1b71f --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_cancel2.vgtest @@ -0,0 +1 @@ +prog: pth_cancel2 diff --git a/VEX/head20041019/corecheck/tests/pth_cvsimple.c b/VEX/head20041019/corecheck/tests/pth_cvsimple.c new file mode 100644 index 0000000000..3bb5085f89 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_cvsimple.c @@ -0,0 +1,83 @@ +/******************************************************** + * An example source module to accompany... + * + * "Using POSIX Threads: Programming with Pthreads" + * by Brad nichols, Dick Buttlar, Jackie Farrell + * O'Reilly & Associates, Inc. + * + ******************************************************** + * + * cvsimple.c + * + * Demonstrates pthread cancellation. + * + */ + +#include +#include + +#define NUM_THREADS 3 +#define TCOUNT 10 +#define COUNT_THRES 12 + +int count = 0; +int thread_ids[3] = {0,1,2}; +pthread_mutex_t count_lock=PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t count_hit_threshold=PTHREAD_COND_INITIALIZER; + +void *inc_count(void *idp) +{ + int i=0; + int *my_id = idp; + + for (i=0; i +#include + +static void *th(void *v) +{ + sleep(1); + pthread_exit(0); +} + +int main() +{ + pthread_t a; + + pthread_create(&a, NULL, th, NULL); + pthread_create(&a, NULL, th, NULL); + pthread_create(&a, NULL, th, NULL); + pthread_create(&a, NULL, th, NULL); + + pthread_exit(0); +} diff --git a/VEX/head20041019/corecheck/tests/pth_exit.stderr.exp b/VEX/head20041019/corecheck/tests/pth_exit.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_exit.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/pth_exit.vgtest b/VEX/head20041019/corecheck/tests/pth_exit.vgtest new file mode 100644 index 0000000000..07cde7606a --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_exit.vgtest @@ -0,0 +1 @@ +prog: pth_exit diff --git a/VEX/head20041019/corecheck/tests/pth_exit2.c b/VEX/head20041019/corecheck/tests/pth_exit2.c new file mode 100644 index 0000000000..e1b9e7ee0e --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_exit2.c @@ -0,0 +1,6 @@ +#include + +int main() +{ + pthread_exit(0); +} diff --git a/VEX/head20041019/corecheck/tests/pth_exit2.stderr.exp b/VEX/head20041019/corecheck/tests/pth_exit2.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_exit2.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/pth_exit2.vgtest b/VEX/head20041019/corecheck/tests/pth_exit2.vgtest new file mode 100644 index 0000000000..1a5457904d --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_exit2.vgtest @@ -0,0 +1 @@ +prog: pth_exit2 diff --git a/VEX/head20041019/corecheck/tests/pth_mutexspeed.c b/VEX/head20041019/corecheck/tests/pth_mutexspeed.c new file mode 100644 index 0000000000..ad263e193d --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_mutexspeed.c @@ -0,0 +1,19 @@ + +#include +#include +#include + +int main ( void ) +{ + const int n = 100000; + int i, r; + pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER; + printf("begin %d lock--unlocks\n", n); + for (i = 0; i < n; i++) { + r = pthread_mutex_lock(&mx); + r |= pthread_mutex_unlock(&mx); + assert(r == 0); + } + printf("done %d lock--unlocks\n", n); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/pth_mutexspeed.stderr.exp b/VEX/head20041019/corecheck/tests/pth_mutexspeed.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_mutexspeed.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/pth_mutexspeed.stdout.exp b/VEX/head20041019/corecheck/tests/pth_mutexspeed.stdout.exp new file mode 100644 index 0000000000..8208168441 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_mutexspeed.stdout.exp @@ -0,0 +1,2 @@ +begin 100000 lock--unlocks +done 100000 lock--unlocks diff --git a/VEX/head20041019/corecheck/tests/pth_mutexspeed.vgtest b/VEX/head20041019/corecheck/tests/pth_mutexspeed.vgtest new file mode 100644 index 0000000000..3daee3a75d --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_mutexspeed.vgtest @@ -0,0 +1 @@ +prog: pth_mutexspeed diff --git a/VEX/head20041019/corecheck/tests/pth_once.c b/VEX/head20041019/corecheck/tests/pth_once.c new file mode 100644 index 0000000000..75f6a1f316 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_once.c @@ -0,0 +1,82 @@ +/******************************************************** + * An example source module to accompany... + * + * "Using POSIX Threads: Programming with Pthreads" + * by Brad nichols, Dick Buttlar, Jackie Farrell + * O'Reilly & Associates, Inc. + * + ******************************************************** + * once_exam.c + * + * An example of using the pthreads_once() call to execute an + * initialization procedure. + * + * A program spawns multiple threads and each one tries to + * execute the routine welcome() using the once call. Only + * the first thread into the once routine will actually + * execute welcome(). + * + * The program's main thread synchronizes its exit with the + * exit of the threads using the pthread_join() operation. + * +*/ + +#include +#include +#include +#include + +#include + +#define NUM_THREADS 10 + +static pthread_once_t welcome_once_block = PTHREAD_ONCE_INIT; + +void welcome(void) +{ + printf("welcome: Welcome\n"); +} + +void *identify_yourself(void *arg) +{ + int *pid=(int *)arg; + int rtn; + + if ((rtn = pthread_once(&welcome_once_block, + welcome)) != 0) { + fprintf(stderr, "pthread_once failed with %d",rtn); + pthread_exit((void *)NULL); + } + printf("identify_yourself: Hi, I'm thread # %d\n",*pid); + return(NULL); +} + +extern int +main(void) +{ + int *id_arg, thread_num, rtn; + pthread_t threads[NUM_THREADS]; + + id_arg = (int *)malloc(NUM_THREADS*sizeof(int)); + + for (thread_num = 0; thread_num < NUM_THREADS; (thread_num)++) { + + id_arg[thread_num] = thread_num; + + if (( rtn = pthread_create(&threads[thread_num], + NULL, + identify_yourself, + (void *) &(id_arg[thread_num]))) + != 0) { + fprintf(stderr, "pthread_create failed with %d",rtn); + exit(1); + } + } + + for (thread_num = 0; thread_num < NUM_THREADS; thread_num++) { + pthread_join(threads[thread_num], NULL); + printf("main: joined to thread %d\n", thread_num); + } + printf("main: Goodbye\n"); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/pth_once.stderr.exp b/VEX/head20041019/corecheck/tests/pth_once.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_once.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/pth_once.stdout.exp b/VEX/head20041019/corecheck/tests/pth_once.stdout.exp new file mode 100644 index 0000000000..97e25d16c0 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_once.stdout.exp @@ -0,0 +1,22 @@ +welcome: Welcome +identify_yourself: Hi, I'm thread # 0 +identify_yourself: Hi, I'm thread # 1 +identify_yourself: Hi, I'm thread # 2 +identify_yourself: Hi, I'm thread # 3 +identify_yourself: Hi, I'm thread # 4 +identify_yourself: Hi, I'm thread # 5 +identify_yourself: Hi, I'm thread # 6 +identify_yourself: Hi, I'm thread # 7 +identify_yourself: Hi, I'm thread # 8 +identify_yourself: Hi, I'm thread # 9 +main: joined to thread 0 +main: joined to thread 1 +main: joined to thread 2 +main: joined to thread 3 +main: joined to thread 4 +main: joined to thread 5 +main: joined to thread 6 +main: joined to thread 7 +main: joined to thread 8 +main: joined to thread 9 +main: Goodbye diff --git a/VEX/head20041019/corecheck/tests/pth_once.vgtest b/VEX/head20041019/corecheck/tests/pth_once.vgtest new file mode 100644 index 0000000000..50bc5b4eb1 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_once.vgtest @@ -0,0 +1 @@ +prog: pth_once diff --git a/VEX/head20041019/corecheck/tests/pth_rwlock.c b/VEX/head20041019/corecheck/tests/pth_rwlock.c new file mode 100644 index 0000000000..75486ea983 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_rwlock.c @@ -0,0 +1,31 @@ +#define _XOPEN_SOURCE 600 + +#include +#include +#include +#include + +#define LOCKS 2000 + +int main(int argc, char **argv) +{ + pthread_rwlock_t locks[LOCKS]; + int n; + int e; + + for (n = 0; n < LOCKS; n++) { + if ((e = pthread_rwlock_init(locks + n, NULL)) != 0) { + fprintf(stderr, "pthread_rwlock_init[%d]: %s\n", n, strerror(e)); + exit(1); + } + } + + for (n = 0; n < LOCKS; n++) { + if ((e = pthread_rwlock_destroy(locks + n)) != 0) { + fprintf(stderr, "pthread_rwlock_destroy[%d]: %s\n", n, strerror(e)); + exit(1); + } + } + + exit(0); +} diff --git a/VEX/head20041019/corecheck/tests/pth_rwlock.stderr.exp b/VEX/head20041019/corecheck/tests/pth_rwlock.stderr.exp new file mode 100644 index 0000000000..67c7b3864f --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_rwlock.stderr.exp @@ -0,0 +1,12 @@ + +warning: Valgrind's pthread_cond_destroy is incomplete + (it doesn't check if the cond is waited on) + your program may misbehave as a result +warning: Valgrind's pthread_cond_destroy is incomplete + (it doesn't check if the cond is waited on) + your program may misbehave as a result +warning: Valgrind's pthread_cond_destroy is incomplete + (it doesn't check if the cond is waited on) + your program may misbehave as a result + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/pth_rwlock.vgtest b/VEX/head20041019/corecheck/tests/pth_rwlock.vgtest new file mode 100644 index 0000000000..3a66473496 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/pth_rwlock.vgtest @@ -0,0 +1 @@ +prog: pth_rwlock diff --git a/VEX/head20041019/corecheck/tests/res_search.c b/VEX/head20041019/corecheck/tests/res_search.c new file mode 100644 index 0000000000..b4ac82bd1b --- /dev/null +++ b/VEX/head20041019/corecheck/tests/res_search.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include + +void* fn(void* arg) +{ + char* dn = (char*)arg; + + unsigned char buff[8000]; + + if(-1 == res_search(dn, 1, 1, buff, 8000)) + { + printf("Error: res_search()\n"); + } + else + { + printf("Success!\n"); + } + return 0; +} + +int main(int argc, char** argv) +{ + pthread_t pid; + if(2 != argc) + { + printf("Usage: %s \n", argv[0]); + return 1; + } + + _res.options |= RES_DEBUG; + if(0 != res_init()) + { + printf("Error: res_init()\n"); + return(1); + } +#if 1 + /* Test it in a different thread -- the failure case */ + if(0 != pthread_create(&pid, 0, fn, (void*)argv[1])) + { + printf("Failed to create thread.\n"); + return 1; + } + + pthread_join(pid, 0); +#else + { + unsigned char buff[8000]; + + if(-1 == res_search(argv[1], 1, 1, buff, 8000)) + { + printf("Error: res_search()\n"); + } + else + { + printf("Success!\n"); + } + } +#endif + return 0; +} + + diff --git a/VEX/head20041019/corecheck/tests/res_search.stderr.exp b/VEX/head20041019/corecheck/tests/res_search.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/corecheck/tests/res_search.stdout.exp b/VEX/head20041019/corecheck/tests/res_search.stdout.exp new file mode 100644 index 0000000000..f985b46aff --- /dev/null +++ b/VEX/head20041019/corecheck/tests/res_search.stdout.exp @@ -0,0 +1 @@ +Success! diff --git a/VEX/head20041019/corecheck/tests/res_search.vgtest b/VEX/head20041019/corecheck/tests/res_search.vgtest new file mode 100644 index 0000000000..879ce278b5 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/res_search.vgtest @@ -0,0 +1,3 @@ +prog: res_search +args: www.yahoo.com +vgopts: -q diff --git a/VEX/head20041019/corecheck/tests/sigkill.c b/VEX/head20041019/corecheck/tests/sigkill.c new file mode 100644 index 0000000000..4fefed3779 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/sigkill.c @@ -0,0 +1,42 @@ + +#include +#include +#include +#include + +static void +abend (int sig) +{ + printf ("Abended on signal %d\n", sig); + exit (2); +} + +int +main (void) +{ + struct sigaction sa; + + int i; + int rc; + for (i = 1; i <= 65; i++) { + // skip signals 32 and 33: some systems say "warning, ignored attempt + // to catch 32 because it's used internally by Valgrind", others say + // "invalid argument". + if (i == 32 || i == 33) { + continue; + } // different systems + sa.sa_flags = 0; + sigemptyset( &sa.sa_mask ); + sa.sa_handler = abend; + fprintf(stderr,"setting signal %d: ", i); + rc = sigaction (i /*SIGKILL*/, &sa, NULL); + if (rc) perror (""); + else fprintf(stderr,"Success\n"); + fprintf(stderr,"getting signal %d: ", i); + rc = sigaction (i /*SIGKILL*/, NULL, &sa); + if (rc) perror (""); + else fprintf(stderr,"Success\n"); + fprintf(stderr,"\n"); + } + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/sigkill.stderr.exp b/VEX/head20041019/corecheck/tests/sigkill.stderr.exp new file mode 100644 index 0000000000..839d09bdb9 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/sigkill.stderr.exp @@ -0,0 +1,198 @@ + +setting signal 1: Success +getting signal 1: Success + +setting signal 2: Success +getting signal 2: Success + +setting signal 3: Success +getting signal 3: Success + +setting signal 4: Success +getting signal 4: Success + +setting signal 5: Success +getting signal 5: Success + +setting signal 6: Success +getting signal 6: Success + +setting signal 7: Success +getting signal 7: Success + +setting signal 8: Success +getting signal 8: Success + +setting signal 9: Warning: ignored attempt to set SIGKILL handler in sigaction(); + the SIGKILL signal is uncatchable +Invalid argument +getting signal 9: Success + +setting signal 10: Success +getting signal 10: Success + +setting signal 11: Success +getting signal 11: Success + +setting signal 12: Success +getting signal 12: Success + +setting signal 13: Success +getting signal 13: Success + +setting signal 14: Success +getting signal 14: Success + +setting signal 15: Success +getting signal 15: Success + +setting signal 16: Success +getting signal 16: Success + +setting signal 17: Success +getting signal 17: Success + +setting signal 18: Success +getting signal 18: Success + +setting signal 19: Warning: ignored attempt to set SIGSTOP handler in sigaction(); + the SIGSTOP signal is uncatchable +Invalid argument +getting signal 19: Success + +setting signal 20: Success +getting signal 20: Success + +setting signal 21: Success +getting signal 21: Success + +setting signal 22: Success +getting signal 22: Success + +setting signal 23: Success +getting signal 23: Success + +setting signal 24: Success +getting signal 24: Success + +setting signal 25: Success +getting signal 25: Success + +setting signal 26: Success +getting signal 26: Success + +setting signal 27: Success +getting signal 27: Success + +setting signal 28: Success +getting signal 28: Success + +setting signal 29: Success +getting signal 29: Success + +setting signal 30: Success +getting signal 30: Success + +setting signal 31: Success +getting signal 31: Success + +setting signal 34: Success +getting signal 34: Success + +setting signal 35: Success +getting signal 35: Success + +setting signal 36: Success +getting signal 36: Success + +setting signal 37: Success +getting signal 37: Success + +setting signal 38: Success +getting signal 38: Success + +setting signal 39: Success +getting signal 39: Success + +setting signal 40: Success +getting signal 40: Success + +setting signal 41: Success +getting signal 41: Success + +setting signal 42: Success +getting signal 42: Success + +setting signal 43: Success +getting signal 43: Success + +setting signal 44: Success +getting signal 44: Success + +setting signal 45: Success +getting signal 45: Success + +setting signal 46: Success +getting signal 46: Success + +setting signal 47: Success +getting signal 47: Success + +setting signal 48: Success +getting signal 48: Success + +setting signal 49: Success +getting signal 49: Success + +setting signal 50: Success +getting signal 50: Success + +setting signal 51: Success +getting signal 51: Success + +setting signal 52: Success +getting signal 52: Success + +setting signal 53: Success +getting signal 53: Success + +setting signal 54: Success +getting signal 54: Success + +setting signal 55: Success +getting signal 55: Success + +setting signal 56: Success +getting signal 56: Success + +setting signal 57: Success +getting signal 57: Success + +setting signal 58: Success +getting signal 58: Success + +setting signal 59: Success +getting signal 59: Success + +setting signal 60: Success +getting signal 60: Success + +setting signal 61: Success +getting signal 61: Success + +setting signal 62: Success +getting signal 62: Success + +setting signal 63: Success +getting signal 63: Success + +setting signal 64: Success +getting signal 64: Success + +setting signal 65: Warning: bad signal number 65 in sigaction() +Invalid argument +getting signal 65: Warning: bad signal number 65 in sigaction() +Invalid argument + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/sigkill.vgtest b/VEX/head20041019/corecheck/tests/sigkill.vgtest new file mode 100644 index 0000000000..a68143011b --- /dev/null +++ b/VEX/head20041019/corecheck/tests/sigkill.vgtest @@ -0,0 +1 @@ +prog: sigkill diff --git a/VEX/head20041019/corecheck/tests/vgprintf.c b/VEX/head20041019/corecheck/tests/vgprintf.c new file mode 100644 index 0000000000..0a023fb532 --- /dev/null +++ b/VEX/head20041019/corecheck/tests/vgprintf.c @@ -0,0 +1,10 @@ +#include "valgrind.h" +#include + +int +main (int argc, char **argv) +{ + int x = VALGRIND_PRINTF("Yo"); + printf ("%d\n", x); + return 0; +} diff --git a/VEX/head20041019/corecheck/tests/vgprintf.stderr.exp b/VEX/head20041019/corecheck/tests/vgprintf.stderr.exp new file mode 100644 index 0000000000..a9e6b58c1a --- /dev/null +++ b/VEX/head20041019/corecheck/tests/vgprintf.stderr.exp @@ -0,0 +1,4 @@ + +Yo + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/VEX/head20041019/corecheck/tests/vgprintf.stdout.exp b/VEX/head20041019/corecheck/tests/vgprintf.stdout.exp new file mode 100644 index 0000000000..9c558e357c --- /dev/null +++ b/VEX/head20041019/corecheck/tests/vgprintf.stdout.exp @@ -0,0 +1 @@ +. diff --git a/VEX/head20041019/corecheck/tests/vgprintf.vgtest b/VEX/head20041019/corecheck/tests/vgprintf.vgtest new file mode 100644 index 0000000000..a8bb288f2a --- /dev/null +++ b/VEX/head20041019/corecheck/tests/vgprintf.vgtest @@ -0,0 +1,2 @@ +prog: vgprintf +stdout_filter: ../../tests/filter_numbers diff --git a/VEX/head20041019/coregrind/.cvsignore b/VEX/head20041019/coregrind/.cvsignore new file mode 100644 index 0000000000..2f1b6d1639 --- /dev/null +++ b/VEX/head20041019/coregrind/.cvsignore @@ -0,0 +1,8 @@ +Makefile.in +Makefile +valgrind +stage2 +vg_toolint.h +vg_toolint.c +vg_intercept.c +vg_replace_malloc.c diff --git a/VEX/head20041019/coregrind/CVS/Entries b/VEX/head20041019/coregrind/CVS/Entries new file mode 100644 index 0000000000..ff96db643b --- /dev/null +++ b/VEX/head20041019/coregrind/CVS/Entries @@ -0,0 +1,55 @@ +/.cvsignore/1.4/Tue Jun 15 10:54:38 2004// +/Makefile.am/1.90/Mon Oct 18 11:52:17 2004// +/core.h/1.42/Mon Oct 18 17:41:36 2004// +/core_asm.h/1.3/Sun Sep 5 20:39:51 2004// +/dosyms/1.2/Sun Dec 15 02:05:02 2002// +/gen_intercepts.pl/1.1/Fri Apr 16 23:02:28 2004// +/gen_toolint.pl/1.5/Thu Sep 2 08:51:40 2004// +/stage1.c/1.25/Mon Oct 18 15:34:13 2004// +/toolfuncs.def/1.4/Sat Sep 11 15:11:47 2004// +/ume.c/1.31/Wed Oct 13 17:55:30 2004// +/ume.h/1.13/Mon Oct 18 11:52:17 2004// +/valgrind.vs/1.5/Fri Sep 3 13:45:27 2004// +/vg_cpuid.S/1.4/Fri Sep 10 14:23:58 2004// +/vg_default.c/1.24/Wed Sep 1 23:58:14 2004// +/vg_demangle.c/1.8/Wed Sep 1 23:58:14 2004// +/vg_dispatch.S/1.18/Thu Sep 2 15:37:38 2004// +/vg_dummy_profile.c/1.10/Wed Sep 1 23:58:14 2004// +/vg_dwarf.c/1.5/Wed Sep 1 23:58:14 2004// +/vg_errcontext.c/1.63/Mon Oct 18 15:47:18 2004// +/vg_execontext.c/1.19/Sun Sep 5 21:32:36 2004// +/vg_from_ucode.c/1.84/Fri Sep 3 13:45:27 2004// +/vg_hashtable.c/1.10/Wed Sep 1 23:58:14 2004// +/vg_helpers.S/1.34/Thu Sep 2 15:37:38 2004// +/vg_instrument.c/1.14/Thu Sep 2 08:51:40 2004// +/vg_intercept.c.base/1.2/Thu Sep 2 00:31:02 2004// +/vg_libpthread.c/1.173/Tue Oct 19 11:38:48 2004// +/vg_libpthread.vs/1.10/Mon Aug 23 18:05:51 2004// +/vg_libpthread_unimp.c/1.49/Sun Oct 17 15:00:20 2004// +/vg_main.c/1.217/Mon Oct 18 11:52:17 2004// +/vg_malloc2.c/1.32/Wed Sep 1 23:58:14 2004// +/vg_memory.c/1.70/Thu Oct 14 13:41:28 2004// +/vg_messages.c/1.15/Wed Sep 1 23:58:14 2004// +/vg_mylibc.c/1.95/Mon Oct 18 18:56:25 2004// +/vg_needs.c/1.20/Sat Oct 9 18:50:16 2004// +/vg_procselfmaps.c/1.13/Tue Sep 7 10:17:01 2004// +/vg_proxylwp.c/1.25/Mon Oct 18 17:41:36 2004// +/vg_replace_malloc.c.base/1.8/Mon Oct 18 17:35:35 2004// +/vg_scheduler.c/1.191/Sun Oct 17 15:18:22 2004// +/vg_signals.c/1.96/Mon Oct 18 14:08:16 2004// +/vg_skiplist.c/1.6/Wed Sep 1 23:58:14 2004// +/vg_stabs.c/1.20/Thu Oct 7 08:33:29 2004// +/vg_symtab2.c/1.91/Mon Oct 18 15:47:18 2004// +/vg_symtab2.h/1.8/Thu Sep 2 08:51:40 2004// +/vg_symtypes.c/1.9/Thu Oct 7 08:21:38 2004// +/vg_symtypes.h/1.3/Sun Jan 4 16:43:20 2004// +/vg_syscall.S/1.15/Fri Sep 10 14:23:58 2004// +/vg_syscalls.c/1.147/Mon Oct 18 17:00:29 2004// +/vg_to_ucode.c/1.148/Mon Oct 18 15:47:18 2004// +/vg_translate.c/1.91/Wed Oct 13 09:47:23 2004// +/vg_transtab.c/1.33/Thu Oct 14 13:41:28 2004// +/vg_unsafe.h/1.36/Thu Oct 14 11:18:26 2004// +D/demangle//// +D/docs//// +D/x86//// +D/x86-linux//// diff --git a/VEX/head20041019/coregrind/CVS/Repository b/VEX/head20041019/coregrind/CVS/Repository new file mode 100644 index 0000000000..4f04ba05dc --- /dev/null +++ b/VEX/head20041019/coregrind/CVS/Repository @@ -0,0 +1 @@ +valgrind/coregrind diff --git a/VEX/head20041019/coregrind/CVS/Root b/VEX/head20041019/coregrind/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/coregrind/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/coregrind/CVS/Template b/VEX/head20041019/coregrind/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/coregrind/Makefile.am b/VEX/head20041019/coregrind/Makefile.am new file mode 100644 index 0000000000..56c006f958 --- /dev/null +++ b/VEX/head20041019/coregrind/Makefile.am @@ -0,0 +1,159 @@ +include $(top_srcdir)/Makefile.all.am +include $(top_srcdir)/Makefile.core-AM_CPPFLAGS.am + +SUBDIRS = $(VG_ARCH) $(VG_PLATFORM) demangle . docs + +AM_CPPFLAGS += -DVG_LIBDIR="\"$(valdir)"\" -I$(srcdir)/demangle \ + -DKICKSTART_BASE=$(KICKSTART_BASE) \ + -DVG_PLATFORM="\"$(VG_PLATFORM)"\" +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -O -fno-omit-frame-pointer \ + @PREFERRED_STACK_BOUNDARY@ -g -DELFSZ=32 +AM_CCASFLAGS = $(add_includes) -I.. + +default.supp: $(SUPP_FILES) + +bin_PROGRAMS = \ + valgrind + +val_PROGRAMS = \ + stage2 \ + libpthread.so \ + vg_inject.so + +EXTRA_DIST = \ + vg_libpthread.vs valgrind.vs \ + gen_toolint.pl toolfuncs.def \ + gen_intercepts.pl vg_replace_malloc.c.base vg_intercept.c.base + +BUILT_SOURCES = vg_toolint.c vg_toolint.h +CLEANFILES = vg_toolint.c vg_toolint.h vg_replace_malloc.c vg_intercept.c + +valgrind_SOURCES = \ + ume.c \ + \ + stage1.c \ + ${VG_ARCH}/jmp_with_stack.c +valgrind_DEPENDENCIES = +valgrind_LDFLAGS=-static -g +valgrind_LDADD= + +# Where stage2 will be put. +# Nb: Hard-wiring this sucks. A configure-time test would be better. A +# load-time test would be even better, but would require building stage2 in +# a position-independent way... +KICKSTART_BASE=0xb0000000 + +stage2_SOURCES = \ + ume.c \ + \ + vg_scheduler.c \ + vg_default.c \ + vg_demangle.c \ + vg_dispatch.S \ + vg_errcontext.c \ + vg_execontext.c \ + vg_from_ucode.c \ + vg_hashtable.c \ + vg_helpers.S \ + vg_instrument.c \ + vg_main.c \ + vg_malloc2.c \ + vg_memory.c \ + vg_messages.c \ + vg_mylibc.c \ + vg_needs.c \ + vg_procselfmaps.c \ + vg_proxylwp.c \ + vg_dummy_profile.c \ + vg_signals.c \ + vg_symtab2.c \ + vg_dwarf.c \ + vg_stabs.c \ + vg_skiplist.c \ + vg_symtypes.c \ + vg_syscalls.c \ + vg_syscall.S \ + vg_to_ucode.c \ + vg_toolint.c \ + vg_translate.c \ + vg_transtab.c \ + vg_cpuid.S +stage2_DEPENDENCIES = $(srcdir)/valgrind.vs ${VG_ARCH}/stage2.lds +stage2_LDFLAGS=-Wl,--export-dynamic -g \ + -Wl,-defsym,kickstart_base=$(KICKSTART_BASE) \ + -Wl,-T,${VG_ARCH}/stage2.lds \ + -Wl,-version-script $(srcdir)/valgrind.vs +stage2_LDADD= \ + demangle/cp-demangle.o \ + demangle/cplus-dem.o \ + demangle/dyn-string.o \ + demangle/safe-ctype.o \ + ${VG_ARCH}/libarch.a \ + ${VG_PLATFORM}/libplatform.a \ + -ldl + +vg_intercept.c: $(srcdir)/gen_intercepts.pl $(srcdir)/vg_intercept.c.base + rm -f $@ + $(PERL) $(srcdir)/gen_intercepts.pl < $(srcdir)/vg_intercept.c.base > $@ + +vg_replace_malloc.c: $(srcdir)/gen_intercepts.pl $(srcdir)/vg_replace_malloc.c.base + rm -f $@ + $(PERL) $(srcdir)/gen_intercepts.pl < $(srcdir)/vg_replace_malloc.c.base > $@ + +vg_toolint.c: $(srcdir)/gen_toolint.pl $(srcdir)/toolfuncs.def ./Makefile + rm -f $@ + $(PERL) $(srcdir)/gen_toolint.pl callwrap < $(srcdir)/toolfuncs.def > $@ || rm -f $@ + $(PERL) $(srcdir)/gen_toolint.pl missingfuncs < $(srcdir)/toolfuncs.def >> $@ || rm -f $@ + $(PERL) $(srcdir)/gen_toolint.pl initfunc < $(srcdir)/toolfuncs.def >> $@ || rm -f $@ + $(PERL) $(srcdir)/gen_toolint.pl initdlsym < $(srcdir)/toolfuncs.def >> $@ || rm -f $@ + $(PERL) $(srcdir)/gen_toolint.pl structdef < $(srcdir)/toolfuncs.def >> $@ || rm -f $@ + +vg_toolint.h: $(srcdir)/gen_toolint.pl $(srcdir)/toolfuncs.def ./Makefile + rm -f $@ + $(PERL) $(srcdir)/gen_toolint.pl proto < $(srcdir)/toolfuncs.def > $@ || rm -f $@ + $(PERL) $(srcdir)/gen_toolint.pl struct < $(srcdir)/toolfuncs.def >> $@ || rm -f $@ + +libpthread_so_SOURCES = \ + vg_libpthread.c \ + vg_libpthread_unimp.c \ + vg_syscall.S +libpthread_so_DEPENDENCIES = $(srcdir)/vg_libpthread.vs +libpthread_so_LDFLAGS = -Werror -fno-omit-frame-pointer -UVG_LIBDIR \ + -shared -fpic -ldl \ + -Wl,-version-script $(srcdir)/vg_libpthread.vs \ + -Wl,-z,nodelete \ + -Wl,--soname=libpthread.so.0 + +vg_inject_so_SOURCES = \ + vg_intercept.c + +# Not really true, but we need to build vg_replace_malloc.o somehow +vg_inject_so_DEPENDENCIES = \ + vg_replace_malloc.o + +vg_inject_so_LDFLAGS = \ + -shared \ + -Wl,--soname,vg_inject.so \ + -Wl,-z,initfirst + +noinst_HEADERS = \ + core.h \ + core_asm.h \ + ume.h \ + vg_symtab2.h \ + vg_symtypes.h \ + vg_toolint.h \ + vg_unsafe.h + +MANUAL_DEPS = $(noinst_HEADERS) $(include_HEADERS) $(inplacedir)/libpthread.so.0 + +vg_replace_malloc.o vg_intercept.o vg_libpthread.o: CFLAGS += -fno-omit-frame-pointer -g -fpic + +all-local: + mkdir -p $(inplacedir) + for i in $(val_PROGRAMS); do \ + to=$(inplacedir)/$$(echo $$i | sed 's,libpthread.so,libpthread.so.0,'); \ + rm -f $$$to; \ + ln -sf ../$(subdir)/$$i $$to; \ + done + diff --git a/VEX/head20041019/coregrind/core.h b/VEX/head20041019/coregrind/core.h new file mode 100644 index 0000000000..a3dc1060eb --- /dev/null +++ b/VEX/head20041019/coregrind/core.h @@ -0,0 +1,1557 @@ + +/*--------------------------------------------------------------------*/ +/*--- A header file for all private parts of Valgrind's core. ---*/ +/*--- Include no other! (more or less...) ---*/ +/*--- core.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __CORE_H +#define __CORE_H + +/* + Header hierarchy: + + - core C files include core.h + - core asm files include core_asm.h + - tool C files include tool.h + - tool asm files include tool_asm.h + + - The hierarchy of the header files themselves is based around the + following rules: + + - core headers include tool headers + - generic headers include arch/OS/platform headers + - C headers include asm headers + + This gives the following hierarchy (only showing 'arch' headers, not + 'os' or 'platform' headers), where arrows indicate inclusion, and + $VG_ARCH==x86: + + + (include/x86/tool_arch_asm.h?) <----- coregrind/x86/core_arch_asm.h + ^ ^ ^ ^ + / \ / \ + / \ / \ + / \ / \ + include/tool_asm.h <-\---- coregrind/core_asm.h \ + ^ \ ^ \ + \ include/x86/tool_arch.h <--------coregrind/x86/core_arch.h + \ ^ \ ^ + \ / \ / + \ / \ / + \ / \ / + include/tool.h <------------ coregrind/core.h + + + Note that core.h contains the *declarations* of arch-specific functions + and variables, which can be used by the core_arch.h file of any + architecture. (The functions/variables are *defined* within arch/.) + However, arch-specific macros and types cannot go into core.h, because + there is no separation between declaration and definition for + macros/types, so they instead go into $VG_ARCH/core_arch.h. + + The tool-specific headers are all in include/ so they can be seen by any + external tools. +*/ + + +/* For system call numbers __NR_... */ +#include "vki_unistd.h" + +#include "core_asm.h" // asm stuff +#include "tool.h" // tool stuff +#include "core_arch.h" // arch-specific stuff, eg. x86/core_arch.h +#include "core_platform.h" // platform-specific stuff, + // eg. x86-linux/core_platform.h + +#include "valgrind.h" + +#undef SK_ +#define SK_(x) vgSkinInternal_##x + + +/* --------------------------------------------------------------------- + Build options and table sizes. You should be able to change these + options or sizes, recompile, and still have a working system. + ------------------------------------------------------------------ */ + +/* Constants for the slow translation lookup cache. */ +#define VG_TRANSTAB_SLOW_BITS 11 +#define VG_TRANSTAB_SLOW_SIZE (1 << VG_TRANSTAB_SLOW_BITS) +#define VG_TRANSTAB_SLOW_MASK ((VG_TRANSTAB_SLOW_SIZE) - 1) + +/* Size of a buffer used for creating messages. */ +#define M_VG_MSGBUF 10000 + +/* Size of a smallish table used to read /proc/self/map entries. */ +#define M_PROCMAP_BUF 50000 + +/* Max length of pathname to a .so/executable file. */ +#define M_VG_LIBNAMESTR 100 + +/* Max length of a text fragment used to construct error messages. */ +#define M_VG_ERRTXT 4096 + +/* Max length of the string copied from env var VG_ARGS at startup. */ +#define M_VG_CMDLINE_STRLEN 1000 + +/* Max number of options for Valgrind which we can handle. */ +#define M_VG_CMDLINE_OPTS 100 + +/* After this many different unsuppressed errors have been observed, + be more conservative about collecting new ones. */ +#define M_VG_COLLECT_ERRORS_SLOWLY_AFTER 50 + +/* After this many different unsuppressed errors have been observed, + stop collecting errors at all, and tell the user their program is + evidently a steaming pile of camel dung. */ +#define M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN 300 + +/* After this many total errors have been observed, stop collecting + errors at all. Counterpart to M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN. */ +#define M_VG_COLLECT_NO_ERRORS_AFTER_FOUND 30000 + +/* The maximum number of calls we're prepared to save in a + backtrace. */ +#define VG_DEEPEST_BACKTRACE 50 + +/* Number of lists in which we keep track of ExeContexts. Should be + prime. */ +#define VG_N_EC_LISTS 4999 /* a prime number */ + +/* Defines the thread-scheduling timeslice, in terms of the number of + basic blocks we attempt to run each thread for. Smaller values + give finer interleaving but much increased scheduling overheads. */ +#define VG_SCHEDULING_QUANTUM 50000 + +/* Number of file descriptors that Valgrind tries to reserve for + it's own use - two per thread plues a small number of extras. */ +#define VG_N_RESERVED_FDS (VG_N_THREADS*2 + 4) + +/* Stack size for a thread. We try and check that they do not go + beyond it. */ +#define VG_PTHREAD_STACK_SIZE (1 << 20) + +/* Number of entries in each thread's cleanup stack. */ +#define VG_N_CLEANUPSTACK 16 + +/* Number of entries in each thread's fork-handler stack. */ +#define VG_N_FORKHANDLERSTACK 4 + +/* Max number of callers for context in a suppression. */ +#define VG_N_SUPP_CALLERS 4 + +/* Numer of entries in each thread's signal queue. */ +#define VG_N_SIGNALQUEUE 8 + +/* Useful macros */ +/* a - alignment - must be a power of 2 */ +#define ROUNDDN(p, a) ((Addr)(p) & ~((a)-1)) +#define ROUNDUP(p, a) ROUNDDN((p)+(a)-1, (a)) +#define PGROUNDDN(p) ROUNDDN(p, VKI_BYTES_PER_PAGE) +#define PGROUNDUP(p) ROUNDUP(p, VKI_BYTES_PER_PAGE) + +/* --------------------------------------------------------------------- + Environment variables + ------------------------------------------------------------------ */ + +/* The directory we look for all our auxillary files in */ +#define VALGRINDLIB "VALGRINDLIB" + +/* Additional command-line arguments; they are overridden by actual + command-line option. Each argument is separated by spaces. There + is no quoting mechanism. + */ +#define VALGRINDOPTS "VALGRIND_OPTS" + +/* If this variable is present in the environment, then valgrind will + not parse the command line for options at all; all options come + from this variable. Arguments are terminated by ^A (\001). There + is no quoting mechanism. + + This variable is not expected to be set by anything other than + Valgrind itself, as part of its handling of execve with + --trace-children=yes. This variable should not be present in the + client environment. + */ +#define VALGRINDCLO "_VALGRIND_CLO" + + +/* --------------------------------------------------------------------- + Command-line-settable options + ------------------------------------------------------------------ */ + +/* Default destination port to be used in logging over a network, if + none specified. */ +#define VG_CLO_DEFAULT_LOGPORT 1500 + +/* The max number of suppression files. */ +#define VG_CLO_MAX_SFILES 10 + +/* Default debugger command. */ +#define VG_CLO_DEFAULT_DBCOMMAND GDB_PATH " -nw %f %p" + +/* Describes where logging output is to be sent. */ +typedef + enum { + VgLogTo_Fd, + VgLogTo_File, + VgLogTo_Socket + } VgLogTo; + +/* pid of main process */ +extern Int VG_(main_pid); + +/* pgrp of process (global to all threads) */ +extern Int VG_(main_pgrp); + +/* Application-visible file descriptor limits */ +extern Int VG_(fd_soft_limit); +extern Int VG_(fd_hard_limit); + +/* Should we stop collecting errors if too many appear? default: YES */ +extern Bool VG_(clo_error_limit); +/* Enquire about whether to attach to a debugger at errors? default: NO */ +extern Bool VG_(clo_db_attach); +/* The debugger command? default: whatever gdb ./configure found */ +extern Char* VG_(clo_db_command); +/* Enquire about generating a suppression for each error? default: NO */ +extern Bool VG_(clo_gen_suppressions); +/* Sanity-check level: 0 = none, 1 (default), > 1 = expensive. */ +extern Int VG_(clo_sanity_level); +/* Automatically attempt to demangle C++ names? default: YES */ +extern Bool VG_(clo_demangle); +/* Simulate child processes? default: NO */ +extern Bool VG_(clo_trace_children); + +/* Where logging output is to be sent to. + + When log_to == VgLogTo_Fd, clo_log_fd holds the file id, and is + taken from the command line. clo_log_name is irrelevant. + + When log_to == VgLogTo_File, clo_log_name holds the log-file + name, and is taken from the command line. clo_log_fd is then + made to hold the relevant file id, by opening clo_log_name + (concatenated with the process ID) for writing. + + When log_to == VgLogTo_Socket, clo_log_name holds the + hostname:portnumber pair, and is taken from the command line. + clo_log_fd is then made to hold the relevant file handle, by + opening a connection to said hostname:portnumber pair. + + Global default is to set log_to == VgLogTo_Fd and log_fd == 2 + (stderr). */ +extern VgLogTo VG_(clo_log_to); +extern Int VG_(clo_log_fd); +extern Char* VG_(clo_log_name); + +/* Add timestamps to log messages? default: NO */ +extern Bool VG_(clo_time_stamp); + +/* The file descriptor to read for input. default: 0 == stdin */ +extern Int VG_(clo_input_fd); +/* The number of suppression files specified. */ +extern Int VG_(clo_n_suppressions); +/* The names of the suppression files. */ +extern Char* VG_(clo_suppressions)[VG_CLO_MAX_SFILES]; + +/* Single stepping? default: NO */ +extern Bool VG_(clo_single_step); +/* Code improvement? default: YES */ +extern Bool VG_(clo_optimise); +/* DEBUG: print generated code? default: 00000 ( == NO ) */ +extern Bool VG_(clo_trace_codegen); +/* DEBUG: print system calls? default: NO */ +extern Bool VG_(clo_trace_syscalls); +/* DEBUG: print signal details? default: NO */ +extern Bool VG_(clo_trace_signals); +/* DEBUG: print symtab details? default: NO */ +extern Bool VG_(clo_trace_symtab); +/* DEBUG: print thread scheduling events? default: NO */ +extern Bool VG_(clo_trace_sched); +/* DEBUG: print pthread (mutex etc) events? default: 0 (none), 1 + (some), 2 (all) */ +extern Int VG_(clo_trace_pthread_level); +/* Display gory details for the k'th most popular error. default: + Infinity. */ +extern Int VG_(clo_dump_error); +/* Number of parents of a backtrace. Default: 8. */ +extern Int VG_(clo_backtrace_size); +/* Engage miscellaneous weird hacks needed for some progs. */ +extern Char* VG_(clo_weird_hacks); +/* How often we should poll for signals, assuming we need to poll for + signals. */ +extern Int VG_(clo_signal_polltime); + +/* Low latency syscalls and signals */ +extern Bool VG_(clo_lowlat_syscalls); +extern Bool VG_(clo_lowlat_signals); + +/* Track open file descriptors? */ +extern Bool VG_(clo_track_fds); + +/* Should we run __libc_freeres at exit? Sometimes causes crashes. + Default: YES. Note this is subservient to VG_(needs).libc_freeres; + if the latter says False, then the setting of VG_(clo_weird_hacks) + is ignored. Ie if a tool says no, I don't want this to run, that + cannot be overridden from the command line. */ +extern Bool VG_(clo_run_libc_freeres); +/* Use the basic-block chaining optimisation? Default: YES */ +extern Bool VG_(clo_chain_bb); +/* Generate branch-prediction hints? */ +extern Bool VG_(clo_branchpred); +/* Continue stack traces below main()? Default: NO */ +extern Bool VG_(clo_show_below_main); +/* Test each client pointer dereference to check it's within the + client address space bounds */ +extern Bool VG_(clo_pointercheck); + +/* Set up the libc freeres wrapper */ +extern void VG_(intercept_libc_freeres_wrapper)(Addr); + +/* --------------------------------------------------------------------- + Profiling stuff + ------------------------------------------------------------------ */ + +extern void VGP_(init_profiling) ( void ); +extern void VGP_(done_profiling) ( void ); + +#undef VGP_PUSHCC +#undef VGP_POPCC +#define VGP_PUSHCC(x) if (VG_(clo_profile)) VGP_(pushcc)(x) +#define VGP_POPCC(x) if (VG_(clo_profile)) VGP_(popcc)(x) + +/* --------------------------------------------------------------------- + Tool-related types + ------------------------------------------------------------------ */ +/* These structs are not exposed to tools to mitigate possibility of + binary-incompatibilities when the core/tool interface changes. Instead, + set functions are provided (see include/tool.h). */ +typedef + struct { + Char* name; + Char* version; + Char* description; + Char* copyright_author; + Char* bug_reports_to; + UInt avg_translation_sizeB; + } + VgDetails; + +extern VgDetails VG_(details); + +/* If new fields are added to this type, update: + * - vg_main.c:initialisation of VG_(needs) + * - vg_main.c:sanity_check_needs() + * + * If the name of this type or any of its fields change, update: + * - dependent comments (just search for "VG_(needs)"). + */ +typedef + struct { + Bool libc_freeres; + Bool core_errors; + Bool skin_errors; + Bool basic_block_discards; + Bool shadow_regs; + Bool command_line_options; + Bool client_requests; + Bool extended_UCode; + Bool syscall_wrapper; + Bool sanity_checks; + Bool data_syms; + Bool shadow_memory; + } + VgNeeds; + +extern VgNeeds VG_(needs); + +extern void VG_(tool_init_dlsym)(void *dlhandle); + +#include "vg_toolint.h" + +/* --------------------------------------------------------------------- + Exports of vg_needs.c + ------------------------------------------------------------------ */ + +void VG_(sanity_check_needs)(void); + +/* --------------------------------------------------------------------- + Exports of vg_malloc2.c + ------------------------------------------------------------------ */ + +/* Allocation arenas. + + CORE for the core's general use. + TOOL for the tool to use (and the only one it uses). + SYMTAB for Valgrind's symbol table storage. + JITTER for small storage during translation. + CLIENT for the client's mallocs/frees, if the tool replaces glibc's + malloc() et al -- redzone size is chosen by the tool. + DEMANGLE for the C++ demangler. + EXECTXT for storing ExeContexts. + ERRORS for storing CoreErrors. + TRANSIENT for very short-term use. It should be empty in between uses. + + When adding a new arena, remember also to add it to ensure_mm_init(). +*/ +typedef Int ArenaId; + +#define VG_N_ARENAS 9 + +#define VG_AR_CORE 0 +#define VG_AR_TOOL 1 +#define VG_AR_SYMTAB 2 +#define VG_AR_JITTER 3 +#define VG_AR_CLIENT 4 +#define VG_AR_DEMANGLE 5 +#define VG_AR_EXECTXT 6 +#define VG_AR_ERRORS 7 +#define VG_AR_TRANSIENT 8 + +// This is both the minimum payload size of a malloc'd block, and its +// minimum alignment. Must be a power of 2 greater than 4, and should be +// greater than 8. +#define VG_MIN_MALLOC_SZB 8 + +// Round-up size for --sloppy-malloc=yes. +#define VG_SLOPPY_MALLOC_SZB 4 + +extern void* VG_(arena_malloc) ( ArenaId arena, Int nbytes ); +extern void VG_(arena_free) ( ArenaId arena, void* ptr ); +extern void* VG_(arena_calloc) ( ArenaId arena, Int alignment, + Int nmemb, Int nbytes ); +extern void* VG_(arena_realloc) ( ArenaId arena, void* ptr, Int alignment, + Int size ); +extern void* VG_(arena_malloc_aligned) ( ArenaId aid, Int req_alignB, + Int req_pszB ); + +extern Int VG_(arena_payload_szB) ( ArenaId aid, void* payload ); + +extern void VG_(sanity_check_malloc_all) ( void ); + +extern void VG_(print_all_arena_stats) ( void ); + +extern Bool VG_(is_empty_arena) ( ArenaId aid ); + +/* --------------------------------------------------------------------- + Exports of vg_intercept.c + ------------------------------------------------------------------ */ + +/* This doesn't export code or data that valgrind.so needs to link + against. However, the scheduler does need to know the following + request codes. A few, publically-visible, request codes are also + defined in valgrind.h, and similar headers for some tools. */ + +#define VG_USERREQ__MALLOC 0x2001 +#define VG_USERREQ__FREE 0x2002 + +/* (Fn, Arg): Create a new thread and run Fn applied to Arg in it. Fn + MUST NOT return -- ever. Eventually it will do either __QUIT or + __WAIT_JOINER. */ +#define VG_USERREQ__APPLY_IN_NEW_THREAD 0x3001 + +/* ( no-args ): calling thread disappears from the system forever. + Reclaim resources. */ +#define VG_USERREQ__QUIT 0x3002 + +/* ( void* ): calling thread waits for joiner and returns the void* to + it. */ +#define VG_USERREQ__WAIT_JOINER 0x3003 + +/* ( ThreadId, void** ): wait to join a thread. */ +#define VG_USERREQ__PTHREAD_JOIN 0x3004 + +/* Set cancellation state and type for this thread. */ +#define VG_USERREQ__SET_CANCELSTATE 0x3005 +#define VG_USERREQ__SET_CANCELTYPE 0x3006 + +/* ( no-args ): Test if we are at a cancellation point. */ +#define VG_USERREQ__TESTCANCEL 0x3007 + +/* ( ThreadId, &thread_exit_wrapper is the only allowable arg ): call + with this arg to indicate that a cancel is now pending for the + specified thread. */ +#define VG_USERREQ__SET_CANCELPEND 0x3008 + +/* Set/get detach state for this thread. */ +#define VG_USERREQ__SET_OR_GET_DETACH 0x3009 + +#define VG_USERREQ__PTHREAD_GET_THREADID 0x300A +#define VG_USERREQ__PTHREAD_MUTEX_LOCK 0x300B +#define VG_USERREQ__PTHREAD_MUTEX_TIMEDLOCK 0x300C +#define VG_USERREQ__PTHREAD_MUTEX_TRYLOCK 0x300D +#define VG_USERREQ__PTHREAD_MUTEX_UNLOCK 0x300E +#define VG_USERREQ__PTHREAD_COND_WAIT 0x300F +#define VG_USERREQ__PTHREAD_COND_TIMEDWAIT 0x3010 +#define VG_USERREQ__PTHREAD_COND_SIGNAL 0x3011 +#define VG_USERREQ__PTHREAD_COND_BROADCAST 0x3012 +#define VG_USERREQ__PTHREAD_KEY_CREATE 0x3013 +#define VG_USERREQ__PTHREAD_KEY_DELETE 0x3014 +#define VG_USERREQ__PTHREAD_SETSPECIFIC_PTR 0x3015 +#define VG_USERREQ__PTHREAD_GETSPECIFIC_PTR 0x3016 +#define VG_USERREQ__READ_MILLISECOND_TIMER 0x3017 +#define VG_USERREQ__PTHREAD_SIGMASK 0x3018 +#define VG_USERREQ__SIGWAIT 0x3019 /* unused */ +#define VG_USERREQ__PTHREAD_KILL 0x301A +#define VG_USERREQ__PTHREAD_YIELD 0x301B +#define VG_USERREQ__PTHREAD_KEY_VALIDATE 0x301C + +#define VG_USERREQ__CLEANUP_PUSH 0x3020 +#define VG_USERREQ__CLEANUP_POP 0x3021 +#define VG_USERREQ__GET_KEY_D_AND_S 0x3022 + +#define VG_USERREQ__NUKE_OTHER_THREADS 0x3023 + +/* Ask how many signal handler returns have happened to this + thread. */ +#define VG_USERREQ__GET_N_SIGS_RETURNED 0x3024 /* unused */ + +/* Get/set entries for a thread's pthread_atfork stack. */ +#define VG_USERREQ__SET_FHSTACK_USED 0x3025 +#define VG_USERREQ__GET_FHSTACK_USED 0x3026 +#define VG_USERREQ__SET_FHSTACK_ENTRY 0x3027 +#define VG_USERREQ__GET_FHSTACK_ENTRY 0x3028 + +/* Denote the finish of __libc_freeres_wrapper(). */ +#define VG_USERREQ__LIBC_FREERES_DONE 0x3029 + +/* Allocate RT signals */ +#define VG_USERREQ__GET_SIGRT_MIN 0x302B +#define VG_USERREQ__GET_SIGRT_MAX 0x302C +#define VG_USERREQ__ALLOC_RTSIG 0x302D + +/* Hook for replace_malloc.o to get malloc functions */ +#define VG_USERREQ__GET_MALLOCFUNCS 0x3030 + +/* Get stack information for a thread. */ +#define VG_USERREQ__GET_STACK_INFO 0x3033 + +/* Cosmetic ... */ +#define VG_USERREQ__GET_PTHREAD_TRACE_LEVEL 0x3101 +/* Log a pthread error from client-space. Cosmetic. */ +#define VG_USERREQ__PTHREAD_ERROR 0x3102 +/* Internal equivalent of VALGRIND_PRINTF . */ +#define VG_USERREQ__INTERNAL_PRINTF 0x3103 +/* Internal equivalent of VALGRIND_PRINTF_BACKTRACE . */ +#define VG_USERREQ__INTERNAL_PRINTF_BACKTRACE 0x3104 + +/* +In core_asm.h: +#define VG_USERREQ__SIGNAL_RETURNS 0x4001 +*/ + +#define VG_INTERCEPT_PREFIX "_vgi__" +#define VG_INTERCEPT_PREFIX_LEN 6 +#define VG_INTERCEPT(name) _vgi__##name +#define VG_INTERCEPT_ALIAS(name) "_vgi__" #name + +#define VG_WRAPPER_PREFIX "_vgw__" +#define VG_WRAPPER_PREFIX_LEN 6 +#define VG_WRAPPER(name) _vgw__##name +#define VG_WRAPPER_ALIAS(name) "_vgw__" #name + + +struct vg_mallocfunc_info { + /* things vg_replace_malloc.o needs to know about */ + Addr sk_malloc; + Addr sk_calloc; + Addr sk_realloc; + Addr sk_memalign; + Addr sk___builtin_new; + Addr sk___builtin_vec_new; + Addr sk_free; + Addr sk___builtin_delete; + Addr sk___builtin_vec_delete; + + Addr arena_payload_szB; + + Bool clo_sloppy_malloc; + Bool clo_trace_malloc; +}; + +/* --------------------------------------------------------------------- + Exports of vg_defaults.c + ------------------------------------------------------------------ */ + +extern Bool VG_(sk_malloc_called_by_scheduler); + +/* --------------------------------------------------------------------- + Exports of vg_libpthread.c + ------------------------------------------------------------------ */ + +/* Replacements for pthread types, shared between vg_libpthread.c and + vg_scheduler.c. See comment in vg_libpthread.c above the other + vg_pthread_*_t types for a description of how these are used. */ + +struct _vg_pthread_fastlock +{ + long int __vg_status; /* "Free" or "taken" or head of waiting list */ + int __vg_spinlock; /* Used by compare_and_swap emulation. Also, + adaptive SMP lock stores spin count here. */ +}; + +typedef struct +{ + int __vg_m_reserved; /* Reserved for future use */ + int __vg_m_count; /* Depth of recursive locking */ + /*_pthread_descr*/ void* __vg_m_owner; /* Owner thread (if recursive or errcheck) */ + int __vg_m_kind; /* Mutex kind: fast, recursive or errcheck */ + struct _vg_pthread_fastlock __vg_m_lock; /* Underlying fast lock */ +} vg_pthread_mutex_t; + +typedef struct +{ + struct _vg_pthread_fastlock __vg_c_lock; /* Protect against concurrent access */ + /*_pthread_descr*/ void* __vg_c_waiting; /* Threads waiting on this condition */ + + // Nb: the following padding removed because it was missing from an + // earlier glibc, so the size test in the CONVERT macro was failing. + // --njn + + // Padding ensures the size is 48 bytes + /*char __vg_padding[48 - sizeof(struct _vg_pthread_fastlock) + - sizeof(void*) - sizeof(long long)]; + long long __vg_align;*/ +} vg_pthread_cond_t; + + +/* --------------------------------------------------------------------- + Exports of vg_scheduler.c + ------------------------------------------------------------------ */ + +typedef + enum ThreadStatus { + VgTs_Empty, /* this slot is not in use */ + VgTs_Runnable, /* waiting to be scheduled */ + VgTs_WaitJoiner, /* waiting for someone to do join on me */ + VgTs_WaitJoinee, /* waiting for the thread I did join on */ + VgTs_WaitMX, /* waiting on a mutex */ + VgTs_WaitCV, /* waiting on a condition variable */ + VgTs_WaitSys, /* waiting for a syscall to complete */ + VgTs_Sleeping, /* sleeping for a while */ + } + ThreadStatus; + +typedef + enum CleanupType { + VgCt_None, /* this cleanup entry is not initialised */ + VgCt_Function, /* an old-style function pointer cleanup */ + VgCt_Longjmp /* a new-style longjmp based cleanup */ + } + CleanupType; + +/* Information on a thread's stack. */ +typedef + struct { + Addr base; + UInt size; + UInt guardsize; + } + StackInfo; + +/* An entry in a threads's cleanup stack. */ +typedef + struct { + CleanupType type; + union { + struct { + void (*fn)(void*); + void* arg; + } function; + struct { + void *ub; + int ctype; + } longjmp; + } data; + } + CleanupEntry; + +/* An entry in a thread's fork-handler stack. */ +typedef + struct { + void (*prepare)(void); + void (*parent)(void); + void (*child)(void); + } + ForkHandlerEntry; + +typedef struct ProxyLWP ProxyLWP; + +typedef + struct _ThreadState { + /* ThreadId == 0 (and hence vg_threads[0]) is NEVER USED. + The thread identity is simply the index in vg_threads[]. + ThreadId == 1 is the root thread and has the special property + that we don't try and allocate or deallocate its stack. For + convenience of generating error message, we also put the + ThreadId in this tid field, but be aware that it should + ALWAYS == the index in vg_threads[]. */ + ThreadId tid; + + /* Current scheduling status. + + Complications: whenever this is set to VgTs_WaitMX, you + should also set .m_edx to whatever the required return value + is for pthread_mutex_lock / pthread_cond_timedwait for when + the mutex finally gets unblocked. */ + ThreadStatus status; + + /* When .status == WaitMX, points to the mutex I am waiting for. + When .status == WaitCV, points to the mutex associated with + the condition variable indicated by the .associated_cv field. + In all other cases, should be NULL. */ + vg_pthread_mutex_t* associated_mx; + + /* When .status == WaitCV, points to the condition variable I am + waiting for. In all other cases, should be NULL. */ + void* /*pthread_cond_t* */ associated_cv; + + /* If VgTs_Sleeping, this is when we should wake up, measured in + milliseconds as supplied by VG_(read_millisecond_timer). + + If VgTs_WaitCV, this indicates the time at which + pthread_cond_timedwait should wake up. If == 0xFFFFFFFF, + this means infinitely far in the future, viz, + pthread_cond_wait. */ + UInt awaken_at; + + /* If VgTs_WaitJoiner, return value, as generated by joinees. */ + void* joinee_retval; + + /* If VgTs_WaitJoinee, place to copy the return value to, and + the identity of the thread we're waiting for. */ + void** joiner_thread_return; + ThreadId joiner_jee_tid; + + /* If VgTs_WaitSys, this is the result of the pre-syscall check */ + void *sys_pre_res; + + /* If VgTs_WaitSys, this is the syscall we're currently running */ + Int syscallno; + + /* If VgTs_WaitSys, this is the syscall flags */ + UInt sys_flags; + + /* Details about this thread's proxy LWP */ + ProxyLWP *proxy; + + /* Whether or not detached. */ + Bool detached; + + /* Cancelability state and type. */ + Bool cancel_st; /* False==PTH_CANCEL_DISABLE; True==.._ENABLE */ + Bool cancel_ty; /* False==PTH_CANC_ASYNCH; True==..._DEFERRED */ + + /* Pointer to fn to call to do cancellation. Indicates whether + or not cancellation is pending. If NULL, not pending. Else + should be &thread_exit_wrapper(), indicating that + cancallation is pending. */ + void (*cancel_pend)(void*); + + /* The cleanup stack. */ + Int custack_used; + CleanupEntry custack[VG_N_CLEANUPSTACK]; + + /* A pointer to the thread's-specific-data. This is handled almost + entirely from vg_libpthread.c. We just provide hooks to get and + set this ptr. This is either NULL, indicating the thread has + read/written none of its specifics so far, OR points to a + void*[VG_N_THREAD_KEYS], allocated and deallocated in + vg_libpthread.c. */ + void** specifics_ptr; + + /* This thread's blocked-signals mask. Semantics is that for a + signal to be delivered to this thread, the signal must not be + blocked by this signal mask. If more than one thread accepts a + signal, then it will be delivered to one at random. If all + threads block the signal, it will remain pending until either a + thread unblocks it or someone uses sigwaitsig/sigtimedwait. + + sig_mask reflects what the client told us its signal mask should + be, but isn't necessarily the current signal mask of the proxy + LWP: it may have more signals blocked because of signal + handling, or it may be different because of sigsuspend. + */ + vki_ksigset_t sig_mask; + + /* Effective signal mask. This is the mask which currently + applies; it may be different from sig_mask while a signal + handler is running. + */ + vki_ksigset_t eff_sig_mask; + + /* Signal queue. This is used when the kernel doesn't route + signals properly in order to remember the signal information + while we are routing the signal. It is a circular queue with + insertions performed at the head and removals at the tail. + */ + vki_ksiginfo_t sigqueue[VG_N_SIGNALQUEUE]; + Int sigqueue_head; + Int sigqueue_tail; + + /* Stacks. When a thread slot is freed, we don't deallocate its + stack; we just leave it lying around for the next use of the + slot. If the next use of the slot requires a larger stack, + only then is the old one deallocated and a new one + allocated. + + For the main thread (threadid == 0), this mechanism doesn't + apply. We don't know the size of the stack since we didn't + allocate it, and furthermore we never reallocate it. */ + + /* The allocated size of this thread's stack (permanently zero + if this is ThreadId == 0, since we didn't allocate its stack) */ + UInt stack_size; + + /* Address of the lowest word in this thread's stack. NULL means + not allocated yet. + */ + Addr stack_base; + + /* The allocated size of this thread's stack's guard area (permanently + zero if this is ThreadId == 0, since we didn't allocate its stack) */ + UInt stack_guard_size; + + /* Address of the highest legitimate word in this stack. This is + used for error messages only -- not critical for execution + correctness. Is is set for all stacks, specifically including + ThreadId == 0 (the main thread). */ + Addr stack_highest_word; + + /* Alternate signal stack */ + vki_kstack_t altstack; + + /* Architecture-specific thread state */ + arch_thread_t arch; +} +ThreadState; + + +/* The thread table. */ +extern ThreadState VG_(threads)[VG_N_THREADS]; + +/* Check that tid is in range and denotes a non-Empty thread. */ +extern Bool VG_(is_valid_tid) ( ThreadId tid ); + +/* Determine if 'tid' is that of the current running thread (Nb: returns + False if no thread is currently running. */ +extern Bool VG_(is_running_thread)(ThreadId tid); + +/* Get the ThreadState for a particular thread */ +extern ThreadState *VG_(get_ThreadState)(ThreadId tid); + +/* Similarly ... */ +extern ThreadId VG_(get_current_tid) ( void ); + +/* Nuke all threads except tid. */ +extern void VG_(nuke_all_threads_except) ( ThreadId me ); + +/* Give a hint to the scheduler that it may be a good time to find a + new runnable thread. If prefer_sched != VG_INVALID_THREADID, then + try to schedule that thread. +*/ +extern void VG_(need_resched) ( ThreadId prefer_sched ); + +/* Return codes from the scheduler. */ +typedef + enum { + VgSrc_Deadlock, /* no runnable threads and no prospect of any + even if we wait for a long time */ + VgSrc_ExitSyscall, /* client called exit(). This is the normal + route out. */ + VgSrc_FatalSig /* Killed by the default action of a fatal + signal */ + } + VgSchedReturnCode; + + +// The scheduler. 'fatal_sigNo' is only set if VgSrc_FatalSig is returned. +extern VgSchedReturnCode VG_(scheduler) + ( Int* exit_code, ThreadId* last_run_thread, Int* fatal_sigNo ); + +extern void VG_(scheduler_init) ( void ); + +extern void VG_(pp_sched_status) ( void ); + +// Longjmp back to the scheduler and thus enter the sighandler immediately. +extern void VG_(resume_scheduler) ( Int sigNo, vki_ksiginfo_t *info ); + +// Longjmp, ending the scheduler, when a fatal signal occurs in the client. +extern void VG_(scheduler_handle_fatal_signal)( Int sigNo ); + +/* The red-zone size which we put at the bottom (highest address) of + thread stacks, for paranoia reasons. This can be arbitrary, and + doesn't really need to be set at compile time. */ +#define VG_AR_CLIENT_STACKBASE_REDZONE_SZB 16 + +// Write a value to a client's thread register, and shadow (if necessary). +// Note that there are some further similar macros in the arch- and +// platform-specific parts; these ones are the totally generic ones. +#define SET_THREAD_REG( zztid, zzval, zzGETREG, zzREG, zzevent, zzargs... ) \ + do { zzGETREG(VG_(threads)[zztid].arch) = (zzval); \ + VG_TRACK( zzevent, zztid, zzREG, ##zzargs ); \ + } while (0) + +#define SET_CLREQ_RETVAL(zztid, zzval) \ + SET_THREAD_REG(zztid, zzval, ARCH_CLREQ_RET, R_CLREQ_RET, \ + post_reg_write_clientreq_return) + +#define SET_CLCALL_RETVAL(zztid, zzval, f) \ + SET_THREAD_REG(zztid, zzval, ARCH_CLREQ_RET, R_CLREQ_RET, \ + post_reg_write_clientcall_return, f) + +#define SET_PTHREQ_ESP(zztid, zzval) \ + SET_THREAD_REG(zztid, zzval, ARCH_STACK_PTR, R_STACK_PTR, \ + post_reg_write_pthread_return) + +#define SET_PTHREQ_RETVAL(zztid, zzval) \ + SET_THREAD_REG(zztid, zzval, ARCH_PTHREQ_RET, R_PTHREQ_RET, \ + post_reg_write_pthread_return) + + +/* --------------------------------------------------------------------- + Exports of vg_signals.c + ------------------------------------------------------------------ */ + +extern Bool VG_(do_signal_routing); /* whether scheduler LWP has to route signals */ + +/* RT signal allocation */ +extern Int VG_(sig_rtmin); +extern Int VG_(sig_rtmax); +extern Int VG_(sig_alloc_rtsig) ( Int high ); + +extern void VG_(sigstartup_actions) ( void ); + +extern void VG_(deliver_signal) ( ThreadId tid, const vki_ksiginfo_t *, Bool async ); +extern void VG_(unblock_host_signal) ( Int sigNo ); + +extern Bool VG_(is_sig_ign) ( Int sigNo ); + +/* Route pending signals from the scheduler LWP to the appropriate + thread LWP. */ +extern void VG_(route_signals) ( void ); + +/* Fake system calls for signal handling. */ +extern void VG_(do__NR_sigaltstack) ( ThreadId tid ); +extern void VG_(do__NR_sigaction) ( ThreadId tid ); +extern void VG_(do__NR_sigprocmask) ( ThreadId tid, Int how, + vki_ksigset_t* set, + vki_ksigset_t* oldset ); +extern void VG_(do_pthread_sigmask_SCSS_upd) ( ThreadId tid, Int how, + vki_ksigset_t* set, + vki_ksigset_t* oldset ); + +/* Modify the current thread's state once we have detected it is + returning from a signal handler. */ +extern Bool VG_(signal_returns) ( ThreadId tid ); + +/* Handy utilities to block/restore all host signals. */ +extern void VG_(block_all_host_signals) + ( /* OUT */ vki_ksigset_t* saved_mask ); +extern void VG_(restore_all_host_signals) + ( /* IN */ vki_ksigset_t* saved_mask ); + +extern void VG_(kill_self)(Int sigNo); + +/* These function synthesize a fault, as if the running instruction + had had a fault. These functions do not return - they longjmp back + into the scheduler so the signal can be delivered. */ +extern void VG_(synth_fault) (ThreadId tid); +extern void VG_(synth_fault_mapping)(ThreadId tid, Addr addr); +extern void VG_(synth_fault_perms) (ThreadId tid, Addr addr); + +extern void VG_(get_sigstack_bounds)( Addr* low, Addr* high ); + +/* --------------------------------------------------------------------- + Exports of vg_mylibc.c + ------------------------------------------------------------------ */ + +#define vg_assert(expr) \ + ((void) ((expr) ? 0 : \ + (VG_(core_assert_fail) (VG__STRING(expr), \ + __FILE__, __LINE__, \ + __PRETTY_FUNCTION__), 0))) +__attribute__ ((__noreturn__)) +extern void VG_(core_assert_fail) ( const Char* expr, const Char* file, + Int line, const Char* fn ); +__attribute__ ((__noreturn__)) +extern void VG_(core_panic) ( Char* str ); +__attribute__ ((__noreturn__)) +extern void VG_(core_panic_at) ( Char* str, ExeContext *ec ); + +/* Tools use VG_(strdup)() which doesn't expose ArenaId */ +extern Char* VG_(arena_strdup) ( ArenaId aid, const Char* s); + +extern Int VG_(fcntl) ( Int fd, Int cmd, Int arg ); +extern Int VG_(poll)( struct vki_pollfd *, UInt nfds, Int timeout); + +/* system/mman.h */ +extern void* VG_(mmap)( void* start, UInt length, UInt prot, UInt flags, + UInt sf_flags, UInt fd, UInt offset ); +extern Int VG_(munmap)( void* start, Int length ); +extern Int VG_(mprotect)( void *start, Int length, UInt prot ); + + +/* Move an fd into the Valgrind-safe range */ +Int VG_(safe_fd)(Int oldfd); + +extern Int VG_(write_socket)( Int sd, void *msg, Int count ); + +/* --- Connecting over the network --- */ +extern Int VG_(connect_via_socket)( UChar* str ); + +/* Environment manipulations */ +extern Char **VG_(env_setenv) ( Char ***envp, const Char* varname, + const Char *val ); +extern void VG_(env_unsetenv) ( Char **env, const Char *varname ); +extern void VG_(env_remove_valgrind_env_stuff) ( Char** env ); + +/* --------------------------------------------------------------------- + Exports of vg_message.c + ------------------------------------------------------------------ */ + +/* Low-level -- send bytes directly to the message sink. Do not + use. */ +extern void VG_(send_bytes_to_logging_sink) ( Char* msg, Int nbytes ); + +// Functions for printing from code within Valgrind, but which runs on the +// sim'd CPU. Defined here because needed for vg_libpthread.c, +// vg_replace_malloc.c, plus the rest of the core. The weak attribute +// ensures the multiple definitions are not a problem. They must be functions +// rather than macros so that va_list can be used. + +__attribute__((weak)) +int +VALGRIND_INTERNAL_PRINTF(char *format, ...) +{ + unsigned int _qzz_res = 0; + va_list vargs; + va_start(vargs, format); + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__INTERNAL_PRINTF, + (unsigned int)format, (unsigned int)vargs, 0, 0); + va_end(vargs); + return _qzz_res; +} + +__attribute__((weak)) +int +VALGRIND_INTERNAL_PRINTF_BACKTRACE(char *format, ...) +{ + unsigned int _qzz_res = 0; + va_list vargs; + va_start(vargs, format); + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__INTERNAL_PRINTF_BACKTRACE, + (unsigned int)format, (unsigned int)vargs, 0, 0); + va_end(vargs); + return _qzz_res; +} + + +/* --------------------------------------------------------------------- + Exports of vg_demangle.c + ------------------------------------------------------------------ */ + +extern void VG_(demangle) ( Char* orig, Char* result, Int result_size ); + +/* --------------------------------------------------------------------- + Exports of vg_from_ucode.c + ------------------------------------------------------------------ */ + +extern UChar* VG_(emit_code) ( UCodeBlock* cb, Int* nbytes, UShort jumps[VG_MAX_JUMPS] ); + +extern void VG_(print_ccall_stats) ( void ); +extern void VG_(print_UInstr_histogram) ( void ); + +extern void VG_(unchain_jumpsite) ( Addr jumpsite ); +extern Addr VG_(get_jmp_dest) ( Addr jumpsite ); + +/* --------------------------------------------------------------------- + Exports of vg_to_ucode.c + ------------------------------------------------------------------ */ + +Bool VG_(cpu_has_feature)(UInt feat); + +extern Int VG_(disBB) ( UCodeBlock* cb, Addr eip0 ); + +/* --------------------------------------------------------------------- + Exports of vg_translate.c + ------------------------------------------------------------------ */ + +/* Expandable arrays of uinstrs. */ +struct _UCodeBlock { + Addr orig_eip; + Int used; + Int size; + UInstr* instrs; + Int nextTemp; +}; + +extern Bool VG_(translate) ( ThreadId tid, Addr orig_addr, Bool debugging ); + +extern void VG_(sanity_check_UInstr) ( UInt n, UInstr* u ); + +extern void VG_(print_reg_alloc_stats) ( void ); + +/* --------------------------------------------------------------------- + Exports of vg_execontext.c. + ------------------------------------------------------------------ */ + +/* Records the PC and a bit of the call chain. The first 4 %eip + values are used in comparisons do remove duplicate errors, and for + comparing against suppression specifications. The rest are purely + informational (but often important). */ + +struct _ExeContext { + struct _ExeContext * next; + /* Variable-length array. The size is VG_(clo_backtrace_size); at + least 1, at most VG_DEEPEST_BACKTRACE. [0] is the current IP, + [1] is its caller, [2] is the caller of [1], etc. */ + Addr ips[0]; +}; + + +/* Print stats (informational only). */ +extern void VG_(print_ExeContext_stats) ( void ); + +/* Like VG_(get_ExeContext), but with a slightly different type */ +extern ExeContext* VG_(get_ExeContext2) ( Addr ip, Addr fp, + Addr fp_min, Addr fp_max ); + + +/* --------------------------------------------------------------------- + Exports of vg_errcontext.c. + ------------------------------------------------------------------ */ + +extern void VG_(load_suppressions) ( void ); + +extern void VG_(record_pthread_error) ( ThreadId tid, Char* msg ); + +extern void VG_(show_all_errors) ( void ); + +extern Bool VG_(is_action_requested) ( Char* action, Bool* clo ); + +extern UInt VG_(get_n_errs_found) ( void ); + +/* --------------------------------------------------------------------- + Exports of vg_procselfmaps.c + ------------------------------------------------------------------ */ + +/* Reads /proc/self/maps into a static buffer which can be parsed by + VG_(parse_procselfmaps)(). */ +extern void VG_(read_procselfmaps) ( void ); + +/* Parses /proc/self/maps, calling `record_mapping' for each entry. If + `read_from_file' is True, /proc/self/maps is read directly, otherwise + it's read from the buffer filled by VG_(read_procselfmaps_contents)(). */ +extern +void VG_(parse_procselfmaps) ( + void (*record_mapping)( Addr addr, UInt len, Char rr, Char ww, Char xx, + UInt dev, UInt ino, ULong foff, + const UChar *filename ) ); + + +/* --------------------------------------------------------------------- + Exports of vg_symtab2.c + ------------------------------------------------------------------ */ + +typedef struct _Segment Segment; + +extern Bool VG_(is_object_file) ( const void *hdr ); +extern void VG_(mini_stack_dump) ( Addr eips[], UInt n_eips ); +extern SegInfo * VG_(read_seg_symbols) ( Segment *seg ); +extern void VG_(symtab_incref) ( SegInfo * ); +extern void VG_(symtab_decref) ( SegInfo *, Addr a, UInt len ); + +extern Bool VG_(get_fnname_nodemangle)( Addr a, Char* fnname, Int n_fnname ); + +/* Set up some default redirects */ +extern void VG_(setup_code_redirect_table) ( void ); + +/* Redirection machinery */ +extern Addr VG_(code_redirect) ( Addr orig ); + +/* --------------------------------------------------------------------- + Exports of vg_main.c + ------------------------------------------------------------------ */ + +/* Is this a SSE/SSE2-capable CPU? If so, we had better save/restore + the SSE state all over the place. This is set up very early, in + main(). We have to determine it early since we can't even + correctly snapshot the startup machine state without it. */ +extern Bool VG_(have_ssestate); + +/* Tell the logging mechanism whether we are logging to a file + descriptor or a socket descriptor. */ +extern Bool VG_(logging_to_filedes); + +/* Sanity checks which may be done at any time. The scheduler decides when. */ +extern void VG_(sanity_check_general) ( Bool force_expensive ); + +/* Address space */ +extern Addr VG_(client_base); /* client address space limits */ +extern Addr VG_(client_end); +extern Addr VG_(client_mapbase); /* base of mappings */ +extern Addr VG_(clstk_base); /* client stack range */ +extern Addr VG_(clstk_end); +extern Addr VG_(client_trampoline_code); + +extern Addr VG_(brk_base); /* start of brk */ +extern Addr VG_(brk_limit); /* current brk */ +extern Addr VG_(shadow_base); /* tool's shadow memory */ +extern Addr VG_(shadow_end); +extern Addr VG_(valgrind_base); /* valgrind's address range */ +extern Addr VG_(valgrind_last); // Nb: last byte, rather than one past the end + +extern vki_rlimit VG_(client_rlimit_data); /* client's original rlimit data */ +extern vki_rlimit VG_(client_rlimit_stack); /* client's original rlimit stack */ + +/* client executable file descriptor */ +extern Int VG_(clexecfd); + +// Help set up the child used when doing execve() with --trace-children=yes +Char* VG_(build_child_VALGRINDCLO) ( Char* exename ); +Char* VG_(build_child_exename) ( void ); + +/* Determine if %esp adjustment must be noted */ +extern Bool VG_(need_to_handle_esp_assignment) ( void ); + +/* Called when some unhandleable client behaviour is detected. + Prints a msg and aborts. */ +extern void VG_(unimplemented) ( Char* msg ) + __attribute__((__noreturn__)); + +/* Something of a function looking for a home ... start up debugger. */ +extern void VG_(start_debugger) ( Int tid ); + +/* Counts downwards in vg_run_innerloop. */ +extern UInt VG_(dispatch_ctr); + +/* --- Counters, for informational purposes only. --- */ + +// These counters must be declared here because they're maintained by +// vg_dispatch.S. +extern UInt VG_(bb_enchain_count); // Counts of chain operations done +extern UInt VG_(bb_dechain_count); // Counts of unchain operations done +extern UInt VG_(unchained_jumps_done); // Number of unchained jumps performed + +extern void VG_(print_scheduler_stats) ( void ); + +extern Int VG_(alloc_BaB)( Int ); // Allocate slots in baseBlock +extern void VG_(align_BaB)( UInt ); // Align baseBlock offset +extern Int VG_(alloc_BaB_1_set)( Addr ); // Allocate & init baseBlock slot + +/* --------------------------------------------------------------------- + Exports of vg_memory.c + ------------------------------------------------------------------ */ + +/* A Segment is mapped piece of client memory. This covers all kinds + of mapped memory (exe, brk, mmap, .so, shm, stack, etc) + + We try to encode everything we know about a particular segment here. +*/ +#define SF_FIXED (1 << 0) // client asked for MAP_FIXED +#define SF_SHARED (1 << 1) // shared +#define SF_SHM (1 << 2) // SYSV SHM (also SF_SHARED) +#define SF_MMAP (1 << 3) // mmap memory +#define SF_FILE (1 << 4) // mapping is backed by a file +#define SF_STACK (1 << 5) // is a stack +#define SF_GROWDOWN (1 << 6) // segment grows down +#define SF_GROWUP (1 << 7) // segment grows up +#define SF_EXEC (1 << 8) // segment created by exec +#define SF_DYNLIB (1 << 9) // mapped from dynamic library +#define SF_NOSYMS (1 << 10) // don't load syms, even if present +#define SF_BRK (1 << 11) // brk segment +#define SF_CORE (1 << 12) // allocated by core on behalf of the client +#define SF_VALGRIND (1 << 13) // a valgrind-internal mapping - not in client +#define SF_CODE (1 << 14) // segment contains cached code + +struct _Segment { + UInt prot; /* VKI_PROT_* */ + UInt flags; /* SF_* */ + + Addr addr; /* mapped addr (page aligned) */ + UInt len; /* size of mapping (page aligned) */ + + /* These are valid if (flags & SF_FILE) */ + ULong offset; /* file offset */ + const Char *filename; /* filename (NULL if unknown) */ + UInt dev; /* device */ + UInt ino; /* inode */ + + SegInfo *symtab; /* symbol table */ +}; + +/* segment mapped from a file descriptor */ +extern void VG_(map_fd_segment) (Addr addr, UInt len, UInt prot, UInt flags, + Int fd, ULong off, const Char *filename); + +/* segment mapped from a file */ +extern void VG_(map_file_segment)(Addr addr, UInt len, UInt prot, UInt flags, + UInt dev, UInt ino, ULong off, const Char *filename); + +/* simple segment */ +extern void VG_(map_segment) (Addr addr, UInt len, UInt prot, UInt flags); + +extern void VG_(unmap_range) (Addr addr, UInt len); +extern void VG_(mprotect_range)(Addr addr, UInt len, UInt prot); +extern Addr VG_(find_map_space)(Addr base, UInt len, Bool for_client); + +extern Segment *VG_(find_segment)(Addr a); +extern Segment *VG_(first_segment)(void); +extern Segment *VG_(next_segment)(Segment *); + +extern Bool VG_(seg_contains)(const Segment *s, Addr ptr, UInt size); +extern Bool VG_(seg_overlaps)(const Segment *s, Addr ptr, UInt size); + +extern void VG_(pad_address_space)(void); +extern void VG_(unpad_address_space)(void); + +extern REGPARM(1) + void VG_(unknown_esp_update) ( Addr new_ESP ); + +/* --------------------------------------------------------------------- + Exports of vg_proxylwp.c + ------------------------------------------------------------------ */ + +enum PXState +{ + PXS_BAD = -1, + PXS_WaitReq, /* waiting for a request */ + PXS_RunSyscall, /* running a syscall */ + PXS_IntReply, /* request interrupted - need to send reply */ + PXS_SysDone, /* small window between syscall + complete and results written out */ + PXS_SigACK, /* waiting for a signal ACK */ +}; + + +/* Issue a syscall for thread tid */ +extern Int VG_(sys_issue)(int tid); + +extern void VG_(proxy_init) ( void ); +extern void VG_(proxy_create) ( ThreadId tid ); +extern void VG_(proxy_delete) ( ThreadId tid, Bool force ); +extern void VG_(proxy_results) ( void ); +extern void VG_(proxy_sendsig) ( ThreadId tid, Int signo ); +extern void VG_(proxy_setsigmask)(ThreadId tid); +extern void VG_(proxy_sigack) ( ThreadId tid, const vki_ksigset_t *); +extern void VG_(proxy_abort_syscall) ( ThreadId tid ); +extern void VG_(proxy_waitsig) ( void ); +extern void VG_(proxy_wait_sys) (ThreadId tid, Bool restart); + +extern void VG_(proxy_shutdown) ( void ); // shut down the syscall workers +extern Int VG_(proxy_resfd) ( void ); // FD something can select on to know + // a syscall finished + +/* Sanity-check the whole proxy-LWP machinery */ +void VG_(sanity_check_proxy)(void); + +/* Send a signal from a thread's proxy to the thread. This longjmps + back into the proxy's main loop, so it doesn't return. */ +__attribute__ ((__noreturn__)) +extern void VG_(proxy_handlesig)( const vki_ksiginfo_t *siginfo, + Addr ip, Int sysnum ); + +/* --------------------------------------------------------------------- + Exports of vg_syscalls.c + ------------------------------------------------------------------ */ + +extern Char *VG_(resolve_filename)(Int fd); + +extern Bool VG_(pre_syscall) ( ThreadId tid ); +extern void VG_(post_syscall)( ThreadId tid, Bool restart ); + +extern Bool VG_(is_kerror) ( Int res ); + +/* Internal atfork handlers */ +typedef void (*vg_atfork_t)(ThreadId); +extern void VG_(atfork)(vg_atfork_t pre, vg_atfork_t parent, vg_atfork_t child); + +/* fd leakage calls. */ +extern void VG_(init_preopened_fds) ( void ); +extern void VG_(show_open_fds) ( void ); + +/* --------------------------------------------------------------------- + Exports of vg_transtab.c + ------------------------------------------------------------------ */ + +/* The fast-cache for tt-lookup. */ +extern Addr VG_(tt_fast)[VG_TT_FAST_SIZE]; + +extern void VG_(init_tt_tc) ( void ); +extern void VG_(add_to_trans_tab) ( Addr orig_addr, Int orig_size, + Addr trans_addr, Int trans_size, + UShort jumps[VG_MAX_JUMPS]); +extern Addr VG_(search_transtab) ( Addr original_addr ); + +extern void VG_(invalidate_translations) ( Addr start, UInt range, + Bool unchain_blocks ); + +extern void VG_(sanity_check_tt_tc) ( void ); + +extern void VG_(print_tt_tc_stats) ( void ); + +extern Int VG_(get_bbs_translated) ( void ); + +/* --------------------------------------------------------------------- + Exports of vg_syscall.S + ------------------------------------------------------------------ */ + +extern Int VG_(do_syscall) ( UInt, ... ); +extern Int VG_(clone) ( Int (*fn)(void *), void *stack, Int flags, void *arg, + Int *child_tid, Int *parent_tid); +extern void VG_(sigreturn)(void); + +/* --------------------------------------------------------------------- + Exports of vg_dispatch.S + ------------------------------------------------------------------ */ + +/* Run a thread for a (very short) while, until some event happens + which means we need to defer to the scheduler. */ +extern UInt VG_(run_innerloop) ( void ); + +/* The patching routing called when a BB wants to chain itself to + another. */ +extern UInt VG_(patch_me); + +/* --------------------------------------------------------------------- + Exports of vg_helpers.S + ------------------------------------------------------------------ */ + +extern void VG_(helper_undefined_instruction); + +/* Information about trampoline code (for signal return and syscalls) */ +extern const Char VG_(trampoline_code_start); +extern const Int VG_(trampoline_code_length); +extern const Int VG_(tramp_sigreturn_offset); +extern const Int VG_(tramp_syscall_offset); + +/* --------------------------------------------------------------------- + Things relating to the used tool + ------------------------------------------------------------------ */ + +#define VG_TRACK(fn, args...) \ + do { \ + if (VG_(defined_##fn)()) \ + SK_(fn)(args); \ + } while(0) + +__attribute__ ((noreturn)) +extern void VG_(missing_tool_func) ( const Char* fn ); + +/* --------------------------------------------------------------------- + The baseBlock -- arch-neutral bits + ------------------------------------------------------------------ */ + +#define INVALID_OFFSET (-1) + +/* An array of words. In generated code, %ebp always points to the + start of this array. Useful stuff, like the simulated CPU state, + and the addresses of helper functions, can then be found by + indexing off %ebp. The following declares variables which, at + startup time, are given values denoting offsets into baseBlock. + These offsets are in *words* from the start of baseBlock. */ + +#define VG_BASEBLOCK_WORDS 400 + +extern UInt VG_(baseBlock)[VG_BASEBLOCK_WORDS]; + +// --------------------------------------------------------------------- +// Architecture-specific things defined in eg. x86/*.c +// --------------------------------------------------------------------- + +// For setting up the baseBlock +extern void VGA_(init_low_baseBlock) ( Addr client_eip, Addr esp_at_startup ); +extern void VGA_(init_high_baseBlock) ( Addr client_eip, Addr esp_at_startup ); + +// Register state moving +extern void VGA_(load_state) ( arch_thread_t*, ThreadId tid ); +extern void VGA_(save_state) ( arch_thread_t*, ThreadId tid ); + +// Thread stuff +extern void VGA_(clear_thread) ( arch_thread_t* ); +extern void VGA_(init_thread) ( arch_thread_t* ); +extern void VGA_(cleanup_thread) ( arch_thread_t* ); +extern void VGA_(setup_child) ( arch_thread_t*, arch_thread_t* ); + +extern void VGA_(set_arg_and_bogus_ret) ( ThreadId tid, UWord arg, Addr ret ); +extern void VGA_(thread_initial_stack) ( ThreadId tid, UWord arg, Addr ret ); + +// Symtab stuff +extern UInt* VGA_(reg_addr_from_BB) ( Int reg ); +extern UInt* VGA_(reg_addr_from_tst) ( Int reg, arch_thread_t* ); + +// Pointercheck +extern Bool VGA_(setup_pointercheck) ( void ); + +// For attaching the debugger +extern Int VGA_(ptrace_setregs_from_BB) ( Int pid ); +extern Int VGA_(ptrace_setregs_from_tst) ( Int pid, arch_thread_t* arch ); + +// Making coredumps +extern void VGA_(fill_elfregs_from_BB) ( struct user_regs_struct* regs ); +extern void VGA_(fill_elfregs_from_tst) ( struct user_regs_struct* regs, + const arch_thread_t* arch ); +extern void VGA_(fill_elffpregs_from_BB) ( elf_fpregset_t* fpu ); +extern void VGA_(fill_elffpregs_from_tst) ( elf_fpregset_t* fpu, + const arch_thread_t* arch ); +extern void VGA_(fill_elffpxregs_from_BB) ( elf_fpxregset_t* xfpu ); +extern void VGA_(fill_elffpxregs_from_tst) ( elf_fpxregset_t* xfpu, + const arch_thread_t* arch ); + +// Signal stuff +extern void VGA_(push_signal_frame) ( ThreadId tid, Addr esp_top_of_frame, + const vki_ksiginfo_t *siginfo, + void *handler, UInt flags, + const vki_ksigset_t *mask); +extern Int VGA_(pop_signal_frame) ( ThreadId tid ); + +// --------------------------------------------------------------------- +// Platform-specific things defined in eg. x86/*.c +// --------------------------------------------------------------------- + +extern const Addr vga_sys_before, vga_sys_restarted, + vga_sys_after, vga_sys_done; + +extern void VGA_(restart_syscall)(arch_thread_t* arch); + +extern void VGA_(thread_syscall)(Int syscallno, arch_thread_t* arch, + enum PXState* state, enum PXState poststate); + +/* --------------------------------------------------------------------- + Finally - autoconf-generated settings + ------------------------------------------------------------------ */ + +#include "config.h" + +#endif /* ndef __CORE_H */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/core_asm.h b/VEX/head20041019/coregrind/core_asm.h new file mode 100644 index 0000000000..5d43ffd289 --- /dev/null +++ b/VEX/head20041019/coregrind/core_asm.h @@ -0,0 +1,159 @@ + +/*--------------------------------------------------------------------*/ +/*--- Asm-specific core stuff. core_asm.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __CORE_ASM_H +#define __CORE_ASM_H + +#include "tool_asm.h" // tool asm stuff +#include "core_arch_asm.h" // arch-specific asm stuff + +/* This file is included in all Valgrind source files, including + assembly ones. */ + +/* Magic values that %ebp might be set to when returning to the + dispatcher. The only other legitimate value is to point to the + start of VG_(baseBlock). These also are return values from + VG_(run_innerloop) to the scheduler. + + EBP means %ebp can legitimately have this value when a basic block + returns to the dispatch loop. TRC means that this value is a valid + thread return code, which the dispatch loop may return to the + scheduler. */ +#define VG_TRC_EBP_JMP_SYSCALL 19 /* EBP and TRC */ +#define VG_TRC_EBP_JMP_CLIENTREQ 23 /* EBP and TRC */ +#define VG_TRC_EBP_JMP_YIELD 27 /* EBP and TRC */ + +#define VG_TRC_INNER_FASTMISS 31 /* TRC only; means fast-cache miss. */ +#define VG_TRC_INNER_COUNTERZERO 29 /* TRC only; means bb ctr == 0 */ +#define VG_TRC_UNRESUMABLE_SIGNAL 37 /* TRC only; got sigsegv/sigbus */ + +/* maximum number of normal jumps which can appear in a basic block */ +#define VG_MAX_JUMPS 2 + +/* Offset of code in a TCEntry */ +#define VG_CODE_OFFSET (8 + VG_MAX_JUMPS * 2) + +/* Client address space segment limit descriptor entry */ +#define VG_POINTERCHECK_SEGIDX 1 + +/* Debugging hack for assembly code ... sigh. */ +#if 0 +#define OYNK(nnn) pushal; pushl $nnn; call VG_(oynk) ; addl $4,%esp; popal +#else +#define OYNK(nnn) +#endif + +#if 0 +#define OYNNK(nnn) pushal; pushl $nnn; call VG_(oynk) ; addl $4,%esp; popal +#else +#define OYNNK(nnn) +#endif + + +/* Constants for the fast translation lookup cache. */ +#define VG_TT_FAST_BITS 15 +#define VG_TT_FAST_SIZE (1 << VG_TT_FAST_BITS) +#define VG_TT_FAST_MASK ((VG_TT_FAST_SIZE) - 1) + +/* Constants for the fast original-code-write check cache. */ + + +/* Assembly code stubs make this request */ +#define VG_USERREQ__SIGNAL_RETURNS 0x4001 + +/* + 0 - standard feature flags + 1 - Intel extended flags + 2 - Valgrind internal flags + 3 - AMD-specific flags + */ +#define VG_N_FEATURE_WORDS 4 + +#define VG_X86_FEAT 0 +#define VG_EXT_FEAT 1 +#define VG_INT_FEAT 2 +#define VG_AMD_FEAT 3 + +/* CPU features (generic) */ +#define VG_X86_FEAT_FPU (VG_X86_FEAT*32 + 0) +#define VG_X86_FEAT_VME (VG_X86_FEAT*32 + 1) +#define VG_X86_FEAT_DE (VG_X86_FEAT*32 + 2) +#define VG_X86_FEAT_PSE (VG_X86_FEAT*32 + 3) +#define VG_X86_FEAT_TSC (VG_X86_FEAT*32 + 4) +#define VG_X86_FEAT_MSR (VG_X86_FEAT*32 + 5) +#define VG_X86_FEAT_PAE (VG_X86_FEAT*32 + 6) +#define VG_X86_FEAT_MCE (VG_X86_FEAT*32 + 7) +#define VG_X86_FEAT_CX8 (VG_X86_FEAT*32 + 8) +#define VG_X86_FEAT_APIC (VG_X86_FEAT*32 + 9) +#define VG_X86_FEAT_SEP (VG_X86_FEAT*32 + 11) +#define VG_X86_FEAT_MTRR (VG_X86_FEAT*32 + 12) +#define VG_X86_FEAT_PGE (VG_X86_FEAT*32 + 13) +#define VG_X86_FEAT_MCA (VG_X86_FEAT*32 + 14) +#define VG_X86_FEAT_CMOV (VG_X86_FEAT*32 + 15) +#define VG_X86_FEAT_PAT (VG_X86_FEAT*32 + 16) +#define VG_X86_FEAT_PSE36 (VG_X86_FEAT*32 + 17) +#define VG_X86_FEAT_CLFSH (VG_X86_FEAT*32 + 19) +#define VG_X86_FEAT_DS (VG_X86_FEAT*32 + 21) +#define VG_X86_FEAT_ACPI (VG_X86_FEAT*32 + 22) +#define VG_X86_FEAT_MMX (VG_X86_FEAT*32 + 23) +#define VG_X86_FEAT_FXSR (VG_X86_FEAT*32 + 24) +#define VG_X86_FEAT_SSE (VG_X86_FEAT*32 + 25) +#define VG_X86_FEAT_SSE2 (VG_X86_FEAT*32 + 26) +#define VG_X86_FEAT_SS (VG_X86_FEAT*32 + 27) +#define VG_X86_FEAT_HT (VG_X86_FEAT*32 + 28) +#define VG_X86_FEAT_TM (VG_X86_FEAT*32 + 29) +#define VG_X86_FEAT_IA64 (VG_X86_FEAT*32 + 30) +#define VG_X86_FEAT_PBE (VG_X86_FEAT*32 + 31) + +/* Intel extended feature word */ +#define VG_X86_FEAT_SSE3 (VG_EXT_FEAT*32 + 0) +#define VG_X86_FEAT_MON (VG_EXT_FEAT*32 + 3) +#define VG_X86_FEAT_DSCPL (VG_EXT_FEAT*32 + 4) +#define VG_X86_FEAT_EST (VG_EXT_FEAT*32 + 7) +#define VG_X86_FEAT_TM2 (VG_EXT_FEAT*32 + 8) +#define VG_X86_FEAT_CNXTID (VG_EXT_FEAT*32 + 10) + +/* Used internally to mark whether CPUID is even implemented */ +#define VG_X86_FEAT_CPUID (VG_INT_FEAT*32 + 0) + +/* AMD special features */ +#define VG_AMD_FEAT_SYSCALL (VG_AMD_FEAT*32 + 11) +#define VG_AMD_FEAT_NXP (VG_AMD_FEAT*32 + 20) +#define VG_AMD_FEAT_MMXEXT (VG_AMD_FEAT*32 + 22) +#define VG_AMD_FEAT_FFXSR (VG_AMD_FEAT*32 + 25) +#define VG_AMD_FEAT_LONGMODE (VG_AMD_FEAT*32 + 29) +#define VG_AMD_FEAT_3DNOWEXT (VG_AMD_FEAT*32 + 30) +#define VG_AMD_FEAT_3DNOW (VG_AMD_FEAT*32 + 31) + +#endif /* ndef __CORE_ASM_H */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/demangle/.cvsignore b/VEX/head20041019/coregrind/demangle/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/coregrind/demangle/CVS/Entries b/VEX/head20041019/coregrind/demangle/CVS/Entries new file mode 100644 index 0000000000..bd1a91c973 --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/CVS/Entries @@ -0,0 +1,11 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:28 2002// +/Makefile.am/1.13/Wed Sep 1 23:20:47 2004// +/ansidecl.h/1.1.1.1/Fri Mar 22 01:29:43 2002// +/cp-demangle.c/1.7/Wed Sep 1 23:58:16 2004// +/cplus-dem.c/1.7/Wed Sep 1 23:58:16 2004// +/demangle.h/1.1.1.1/Fri Mar 22 01:29:43 2002// +/dyn-string.c/1.6/Wed Sep 1 23:58:16 2004// +/dyn-string.h/1.1.1.1/Fri Mar 22 01:29:43 2002// +/safe-ctype.c/1.2/Mon Feb 24 10:49:08 2003// +/safe-ctype.h/1.1.1.1/Fri Mar 22 01:29:43 2002// +D diff --git a/VEX/head20041019/coregrind/demangle/CVS/Repository b/VEX/head20041019/coregrind/demangle/CVS/Repository new file mode 100644 index 0000000000..1e1632d284 --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/CVS/Repository @@ -0,0 +1 @@ +valgrind/coregrind/demangle diff --git a/VEX/head20041019/coregrind/demangle/CVS/Root b/VEX/head20041019/coregrind/demangle/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/coregrind/demangle/CVS/Template b/VEX/head20041019/coregrind/demangle/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/coregrind/demangle/Makefile.am b/VEX/head20041019/coregrind/demangle/Makefile.am new file mode 100644 index 0000000000..9aa1b3ad9b --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/Makefile.am @@ -0,0 +1,20 @@ +include $(top_srcdir)/Makefile.all.am +include $(top_srcdir)/Makefile.core-AM_CPPFLAGS.am + +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -O -fomit-frame-pointer -g + +noinst_HEADERS = \ + ansidecl.h \ + dyn-string.h \ + demangle.h \ + safe-ctype.h + +noinst_LIBRARIES = libdemangle.a + +libdemangle_a_SOURCES = \ + cp-demangle.c cplus-dem.c dyn-string.c safe-ctype.c + +## Ignore harmless warnings for these ones +cp-demangle.o: CFLAGS += -Wno-unused -Wno-shadow +cplus-dem.o: CFLAGS += -Wno-unused + diff --git a/VEX/head20041019/coregrind/demangle/ansidecl.h b/VEX/head20041019/coregrind/demangle/ansidecl.h new file mode 100644 index 0000000000..9a7c5777ff --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/ansidecl.h @@ -0,0 +1,295 @@ +/* ANSI and traditional C compatability macros + Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + +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 2 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, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* ANSI and traditional C compatibility macros + + ANSI C is assumed if __STDC__ is #defined. + + Macro ANSI C definition Traditional C definition + ----- ---- - ---------- ----------- - ---------- + ANSI_PROTOTYPES 1 not defined + PTR `void *' `char *' + PTRCONST `void *const' `char *' + LONG_DOUBLE `long double' `double' + const not defined `' + volatile not defined `' + signed not defined `' + VA_START(ap, var) va_start(ap, var) va_start(ap) + + Note that it is safe to write "void foo();" indicating a function + with no return value, in all K+R compilers we have been able to test. + + For declaring functions with prototypes, we also provide these: + + PARAMS ((prototype)) + -- for functions which take a fixed number of arguments. Use this + when declaring the function. When defining the function, write a + K+R style argument list. For example: + + char *strcpy PARAMS ((char *dest, char *source)); + ... + char * + strcpy (dest, source) + char *dest; + char *source; + { ... } + + + VPARAMS ((prototype, ...)) + -- for functions which take a variable number of arguments. Use + PARAMS to declare the function, VPARAMS to define it. For example: + + int printf PARAMS ((const char *format, ...)); + ... + int + printf VPARAMS ((const char *format, ...)) + { + ... + } + + For writing functions which take variable numbers of arguments, we + also provide the VA_OPEN, VA_CLOSE, and VA_FIXEDARG macros. These + hide the differences between K+R and C89 more + thoroughly than the simple VA_START() macro mentioned above. + + VA_OPEN and VA_CLOSE are used *instead of* va_start and va_end. + Immediately after VA_OPEN, put a sequence of VA_FIXEDARG calls + corresponding to the list of fixed arguments. Then use va_arg + normally to get the variable arguments, or pass your va_list object + around. You do not declare the va_list yourself; VA_OPEN does it + for you. + + Here is a complete example: + + int + printf VPARAMS ((const char *format, ...)) + { + int result; + + VA_OPEN (ap, format); + VA_FIXEDARG (ap, const char *, format); + + result = vfprintf (stdout, format, ap); + VA_CLOSE (ap); + + return result; + } + + + You can declare variables either before or after the VA_OPEN, + VA_FIXEDARG sequence. Also, VA_OPEN and VA_CLOSE are the beginning + and end of a block. They must appear at the same nesting level, + and any variables declared after VA_OPEN go out of scope at + VA_CLOSE. Unfortunately, with a K+R compiler, that includes the + argument list. You can have multiple instances of VA_OPEN/VA_CLOSE + pairs in a single function in case you need to traverse the + argument list more than once. + + For ease of writing code which uses GCC extensions but needs to be + portable to other compilers, we provide the GCC_VERSION macro that + simplifies testing __GNUC__ and __GNUC_MINOR__ together, and various + wrappers around __attribute__. Also, __extension__ will be #defined + to nothing if it doesn't work. See below. + + This header also defines a lot of obsolete macros: + CONST, VOLATILE, SIGNED, PROTO, EXFUN, DEFUN, DEFUN_VOID, + AND, DOTS, NOARGS. Don't use them. */ + +#ifndef _ANSIDECL_H +#define _ANSIDECL_H 1 + +/* Every source file includes this file, + so they will all get the switch for lint. */ +/* LINTLIBRARY */ + +/* Using MACRO(x,y) in cpp #if conditionals does not work with some + older preprocessors. Thus we can't define something like this: + +#define HAVE_GCC_VERSION(MAJOR, MINOR) \ + (__GNUC__ > (MAJOR) || (__GNUC__ == (MAJOR) && __GNUC_MINOR__ >= (MINOR))) + +and then test "#if HAVE_GCC_VERSION(2,7)". + +So instead we use the macro below and test it against specific values. */ + +/* This macro simplifies testing whether we are using gcc, and if it + is of a particular minimum version. (Both major & minor numbers are + significant.) This macro will evaluate to 0 if we are not using + gcc at all. */ +#ifndef GCC_VERSION +#define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) +#endif /* GCC_VERSION */ + +#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(_WIN32) +/* All known AIX compilers implement these things (but don't always + define __STDC__). The RISC/OS MIPS compiler defines these things + in SVR4 mode, but does not define __STDC__. */ + +#define ANSI_PROTOTYPES 1 +#define PTR void * +#define PTRCONST void *const +#define LONG_DOUBLE long double + +#define PARAMS(ARGS) ARGS +#define VPARAMS(ARGS) ARGS +#define VA_START(VA_LIST, VAR) va_start(VA_LIST, VAR) + +/* variadic function helper macros */ +/* "struct Qdmy" swallows the semicolon after VA_OPEN/VA_FIXEDARG's + use without inhibiting further decls and without declaring an + actual variable. */ +#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP, VAR); { struct Qdmy +#define VA_CLOSE(AP) } va_end(AP); } +#define VA_FIXEDARG(AP, T, N) struct Qdmy + +#undef const +#undef volatile +#undef signed + +/* inline requires special treatment; it's in C99, and GCC >=2.7 supports + it too, but it's not in C89. */ +#undef inline +#if __STDC_VERSION__ > 199901L +/* it's a keyword */ +#else +# if GCC_VERSION >= 2007 +# define inline __inline__ /* __inline__ prevents -pedantic warnings */ +# else +# define inline /* nothing */ +# endif +#endif + +/* These are obsolete. Do not use. */ +#ifndef IN_GCC +#define CONST const +#define VOLATILE volatile +#define SIGNED signed + +#define PROTO(type, name, arglist) type name arglist +#define EXFUN(name, proto) name proto +#define DEFUN(name, arglist, args) name(args) +#define DEFUN_VOID(name) name(void) +#define AND , +#define DOTS , ... +#define NOARGS void +#endif /* ! IN_GCC */ + +#else /* Not ANSI C. */ + +#undef ANSI_PROTOTYPES +#define PTR char * +#define PTRCONST PTR +#define LONG_DOUBLE double + +#define PARAMS(args) () +#define VPARAMS(args) (va_alist) va_dcl +#define VA_START(va_list, var) va_start(va_list) + +#define VA_OPEN(AP, VAR) { va_list AP; va_start(AP); { struct Qdmy +#define VA_CLOSE(AP) } va_end(AP); } +#define VA_FIXEDARG(AP, TYPE, NAME) TYPE NAME = va_arg(AP, TYPE) + +/* some systems define these in header files for non-ansi mode */ +#undef const +#undef volatile +#undef signed +#undef inline +#define const +#define volatile +#define signed +#define inline + +#ifndef IN_GCC +#define CONST +#define VOLATILE +#define SIGNED + +#define PROTO(type, name, arglist) type name () +#define EXFUN(name, proto) name() +#define DEFUN(name, arglist, args) name arglist args; +#define DEFUN_VOID(name) name() +#define AND ; +#define DOTS +#define NOARGS +#endif /* ! IN_GCC */ + +#endif /* ANSI C. */ + +/* Define macros for some gcc attributes. This permits us to use the + macros freely, and know that they will come into play for the + version of gcc in which they are supported. */ + +#if (GCC_VERSION < 2007) +# define __attribute__(x) +#endif + +/* Attribute __malloc__ on functions was valid as of gcc 2.96. */ +#ifndef ATTRIBUTE_MALLOC +# if (GCC_VERSION >= 2096) +# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) +# else +# define ATTRIBUTE_MALLOC +# endif /* GNUC >= 2.96 */ +#endif /* ATTRIBUTE_MALLOC */ + +/* Attributes on labels were valid as of gcc 2.93. */ +#ifndef ATTRIBUTE_UNUSED_LABEL +# if (GCC_VERSION >= 2093) +# define ATTRIBUTE_UNUSED_LABEL ATTRIBUTE_UNUSED +# else +# define ATTRIBUTE_UNUSED_LABEL +# endif /* GNUC >= 2.93 */ +#endif /* ATTRIBUTE_UNUSED_LABEL */ + +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif /* ATTRIBUTE_UNUSED */ + +#ifndef ATTRIBUTE_NORETURN +#define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) +#endif /* ATTRIBUTE_NORETURN */ + +#ifndef ATTRIBUTE_PRINTF +#define ATTRIBUTE_PRINTF(m, n) __attribute__ ((__format__ (__printf__, m, n))) +#define ATTRIBUTE_PRINTF_1 ATTRIBUTE_PRINTF(1, 2) +#define ATTRIBUTE_PRINTF_2 ATTRIBUTE_PRINTF(2, 3) +#define ATTRIBUTE_PRINTF_3 ATTRIBUTE_PRINTF(3, 4) +#define ATTRIBUTE_PRINTF_4 ATTRIBUTE_PRINTF(4, 5) +#define ATTRIBUTE_PRINTF_5 ATTRIBUTE_PRINTF(5, 6) +#endif /* ATTRIBUTE_PRINTF */ + +/* We use __extension__ in some places to suppress -pedantic warnings + about GCC extensions. This feature didn't work properly before + gcc 2.8. */ +#if GCC_VERSION < 2008 +#define __extension__ +#endif + +/* Bootstrap support: Adjust certain macros defined by Autoconf, + which are only valid for the stage1 compiler. If we detect + a modern version of GCC, we are probably in stage2 or beyond, + so unconditionally reset the values. Note that const, inline, + etc. have been dealt with above. */ +#if (GCC_VERSION >= 2007) +# ifndef HAVE_LONG_DOUBLE +# define HAVE_LONG_DOUBLE 1 +# endif +#endif /* GCC >= 2.7 */ + +#endif /* ansidecl.h */ diff --git a/VEX/head20041019/coregrind/demangle/cp-demangle.c b/VEX/head20041019/coregrind/demangle/cp-demangle.c new file mode 100644 index 0000000000..4bd37481f9 --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/cp-demangle.c @@ -0,0 +1,4176 @@ +/* Demangler for IA64 / g++ V3 ABI. + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Written by Alex Samuel . + + This file is part of GNU CC. + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* This file implements demangling of C++ names mangled according to + the IA64 / g++ V3 ABI. Use the cp_demangle function to + demangle a mangled name, or compile with the preprocessor macro + STANDALONE_DEMANGLER defined to create a demangling filter + executable (functionally similar to c++filt, but includes this + demangler only). */ + +/*#include */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/*#ifdef HAVE_STDLIB_H +#include +#endif*/ + +/*#ifdef HAVE_STRING_H +#include +#endif*/ + +#include "core.h" +#include "ansidecl.h" +#include "dyn-string.h" +#include "demangle.h" + +#ifndef STANDALONE +#define size_t Int + +#define malloc(s) VG_(arena_malloc) (VG_AR_DEMANGLE, s) +#define free(p) VG_(arena_free) (VG_AR_DEMANGLE, p) +#define realloc(p,s) VG_(arena_realloc)(VG_AR_DEMANGLE, p, VG_MIN_MALLOC_SZB, s) +#endif + +/* If CP_DEMANGLE_DEBUG is defined, a trace of the grammar evaluation, + and other debugging output, will be generated. */ +#ifdef CP_DEMANGLE_DEBUG +#define DEMANGLE_TRACE(PRODUCTION, DM) \ + fprintf (stderr, " -> %-24s at position %3d\n", \ + (PRODUCTION), current_position (DM)); +#else +#define DEMANGLE_TRACE(PRODUCTION, DM) +#endif + +/* Don't include , to prevent additional unresolved symbols + from being dragged into the C++ runtime library. */ +#define IS_DIGIT(CHAR) ((CHAR) >= '0' && (CHAR) <= '9') +#define IS_ALPHA(CHAR) \ + (((CHAR) >= 'a' && (CHAR) <= 'z') \ + || ((CHAR) >= 'A' && (CHAR) <= 'Z')) + +/* The prefix prepended by GCC to an identifier represnting the + anonymous namespace. */ +#define ANONYMOUS_NAMESPACE_PREFIX "_GLOBAL_" + +/* Character(s) to use for namespace separation in demangled output */ +#define NAMESPACE_SEPARATOR (dm->style == DMGL_JAVA ? "." : "::") + +/* If flag_verbose is zero, some simplifications will be made to the + output to make it easier to read and suppress details that are + generally not of interest to the average C++ programmer. + Otherwise, the demangled representation will attempt to convey as + much information as the mangled form. */ +static int flag_verbose; + +/* If flag_strict is non-zero, demangle strictly according to the + specification -- don't demangle special g++ manglings. */ +static int flag_strict; + +/* String_list_t is an extended form of dyn_string_t which provides a + link field and a caret position for additions to the string. A + string_list_t may safely be cast to and used as a dyn_string_t. */ + +struct string_list_def +{ + /* The dyn_string; must be first. */ + struct dyn_string string; + + /* The position at which additional text is added to this string + (using the result_add* macros). This value is an offset from the + end of the string, not the beginning (and should be + non-positive). */ + int caret_position; + + /* The next string in the list. */ + struct string_list_def *next; +}; + +typedef struct string_list_def *string_list_t; + +/* Data structure representing a potential substitution. */ + +struct substitution_def +{ + /* The demangled text of the substitution. */ + dyn_string_t text; + + /* Whether this substitution represents a template item. */ + int template_p : 1; +}; + +/* Data structure representing a template argument list. */ + +struct template_arg_list_def +{ + /* The next (lower) template argument list in the stack of currently + active template arguments. */ + struct template_arg_list_def *next; + + /* The first element in the list of template arguments in + left-to-right order. */ + string_list_t first_argument; + + /* The last element in the arguments lists. */ + string_list_t last_argument; +}; + +typedef struct template_arg_list_def *template_arg_list_t; + +/* Data structure to maintain the state of the current demangling. */ + +struct demangling_def +{ + /* The full mangled name being mangled. */ + const char *name; + + /* Pointer into name at the current position. */ + const char *next; + + /* Stack for strings containing demangled result generated so far. + Text is emitted to the topmost (first) string. */ + string_list_t result; + + /* The number of presently available substitutions. */ + int num_substitutions; + + /* The allocated size of the substitutions array. */ + int substitutions_allocated; + + /* An array of available substitutions. The number of elements in + the array is given by num_substitions, and the allocated array + size in substitutions_size. + + The most recent substition is at the end, so + + - `S_' corresponds to substititutions[num_substitutions - 1] + - `S0_' corresponds to substititutions[num_substitutions - 2] + + etc. */ + struct substitution_def *substitutions; + + /* The stack of template argument lists. */ + template_arg_list_t template_arg_lists; + + /* The most recently demangled source-name. */ + dyn_string_t last_source_name; + + /* Language style to use for demangled output. */ + int style; + + /* Set to non-zero iff this name is a constructor. The actual value + indicates what sort of constructor this is; see demangle.h. */ + enum gnu_v3_ctor_kinds is_constructor; + + /* Set to non-zero iff this name is a destructor. The actual value + indicates what sort of destructor this is; see demangle.h. */ + enum gnu_v3_dtor_kinds is_destructor; + +}; + +typedef struct demangling_def *demangling_t; + +/* This type is the standard return code from most functions. Values + other than STATUS_OK contain descriptive messages. */ +typedef const char *status_t; + +/* Special values that can be used as a status_t. */ +#define STATUS_OK NULL +#define STATUS_ERROR "Error." +#define STATUS_UNIMPLEMENTED "Unimplemented." +#define STATUS_INTERNAL_ERROR "Internal error." + +/* This status code indicates a failure in malloc or realloc. */ +static const char *const status_allocation_failed = "Allocation failed."; +#define STATUS_ALLOCATION_FAILED status_allocation_failed + +/* Non-zero if STATUS indicates that no error has occurred. */ +#define STATUS_NO_ERROR(STATUS) ((STATUS) == STATUS_OK) + +/* Evaluate EXPR, which must produce a status_t. If the status code + indicates an error, return from the current function with that + status code. */ +#define RETURN_IF_ERROR(EXPR) \ + do \ + { \ + status_t s = EXPR; \ + if (!STATUS_NO_ERROR (s)) \ + return s; \ + } \ + while (0) + +static status_t int_to_dyn_string + PARAMS ((int, dyn_string_t)); +static string_list_t string_list_new + PARAMS ((int)); +static void string_list_delete + PARAMS ((string_list_t)); +static status_t result_add_separated_char + PARAMS ((demangling_t, int)); +static status_t result_push + PARAMS ((demangling_t)); +static string_list_t result_pop + PARAMS ((demangling_t)); +static int substitution_start + PARAMS ((demangling_t)); +static status_t substitution_add + PARAMS ((demangling_t, int, int)); +static dyn_string_t substitution_get + PARAMS ((demangling_t, int, int *)); +#ifdef CP_DEMANGLE_DEBUG +static void substitutions_print + PARAMS ((demangling_t, FILE *)); +#endif +static template_arg_list_t template_arg_list_new + PARAMS ((void)); +static void template_arg_list_delete + PARAMS ((template_arg_list_t)); +static void template_arg_list_add_arg + PARAMS ((template_arg_list_t, string_list_t)); +static string_list_t template_arg_list_get_arg + PARAMS ((template_arg_list_t, int)); +static void push_template_arg_list + PARAMS ((demangling_t, template_arg_list_t)); +static void pop_to_template_arg_list + PARAMS ((demangling_t, template_arg_list_t)); +#ifdef CP_DEMANGLE_DEBUG +static void template_arg_list_print + PARAMS ((template_arg_list_t, FILE *)); +#endif +static template_arg_list_t current_template_arg_list + PARAMS ((demangling_t)); +static demangling_t demangling_new + PARAMS ((const char *, int)); +static void demangling_delete + PARAMS ((demangling_t)); + +/* The last character of DS. Warning: DS is evaluated twice. */ +#define dyn_string_last_char(DS) \ + (dyn_string_buf (DS)[dyn_string_length (DS) - 1]) + +/* Append a space character (` ') to DS if it does not already end + with one. Evaluates to 1 on success, or 0 on allocation failure. */ +#define dyn_string_append_space(DS) \ + ((dyn_string_length (DS) > 0 \ + && dyn_string_last_char (DS) != ' ') \ + ? dyn_string_append_char ((DS), ' ') \ + : 1) + +/* Returns the index of the current position in the mangled name. */ +#define current_position(DM) ((DM)->next - (DM)->name) + +/* Returns the character at the current position of the mangled name. */ +#define peek_char(DM) (*((DM)->next)) + +/* Returns the character one past the current position of the mangled + name. */ +#define peek_char_next(DM) \ + (peek_char (DM) == '\0' ? '\0' : (*((DM)->next + 1))) + +/* Returns the character at the current position, and advances the + current position to the next character. */ +#define next_char(DM) (*((DM)->next)++) + +/* Returns non-zero if the current position is the end of the mangled + name, i.e. one past the last character. */ +#define end_of_name_p(DM) (peek_char (DM) == '\0') + +/* Advances the current position by one character. */ +#define advance_char(DM) (++(DM)->next) + +/* Returns the string containing the current demangled result. */ +#define result_string(DM) (&(DM)->result->string) + +/* Returns the position at which new text is inserted into the + demangled result. */ +#define result_caret_pos(DM) \ + (result_length (DM) + \ + ((string_list_t) result_string (DM))->caret_position) + +/* Adds a dyn_string_t to the demangled result. */ +#define result_add_string(DM, STRING) \ + (dyn_string_insert (&(DM)->result->string, \ + result_caret_pos (DM), (STRING)) \ + ? STATUS_OK : STATUS_ALLOCATION_FAILED) + +/* Adds NUL-terminated string CSTR to the demangled result. */ +#define result_add(DM, CSTR) \ + (dyn_string_insert_cstr (&(DM)->result->string, \ + result_caret_pos (DM), (CSTR)) \ + ? STATUS_OK : STATUS_ALLOCATION_FAILED) + +/* Adds character CHAR to the demangled result. */ +#define result_add_char(DM, CHAR) \ + (dyn_string_insert_char (&(DM)->result->string, \ + result_caret_pos (DM), (CHAR)) \ + ? STATUS_OK : STATUS_ALLOCATION_FAILED) + +/* Inserts a dyn_string_t to the demangled result at position POS. */ +#define result_insert_string(DM, POS, STRING) \ + (dyn_string_insert (&(DM)->result->string, (POS), (STRING)) \ + ? STATUS_OK : STATUS_ALLOCATION_FAILED) + +/* Inserts NUL-terminated string CSTR to the demangled result at + position POS. */ +#define result_insert(DM, POS, CSTR) \ + (dyn_string_insert_cstr (&(DM)->result->string, (POS), (CSTR)) \ + ? STATUS_OK : STATUS_ALLOCATION_FAILED) + +/* Inserts character CHAR to the demangled result at position POS. */ +#define result_insert_char(DM, POS, CHAR) \ + (dyn_string_insert_char (&(DM)->result->string, (POS), (CHAR)) \ + ? STATUS_OK : STATUS_ALLOCATION_FAILED) + +/* The length of the current demangled result. */ +#define result_length(DM) \ + dyn_string_length (&(DM)->result->string) + +/* Appends a (less-than, greater-than) character to the result in DM + to (open, close) a template argument or parameter list. Appends a + space first if necessary to prevent spurious elision of angle + brackets with the previous character. */ +#define result_open_template_list(DM) result_add_separated_char(DM, '<') +#define result_close_template_list(DM) result_add_separated_char(DM, '>') + +/* Appends a base 10 representation of VALUE to DS. STATUS_OK on + success. On failure, deletes DS and returns an error code. */ + +static status_t +int_to_dyn_string (value, ds) + int value; + dyn_string_t ds; +{ + int i; + int mask = 1; + + /* Handle zero up front. */ + if (value == 0) + { + if (!dyn_string_append_char (ds, '0')) + return STATUS_ALLOCATION_FAILED; + return STATUS_OK; + } + + /* For negative numbers, emit a minus sign. */ + if (value < 0) + { + if (!dyn_string_append_char (ds, '-')) + return STATUS_ALLOCATION_FAILED; + value = -value; + } + + /* Find the power of 10 of the first digit. */ + i = value; + while (i > 9) + { + mask *= 10; + i /= 10; + } + + /* Write the digits. */ + while (mask > 0) + { + int digit = value / mask; + + if (!dyn_string_append_char (ds, '0' + digit)) + return STATUS_ALLOCATION_FAILED; + + value -= digit * mask; + mask /= 10; + } + + return STATUS_OK; +} + +/* Creates a new string list node. The contents of the string are + empty, but the initial buffer allocation is LENGTH. The string + list node should be deleted with string_list_delete. Returns NULL + if allocation fails. */ + +static string_list_t +string_list_new (length) + int length; +{ + string_list_t s = (string_list_t) malloc (sizeof (struct string_list_def)); + s->caret_position = 0; + if (s == NULL) + return NULL; + if (!dyn_string_init ((dyn_string_t) s, length)) + return NULL; + return s; +} + +/* Deletes the entire string list starting at NODE. */ + +static void +string_list_delete (node) + string_list_t node; +{ + while (node != NULL) + { + string_list_t next = node->next; + dyn_string_delete ((dyn_string_t) node); + node = next; + } +} + +/* Appends CHARACTER to the demangled result. If the current trailing + character of the result is CHARACTER, a space is inserted first. */ + +static status_t +result_add_separated_char (dm, character) + demangling_t dm; + int character; +{ + char *result = dyn_string_buf (result_string (dm)); + int caret_pos = result_caret_pos (dm); + + /* Add a space if the last character is already the character we + want to add. */ + if (caret_pos > 0 && result[caret_pos - 1] == character) + RETURN_IF_ERROR (result_add_char (dm, ' ')); + /* Add the character. */ + RETURN_IF_ERROR (result_add_char (dm, character)); + + return STATUS_OK; +} + +/* Allocates and pushes a new string onto the demangled results stack + for DM. Subsequent demangling with DM will emit to the new string. + Returns STATUS_OK on success, STATUS_ALLOCATION_FAILED on + allocation failure. */ + +static status_t +result_push (dm) + demangling_t dm; +{ + string_list_t new_string = string_list_new (0); + if (new_string == NULL) + /* Allocation failed. */ + return STATUS_ALLOCATION_FAILED; + + /* Link the new string to the front of the list of result strings. */ + new_string->next = (string_list_t) dm->result; + dm->result = new_string; + return STATUS_OK; +} + +/* Removes and returns the topmost element on the demangled results + stack for DM. The caller assumes ownership for the returned + string. */ + +static string_list_t +result_pop (dm) + demangling_t dm; +{ + string_list_t top = dm->result; + dm->result = top->next; + return top; +} + +/* Returns the current value of the caret for the result string. The + value is an offet from the end of the result string. */ + +static int +result_get_caret (dm) + demangling_t dm; +{ + return ((string_list_t) result_string (dm))->caret_position; +} + +/* Sets the value of the caret for the result string, counted as an + offet from the end of the result string. */ + +static void +result_set_caret (dm, position) + demangling_t dm; + int position; +{ + ((string_list_t) result_string (dm))->caret_position = position; +} + +/* Shifts the position of the next addition to the result by + POSITION_OFFSET. A negative value shifts the caret to the left. */ + +static void +result_shift_caret (dm, position_offset) + demangling_t dm; + int position_offset; +{ + ((string_list_t) result_string (dm))->caret_position += position_offset; +} + +/* Returns non-zero if the character that comes right before the place + where text will be added to the result is a space. In this case, + the caller should suppress adding another space. */ + +static int +result_previous_char_is_space (dm) + demangling_t dm; +{ + char *result = dyn_string_buf (result_string (dm)); + int pos = result_caret_pos (dm); + return pos > 0 && result[pos - 1] == ' '; +} + +/* Returns the start position of a fragment of the demangled result + that will be a substitution candidate. Should be called at the + start of productions that can add substitutions. */ + +static int +substitution_start (dm) + demangling_t dm; +{ + return result_caret_pos (dm); +} + +/* Adds the suffix of the current demangled result of DM starting at + START_POSITION as a potential substitution. If TEMPLATE_P is + non-zero, this potential substitution is a template-id. */ + +static status_t +substitution_add (dm, start_position, template_p) + demangling_t dm; + int start_position; + int template_p; +{ + dyn_string_t result = result_string (dm); + dyn_string_t substitution = dyn_string_new (0); + int i; + + if (substitution == NULL) + return STATUS_ALLOCATION_FAILED; + + /* Extract the substring of the current demangling result that + represents the subsitution candidate. */ + if (!dyn_string_substring (substitution, + result, start_position, result_caret_pos (dm))) + { + dyn_string_delete (substitution); + return STATUS_ALLOCATION_FAILED; + } + + /* If there's no room for the new entry, grow the array. */ + if (dm->substitutions_allocated == dm->num_substitutions) + { + size_t new_array_size; + if (dm->substitutions_allocated > 0) + dm->substitutions_allocated *= 2; + else + dm->substitutions_allocated = 2; + new_array_size = + sizeof (struct substitution_def) * dm->substitutions_allocated; + + dm->substitutions = (struct substitution_def *) + realloc (dm->substitutions, new_array_size); + if (dm->substitutions == NULL) + /* Realloc failed. */ + { + dyn_string_delete (substitution); + return STATUS_ALLOCATION_FAILED; + } + } + + /* Add the substitution to the array. */ + i = dm->num_substitutions++; + dm->substitutions[i].text = substitution; + dm->substitutions[i].template_p = template_p; + +#ifdef CP_DEMANGLE_DEBUG + substitutions_print (dm, stderr); +#endif + + return STATUS_OK; +} + +/* Returns the Nth-most-recent substitution. Sets *TEMPLATE_P to + non-zero if the substitution is a template-id, zero otherwise. + N is numbered from zero. DM retains ownership of the returned + string. If N is negative, or equal to or greater than the current + number of substitution candidates, returns NULL. */ + +static dyn_string_t +substitution_get (dm, n, template_p) + demangling_t dm; + int n; + int *template_p; +{ + struct substitution_def *sub; + + /* Make sure N is in the valid range. */ + if (n < 0 || n >= dm->num_substitutions) + return NULL; + + sub = &(dm->substitutions[n]); + *template_p = sub->template_p; + return sub->text; +} + +#ifdef CP_DEMANGLE_DEBUG +/* Debugging routine to print the current substitutions to FP. */ + +static void +substitutions_print (dm, fp) + demangling_t dm; + FILE *fp; +{ + int seq_id; + int num = dm->num_substitutions; + + fprintf (fp, "SUBSTITUTIONS:\n"); + for (seq_id = -1; seq_id < num - 1; ++seq_id) + { + int template_p; + dyn_string_t text = substitution_get (dm, seq_id + 1, &template_p); + + if (seq_id == -1) + fprintf (fp, " S_ "); + else + fprintf (fp, " S%d_", seq_id); + fprintf (fp, " %c: %s\n", template_p ? '*' : ' ', dyn_string_buf (text)); + } +} + +#endif /* CP_DEMANGLE_DEBUG */ + +/* Creates a new template argument list. Returns NULL if allocation + fails. */ + +static template_arg_list_t +template_arg_list_new () +{ + template_arg_list_t new_list = + (template_arg_list_t) malloc (sizeof (struct template_arg_list_def)); + if (new_list == NULL) + return NULL; + /* Initialize the new list to have no arguments. */ + new_list->first_argument = NULL; + new_list->last_argument = NULL; + /* Return the new list. */ + return new_list; +} + +/* Deletes a template argument list and the template arguments it + contains. */ + +static void +template_arg_list_delete (list) + template_arg_list_t list; +{ + /* If there are any arguments on LIST, delete them. */ + if (list->first_argument != NULL) + string_list_delete (list->first_argument); + /* Delete LIST. */ + free (list); +} + +/* Adds ARG to the template argument list ARG_LIST. */ + +static void +template_arg_list_add_arg (arg_list, arg) + template_arg_list_t arg_list; + string_list_t arg; +{ + if (arg_list->first_argument == NULL) + /* If there were no arguments before, ARG is the first one. */ + arg_list->first_argument = arg; + else + /* Make ARG the last argument on the list. */ + arg_list->last_argument->next = arg; + /* Make ARG the last on the list. */ + arg_list->last_argument = arg; + arg->next = NULL; +} + +/* Returns the template arugment at position INDEX in template + argument list ARG_LIST. */ + +static string_list_t +template_arg_list_get_arg (arg_list, index) + template_arg_list_t arg_list; + int index; +{ + string_list_t arg = arg_list->first_argument; + /* Scan down the list of arguments to find the one at position + INDEX. */ + while (index--) + { + arg = arg->next; + if (arg == NULL) + /* Ran out of arguments before INDEX hit zero. That's an + error. */ + return NULL; + } + /* Return the argument at position INDEX. */ + return arg; +} + +/* Pushes ARG_LIST onto the top of the template argument list stack. */ + +static void +push_template_arg_list (dm, arg_list) + demangling_t dm; + template_arg_list_t arg_list; +{ + arg_list->next = dm->template_arg_lists; + dm->template_arg_lists = arg_list; +#ifdef CP_DEMANGLE_DEBUG + fprintf (stderr, " ** pushing template arg list\n"); + template_arg_list_print (arg_list, stderr); +#endif +} + +/* Pops and deletes elements on the template argument list stack until + arg_list is the topmost element. If arg_list is NULL, all elements + are popped and deleted. */ + +static void +pop_to_template_arg_list (dm, arg_list) + demangling_t dm; + template_arg_list_t arg_list; +{ + while (dm->template_arg_lists != arg_list) + { + template_arg_list_t top = dm->template_arg_lists; + /* Disconnect the topmost element from the list. */ + dm->template_arg_lists = top->next; + /* Delete the popped element. */ + template_arg_list_delete (top); +#ifdef CP_DEMANGLE_DEBUG + fprintf (stderr, " ** removing template arg list\n"); +#endif + } +} + +#ifdef CP_DEMANGLE_DEBUG + +/* Prints the contents of ARG_LIST to FP. */ + +static void +template_arg_list_print (arg_list, fp) + template_arg_list_t arg_list; + FILE *fp; +{ + string_list_t arg; + int index = -1; + + fprintf (fp, "TEMPLATE ARGUMENT LIST:\n"); + for (arg = arg_list->first_argument; arg != NULL; arg = arg->next) + { + if (index == -1) + fprintf (fp, " T_ : "); + else + fprintf (fp, " T%d_ : ", index); + ++index; + fprintf (fp, "%s\n", dyn_string_buf ((dyn_string_t) arg)); + } +} + +#endif /* CP_DEMANGLE_DEBUG */ + +/* Returns the topmost element on the stack of template argument + lists. If there is no list of template arguments, returns NULL. */ + +static template_arg_list_t +current_template_arg_list (dm) + demangling_t dm; +{ + return dm->template_arg_lists; +} + +/* Allocates a demangling_t object for demangling mangled NAME. A new + result must be pushed before the returned object can be used. + Returns NULL if allocation fails. */ + +static demangling_t +demangling_new (name, style) + const char *name; + int style; +{ + demangling_t dm; + dm = (demangling_t) malloc (sizeof (struct demangling_def)); + if (dm == NULL) + return NULL; + + dm->name = name; + dm->next = name; + dm->result = NULL; + dm->num_substitutions = 0; + dm->substitutions_allocated = 10; + dm->template_arg_lists = NULL; + dm->last_source_name = dyn_string_new (0); + if (dm->last_source_name == NULL) + return NULL; + dm->substitutions = (struct substitution_def *) + malloc (dm->substitutions_allocated * sizeof (struct substitution_def)); + if (dm->substitutions == NULL) + { + dyn_string_delete (dm->last_source_name); + return NULL; + } + dm->style = style; + dm->is_constructor = 0; + dm->is_destructor = 0; + + return dm; +} + +/* Deallocates a demangling_t object and all memory associated with + it. */ + +static void +demangling_delete (dm) + demangling_t dm; +{ + int i; + template_arg_list_t arg_list = dm->template_arg_lists; + + /* Delete the stack of template argument lists. */ + while (arg_list != NULL) + { + template_arg_list_t next = arg_list->next; + template_arg_list_delete (arg_list); + arg_list = next; + } + /* Delete the list of substitutions. */ + for (i = dm->num_substitutions; --i >= 0; ) + dyn_string_delete (dm->substitutions[i].text); + free (dm->substitutions); + /* Delete the demangled result. */ + string_list_delete (dm->result); + /* Delete the stored identifier name. */ + dyn_string_delete (dm->last_source_name); + /* Delete the context object itself. */ + free (dm); +} + +/* These functions demangle an alternative of the corresponding + production in the mangling spec. The first argument of each is a + demangling context structure for the current demangling + operation. Most emit demangled text directly to the topmost result + string on the result string stack in the demangling context + structure. */ + +static status_t demangle_char + PARAMS ((demangling_t, int)); +static status_t demangle_mangled_name + PARAMS ((demangling_t)); +static status_t demangle_encoding + PARAMS ((demangling_t)); +static status_t demangle_name + PARAMS ((demangling_t, int *)); +static status_t demangle_nested_name + PARAMS ((demangling_t, int *)); +static status_t demangle_prefix_v3 + PARAMS ((demangling_t, int *)); +static status_t demangle_unqualified_name + PARAMS ((demangling_t, int *)); +static status_t demangle_source_name + PARAMS ((demangling_t)); +static status_t demangle_number + PARAMS ((demangling_t, int *, int, int)); +static status_t demangle_number_literally + PARAMS ((demangling_t, dyn_string_t, int, int)); +static status_t demangle_identifier + PARAMS ((demangling_t, int, dyn_string_t)); +static status_t demangle_operator_name + PARAMS ((demangling_t, int, int *)); +static status_t demangle_nv_offset + PARAMS ((demangling_t)); +static status_t demangle_v_offset + PARAMS ((demangling_t)); +static status_t demangle_call_offset + PARAMS ((demangling_t)); +static status_t demangle_special_name + PARAMS ((demangling_t)); +static status_t demangle_ctor_dtor_name + PARAMS ((demangling_t)); +static status_t demangle_type_ptr + PARAMS ((demangling_t, int *, int)); +static status_t demangle_type + PARAMS ((demangling_t)); +static status_t demangle_CV_qualifiers + PARAMS ((demangling_t, dyn_string_t)); +static status_t demangle_builtin_type + PARAMS ((demangling_t)); +static status_t demangle_function_type + PARAMS ((demangling_t, int *)); +static status_t demangle_bare_function_type + PARAMS ((demangling_t, int *)); +static status_t demangle_class_enum_type + PARAMS ((demangling_t, int *)); +static status_t demangle_array_type + PARAMS ((demangling_t, int *)); +static status_t demangle_template_param + PARAMS ((demangling_t)); +static status_t demangle_template_args_1 + PARAMS ((demangling_t, template_arg_list_t)); +static status_t demangle_template_args + PARAMS ((demangling_t)); +static status_t demangle_literal + PARAMS ((demangling_t)); +static status_t demangle_template_arg + PARAMS ((demangling_t)); +static status_t demangle_expression_v3 + PARAMS ((demangling_t)); +static status_t demangle_scope_expression + PARAMS ((demangling_t)); +static status_t demangle_expr_primary + PARAMS ((demangling_t)); +static status_t demangle_substitution + PARAMS ((demangling_t, int *)); +static status_t demangle_local_name + PARAMS ((demangling_t)); +static status_t demangle_discriminator + PARAMS ((demangling_t, int)); +static status_t cp_demangle + PARAMS ((const char *, dyn_string_t, int)); +#ifdef IN_LIBGCC2 +static status_t cp_demangle_type + PARAMS ((const char*, dyn_string_t)); +#endif + +/* When passed to demangle_bare_function_type, indicates that the + function's return type is not encoded before its parameter types. */ +#define BFT_NO_RETURN_TYPE NULL + +/* Check that the next character is C. If so, consume it. If not, + return an error. */ + +static status_t +demangle_char (dm, c) + demangling_t dm; + int c; +{ + static char *error_message = NULL; + + if (peek_char (dm) == c) + { + advance_char (dm); + return STATUS_OK; + } + else + { + vg_assert (0); + /* + if (error_message == NULL) + error_message = strdup ("Expected ?"); + error_message[9] = c; + return error_message; + */ + } +} + +/* Demangles and emits a . + + ::= _Z */ + +static status_t +demangle_mangled_name (dm) + demangling_t dm; +{ + DEMANGLE_TRACE ("mangled-name", dm); + RETURN_IF_ERROR (demangle_char (dm, '_')); + RETURN_IF_ERROR (demangle_char (dm, 'Z')); + RETURN_IF_ERROR (demangle_encoding (dm)); + return STATUS_OK; +} + +/* Demangles and emits an . + + ::= + ::= + ::= */ + +static status_t +demangle_encoding (dm) + demangling_t dm; +{ + int encode_return_type; + int start_position; + template_arg_list_t old_arg_list = current_template_arg_list (dm); + char peek = peek_char (dm); + + DEMANGLE_TRACE ("encoding", dm); + + /* Remember where the name starts. If it turns out to be a template + function, we'll have to insert the return type here. */ + start_position = result_caret_pos (dm); + + if (peek == 'G' || peek == 'T') + RETURN_IF_ERROR (demangle_special_name (dm)); + else + { + /* Now demangle the name. */ + RETURN_IF_ERROR (demangle_name (dm, &encode_return_type)); + + /* If there's anything left, the name was a function name, with + maybe its return type, and its parameter types, following. */ + if (!end_of_name_p (dm) + && peek_char (dm) != 'E') + { + if (encode_return_type) + /* Template functions have their return type encoded. The + return type should be inserted at start_position. */ + RETURN_IF_ERROR + (demangle_bare_function_type (dm, &start_position)); + else + /* Non-template functions don't have their return type + encoded. */ + RETURN_IF_ERROR + (demangle_bare_function_type (dm, BFT_NO_RETURN_TYPE)); + } + } + + /* Pop off template argument lists that were built during the + mangling of this name, to restore the old template context. */ + pop_to_template_arg_list (dm, old_arg_list); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= + ::= + ::= + ::= + + ::= + ::= St # ::std:: + + + ::= + ::= */ + +static status_t +demangle_name (dm, encode_return_type) + demangling_t dm; + int *encode_return_type; +{ + int start = substitution_start (dm); + char peek = peek_char (dm); + int is_std_substitution = 0; + + /* Generally, the return type is encoded if the function is a + template-id, and suppressed otherwise. There are a few cases, + though, in which the return type is not encoded even for a + templated function. In these cases, this flag is set. */ + int suppress_return_type = 0; + + DEMANGLE_TRACE ("name", dm); + + switch (peek) + { + case 'N': + /* This is a . */ + RETURN_IF_ERROR (demangle_nested_name (dm, encode_return_type)); + break; + + case 'Z': + RETURN_IF_ERROR (demangle_local_name (dm)); + *encode_return_type = 0; + break; + + case 'S': + /* The `St' substitution allows a name nested in std:: to appear + without being enclosed in a nested name. */ + if (peek_char_next (dm) == 't') + { + (void) next_char (dm); + (void) next_char (dm); + RETURN_IF_ERROR (result_add (dm, "std::")); + RETURN_IF_ERROR + (demangle_unqualified_name (dm, &suppress_return_type)); + is_std_substitution = 1; + } + else + RETURN_IF_ERROR (demangle_substitution (dm, encode_return_type)); + /* Check if a template argument list immediately follows. + If so, then we just demangled an . */ + if (peek_char (dm) == 'I') + { + /* A template name of the form std:: is a + substitution candidate. */ + if (is_std_substitution) + RETURN_IF_ERROR (substitution_add (dm, start, 0)); + /* Demangle the here. */ + RETURN_IF_ERROR (demangle_template_args (dm)); + *encode_return_type = !suppress_return_type; + } + else + *encode_return_type = 0; + + break; + + default: + /* This is an or . */ + RETURN_IF_ERROR (demangle_unqualified_name (dm, &suppress_return_type)); + + /* If the is followed by template args, this + is an . */ + if (peek_char (dm) == 'I') + { + /* Add a substitution for the unqualified template name. */ + RETURN_IF_ERROR (substitution_add (dm, start, 0)); + + RETURN_IF_ERROR (demangle_template_args (dm)); + *encode_return_type = !suppress_return_type; + } + else + *encode_return_type = 0; + + break; + } + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= N [] E */ + +static status_t +demangle_nested_name (dm, encode_return_type) + demangling_t dm; + int *encode_return_type; +{ + char peek; + + DEMANGLE_TRACE ("nested-name", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'N')); + + peek = peek_char (dm); + if (peek == 'r' || peek == 'V' || peek == 'K') + { + dyn_string_t cv_qualifiers; + status_t status; + + /* Snarf up CV qualifiers. */ + cv_qualifiers = dyn_string_new (24); + if (cv_qualifiers == NULL) + return STATUS_ALLOCATION_FAILED; + demangle_CV_qualifiers (dm, cv_qualifiers); + + /* Emit them, preceded by a space. */ + status = result_add_char (dm, ' '); + if (STATUS_NO_ERROR (status)) + status = result_add_string (dm, cv_qualifiers); + /* The CV qualifiers that occur in a will be + qualifiers for member functions. These are placed at the end + of the function. Therefore, shift the caret to the left by + the length of the qualifiers, so other text is inserted + before them and they stay at the end. */ + result_shift_caret (dm, -dyn_string_length (cv_qualifiers) - 1); + /* Clean up. */ + dyn_string_delete (cv_qualifiers); + RETURN_IF_ERROR (status); + } + + RETURN_IF_ERROR (demangle_prefix_v3 (dm, encode_return_type)); + /* No need to demangle the final ; demangle_prefix + will handle it. */ + RETURN_IF_ERROR (demangle_char (dm, 'E')); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= + ::= + ::= # empty + ::= + + ::= + ::= */ + +static status_t +demangle_prefix_v3 (dm, encode_return_type) + demangling_t dm; + int *encode_return_type; +{ + int start = substitution_start (dm); + int nested = 0; + + /* ENCODE_RETURN_TYPE is updated as we decend the nesting chain. + After , it is set to non-zero; after everything + else it is set to zero. */ + + /* Generally, the return type is encoded if the function is a + template-id, and suppressed otherwise. There are a few cases, + though, in which the return type is not encoded even for a + templated function. In these cases, this flag is set. */ + int suppress_return_type = 0; + + DEMANGLE_TRACE ("prefix", dm); + + while (1) + { + char peek; + + if (end_of_name_p (dm)) + return "Unexpected end of name in ."; + + peek = peek_char (dm); + + /* We'll initialize suppress_return_type to false, and set it to true + if we end up demangling a constructor name. However, make + sure we're not actually about to demangle template arguments + -- if so, this is the following a + , so we'll want the previous flag value + around. */ + if (peek != 'I') + suppress_return_type = 0; + + if (IS_DIGIT ((unsigned char) peek) + || (peek >= 'a' && peek <= 'z') + || peek == 'C' || peek == 'D' + || peek == 'S') + { + /* We have another level of scope qualification. */ + if (nested) + RETURN_IF_ERROR (result_add (dm, NAMESPACE_SEPARATOR)); + else + nested = 1; + + if (peek == 'S') + /* The substitution determines whether this is a + template-id. */ + RETURN_IF_ERROR (demangle_substitution (dm, encode_return_type)); + else + { + /* It's just a name. */ + RETURN_IF_ERROR + (demangle_unqualified_name (dm, &suppress_return_type)); + *encode_return_type = 0; + } + } + else if (peek == 'Z') + RETURN_IF_ERROR (demangle_local_name (dm)); + else if (peek == 'I') + { + RETURN_IF_ERROR (demangle_template_args (dm)); + + /* Now we want to indicate to the caller that we've + demangled template arguments, thus the prefix was a + . That's so that the caller knows to + demangle the function's return type, if this turns out to + be a function name. But, if it's a member template + constructor or a templated conversion operator, report it + as untemplated. Those never get encoded return types. */ + *encode_return_type = !suppress_return_type; + } + else if (peek == 'E') + /* All done. */ + return STATUS_OK; + else + return "Unexpected character in ."; + + if (peek != 'S' + && peek_char (dm) != 'E') + /* Add a new substitution for the prefix thus far. */ + RETURN_IF_ERROR (substitution_add (dm, start, *encode_return_type)); + } +} + +/* Demangles and emits an . If this + is for a special function type that should never + have its return type encoded (particularly, a constructor or + conversion operator), *SUPPRESS_RETURN_TYPE is set to 1; otherwise, + it is set to zero. + + ::= + ::= + ::= */ + +static status_t +demangle_unqualified_name (dm, suppress_return_type) + demangling_t dm; + int *suppress_return_type; +{ + char peek = peek_char (dm); + + DEMANGLE_TRACE ("unqualified-name", dm); + + /* By default, don't force suppression of the return type (though + non-template functions still don't get a return type encoded). */ + *suppress_return_type = 0; + + if (IS_DIGIT ((unsigned char) peek)) + RETURN_IF_ERROR (demangle_source_name (dm)); + else if (peek >= 'a' && peek <= 'z') + { + int num_args; + + /* Conversion operators never have a return type encoded. */ + if (peek == 'c' && peek_char_next (dm) == 'v') + *suppress_return_type = 1; + + RETURN_IF_ERROR (demangle_operator_name (dm, 0, &num_args)); + } + else if (peek == 'C' || peek == 'D') + { + /* Constructors never have a return type encoded. */ + if (peek == 'C') + *suppress_return_type = 1; + + RETURN_IF_ERROR (demangle_ctor_dtor_name (dm)); + } + else + return "Unexpected character in ."; + + return STATUS_OK; +} + +/* Demangles and emits . + + ::= */ + +static status_t +demangle_source_name (dm) + demangling_t dm; +{ + int length; + + DEMANGLE_TRACE ("source-name", dm); + + /* Decode the length of the identifier. */ + RETURN_IF_ERROR (demangle_number (dm, &length, 10, 0)); + if (length == 0) + return "Zero length in ."; + + /* Now the identifier itself. It's placed into last_source_name, + where it can be used to build a constructor or destructor name. */ + RETURN_IF_ERROR (demangle_identifier (dm, length, + dm->last_source_name)); + + /* Emit it. */ + RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name)); + + return STATUS_OK; +} + +/* Demangles a number, either a or a at the + current position, consuming all consecutive digit characters. Sets + *VALUE to the resulting numberand returns STATUS_OK. The number is + interpreted as BASE, which must be either 10 or 36. If IS_SIGNED + is non-zero, negative numbers -- prefixed with `n' -- are accepted. + + ::= [n] + + ::= */ + +static status_t +demangle_number (dm, value, base, is_signed) + demangling_t dm; + int *value; + int base; + int is_signed; +{ + dyn_string_t number = dyn_string_new (10); + + DEMANGLE_TRACE ("number", dm); + + if (number == NULL) + return STATUS_ALLOCATION_FAILED; + + demangle_number_literally (dm, number, base, is_signed); + /* + *value = strtol (dyn_string_buf (number), NULL, base); + */ + /* vg_assert( base == 10 ); */ + if ( base != 10 && base != 36 ) { + dyn_string_delete(number); + return STATUS_UNIMPLEMENTED; + } + + if (base == 36) { + *value = VG_(atoll36) (36, dyn_string_buf (number)); + } else { + *value = VG_(atoll) (dyn_string_buf (number)); + } + dyn_string_delete (number); + + return STATUS_OK; +} + +/* Demangles a number at the current position. The digits (and minus + sign, if present) that make up the number are appended to STR. + Only base-BASE digits are accepted; BASE must be either 10 or 36. + If IS_SIGNED, negative numbers -- prefixed with `n' -- are + accepted. Does not consume a trailing underscore or other + terminating character. */ + +static status_t +demangle_number_literally (dm, str, base, is_signed) + demangling_t dm; + dyn_string_t str; + int base; + int is_signed; +{ + DEMANGLE_TRACE ("number*", dm); + + if (base != 10 && base != 36) + return STATUS_INTERNAL_ERROR; + + /* An `n' denotes a negative number. */ + if (is_signed && peek_char (dm) == 'n') + { + /* Skip past the n. */ + advance_char (dm); + /* The normal way to write a negative number is with a minus + sign. */ + if (!dyn_string_append_char (str, '-')) + return STATUS_ALLOCATION_FAILED; + } + + /* Loop until we hit a non-digit. */ + while (1) + { + char peek = peek_char (dm); + if (IS_DIGIT ((unsigned char) peek) + || (base == 36 && peek >= 'A' && peek <= 'Z')) + { + /* Accumulate digits. */ + if (!dyn_string_append_char (str, next_char (dm))) + return STATUS_ALLOCATION_FAILED; + } + else + /* Not a digit? All done. */ + break; + } + + return STATUS_OK; +} + +/* Demangles an identifier at the current position of LENGTH + characters and places it in IDENTIFIER. */ + +static status_t +demangle_identifier (dm, length, identifier) + demangling_t dm; + int length; + dyn_string_t identifier; +{ + DEMANGLE_TRACE ("identifier", dm); + + dyn_string_clear (identifier); + if (!dyn_string_resize (identifier, length)) + return STATUS_ALLOCATION_FAILED; + + while (length-- > 0) + { + if (end_of_name_p (dm)) + return "Unexpected end of name in ."; + if (!dyn_string_append_char (identifier, next_char (dm))) + return STATUS_ALLOCATION_FAILED; + } + + /* GCC encodes anonymous namespaces using a `_GLOBAL_[_.$]N.' + followed by the source file name and some random characters. + Unless we're in strict mode, decipher these names appropriately. */ + if (!flag_strict) + { + char *name = dyn_string_buf (identifier); + int prefix_length = VG_(strlen) (ANONYMOUS_NAMESPACE_PREFIX); + + /* Compare the first, fixed part. */ + if (VG_(strncmp) (name, ANONYMOUS_NAMESPACE_PREFIX, prefix_length) == 0) + { + name += prefix_length; + /* The next character might be a period, an underscore, or + dollar sign, depending on the target architecture's + assembler's capabilities. After that comes an `N'. */ + if ((*name == '.' || *name == '_' || *name == '$') + && *(name + 1) == 'N') + /* This looks like the anonymous namespace identifier. + Replace it with something comprehensible. */ + dyn_string_copy_cstr (identifier, "(anonymous namespace)"); + } + } + + return STATUS_OK; +} + +/* Demangles and emits an . If SHORT_NAME is non-zero, + the short form is emitted; otherwise the full source form + (`operator +' etc.) is emitted. *NUM_ARGS is set to the number of + operands that the operator takes. + + + ::= nw # new + ::= na # new[] + ::= dl # delete + ::= da # delete[] + ::= ps # + (unary) + ::= ng # - (unary) + ::= ad # & (unary) + ::= de # * (unary) + ::= co # ~ + ::= pl # + + ::= mi # - + ::= ml # * + ::= dv # / + ::= rm # % + ::= an # & + ::= or # | + ::= eo # ^ + ::= aS # = + ::= pL # += + ::= mI # -= + ::= mL # *= + ::= dV # /= + ::= rM # %= + ::= aN # &= + ::= oR # |= + ::= eO # ^= + ::= ls # << + ::= rs # >> + ::= lS # <<= + ::= rS # >>= + ::= eq # == + ::= ne # != + ::= lt # < + ::= gt # > + ::= le # <= + ::= ge # >= + ::= nt # ! + ::= aa # && + ::= oo # || + ::= pp # ++ + ::= mm # -- + ::= cm # , + ::= pm # ->* + ::= pt # -> + ::= cl # () + ::= ix # [] + ::= qu # ? + ::= sz # sizeof + ::= cv # cast + ::= v [0-9] # vendor extended operator */ + +static status_t +demangle_operator_name (dm, short_name, num_args) + demangling_t dm; + int short_name; + int *num_args; +{ + struct operator_code + { + /* The mangled code for this operator. */ + const char *const code; + /* The source name of this operator. */ + const char *const name; + /* The number of arguments this operator takes. */ + const int num_args; + }; + + static const struct operator_code operators[] = + { + { "aN", "&=" , 2 }, + { "aS", "=" , 2 }, + { "aa", "&&" , 2 }, + { "ad", "&" , 1 }, + { "an", "&" , 2 }, + { "cl", "()" , 0 }, + { "cm", "," , 2 }, + { "co", "~" , 1 }, + { "dV", "/=" , 2 }, + { "da", " delete[]", 1 }, + { "de", "*" , 1 }, + { "dl", " delete" , 1 }, + { "dv", "/" , 2 }, + { "eO", "^=" , 2 }, + { "eo", "^" , 2 }, + { "eq", "==" , 2 }, + { "ge", ">=" , 2 }, + { "gt", ">" , 2 }, + { "ix", "[]" , 2 }, + { "lS", "<<=" , 2 }, + { "le", "<=" , 2 }, + { "ls", "<<" , 2 }, + { "lt", "<" , 2 }, + { "mI", "-=" , 2 }, + { "mL", "*=" , 2 }, + { "mi", "-" , 2 }, + { "ml", "*" , 2 }, + { "mm", "--" , 1 }, + { "na", " new[]" , 1 }, + { "ne", "!=" , 2 }, + { "ng", "-" , 1 }, + { "nt", "!" , 1 }, + { "nw", " new" , 1 }, + { "oR", "|=" , 2 }, + { "oo", "||" , 2 }, + { "or", "|" , 2 }, + { "pL", "+=" , 2 }, + { "pl", "+" , 2 }, + { "pm", "->*" , 2 }, + { "pp", "++" , 1 }, + { "ps", "+" , 1 }, + { "pt", "->" , 2 }, + { "qu", "?" , 3 }, + { "rM", "%=" , 2 }, + { "rS", ">>=" , 2 }, + { "rm", "%" , 2 }, + { "rs", ">>" , 2 }, + { "sz", " sizeof" , 1 } + }; + + const int num_operators = + sizeof (operators) / sizeof (struct operator_code); + + int c0 = next_char (dm); + int c1 = next_char (dm); + const struct operator_code* p1 = operators; + const struct operator_code* p2 = operators + num_operators; + + DEMANGLE_TRACE ("operator-name", dm); + + /* Is this a vendor-extended operator? */ + if (c0 == 'v' && IS_DIGIT (c1)) + { + RETURN_IF_ERROR (result_add (dm, "operator ")); + RETURN_IF_ERROR (demangle_source_name (dm)); + *num_args = 0; + return STATUS_OK; + } + + /* Is this a conversion operator? */ + if (c0 == 'c' && c1 == 'v') + { + RETURN_IF_ERROR (result_add (dm, "operator ")); + /* Demangle the converted-to type. */ + RETURN_IF_ERROR (demangle_type (dm)); + *num_args = 0; + return STATUS_OK; + } + + /* Perform a binary search for the operator code. */ + while (1) + { + const struct operator_code* p = p1 + (p2 - p1) / 2; + char match0 = p->code[0]; + char match1 = p->code[1]; + + if (c0 == match0 && c1 == match1) + /* Found it. */ + { + if (!short_name) + RETURN_IF_ERROR (result_add (dm, "operator")); + RETURN_IF_ERROR (result_add (dm, p->name)); + *num_args = p->num_args; + + return STATUS_OK; + } + + if (p == p1) + /* Couldn't find it. */ + return "Unknown code in ."; + + /* Try again. */ + if (c0 < match0 || (c0 == match0 && c1 < match1)) + p2 = p; + else + p1 = p; + } +} + +/* Demangles and omits an . + + ::= # non-virtual base override */ + +static status_t +demangle_nv_offset (dm) + demangling_t dm; +{ + dyn_string_t number; + status_t status = STATUS_OK; + + DEMANGLE_TRACE ("h-offset", dm); + + /* Demangle the offset. */ + number = dyn_string_new (4); + if (number == NULL) + return STATUS_ALLOCATION_FAILED; + demangle_number_literally (dm, number, 10, 1); + + /* Don't display the offset unless in verbose mode. */ + if (flag_verbose) + { + status = result_add (dm, " [nv:"); + if (STATUS_NO_ERROR (status)) + status = result_add_string (dm, number); + if (STATUS_NO_ERROR (status)) + status = result_add_char (dm, ']'); + } + + /* Clean up. */ + dyn_string_delete (number); + RETURN_IF_ERROR (status); + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= _ + # virtual base override, with vcall offset */ + +static status_t +demangle_v_offset (dm) + demangling_t dm; +{ + dyn_string_t number; + status_t status = STATUS_OK; + + DEMANGLE_TRACE ("v-offset", dm); + + /* Demangle the offset. */ + number = dyn_string_new (4); + if (number == NULL) + return STATUS_ALLOCATION_FAILED; + demangle_number_literally (dm, number, 10, 1); + + /* Don't display the offset unless in verbose mode. */ + if (flag_verbose) + { + status = result_add (dm, " [v:"); + if (STATUS_NO_ERROR (status)) + status = result_add_string (dm, number); + if (STATUS_NO_ERROR (status)) + result_add_char (dm, ','); + } + dyn_string_delete (number); + RETURN_IF_ERROR (status); + + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + + /* Demangle the vcall offset. */ + number = dyn_string_new (4); + if (number == NULL) + return STATUS_ALLOCATION_FAILED; + demangle_number_literally (dm, number, 10, 1); + + /* Don't display the vcall offset unless in verbose mode. */ + if (flag_verbose) + { + status = result_add_string (dm, number); + if (STATUS_NO_ERROR (status)) + status = result_add_char (dm, ']'); + } + dyn_string_delete (number); + RETURN_IF_ERROR (status); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= h _ + ::= v _ */ + +static status_t +demangle_call_offset (dm) + demangling_t dm; +{ + DEMANGLE_TRACE ("call-offset", dm); + + switch (peek_char (dm)) + { + case 'h': + advance_char (dm); + /* Demangle the offset. */ + RETURN_IF_ERROR (demangle_nv_offset (dm)); + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + break; + + case 'v': + advance_char (dm); + /* Demangle the offset. */ + RETURN_IF_ERROR (demangle_v_offset (dm)); + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + break; + + default: + return "Unrecognized ."; + } + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= GV # Guard variable + ::= TV # virtual table + ::= TT # VTT + ::= TI # typeinfo structure + ::= TS # typeinfo name + + Other relevant productions include thunks: + + ::= T + # base is the nominal target function of thunk + + ::= Tc + # base is the nominal target function of thunk + # first call-offset is 'this' adjustment + # second call-offset is result adjustment + + where + + ::= h _ + ::= v _ + + Also demangles the special g++ manglings, + + ::= TC _ + # construction vtable + ::= TF # typeinfo function (old ABI only) + ::= TJ # java Class structure */ + +static status_t +demangle_special_name (dm) + demangling_t dm; +{ + dyn_string_t number; + int unused; + char peek = peek_char (dm); + + DEMANGLE_TRACE ("special-name", dm); + + if (peek == 'G') + { + /* Consume the G. */ + advance_char (dm); + switch (peek_char (dm)) + { + case 'V': + /* A guard variable name. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "guard variable for ")); + RETURN_IF_ERROR (demangle_name (dm, &unused)); + break; + + case 'R': + /* A reference temporary. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "reference temporary for ")); + RETURN_IF_ERROR (demangle_name (dm, &unused)); + break; + + default: + return "Unrecognized ."; + } + } + else if (peek == 'T') + { + status_t status = STATUS_OK; + + /* Other C++ implementation miscellania. Consume the T. */ + advance_char (dm); + + switch (peek_char (dm)) + { + case 'V': + /* Virtual table. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "vtable for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'T': + /* VTT structure. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "VTT for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'I': + /* Typeinfo structure. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "typeinfo for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'F': + /* Typeinfo function. Used only in old ABI with new mangling. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "typeinfo fn for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'S': + /* Character string containing type name, used in typeinfo. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "typeinfo name for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'J': + /* The java Class variable corresponding to a C++ class. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "java Class for ")); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'h': + /* Non-virtual thunk. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "non-virtual thunk")); + RETURN_IF_ERROR (demangle_nv_offset (dm)); + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + /* Demangle and emit the target name and function type. */ + RETURN_IF_ERROR (result_add (dm, " to ")); + RETURN_IF_ERROR (demangle_encoding (dm)); + break; + + case 'v': + /* Virtual thunk. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "virtual thunk")); + RETURN_IF_ERROR (demangle_v_offset (dm)); + /* Demangle the separator. */ + RETURN_IF_ERROR (demangle_char (dm, '_')); + /* Demangle and emit the target function. */ + RETURN_IF_ERROR (result_add (dm, " to ")); + RETURN_IF_ERROR (demangle_encoding (dm)); + break; + + case 'c': + /* Covariant return thunk. */ + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "covariant return thunk")); + RETURN_IF_ERROR (demangle_call_offset (dm)); + RETURN_IF_ERROR (demangle_call_offset (dm)); + /* Demangle and emit the target function. */ + RETURN_IF_ERROR (result_add (dm, " to ")); + RETURN_IF_ERROR (demangle_encoding (dm)); + break; + + case 'C': + /* TC is a special g++ mangling for a construction vtable. */ + if (!flag_strict) + { + dyn_string_t derived_type; + + advance_char (dm); + RETURN_IF_ERROR (result_add (dm, "construction vtable for ")); + + /* Demangle the derived type off to the side. */ + RETURN_IF_ERROR (result_push (dm)); + RETURN_IF_ERROR (demangle_type (dm)); + derived_type = (dyn_string_t) result_pop (dm); + + /* Demangle the offset. */ + number = dyn_string_new (4); + if (number == NULL) + { + dyn_string_delete (derived_type); + return STATUS_ALLOCATION_FAILED; + } + demangle_number_literally (dm, number, 10, 1); + /* Demangle the underscore separator. */ + status = demangle_char (dm, '_'); + + /* Demangle the base type. */ + if (STATUS_NO_ERROR (status)) + status = demangle_type (dm); + + /* Emit the derived type. */ + if (STATUS_NO_ERROR (status)) + status = result_add (dm, "-in-"); + if (STATUS_NO_ERROR (status)) + status = result_add_string (dm, derived_type); + dyn_string_delete (derived_type); + + /* Don't display the offset unless in verbose mode. */ + if (flag_verbose) + { + status = result_add_char (dm, ' '); + if (STATUS_NO_ERROR (status)) + result_add_string (dm, number); + } + dyn_string_delete (number); + RETURN_IF_ERROR (status); + break; + } + /* If flag_strict, fall through. */ + + default: + return "Unrecognized ."; + } + } + else + return STATUS_ERROR; + + return STATUS_OK; +} + +/* Demangles and emits a . + + + ::= C1 # complete object (in-charge) ctor + ::= C2 # base object (not-in-charge) ctor + ::= C3 # complete object (in-charge) allocating ctor + ::= D0 # deleting (in-charge) dtor + ::= D1 # complete object (in-charge) dtor + ::= D2 # base object (not-in-charge) dtor */ + +static status_t +demangle_ctor_dtor_name (dm) + demangling_t dm; +{ + static const char *const ctor_flavors[] = + { + "in-charge", + "not-in-charge", + "allocating" + }; + static const char *const dtor_flavors[] = + { + "in-charge deleting", + "in-charge", + "not-in-charge" + }; + + int flavor; + char peek = peek_char (dm); + + DEMANGLE_TRACE ("ctor-dtor-name", dm); + + if (peek == 'C') + { + /* A constructor name. Consume the C. */ + advance_char (dm); + flavor = next_char (dm); + if (flavor < '1' || flavor > '3') + return "Unrecognized constructor."; + RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name)); + switch (flavor) + { + case '1': dm->is_constructor = gnu_v3_complete_object_ctor; + break; + case '2': dm->is_constructor = gnu_v3_base_object_ctor; + break; + case '3': dm->is_constructor = gnu_v3_complete_object_allocating_ctor; + break; + } + /* Print the flavor of the constructor if in verbose mode. */ + if (flag_verbose) + { + RETURN_IF_ERROR (result_add (dm, "[")); + RETURN_IF_ERROR (result_add (dm, ctor_flavors[flavor - '1'])); + RETURN_IF_ERROR (result_add_char (dm, ']')); + } + } + else if (peek == 'D') + { + /* A destructor name. Consume the D. */ + advance_char (dm); + flavor = next_char (dm); + if (flavor < '0' || flavor > '2') + return "Unrecognized destructor."; + RETURN_IF_ERROR (result_add_char (dm, '~')); + RETURN_IF_ERROR (result_add_string (dm, dm->last_source_name)); + switch (flavor) + { + case '0': dm->is_destructor = gnu_v3_deleting_dtor; + break; + case '1': dm->is_destructor = gnu_v3_complete_object_dtor; + break; + case '2': dm->is_destructor = gnu_v3_base_object_dtor; + break; + } + /* Print the flavor of the destructor if in verbose mode. */ + if (flag_verbose) + { + RETURN_IF_ERROR (result_add (dm, " [")); + RETURN_IF_ERROR (result_add (dm, dtor_flavors[flavor - '0'])); + RETURN_IF_ERROR (result_add_char (dm, ']')); + } + } + else + return STATUS_ERROR; + + return STATUS_OK; +} + +/* Handle pointer, reference, and pointer-to-member cases for + demangle_type. All consecutive `P's, `R's, and 'M's are joined to + build a pointer/reference type. We snarf all these, plus the + following , all at once since we need to know whether we have + a pointer to data or pointer to function to construct the right + output syntax. C++'s pointer syntax is hairy. + + This function adds substitution candidates for every nested + pointer/reference type it processes, including the outermost, final + type, assuming the substitution starts at SUBSTITUTION_START in the + demangling result. For example, if this function demangles + `PP3Foo', it will add a substitution for `Foo', `Foo*', and + `Foo**', in that order. + + *INSERT_POS is a quantity used internally, when this function calls + itself recursively, to figure out where to insert pointer + punctuation on the way up. On entry to this function, INSERT_POS + should point to a temporary value, but that value need not be + initialized. + + ::= P + ::= R + ::= + + ::= M */ + +static status_t +demangle_type_ptr (dm, insert_pos, substitution_start) + demangling_t dm; + int *insert_pos; + int substitution_start; +{ + status_t status; + int is_substitution_candidate = 1; + + DEMANGLE_TRACE ("type*", dm); + + /* Scan forward, collecting pointers and references into symbols, + until we hit something else. Then emit the type. */ + switch (peek_char (dm)) + { + case 'P': + /* A pointer. Snarf the `P'. */ + advance_char (dm); + /* Demangle the underlying type. */ + RETURN_IF_ERROR (demangle_type_ptr (dm, insert_pos, + substitution_start)); + /* Insert an asterisk where we're told to; it doesn't + necessarily go at the end. If we're doing Java style output, + there is no pointer symbol. */ + if (dm->style != DMGL_JAVA) + RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '*')); + /* The next (outermost) pointer or reference character should go + after this one. */ + ++(*insert_pos); + break; + + case 'R': + /* A reference. Snarf the `R'. */ + advance_char (dm); + /* Demangle the underlying type. */ + RETURN_IF_ERROR (demangle_type_ptr (dm, insert_pos, + substitution_start)); + /* Insert an ampersand where we're told to; it doesn't + necessarily go at the end. */ + RETURN_IF_ERROR (result_insert_char (dm, *insert_pos, '&')); + /* The next (outermost) pointer or reference character should go + after this one. */ + ++(*insert_pos); + break; + + case 'M': + { + /* A pointer-to-member. */ + dyn_string_t class_type; + + /* Eat the 'M'. */ + advance_char (dm); + + /* Capture the type of which this is a pointer-to-member. */ + RETURN_IF_ERROR (result_push (dm)); + RETURN_IF_ERROR (demangle_type (dm)); + class_type = (dyn_string_t) result_pop (dm); + + if (peek_char (dm) == 'F') + /* A pointer-to-member function. We want output along the + lines of `void (C::*) (int, int)'. Demangle the function + type, which would in this case give `void () (int, int)' + and set *insert_pos to the spot between the first + parentheses. */ + status = demangle_type_ptr (dm, insert_pos, substitution_start); + else if (peek_char (dm) == 'A') + /* A pointer-to-member array variable. We want output that + looks like `int (Klass::*) [10]'. Demangle the array type + as `int () [10]', and set *insert_pos to the spot between + the parentheses. */ + status = demangle_array_type (dm, insert_pos); + else + { + /* A pointer-to-member variable. Demangle the type of the + pointed-to member. */ + status = demangle_type (dm); + /* Make it pretty. */ + if (STATUS_NO_ERROR (status) + && !result_previous_char_is_space (dm)) + status = result_add_char (dm, ' '); + /* The pointer-to-member notation (e.g. `C::*') follows the + member's type. */ + *insert_pos = result_caret_pos (dm); + } + + /* Build the pointer-to-member notation. */ + if (STATUS_NO_ERROR (status)) + status = result_insert (dm, *insert_pos, "::*"); + if (STATUS_NO_ERROR (status)) + status = result_insert_string (dm, *insert_pos, class_type); + /* There may be additional levels of (pointer or reference) + indirection in this type. If so, the `*' and `&' should be + added after the pointer-to-member notation (e.g. `C::*&' for + a reference to a pointer-to-member of class C). */ + *insert_pos += dyn_string_length (class_type) + 3; + + /* Clean up. */ + dyn_string_delete (class_type); + + RETURN_IF_ERROR (status); + } + break; + + case 'F': + /* Ooh, tricky, a pointer-to-function. When we demangle the + function type, the return type should go at the very + beginning. */ + *insert_pos = result_caret_pos (dm); + /* The parentheses indicate this is a function pointer or + reference type. */ + RETURN_IF_ERROR (result_add (dm, "()")); + /* Now demangle the function type. The return type will be + inserted before the `()', and the argument list will go after + it. */ + RETURN_IF_ERROR (demangle_function_type (dm, insert_pos)); + /* We should now have something along the lines of + `void () (int, int)'. The pointer or reference characters + have to inside the first set of parentheses. *insert_pos has + already been updated to point past the end of the return + type. Move it one character over so it points inside the + `()'. */ + ++(*insert_pos); + break; + + case 'A': + /* An array pointer or reference. demangle_array_type will figure + out where the asterisks and ampersands go. */ + RETURN_IF_ERROR (demangle_array_type (dm, insert_pos)); + break; + + default: + /* No more pointer or reference tokens; this is therefore a + pointer to data. Finish up by demangling the underlying + type. */ + RETURN_IF_ERROR (demangle_type (dm)); + /* The pointer or reference characters follow the underlying + type, as in `int*&'. */ + *insert_pos = result_caret_pos (dm); + /* Because of the production ::= , + demangle_type will already have added the underlying type as + a substitution candidate. Don't do it again. */ + is_substitution_candidate = 0; + break; + } + + if (is_substitution_candidate) + RETURN_IF_ERROR (substitution_add (dm, substitution_start, 0)); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= + ::= + ::= + ::= + ::= + ::= + ::= + ::= + ::= P # pointer-to + ::= R # reference-to + ::= C # complex pair (C 2000) + ::= G # imaginary (C 2000) + ::= U # vendor extended type qualifier + ::= */ + +static status_t +demangle_type (dm) + demangling_t dm; +{ + int start = substitution_start (dm); + char peek = peek_char (dm); + char peek_next; + int encode_return_type = 0; + template_arg_list_t old_arg_list = current_template_arg_list (dm); + int insert_pos; + + /* A can be a ; therefore, this is a + substitution candidate unless a special condition holds (see + below). */ + int is_substitution_candidate = 1; + + DEMANGLE_TRACE ("type", dm); + + /* A can start with a digit (a ), an + N (a ), or a Z (a ). */ + if (IS_DIGIT ((unsigned char) peek) || peek == 'N' || peek == 'Z') + RETURN_IF_ERROR (demangle_class_enum_type (dm, &encode_return_type)); + /* Lower-case letters begin s, except for `r', which + denotes restrict. */ + else if (peek >= 'a' && peek <= 'z' && peek != 'r') + { + RETURN_IF_ERROR (demangle_builtin_type (dm)); + /* Built-in types are not substitution candidates. */ + is_substitution_candidate = 0; + } + else + switch (peek) + { + case 'r': + case 'V': + case 'K': + /* CV-qualifiers (including restrict). We have to demangle + them off to the side, since C++ syntax puts them in a funny + place for qualified pointer and reference types. */ + { + status_t status; + dyn_string_t cv_qualifiers = dyn_string_new (24); + int old_caret_position = result_get_caret (dm); + + if (cv_qualifiers == NULL) + return STATUS_ALLOCATION_FAILED; + + /* Decode all adjacent CV qualifiers. */ + demangle_CV_qualifiers (dm, cv_qualifiers); + /* Emit them, and shift the caret left so that the + underlying type will be emitted before the qualifiers. */ + status = result_add_string (dm, cv_qualifiers); + result_shift_caret (dm, -dyn_string_length (cv_qualifiers)); + /* Clean up. */ + dyn_string_delete (cv_qualifiers); + RETURN_IF_ERROR (status); + /* Also prepend a blank, if needed. */ + RETURN_IF_ERROR (result_add_char (dm, ' ')); + result_shift_caret (dm, -1); + + /* Demangle the underlying type. It will be emitted before + the CV qualifiers, since we moved the caret. */ + RETURN_IF_ERROR (demangle_type (dm)); + + /* Put the caret back where it was previously. */ + result_set_caret (dm, old_caret_position); + } + break; + + case 'F': + return "Non-pointer or -reference function type."; + + case 'A': + RETURN_IF_ERROR (demangle_array_type (dm, NULL)); + break; + + case 'T': + /* It's either a or a + . In either case, demangle the + `T' token first. */ + RETURN_IF_ERROR (demangle_template_param (dm)); + + /* Check for a template argument list; if one is found, it's a + ::= + ::= */ + if (peek_char (dm) == 'I') + { + /* Add a substitution candidate. The template parameter + `T' token is a substitution candidate by itself, + without the template argument list. */ + RETURN_IF_ERROR (substitution_add (dm, start, encode_return_type)); + + /* Now demangle the template argument list. */ + RETURN_IF_ERROR (demangle_template_args (dm)); + /* The entire type, including the template template + parameter and its argument list, will be added as a + substitution candidate below. */ + } + + break; + + case 'S': + /* First check if this is a special substitution. If it is, + this is a . Special substitutions have a + letter following the `S'; other substitutions have a digit + or underscore. */ + peek_next = peek_char_next (dm); + if (IS_DIGIT (peek_next) || peek_next == '_') + { + RETURN_IF_ERROR (demangle_substitution (dm, &encode_return_type)); + + /* The substituted name may have been a template name. + Check if template arguments follow, and if so, demangle + them. */ + if (peek_char (dm) == 'I') + RETURN_IF_ERROR (demangle_template_args (dm)); + else + /* A substitution token is not itself a substitution + candidate. (However, if the substituted template is + instantiated, the resulting type is.) */ + is_substitution_candidate = 0; + } + else + { + /* Now some trickiness. We have a special substitution + here. Often, the special substitution provides the + name of a template that's subsequently instantiated, + for instance `SaIcE' => std::allocator. In these + cases we need to add a substitution candidate for the + entire and thus don't want to clear + the is_substitution_candidate flag. + + However, it's possible that what we have here is a + substitution token representing an entire type, such as + `Ss' => std::string. In this case, we mustn't add a + new substitution candidate for this substitution token. + To detect this case, remember where the start of the + substitution token is. */ + const char *next = dm->next; + /* Now demangle the . */ + RETURN_IF_ERROR + (demangle_class_enum_type (dm, &encode_return_type)); + /* If all that was just demangled is the two-character + special substitution token, suppress the addition of a + new candidate for it. */ + if (dm->next == next + 2) + is_substitution_candidate = 0; + } + + break; + + case 'P': + case 'R': + case 'M': + RETURN_IF_ERROR (demangle_type_ptr (dm, &insert_pos, start)); + /* demangle_type_ptr adds all applicable substitution + candidates. */ + is_substitution_candidate = 0; + break; + + case 'C': + /* A C99 complex type. */ + RETURN_IF_ERROR (result_add (dm, "complex ")); + advance_char (dm); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'G': + /* A C99 imaginary type. */ + RETURN_IF_ERROR (result_add (dm, "imaginary ")); + advance_char (dm); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + case 'U': + /* Vendor-extended type qualifier. */ + advance_char (dm); + RETURN_IF_ERROR (demangle_source_name (dm)); + RETURN_IF_ERROR (result_add_char (dm, ' ')); + RETURN_IF_ERROR (demangle_type (dm)); + break; + + default: + return "Unexpected character in ."; + } + + if (is_substitution_candidate) + /* Add a new substitution for the type. If this type was a + , pass its index since from the point of + substitutions; a token is a substitution + candidate distinct from the type that is substituted for it. */ + RETURN_IF_ERROR (substitution_add (dm, start, encode_return_type)); + + /* Pop off template argument lists added during mangling of this + type. */ + pop_to_template_arg_list (dm, old_arg_list); + + return STATUS_OK; +} + +/* C++ source names of builtin types, indexed by the mangled code + letter's position in the alphabet ('a' -> 0, 'b' -> 1, etc). */ +static const char *const builtin_type_names[26] = +{ + "signed char", /* a */ + "bool", /* b */ + "char", /* c */ + "double", /* d */ + "long double", /* e */ + "float", /* f */ + "__float128", /* g */ + "unsigned char", /* h */ + "int", /* i */ + "unsigned", /* j */ + NULL, /* k */ + "long", /* l */ + "unsigned long", /* m */ + "__int128", /* n */ + "unsigned __int128", /* o */ + NULL, /* p */ + NULL, /* q */ + NULL, /* r */ + "short", /* s */ + "unsigned short", /* t */ + NULL, /* u */ + "void", /* v */ + "wchar_t", /* w */ + "long long", /* x */ + "unsigned long long", /* y */ + "..." /* z */ +}; + +/* Java source names of builtin types. Types that arn't valid in Java + are also included here - we don't fail if someone attempts to demangle a + C++ symbol in Java style. */ +static const char *const java_builtin_type_names[26] = +{ + "signed char", /* a */ + "boolean", /* C++ "bool" */ /* b */ + "byte", /* C++ "char" */ /* c */ + "double", /* d */ + "long double", /* e */ + "float", /* f */ + "__float128", /* g */ + "unsigned char", /* h */ + "int", /* i */ + "unsigned", /* j */ + NULL, /* k */ + "long", /* l */ + "unsigned long", /* m */ + "__int128", /* n */ + "unsigned __int128", /* o */ + NULL, /* p */ + NULL, /* q */ + NULL, /* r */ + "short", /* s */ + "unsigned short", /* t */ + NULL, /* u */ + "void", /* v */ + "char", /* C++ "wchar_t" */ /* w */ + "long", /* C++ "long long" */ /* x */ + "unsigned long long", /* y */ + "..." /* z */ +}; + +/* Demangles and emits a . + + ::= v # void + ::= w # wchar_t + ::= b # bool + ::= c # char + ::= a # signed char + ::= h # unsigned char + ::= s # short + ::= t # unsigned short + ::= i # int + ::= j # unsigned int + ::= l # long + ::= m # unsigned long + ::= x # long long, __int64 + ::= y # unsigned long long, __int64 + ::= n # __int128 + ::= o # unsigned __int128 + ::= f # float + ::= d # double + ::= e # long double, __float80 + ::= g # __float128 + ::= z # ellipsis + ::= u # vendor extended type */ + +static status_t +demangle_builtin_type (dm) + demangling_t dm; +{ + + char code = peek_char (dm); + + DEMANGLE_TRACE ("builtin-type", dm); + + if (code == 'u') + { + advance_char (dm); + RETURN_IF_ERROR (demangle_source_name (dm)); + return STATUS_OK; + } + else if (code >= 'a' && code <= 'z') + { + const char *type_name; + /* Java uses different names for some built-in types. */ + if (dm->style == DMGL_JAVA) + type_name = java_builtin_type_names[code - 'a']; + else + type_name = builtin_type_names[code - 'a']; + if (type_name == NULL) + return "Unrecognized code."; + + RETURN_IF_ERROR (result_add (dm, type_name)); + advance_char (dm); + return STATUS_OK; + } + else + return "Non-alphabetic code."; +} + +/* Demangles all consecutive CV-qualifiers (const, volatile, and + restrict) at the current position. The qualifiers are appended to + QUALIFIERS. Returns STATUS_OK. */ + +static status_t +demangle_CV_qualifiers (dm, qualifiers) + demangling_t dm; + dyn_string_t qualifiers; +{ + DEMANGLE_TRACE ("CV-qualifiers", dm); + + while (1) + { + switch (peek_char (dm)) + { + case 'r': + if (!dyn_string_append_space (qualifiers)) + return STATUS_ALLOCATION_FAILED; + if (!dyn_string_append_cstr (qualifiers, "restrict")) + return STATUS_ALLOCATION_FAILED; + break; + + case 'V': + if (!dyn_string_append_space (qualifiers)) + return STATUS_ALLOCATION_FAILED; + if (!dyn_string_append_cstr (qualifiers, "volatile")) + return STATUS_ALLOCATION_FAILED; + break; + + case 'K': + if (!dyn_string_append_space (qualifiers)) + return STATUS_ALLOCATION_FAILED; + if (!dyn_string_append_cstr (qualifiers, "const")) + return STATUS_ALLOCATION_FAILED; + break; + + default: + return STATUS_OK; + } + + advance_char (dm); + } +} + +/* Demangles and emits a . *FUNCTION_NAME_POS is the + position in the result string of the start of the function + identifier, at which the function's return type will be inserted; + *FUNCTION_NAME_POS is updated to position past the end of the + function's return type. + + ::= F [Y] E */ + +static status_t +demangle_function_type (dm, function_name_pos) + demangling_t dm; + int *function_name_pos; +{ + DEMANGLE_TRACE ("function-type", dm); + RETURN_IF_ERROR (demangle_char (dm, 'F')); + if (peek_char (dm) == 'Y') + { + /* Indicate this function has C linkage if in verbose mode. */ + if (flag_verbose) + RETURN_IF_ERROR (result_add (dm, " [extern \"C\"] ")); + advance_char (dm); + } + RETURN_IF_ERROR (demangle_bare_function_type (dm, function_name_pos)); + RETURN_IF_ERROR (demangle_char (dm, 'E')); + return STATUS_OK; +} + +/* Demangles and emits a . RETURN_TYPE_POS is the + position in the result string at which the function return type + should be inserted. If RETURN_TYPE_POS is BFT_NO_RETURN_TYPE, the + function's return type is assumed not to be encoded. + + ::= + */ + +static status_t +demangle_bare_function_type (dm, return_type_pos) + demangling_t dm; + int *return_type_pos; +{ + /* Sequence is the index of the current function parameter, counting + from zero. The value -1 denotes the return type. */ + int sequence = + (return_type_pos == BFT_NO_RETURN_TYPE ? 0 : -1); + + DEMANGLE_TRACE ("bare-function-type", dm); + + RETURN_IF_ERROR (result_add_char (dm, '(')); + while (!end_of_name_p (dm) && peek_char (dm) != 'E') + { + if (sequence == -1) + /* We're decoding the function's return type. */ + { + dyn_string_t return_type; + status_t status = STATUS_OK; + + /* Decode the return type off to the side. */ + RETURN_IF_ERROR (result_push (dm)); + RETURN_IF_ERROR (demangle_type (dm)); + return_type = (dyn_string_t) result_pop (dm); + + /* Add a space to the end of the type. Insert the return + type where we've been asked to. */ + if (!dyn_string_append_space (return_type)) + status = STATUS_ALLOCATION_FAILED; + if (STATUS_NO_ERROR (status)) + { + if (!dyn_string_insert (result_string (dm), *return_type_pos, + return_type)) + status = STATUS_ALLOCATION_FAILED; + else + *return_type_pos += dyn_string_length (return_type); + } + + dyn_string_delete (return_type); + RETURN_IF_ERROR (status); + } + else + { + /* Skip `void' parameter types. One should only occur as + the only type in a parameter list; in that case, we want + to print `foo ()' instead of `foo (void)'. */ + if (peek_char (dm) == 'v') + /* Consume the v. */ + advance_char (dm); + else + { + /* Separate parameter types by commas. */ + if (sequence > 0) + RETURN_IF_ERROR (result_add (dm, ", ")); + /* Demangle the type. */ + RETURN_IF_ERROR (demangle_type (dm)); + } + } + + ++sequence; + } + RETURN_IF_ERROR (result_add_char (dm, ')')); + + /* We should have demangled at least one parameter type (which would + be void, for a function that takes no parameters), plus the + return type, if we were supposed to demangle that. */ + if (sequence == -1) + return "Missing function return type."; + else if (sequence == 0) + return "Missing function parameter."; + + return STATUS_OK; +} + +/* Demangles and emits a . *ENCODE_RETURN_TYPE is set to + non-zero if the type is a template-id, zero otherwise. + + ::= */ + +static status_t +demangle_class_enum_type (dm, encode_return_type) + demangling_t dm; + int *encode_return_type; +{ + DEMANGLE_TRACE ("class-enum-type", dm); + + RETURN_IF_ERROR (demangle_name (dm, encode_return_type)); + return STATUS_OK; +} + +/* Demangles and emits an . + + If PTR_INSERT_POS is not NULL, the array type is formatted as a + pointer or reference to an array, except that asterisk and + ampersand punctuation is omitted (since it's not know at this + point). *PTR_INSERT_POS is set to the position in the demangled + name at which this punctuation should be inserted. For example, + `A10_i' is demangled to `int () [10]' and *PTR_INSERT_POS points + between the parentheses. + + If PTR_INSERT_POS is NULL, the array type is assumed not to be + pointer- or reference-qualified. Then, for example, `A10_i' is + demangled simply as `int[10]'. + + ::= A [] _ + ::= A _ */ + +static status_t +demangle_array_type (dm, ptr_insert_pos) + demangling_t dm; + int *ptr_insert_pos; +{ + status_t status = STATUS_OK; + dyn_string_t array_size = NULL; + char peek; + + DEMANGLE_TRACE ("array-type", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'A')); + + /* Demangle the array size into array_size. */ + peek = peek_char (dm); + if (peek == '_') + /* Array bound is omitted. This is a C99-style VLA. */ + ; + else if (IS_DIGIT (peek_char (dm))) + { + /* It looks like a constant array bound. */ + array_size = dyn_string_new (10); + if (array_size == NULL) + return STATUS_ALLOCATION_FAILED; + status = demangle_number_literally (dm, array_size, 10, 0); + } + else + { + /* Anything is must be an expression for a nont-constant array + bound. This happens if the array type occurs in a template + and the array bound references a template parameter. */ + RETURN_IF_ERROR (result_push (dm)); + RETURN_IF_ERROR (demangle_expression_v3 (dm)); + array_size = (dyn_string_t) result_pop (dm); + } + /* array_size may have been allocated by now, so we can't use + RETURN_IF_ERROR until it's been deallocated. */ + + /* Demangle the base type of the array. */ + if (STATUS_NO_ERROR (status)) + status = demangle_char (dm, '_'); + if (STATUS_NO_ERROR (status)) + status = demangle_type (dm); + + if (ptr_insert_pos != NULL) + { + /* This array is actually part of an pointer- or + reference-to-array type. Format appropriately, except we + don't know which and how much punctuation to use. */ + if (STATUS_NO_ERROR (status)) + status = result_add (dm, " () "); + /* Let the caller know where to insert the punctuation. */ + *ptr_insert_pos = result_caret_pos (dm) - 2; + } + + /* Emit the array dimension syntax. */ + if (STATUS_NO_ERROR (status)) + status = result_add_char (dm, '['); + if (STATUS_NO_ERROR (status) && array_size != NULL) + status = result_add_string (dm, array_size); + if (STATUS_NO_ERROR (status)) + status = result_add_char (dm, ']'); + if (array_size != NULL) + dyn_string_delete (array_size); + + RETURN_IF_ERROR (status); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= T_ # first template parameter + ::= T _ */ + +static status_t +demangle_template_param (dm) + demangling_t dm; +{ + int parm_number; + template_arg_list_t current_arg_list = current_template_arg_list (dm); + string_list_t arg; + + DEMANGLE_TRACE ("template-param", dm); + + /* Make sure there is a template argmust list in which to look up + this parameter reference. */ + if (current_arg_list == NULL) + return "Template parameter outside of template."; + + RETURN_IF_ERROR (demangle_char (dm, 'T')); + if (peek_char (dm) == '_') + parm_number = 0; + else + { + RETURN_IF_ERROR (demangle_number (dm, &parm_number, 10, 0)); + ++parm_number; + } + RETURN_IF_ERROR (demangle_char (dm, '_')); + + arg = template_arg_list_get_arg (current_arg_list, parm_number); + if (arg == NULL) + /* parm_number exceeded the number of arguments in the current + template argument list. */ + return "Template parameter number out of bounds."; + RETURN_IF_ERROR (result_add_string (dm, (dyn_string_t) arg)); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= I + E */ + +static status_t +demangle_template_args_1 (dm, arg_list) + demangling_t dm; + template_arg_list_t arg_list; +{ + int first = 1; + + DEMANGLE_TRACE ("template-args", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'I')); + RETURN_IF_ERROR (result_open_template_list (dm)); + do + { + string_list_t arg; + + if (first) + first = 0; + else + RETURN_IF_ERROR (result_add (dm, ", ")); + + /* Capture the template arg. */ + RETURN_IF_ERROR (result_push (dm)); + RETURN_IF_ERROR (demangle_template_arg (dm)); + arg = result_pop (dm); + + /* Emit it in the demangled name. */ + RETURN_IF_ERROR (result_add_string (dm, (dyn_string_t) arg)); + + /* Save it for use in expanding s. */ + template_arg_list_add_arg (arg_list, arg); + } + while (peek_char (dm) != 'E'); + /* Append the '>'. */ + RETURN_IF_ERROR (result_close_template_list (dm)); + + /* Consume the 'E'. */ + advance_char (dm); + + return STATUS_OK; +} + +static status_t +demangle_template_args (dm) + demangling_t dm; +{ + int first = 1; + dyn_string_t old_last_source_name; + dyn_string_t new_name; + template_arg_list_t arg_list = template_arg_list_new (); + status_t status; + + if (arg_list == NULL) + return STATUS_ALLOCATION_FAILED; + + /* Preserve the most recently demangled source name. */ + old_last_source_name = dm->last_source_name; + new_name = dyn_string_new (0); + + if (new_name == NULL) + { + template_arg_list_delete (arg_list); + return STATUS_ALLOCATION_FAILED; + } + + dm->last_source_name = new_name; + + status = demangle_template_args_1 (dm, arg_list); + /* Restore the most recent demangled source name. */ + dyn_string_delete (dm->last_source_name); + dm->last_source_name = old_last_source_name; + + if (!STATUS_NO_ERROR (status)) + { + template_arg_list_delete (arg_list); + return status; + } + + /* Push the list onto the top of the stack of template argument + lists, so that arguments from it are used from now on when + expanding s. */ + push_template_arg_list (dm, arg_list); + + return STATUS_OK; +} + +/* This function, which does not correspond to a production in the + mangling spec, handles the `literal' production for both + and . It does not expect or consume + the initial `L' or final `E'. The demangling is given by: + + ::= + + and the emitted output is `(type)number'. */ + +static status_t +demangle_literal (dm) + demangling_t dm; +{ + char peek = peek_char (dm); + dyn_string_t value_string; + status_t status; + + DEMANGLE_TRACE ("literal", dm); + + if (!flag_verbose && peek >= 'a' && peek <= 'z') + { + /* If not in verbose mode and this is a builtin type, see if we + can produce simpler numerical output. In particular, for + integer types shorter than `long', just write the number + without type information; for bools, write `true' or `false'. + Other refinements could be made here too. */ + + /* This constant string is used to map from codes + (26 letters of the alphabet) to codes that determine how the + value will be displayed. The codes are: + b: display as bool + i: display as int + l: display as long + A space means the value will be represented using cast + notation. */ + static const char *const code_map = "ibi iii ll ii i "; + + char code = code_map[peek - 'a']; + /* FIXME: Implement demangling of floats and doubles. */ + if (code == 'u') + return STATUS_UNIMPLEMENTED; + if (code == 'b') + { + /* It's a boolean. */ + char value; + + /* Consume the b. */ + advance_char (dm); + /* Look at the next character. It should be 0 or 1, + corresponding to false or true, respectively. */ + value = peek_char (dm); + if (value == '0') + RETURN_IF_ERROR (result_add (dm, "false")); + else if (value == '1') + RETURN_IF_ERROR (result_add (dm, "true")); + else + return "Unrecognized bool constant."; + /* Consume the 0 or 1. */ + advance_char (dm); + return STATUS_OK; + } + else if (code == 'i' || code == 'l') + { + /* It's an integer or long. */ + + /* Consume the type character. */ + advance_char (dm); + + /* Demangle the number and write it out. */ + value_string = dyn_string_new (0); + status = demangle_number_literally (dm, value_string, 10, 1); + if (STATUS_NO_ERROR (status)) + status = result_add_string (dm, value_string); + /* For long integers, append an l. */ + if (code == 'l' && STATUS_NO_ERROR (status)) + status = result_add_char (dm, code); + dyn_string_delete (value_string); + + RETURN_IF_ERROR (status); + return STATUS_OK; + } + /* ...else code == ' ', so fall through to represent this + literal's type explicitly using cast syntax. */ + } + + RETURN_IF_ERROR (result_add_char (dm, '(')); + RETURN_IF_ERROR (demangle_type (dm)); + RETURN_IF_ERROR (result_add_char (dm, ')')); + + value_string = dyn_string_new (0); + if (value_string == NULL) + return STATUS_ALLOCATION_FAILED; + + status = demangle_number_literally (dm, value_string, 10, 1); + if (STATUS_NO_ERROR (status)) + status = result_add_string (dm, value_string); + dyn_string_delete (value_string); + RETURN_IF_ERROR (status); + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= # type + ::= L E # literal + ::= LZ E # external name + ::= X E # expression */ + +static status_t +demangle_template_arg (dm) + demangling_t dm; +{ + DEMANGLE_TRACE ("template-arg", dm); + + switch (peek_char (dm)) + { + case 'L': + advance_char (dm); + + if (peek_char (dm) == 'Z') + { + /* External name. */ + advance_char (dm); + /* FIXME: Standard is contradictory here. */ + RETURN_IF_ERROR (demangle_encoding (dm)); + } + else + RETURN_IF_ERROR (demangle_literal (dm)); + RETURN_IF_ERROR (demangle_char (dm, 'E')); + break; + + case 'X': + /* Expression. */ + advance_char (dm); + RETURN_IF_ERROR (demangle_expression_v3 (dm)); + RETURN_IF_ERROR (demangle_char (dm, 'E')); + break; + + default: + RETURN_IF_ERROR (demangle_type (dm)); + break; + } + + return STATUS_OK; +} + +/* Demangles and emits an . + + ::= + ::= + ::= + ::= */ + +static status_t +demangle_expression_v3 (dm) + demangling_t dm; +{ + char peek = peek_char (dm); + + DEMANGLE_TRACE ("expression", dm); + + if (peek == 'L' || peek == 'T') + RETURN_IF_ERROR (demangle_expr_primary (dm)); + else if (peek == 's' && peek_char_next (dm) == 'r') + RETURN_IF_ERROR (demangle_scope_expression (dm)); + else + /* An operator expression. */ + { + int num_args; + status_t status = STATUS_OK; + dyn_string_t operator_name; + + /* We have an operator name. Since we want to output binary + operations in infix notation, capture the operator name + first. */ + RETURN_IF_ERROR (result_push (dm)); + RETURN_IF_ERROR (demangle_operator_name (dm, 1, &num_args)); + operator_name = (dyn_string_t) result_pop (dm); + + /* If it's binary, do an operand first. */ + if (num_args > 1) + { + status = result_add_char (dm, '('); + if (STATUS_NO_ERROR (status)) + status = demangle_expression_v3 (dm); + if (STATUS_NO_ERROR (status)) + status = result_add_char (dm, ')'); + } + + /* Emit the operator. */ + if (STATUS_NO_ERROR (status)) + status = result_add_string (dm, operator_name); + dyn_string_delete (operator_name); + RETURN_IF_ERROR (status); + + /* Emit its second (if binary) or only (if unary) operand. */ + RETURN_IF_ERROR (result_add_char (dm, '(')); + RETURN_IF_ERROR (demangle_expression_v3 (dm)); + RETURN_IF_ERROR (result_add_char (dm, ')')); + + /* The ternary operator takes a third operand. */ + if (num_args == 3) + { + RETURN_IF_ERROR (result_add (dm, ":(")); + RETURN_IF_ERROR (demangle_expression_v3 (dm)); + RETURN_IF_ERROR (result_add_char (dm, ')')); + } + } + + return STATUS_OK; +} + +/* Demangles and emits a . + + ::= sr + ::= sr */ + +static status_t +demangle_scope_expression (dm) + demangling_t dm; +{ + RETURN_IF_ERROR (demangle_char (dm, 's')); + RETURN_IF_ERROR (demangle_char (dm, 'r')); + RETURN_IF_ERROR (demangle_type (dm)); + RETURN_IF_ERROR (result_add (dm, "::")); + RETURN_IF_ERROR (demangle_encoding (dm)); + return STATUS_OK; +} + +/* Demangles and emits an . + + ::= + ::= L E # literal + ::= L E # external name */ + +static status_t +demangle_expr_primary (dm) + demangling_t dm; +{ + char peek = peek_char (dm); + + DEMANGLE_TRACE ("expr-primary", dm); + + if (peek == 'T') + RETURN_IF_ERROR (demangle_template_param (dm)); + else if (peek == 'L') + { + /* Consume the `L'. */ + advance_char (dm); + peek = peek_char (dm); + + if (peek == '_') + RETURN_IF_ERROR (demangle_mangled_name (dm)); + else + RETURN_IF_ERROR (demangle_literal (dm)); + + RETURN_IF_ERROR (demangle_char (dm, 'E')); + } + else + return STATUS_ERROR; + + return STATUS_OK; +} + +/* Demangles and emits a . Sets *TEMPLATE_P to non-zero + if the substitution is the name of a template, zero otherwise. + + ::= S _ + ::= S_ + + ::= St # ::std:: + ::= Sa # ::std::allocator + ::= Sb # ::std::basic_string + ::= Ss # ::std::basic_string, + ::std::allocator > + ::= Si # ::std::basic_istream > + ::= So # ::std::basic_ostream > + ::= Sd # ::std::basic_iostream > +*/ + +static status_t +demangle_substitution (dm, template_p) + demangling_t dm; + int *template_p; +{ + int seq_id; + int peek; + dyn_string_t text; + + DEMANGLE_TRACE ("substitution", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'S')); + + /* Scan the substitution sequence index. A missing number denotes + the first index. */ + peek = peek_char (dm); + if (peek == '_') + seq_id = -1; + /* If the following character is 0-9 or a capital letter, interpret + the sequence up to the next underscore as a base-36 substitution + index. */ + else if (IS_DIGIT ((unsigned char) peek) + || (peek >= 'A' && peek <= 'Z')) + RETURN_IF_ERROR (demangle_number (dm, &seq_id, 36, 0)); + else + { + const char *new_last_source_name = NULL; + + switch (peek) + { + case 't': + RETURN_IF_ERROR (result_add (dm, "std")); + break; + + case 'a': + RETURN_IF_ERROR (result_add (dm, "std::allocator")); + new_last_source_name = "allocator"; + *template_p = 1; + break; + + case 'b': + RETURN_IF_ERROR (result_add (dm, "std::basic_string")); + new_last_source_name = "basic_string"; + *template_p = 1; + break; + + case 's': + if (!flag_verbose) + { + RETURN_IF_ERROR (result_add (dm, "std::string")); + new_last_source_name = "string"; + } + else + { + RETURN_IF_ERROR (result_add (dm, "std::basic_string, std::allocator >")); + new_last_source_name = "basic_string"; + } + *template_p = 0; + break; + + case 'i': + if (!flag_verbose) + { + RETURN_IF_ERROR (result_add (dm, "std::istream")); + new_last_source_name = "istream"; + } + else + { + RETURN_IF_ERROR (result_add (dm, "std::basic_istream >")); + new_last_source_name = "basic_istream"; + } + *template_p = 0; + break; + + case 'o': + if (!flag_verbose) + { + RETURN_IF_ERROR (result_add (dm, "std::ostream")); + new_last_source_name = "ostream"; + } + else + { + RETURN_IF_ERROR (result_add (dm, "std::basic_ostream >")); + new_last_source_name = "basic_ostream"; + } + *template_p = 0; + break; + + case 'd': + if (!flag_verbose) + { + RETURN_IF_ERROR (result_add (dm, "std::iostream")); + new_last_source_name = "iostream"; + } + else + { + RETURN_IF_ERROR (result_add (dm, "std::basic_iostream >")); + new_last_source_name = "basic_iostream"; + } + *template_p = 0; + break; + + default: + return "Unrecognized ."; + } + + /* Consume the character we just processed. */ + advance_char (dm); + + if (new_last_source_name != NULL) + { + if (!dyn_string_copy_cstr (dm->last_source_name, + new_last_source_name)) + return STATUS_ALLOCATION_FAILED; + } + + return STATUS_OK; + } + + /* Look up the substitution text. Since `S_' is the most recent + substitution, `S0_' is the second-most-recent, etc., shift the + numbering by one. */ + text = substitution_get (dm, seq_id + 1, template_p); + if (text == NULL) + return "Substitution number out of range."; + + /* Emit the substitution text. */ + RETURN_IF_ERROR (result_add_string (dm, text)); + + RETURN_IF_ERROR (demangle_char (dm, '_')); + return STATUS_OK; +} + +/* Demangles and emits a . + + := Z E [] + := Z E s [] */ + +static status_t +demangle_local_name (dm) + demangling_t dm; +{ + DEMANGLE_TRACE ("local-name", dm); + + RETURN_IF_ERROR (demangle_char (dm, 'Z')); + RETURN_IF_ERROR (demangle_encoding (dm)); + RETURN_IF_ERROR (demangle_char (dm, 'E')); + RETURN_IF_ERROR (result_add (dm, "::")); + + if (peek_char (dm) == 's') + { + /* Local character string literal. */ + RETURN_IF_ERROR (result_add (dm, "string literal")); + /* Consume the s. */ + advance_char (dm); + RETURN_IF_ERROR (demangle_discriminator (dm, 0)); + } + else + { + int unused; + /* Local name for some other entity. Demangle its name. */ + RETURN_IF_ERROR (demangle_name (dm, &unused)); + RETURN_IF_ERROR (demangle_discriminator (dm, 1)); + } + + return STATUS_OK; + } + + /* Optimonally demangles and emits a . If there is no + at the current position in the mangled string, the + descriminator is assumed to be zero. Emit the discriminator number + in parentheses, unless SUPPRESS_FIRST is non-zero and the + discriminator is zero. + + ::= _ */ + +static status_t +demangle_discriminator (dm, suppress_first) + demangling_t dm; + int suppress_first; +{ + /* Output for s to the demangled name is completely + suppressed if not in verbose mode. */ + + if (peek_char (dm) == '_') + { + /* Consume the underscore. */ + advance_char (dm); + if (flag_verbose) + RETURN_IF_ERROR (result_add (dm, " [#")); + /* Check if there's a number following the underscore. */ + if (IS_DIGIT ((unsigned char) peek_char (dm))) + { + int discriminator; + /* Demangle the number. */ + RETURN_IF_ERROR (demangle_number (dm, &discriminator, 10, 0)); + if (flag_verbose) + /* Write the discriminator. The mangled number is two + less than the discriminator ordinal, counting from + zero. */ + RETURN_IF_ERROR (int_to_dyn_string (discriminator + 1, + (dyn_string_t) dm->result)); + } + else + return STATUS_ERROR; + if (flag_verbose) + RETURN_IF_ERROR (result_add_char (dm, ']')); + } + else if (!suppress_first) + { + if (flag_verbose) + RETURN_IF_ERROR (result_add (dm, " [#0]")); + } + + return STATUS_OK; +} + +/* Demangle NAME into RESULT, which must be an initialized + dyn_string_t. On success, returns STATUS_OK. On failure, returns + an error message, and the contents of RESULT are unchanged. */ + +static status_t +cp_demangle (name, result, style) + const char *name; + dyn_string_t result; + int style; +{ + status_t status; + int length = VG_(strlen) (name); + + if (length > 2 && name[0] == '_' && name[1] == 'Z') + { + demangling_t dm = demangling_new (name, style); + if (dm == NULL) + return STATUS_ALLOCATION_FAILED; + + status = result_push (dm); + if (status != STATUS_OK) + { + demangling_delete (dm); + return status; + } + + status = demangle_mangled_name (dm); + if (STATUS_NO_ERROR (status)) + { + dyn_string_t demangled = (dyn_string_t) result_pop (dm); + if (!dyn_string_copy (result, demangled)) + { + demangling_delete (dm); + return STATUS_ALLOCATION_FAILED; + } + dyn_string_delete (demangled); + } + + demangling_delete (dm); + } + else + { + /* It's evidently not a mangled C++ name. It could be the name + of something with C linkage, though, so just copy NAME into + RESULT. */ + if (!dyn_string_copy_cstr (result, name)) + return STATUS_ALLOCATION_FAILED; + status = STATUS_OK; + } + + return status; +} + +/* Demangle TYPE_NAME into RESULT, which must be an initialized + dyn_string_t. On success, returns STATUS_OK. On failiure, returns + an error message, and the contents of RESULT are unchanged. */ + +#ifdef IN_LIBGCC2 +static status_t +cp_demangle_type (type_name, result) + const char* type_name; + dyn_string_t result; +{ + status_t status; + demangling_t dm = demangling_new (type_name); + + if (dm == NULL) + return STATUS_ALLOCATION_FAILED; + + /* Demangle the type name. The demangled name is stored in dm. */ + status = result_push (dm); + if (status != STATUS_OK) + { + demangling_delete (dm); + return status; + } + + status = demangle_type (dm); + + if (STATUS_NO_ERROR (status)) + { + /* The demangling succeeded. Pop the result out of dm and copy + it into RESULT. */ + dyn_string_t demangled = (dyn_string_t) result_pop (dm); + if (!dyn_string_copy (result, demangled)) + return STATUS_ALLOCATION_FAILED; + dyn_string_delete (demangled); + } + + /* Clean up. */ + demangling_delete (dm); + + return status; +} + +extern char *__cxa_demangle PARAMS ((const char *, char *, size_t *, int *)); + +/* ia64 ABI-mandated entry point in the C++ runtime library for performing + demangling. MANGLED_NAME is a NUL-terminated character string + containing the name to be demangled. + + OUTPUT_BUFFER is a region of memory, allocated with malloc, of + *LENGTH bytes, into which the demangled name is stored. If + OUTPUT_BUFFER is not long enough, it is expanded using realloc. + OUTPUT_BUFFER may instead be NULL; in that case, the demangled name + is placed in a region of memory allocated with malloc. + + If LENGTH is non-NULL, the length of the buffer conaining the + demangled name, is placed in *LENGTH. + + The return value is a pointer to the start of the NUL-terminated + demangled name, or NULL if the demangling fails. The caller is + responsible for deallocating this memory using free. + + *STATUS is set to one of the following values: + 0: The demangling operation succeeded. + -1: A memory allocation failiure occurred. + -2: MANGLED_NAME is not a valid name under the C++ ABI mangling rules. + -3: One of the arguments is invalid. + + The demagling is performed using the C++ ABI mangling rules, with + GNU extensions. */ + +char * +__cxa_demangle (mangled_name, output_buffer, length, status) + const char *mangled_name; + char *output_buffer; + size_t *length; + int *status; +{ + struct dyn_string demangled_name; + status_t result; + + if (status == NULL) + return NULL; + + if (mangled_name == NULL) { + *status = -3; + return NULL; + } + + /* Did the caller provide a buffer for the demangled name? */ + if (output_buffer == NULL) { + /* No; dyn_string will malloc a buffer for us. */ + if (!dyn_string_init (&demangled_name, 0)) + { + *status = -1; + return NULL; + } + } + else { + /* Yes. Check that the length was provided. */ + if (length == NULL) { + *status = -3; + return NULL; + } + /* Install the buffer into a dyn_string. */ + demangled_name.allocated = *length; + demangled_name.length = 0; + demangled_name.s = output_buffer; + } + + if (mangled_name[0] == '_' && mangled_name[1] == 'Z') + /* MANGLED_NAME apprears to be a function or variable name. + Demangle it accordingly. */ + result = cp_demangle (mangled_name, &demangled_name, 0); + else + /* Try to demangled MANGLED_NAME as the name of a type. */ + result = cp_demangle_type (mangled_name, &demangled_name); + + if (result == STATUS_OK) + /* The demangling succeeded. */ + { + /* If LENGTH isn't NULL, store the allocated buffer length + there; the buffer may have been realloced by dyn_string + functions. */ + if (length != NULL) + *length = demangled_name.allocated; + /* The operation was a success. */ + *status = 0; + return dyn_string_buf (&demangled_name); + } + else if (result == STATUS_ALLOCATION_FAILED) + /* A call to malloc or realloc failed during the demangling + operation. */ + { + *status = -1; + return NULL; + } + else + /* The demangling failed for another reason, most probably because + MANGLED_NAME isn't a valid mangled name. */ + { + /* If the buffer containing the demangled name wasn't provided + by the caller, free it. */ + if (output_buffer == NULL) + free (dyn_string_buf (&demangled_name)); + *status = -2; + return NULL; + } +} + +#else /* !IN_LIBGCC2 */ + +/* Variant entry point for integration with the existing cplus-dem + demangler. Attempts to demangle MANGLED. If the demangling + succeeds, returns a buffer, allocated with malloc, containing the + demangled name. The caller must deallocate the buffer using free. + If the demangling failes, returns NULL. */ + +char * +VG_(cplus_demangle_v3) (mangled) + const char* mangled; +{ + dyn_string_t demangled; + status_t status; + + /* If this isn't a mangled name, don't pretend to demangle it. */ + if (VG_(strncmp) (mangled, "_Z", 2) != 0) + return NULL; + + /* Create a dyn_string to hold the demangled name. */ + demangled = dyn_string_new (0); + /* Attempt the demangling. */ + status = cp_demangle ((char *) mangled, demangled, 0); + + if (STATUS_NO_ERROR (status)) + /* Demangling succeeded. */ + { + /* Grab the demangled result from the dyn_string. It was + allocated with malloc, so we can return it directly. */ + char *return_value = dyn_string_release (demangled); + /* Hand back the demangled name. */ + return return_value; + } + else if (status == STATUS_ALLOCATION_FAILED) + { + vg_assert (0); + /* + fprintf (stderr, "Memory allocation failed.\n"); + abort (); + */ + } + else + /* Demangling failed. */ + { + dyn_string_delete (demangled); + return NULL; + } +} + +/* Demangle a Java symbol. Java uses a subset of the V3 ABI C++ mangling + conventions, but the output formatting is a little different. + This instructs the C++ demangler not to emit pointer characters ("*"), and + to use Java's namespace separator symbol ("." instead of "::"). It then + does an additional pass over the demangled output to replace instances + of JArray with TYPE[]. */ + +char * +VG_(java_demangle_v3) (mangled) + const char* mangled; +{ + dyn_string_t demangled; + char *next; + char *end; + int len; + status_t status; + int nesting = 0; + char *cplus_demangled; + char *return_value; + + /* Create a dyn_string to hold the demangled name. */ + demangled = dyn_string_new (0); + + /* Attempt the demangling. */ + status = cp_demangle ((char *) mangled, demangled, DMGL_JAVA); + + if (STATUS_NO_ERROR (status)) + /* Demangling succeeded. */ + { + /* Grab the demangled result from the dyn_string. */ + cplus_demangled = dyn_string_release (demangled); + } + else if (status == STATUS_ALLOCATION_FAILED) + { + vg_assert (0); + /* + fprintf (stderr, "Memory allocation failed.\n"); + abort (); + */ + } + else + /* Demangling failed. */ + { + dyn_string_delete (demangled); + return NULL; + } + + len = VG_(strlen) (cplus_demangled); + next = cplus_demangled; + end = next + len; + demangled = NULL; + + /* Replace occurrences of JArray with TYPE[]. */ + while (next < end) + { + char *open_str = VG_(strstr) (next, "JArray<"); + char *close_str = NULL; + if (nesting > 0) + close_str = VG_(strchr) (next, '>'); + + if (open_str != NULL && (close_str == NULL || close_str > open_str)) + { + ++nesting; + + if (!demangled) + demangled = dyn_string_new(len); + + /* Copy prepending symbols, if any. */ + if (open_str > next) + { + open_str[0] = 0; + dyn_string_append_cstr (demangled, next); + } + next = open_str + 7; + } + else if (close_str != NULL) + { + --nesting; + + /* Copy prepending type symbol, if any. Squash any spurious + whitespace. */ + if (close_str > next && next[0] != ' ') + { + close_str[0] = 0; + dyn_string_append_cstr (demangled, next); + } + dyn_string_append_cstr (demangled, "[]"); + next = close_str + 1; + } + else + { + /* There are no more arrays. Copy the rest of the symbol, or + simply return the original symbol if no changes were made. */ + if (next == cplus_demangled) + return cplus_demangled; + + dyn_string_append_cstr (demangled, next); + next = end; + } + } + + free (cplus_demangled); + + return_value = dyn_string_release (demangled); + return return_value; +} + +#endif /* IN_LIBGCC2 */ + + +/* Demangle NAME in the G++ V3 ABI demangling style, and return either + zero, indicating that some error occurred, or a demangling_t + holding the results. */ +static demangling_t +demangle_v3_with_details (name) + const char *name; +{ + demangling_t dm; + status_t status; + + if (VG_(strncmp) (name, "_Z", 2)) + return 0; + + dm = demangling_new (name, DMGL_GNU_V3); + if (dm == NULL) + { + vg_assert (0); + /* + fprintf (stderr, "Memory allocation failed.\n"); + abort (); + */ + } + + status = result_push (dm); + if (! STATUS_NO_ERROR (status)) + { + demangling_delete (dm); + vg_assert (0); + /* + fprintf (stderr, "%s\n", status); + abort (); + */ + } + + status = demangle_mangled_name (dm); + if (STATUS_NO_ERROR (status)) + return dm; + + demangling_delete (dm); + return 0; +} + + +/* Return non-zero iff NAME is the mangled form of a constructor name + in the G++ V3 ABI demangling style. Specifically, return: + - '1' if NAME is a complete object constructor, + - '2' if NAME is a base object constructor, or + - '3' if NAME is a complete object allocating constructor. */ +/* +enum gnu_v3_ctor_kinds +is_gnu_v3_mangled_ctor (name) + const char *name; +{ + demangling_t dm = demangle_v3_with_details (name); + + if (dm) + { + enum gnu_v3_ctor_kinds result = dm->is_constructor; + demangling_delete (dm); + return result; + } + else + return 0; +} +*/ + + +/* Return non-zero iff NAME is the mangled form of a destructor name + in the G++ V3 ABI demangling style. Specifically, return: + - '0' if NAME is a deleting destructor, + - '1' if NAME is a complete object destructor, or + - '2' if NAME is a base object destructor. */ +/* +enum gnu_v3_dtor_kinds +is_gnu_v3_mangled_dtor (name) + const char *name; +{ + demangling_t dm = demangle_v3_with_details (name); + + if (dm) + { + enum gnu_v3_dtor_kinds result = dm->is_destructor; + demangling_delete (dm); + return result; + } + else + return 0; +} +*/ + +#ifdef STANDALONE_DEMANGLER + +#include "getopt.h" + +static void print_usage + PARAMS ((FILE* fp, int exit_value)); + +/* Non-zero if CHAR is a character than can occur in a mangled name. */ +#define is_mangled_char(CHAR) \ + (IS_ALPHA (CHAR) || IS_DIGIT (CHAR) \ + || (CHAR) == '_' || (CHAR) == '.' || (CHAR) == '$') + +/* The name of this program, as invoked. */ +const char* program_name; + +/* Prints usage summary to FP and then exits with EXIT_VALUE. */ + +static void +print_usage (fp, exit_value) + FILE* fp; + int exit_value; +{ + fprintf (fp, "Usage: %s [options] [names ...]\n", program_name); + fprintf (fp, "Options:\n"); + fprintf (fp, " -h,--help Display this message.\n"); + fprintf (fp, " -s,--strict Demangle standard names only.\n"); + fprintf (fp, " -v,--verbose Produce verbose demanglings.\n"); + fprintf (fp, "If names are provided, they are demangled. Otherwise filters standard input.\n"); + + exit (exit_value); +} + +/* Option specification for getopt_long. */ +static const struct option long_options[] = +{ + { "help", no_argument, NULL, 'h' }, + { "strict", no_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { NULL, no_argument, NULL, 0 }, +}; + +/* Main entry for a demangling filter executable. It will demangle + its command line arguments, if any. If none are provided, it will + filter stdin to stdout, replacing any recognized mangled C++ names + with their demangled equivalents. */ + +int +main (argc, argv) + int argc; + char *argv[]; +{ + status_t status; + int i; + int opt_char; + + /* Use the program name of this program, as invoked. */ + program_name = argv[0]; + + /* Parse options. */ + do + { + opt_char = getopt_long (argc, argv, "hsv", long_options, NULL); + switch (opt_char) + { + case '?': /* Unrecognized option. */ + print_usage (stderr, 1); + break; + + case 'h': + print_usage (stdout, 0); + break; + + case 's': + flag_strict = 1; + break; + + case 'v': + flag_verbose = 1; + break; + } + } + while (opt_char != -1); + + if (optind == argc) + /* No command line arguments were provided. Filter stdin. */ + { + dyn_string_t mangled = dyn_string_new (3); + dyn_string_t demangled = dyn_string_new (0); + status_t status; + + /* Read all of input. */ + while (!feof (stdin)) + { + char c = getchar (); + + /* The first character of a mangled name is an underscore. */ + if (feof (stdin)) + break; + if (c != '_') + { + /* It's not a mangled name. Print the character and go + on. */ + putchar (c); + continue; + } + c = getchar (); + + /* The second character of a mangled name is a capital `Z'. */ + if (feof (stdin)) + break; + if (c != 'Z') + { + /* It's not a mangled name. Print the previous + underscore, the `Z', and go on. */ + putchar ('_'); + putchar (c); + continue; + } + + /* Start keeping track of the candidate mangled name. */ + dyn_string_append_char (mangled, '_'); + dyn_string_append_char (mangled, 'Z'); + + /* Pile characters into mangled until we hit one that can't + occur in a mangled name. */ + c = getchar (); + while (!feof (stdin) && is_mangled_char (c)) + { + dyn_string_append_char (mangled, c); + if (feof (stdin)) + break; + c = getchar (); + } + + /* Attempt to demangle the name. */ + status = cp_demangle (dyn_string_buf (mangled), demangled, 0); + + /* If the demangling succeeded, great! Print out the + demangled version. */ + if (STATUS_NO_ERROR (status)) + fputs (dyn_string_buf (demangled), stdout); + /* Abort on allocation failures. */ + else if (status == STATUS_ALLOCATION_FAILED) + { + fprintf (stderr, "Memory allocation failed.\n"); + abort (); + } + /* Otherwise, it might not have been a mangled name. Just + print out the original text. */ + else + fputs (dyn_string_buf (mangled), stdout); + + /* If we haven't hit EOF yet, we've read one character that + can't occur in a mangled name, so print it out. */ + if (!feof (stdin)) + putchar (c); + + /* Clear the candidate mangled name, to start afresh next + time we hit a `_Z'. */ + dyn_string_clear (mangled); + } + + dyn_string_delete (mangled); + dyn_string_delete (demangled); + } + else + /* Demangle command line arguments. */ + { + dyn_string_t result = dyn_string_new (0); + + /* Loop over command line arguments. */ + for (i = optind; i < argc; ++i) + { + /* Attempt to demangle. */ + status = cp_demangle (argv[i], result, 0); + + /* If it worked, print the demangled name. */ + if (STATUS_NO_ERROR (status)) + printf ("%s\n", dyn_string_buf (result)); + /* Abort on allocaiton failures. */ + else if (status == STATUS_ALLOCATION_FAILED) + { + fprintf (stderr, "Memory allocation failed.\n"); + abort (); + } + /* If not, print the error message to stderr instead. */ + else + fprintf (stderr, "%s\n", status); + } + dyn_string_delete (result); + } + + return 0; +} + +#endif /* STANDALONE_DEMANGLER */ diff --git a/VEX/head20041019/coregrind/demangle/cplus-dem.c b/VEX/head20041019/coregrind/demangle/cplus-dem.c new file mode 100644 index 0000000000..311b84fb95 --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/cplus-dem.c @@ -0,0 +1,5275 @@ +/* Demangler for GNU C++ + Copyright 1989, 1991, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001 Free Software Foundation, Inc. + Written by James Clark (jjc@jclark.uucp) + Rewritten by Fred Fish (fnf@cygnus.com) for ARM and Lucid demangling + Modified by Satish Pai (pai@apollo.hp.com) for HP demangling + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* This file exports two functions; cplus_mangle_opname and cplus_demangle. + + This file imports xmalloc and xrealloc, which are like malloc and + realloc except that they generate a fatal error if there is no + available memory. */ + +/* This file lives in both GCC and libiberty. When making changes, please + try not to break either. */ + +#define __NO_STRING_INLINES + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "safe-ctype.h" +#include "core.h" + +/*#include +#include +#include */ + +/*#ifdef HAVE_STDLIB_H +#include +#else +char * malloc (); +char * realloc (); +#endif*/ + +#include +#include "dyn-string.h" +#undef CURRENT_DEMANGLING_STYLE +#define CURRENT_DEMANGLING_STYLE work->options + +/*#include "libiberty.h"*/ + +static char *ada_demangle PARAMS ((const char *, int)); + +#define min(X,Y) (((X) < (Y)) ? (X) : (Y)) + +/* A value at least one greater than the maximum number of characters + that will be output when using the `%d' format with `printf'. */ +#define INTBUF_SIZE 32 + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) +#endif + +#ifndef STANDALONE +#define size_t Int + +#define xstrdup(ptr) VG_(arena_strdup) (VG_AR_DEMANGLE, ptr) +#define free(ptr) VG_(arena_free) (VG_AR_DEMANGLE, ptr) +#define xmalloc(size) VG_(arena_malloc) (VG_AR_DEMANGLE, size) +#define xrealloc(ptr, size) VG_(arena_realloc)(VG_AR_DEMANGLE, ptr, \ + VG_MIN_MALLOC_SZB, size) + +#define abort() vg_assert(0) +#undef strstr +#define strstr VG_(strstr) +#define sprintf VG_(sprintf) +#define strcpy VG_(strcpy) +#define strncpy VG_(strncpy) +#define strncat VG_(strncat) +#define strchr VG_(strchr) +#define strpbrk VG_(strpbrk) +#define strlen VG_(strlen) +#define strcmp VG_(strcmp) +#define strncmp VG_(strncmp) +#define memcpy VG_(memcpy) +#define memset VG_(memset) +#define memcmp VG_(memcmp) +#endif + +extern void fancy_abort PARAMS ((void)) ATTRIBUTE_NORETURN; + +/* In order to allow a single demangler executable to demangle strings + using various common values of CPLUS_MARKER, as well as any specific + one set at compile time, we maintain a string containing all the + commonly used ones, and check to see if the marker we are looking for + is in that string. CPLUS_MARKER is usually '$' on systems where the + assembler can deal with that. Where the assembler can't, it's usually + '.' (but on many systems '.' is used for other things). We put the + current defined CPLUS_MARKER first (which defaults to '$'), followed + by the next most common value, followed by an explicit '$' in case + the value of CPLUS_MARKER is not '$'. + + We could avoid this if we could just get g++ to tell us what the actual + cplus marker character is as part of the debug information, perhaps by + ensuring that it is the character that terminates the gcc_compiled + marker symbol (FIXME). */ + +#if !defined (CPLUS_MARKER) +#define CPLUS_MARKER '$' +#endif + +enum demangling_styles current_demangling_style = auto_demangling; + +static char cplus_markers[] = { CPLUS_MARKER, '.', '$', '\0' }; + +static char char_str[2] = { '\000', '\000' }; + +/* +void +set_cplus_marker_for_demangling (ch) + int ch; +{ + cplus_markers[0] = ch; +} +*/ + +typedef struct string /* Beware: these aren't required to be */ +{ /* '\0' terminated. */ + char *b; /* pointer to start of string */ + char *p; /* pointer after last character */ + char *e; /* pointer after end of allocated space */ +} string; + +/* Stuff that is shared between sub-routines. + Using a shared structure allows cplus_demangle to be reentrant. */ + +struct work_stuff +{ + int options; + char **typevec; + char **ktypevec; + char **btypevec; + int numk; + int numb; + int ksize; + int bsize; + int ntypes; + int typevec_size; + int constructor; + int destructor; + int static_type; /* A static member function */ + int temp_start; /* index in demangled to start of template args */ + int type_quals; /* The type qualifiers. */ + int dllimported; /* Symbol imported from a PE DLL */ + char **tmpl_argvec; /* Template function arguments. */ + int ntmpl_args; /* The number of template function arguments. */ + int forgetting_types; /* Nonzero if we are not remembering the types + we see. */ + string* previous_argument; /* The last function argument demangled. */ + int nrepeats; /* The number of times to repeat the previous + argument. */ +}; + +#define PRINT_ANSI_QUALIFIERS (work -> options & DMGL_ANSI) +#define PRINT_ARG_TYPES (work -> options & DMGL_PARAMS) + +static const struct optable +{ + const char *const in; + const char *const out; + const int flags; +} optable[] = { + {"nw", " new", DMGL_ANSI}, /* new (1.92, ansi) */ + {"dl", " delete", DMGL_ANSI}, /* new (1.92, ansi) */ + {"new", " new", 0}, /* old (1.91, and 1.x) */ + {"delete", " delete", 0}, /* old (1.91, and 1.x) */ + {"vn", " new []", DMGL_ANSI}, /* GNU, pending ansi */ + {"vd", " delete []", DMGL_ANSI}, /* GNU, pending ansi */ + {"as", "=", DMGL_ANSI}, /* ansi */ + {"ne", "!=", DMGL_ANSI}, /* old, ansi */ + {"eq", "==", DMGL_ANSI}, /* old, ansi */ + {"ge", ">=", DMGL_ANSI}, /* old, ansi */ + {"gt", ">", DMGL_ANSI}, /* old, ansi */ + {"le", "<=", DMGL_ANSI}, /* old, ansi */ + {"lt", "<", DMGL_ANSI}, /* old, ansi */ + {"plus", "+", 0}, /* old */ + {"pl", "+", DMGL_ANSI}, /* ansi */ + {"apl", "+=", DMGL_ANSI}, /* ansi */ + {"minus", "-", 0}, /* old */ + {"mi", "-", DMGL_ANSI}, /* ansi */ + {"ami", "-=", DMGL_ANSI}, /* ansi */ + {"mult", "*", 0}, /* old */ + {"ml", "*", DMGL_ANSI}, /* ansi */ + {"amu", "*=", DMGL_ANSI}, /* ansi (ARM/Lucid) */ + {"aml", "*=", DMGL_ANSI}, /* ansi (GNU/g++) */ + {"convert", "+", 0}, /* old (unary +) */ + {"negate", "-", 0}, /* old (unary -) */ + {"trunc_mod", "%", 0}, /* old */ + {"md", "%", DMGL_ANSI}, /* ansi */ + {"amd", "%=", DMGL_ANSI}, /* ansi */ + {"trunc_div", "/", 0}, /* old */ + {"dv", "/", DMGL_ANSI}, /* ansi */ + {"adv", "/=", DMGL_ANSI}, /* ansi */ + {"truth_andif", "&&", 0}, /* old */ + {"aa", "&&", DMGL_ANSI}, /* ansi */ + {"truth_orif", "||", 0}, /* old */ + {"oo", "||", DMGL_ANSI}, /* ansi */ + {"truth_not", "!", 0}, /* old */ + {"nt", "!", DMGL_ANSI}, /* ansi */ + {"postincrement","++", 0}, /* old */ + {"pp", "++", DMGL_ANSI}, /* ansi */ + {"postdecrement","--", 0}, /* old */ + {"mm", "--", DMGL_ANSI}, /* ansi */ + {"bit_ior", "|", 0}, /* old */ + {"or", "|", DMGL_ANSI}, /* ansi */ + {"aor", "|=", DMGL_ANSI}, /* ansi */ + {"bit_xor", "^", 0}, /* old */ + {"er", "^", DMGL_ANSI}, /* ansi */ + {"aer", "^=", DMGL_ANSI}, /* ansi */ + {"bit_and", "&", 0}, /* old */ + {"ad", "&", DMGL_ANSI}, /* ansi */ + {"aad", "&=", DMGL_ANSI}, /* ansi */ + {"bit_not", "~", 0}, /* old */ + {"co", "~", DMGL_ANSI}, /* ansi */ + {"call", "()", 0}, /* old */ + {"cl", "()", DMGL_ANSI}, /* ansi */ + {"alshift", "<<", 0}, /* old */ + {"ls", "<<", DMGL_ANSI}, /* ansi */ + {"als", "<<=", DMGL_ANSI}, /* ansi */ + {"arshift", ">>", 0}, /* old */ + {"rs", ">>", DMGL_ANSI}, /* ansi */ + {"ars", ">>=", DMGL_ANSI}, /* ansi */ + {"component", "->", 0}, /* old */ + {"pt", "->", DMGL_ANSI}, /* ansi; Lucid C++ form */ + {"rf", "->", DMGL_ANSI}, /* ansi; ARM/GNU form */ + {"indirect", "*", 0}, /* old */ + {"method_call", "->()", 0}, /* old */ + {"addr", "&", 0}, /* old (unary &) */ + {"array", "[]", 0}, /* old */ + {"vc", "[]", DMGL_ANSI}, /* ansi */ + {"compound", ", ", 0}, /* old */ + {"cm", ", ", DMGL_ANSI}, /* ansi */ + {"cond", "?:", 0}, /* old */ + {"cn", "?:", DMGL_ANSI}, /* pseudo-ansi */ + {"max", ">?", 0}, /* old */ + {"mx", ">?", DMGL_ANSI}, /* pseudo-ansi */ + {"min", "*", DMGL_ANSI}, /* ansi */ + {"sz", "sizeof ", DMGL_ANSI} /* pseudo-ansi */ +}; + +/* These values are used to indicate the various type varieties. + They are all non-zero so that they can be used as `success' + values. */ +typedef enum type_kind_t +{ + tk_none, + tk_pointer, + tk_reference, + tk_integral, + tk_bool, + tk_char, + tk_real +} type_kind_t; + +const struct demangler_engine libiberty_demanglers[] = +{ + { + NO_DEMANGLING_STYLE_STRING, + no_demangling, + "Demangling disabled" + } + , + { + AUTO_DEMANGLING_STYLE_STRING, + auto_demangling, + "Automatic selection based on executable" + } + , + { + GNU_DEMANGLING_STYLE_STRING, + gnu_demangling, + "GNU (g++) style demangling" + } + , + { + LUCID_DEMANGLING_STYLE_STRING, + lucid_demangling, + "Lucid (lcc) style demangling" + } + , + { + ARM_DEMANGLING_STYLE_STRING, + arm_demangling, + "ARM style demangling" + } + , + { + HP_DEMANGLING_STYLE_STRING, + hp_demangling, + "HP (aCC) style demangling" + } + , + { + EDG_DEMANGLING_STYLE_STRING, + edg_demangling, + "EDG style demangling" + } + , + { + GNU_V3_DEMANGLING_STYLE_STRING, + gnu_v3_demangling, + "GNU (g++) V3 ABI-style demangling" + } + , + { + JAVA_DEMANGLING_STYLE_STRING, + java_demangling, + "Java style demangling" + } + , + { + GNAT_DEMANGLING_STYLE_STRING, + gnat_demangling, + "GNAT style demangling" + } + , + { + NULL, unknown_demangling, NULL + } +}; + +#define STRING_EMPTY(str) ((str) -> b == (str) -> p) +#define PREPEND_BLANK(str) {if (!STRING_EMPTY(str)) \ + string_prepend(str, " ");} +#define APPEND_BLANK(str) {if (!STRING_EMPTY(str)) \ + string_append(str, " ");} +#define LEN_STRING(str) ( (STRING_EMPTY(str))?0:((str)->p - (str)->b)) + +/* The scope separator appropriate for the language being demangled. */ + +#define SCOPE_STRING(work) ((work->options & DMGL_JAVA) ? "." : "::") + +#define ARM_VTABLE_STRING "__vtbl__" /* Lucid/ARM virtual table prefix */ +#define ARM_VTABLE_STRLEN 8 /* strlen (ARM_VTABLE_STRING) */ + +/* Prototypes for local functions */ + +static void +delete_work_stuff PARAMS ((struct work_stuff *)); + +static void +delete_non_B_K_work_stuff PARAMS ((struct work_stuff *)); + +static char * +mop_up PARAMS ((struct work_stuff *, string *, int)); + +static void +squangle_mop_up PARAMS ((struct work_stuff *)); + +static void +work_stuff_copy_to_from PARAMS ((struct work_stuff *, struct work_stuff *)); + +#if 0 +static int +demangle_method_args PARAMS ((struct work_stuff *, const char **, string *)); +#endif + +static char * +internal_cplus_demangle PARAMS ((struct work_stuff *, const char *)); + +static int +demangle_template_template_parm PARAMS ((struct work_stuff *work, + const char **, string *)); + +static int +demangle_template PARAMS ((struct work_stuff *work, const char **, string *, + string *, int, int)); + +static int +arm_pt PARAMS ((struct work_stuff *, const char *, int, const char **, + const char **)); + +static int +demangle_class_name PARAMS ((struct work_stuff *, const char **, string *)); + +static int +demangle_qualified PARAMS ((struct work_stuff *, const char **, string *, + int, int)); + +static int +demangle_class PARAMS ((struct work_stuff *, const char **, string *)); + +static int +demangle_fund_type PARAMS ((struct work_stuff *, const char **, string *)); + +static int +demangle_signature PARAMS ((struct work_stuff *, const char **, string *)); + +static int +demangle_prefix PARAMS ((struct work_stuff *, const char **, string *)); + +static int +gnu_special PARAMS ((struct work_stuff *, const char **, string *)); + +static int +arm_special PARAMS ((const char **, string *)); + +static void +string_need PARAMS ((string *, int)); + +static void +string_delete PARAMS ((string *)); + +static void +string_init PARAMS ((string *)); + +static void +string_clear PARAMS ((string *)); + +#if 0 +static int +string_empty PARAMS ((string *)); +#endif + +static void +string_append PARAMS ((string *, const char *)); + +static void +string_appends PARAMS ((string *, string *)); + +static void +string_appendn PARAMS ((string *, const char *, int)); + +static void +string_prepend PARAMS ((string *, const char *)); + +static void +string_prependn PARAMS ((string *, const char *, int)); + +static void +string_append_template_idx PARAMS ((string *, int)); + +static int +get_count PARAMS ((const char **, int *)); + +static int +consume_count PARAMS ((const char **)); + +static int +consume_count_with_underscores PARAMS ((const char**)); + +static int +demangle_args PARAMS ((struct work_stuff *, const char **, string *)); + +static int +demangle_nested_args PARAMS ((struct work_stuff*, const char**, string*)); + +static int +do_type PARAMS ((struct work_stuff *, const char **, string *)); + +static int +do_arg PARAMS ((struct work_stuff *, const char **, string *)); + +static void +demangle_function_name PARAMS ((struct work_stuff *, const char **, string *, + const char *)); + +static int +iterate_demangle_function PARAMS ((struct work_stuff *, + const char **, string *, const char *)); + +static void +remember_type PARAMS ((struct work_stuff *, const char *, int)); + +static void +remember_Btype PARAMS ((struct work_stuff *, const char *, int, int)); + +static int +register_Btype PARAMS ((struct work_stuff *)); + +static void +remember_Ktype PARAMS ((struct work_stuff *, const char *, int)); + +static void +forget_types PARAMS ((struct work_stuff *)); + +static void +forget_B_and_K_types PARAMS ((struct work_stuff *)); + +static void +string_prepends PARAMS ((string *, string *)); + +static int +demangle_template_value_parm PARAMS ((struct work_stuff*, const char**, + string*, type_kind_t)); + +static int +do_hpacc_template_const_value PARAMS ((struct work_stuff *, const char **, string *)); + +static int +do_hpacc_template_literal PARAMS ((struct work_stuff *, const char **, string *)); + +static int +snarf_numeric_literal PARAMS ((const char **, string *)); + +/* There is a TYPE_QUAL value for each type qualifier. They can be + combined by bitwise-or to form the complete set of qualifiers for a + type. */ + +#define TYPE_UNQUALIFIED 0x0 +#define TYPE_QUAL_CONST 0x1 +#define TYPE_QUAL_VOLATILE 0x2 +#define TYPE_QUAL_RESTRICT 0x4 + +static int +code_for_qualifier PARAMS ((int)); + +static const char* +qualifier_string PARAMS ((int)); + +static const char* +demangle_qualifier PARAMS ((int)); + +static int +demangle_expression PARAMS ((struct work_stuff *, const char **, string *, + type_kind_t)); + +static int +demangle_integral_value PARAMS ((struct work_stuff *, const char **, + string *)); + +static int +demangle_real_value PARAMS ((struct work_stuff *, const char **, string *)); + +static void +demangle_arm_hp_template PARAMS ((struct work_stuff *, const char **, int, + string *)); + +static void +recursively_demangle PARAMS ((struct work_stuff *, const char **, string *, + int)); + +static void +grow_vect PARAMS ((void **, size_t *, size_t, int)); + +/* Translate count to integer, consuming tokens in the process. + Conversion terminates on the first non-digit character. + + Trying to consume something that isn't a count results in no + consumption of input and a return of -1. + + Overflow consumes the rest of the digits, and returns -1. */ + +static int +consume_count (type) + const char **type; +{ + int count = 0; + + if (! ISDIGIT ((unsigned char)**type)) + return -1; + + while (ISDIGIT ((unsigned char)**type)) + { + count *= 10; + + /* Check for overflow. + We assume that count is represented using two's-complement; + no power of two is divisible by ten, so if an overflow occurs + when multiplying by ten, the result will not be a multiple of + ten. */ + if ((count % 10) != 0) + { + while (ISDIGIT ((unsigned char) **type)) + (*type)++; + return -1; + } + + count += **type - '0'; + (*type)++; + } + + if (count < 0) + count = -1; + + return (count); +} + + +/* Like consume_count, but for counts that are preceded and followed + by '_' if they are greater than 10. Also, -1 is returned for + failure, since 0 can be a valid value. */ + +static int +consume_count_with_underscores (mangled) + const char **mangled; +{ + int idx; + + if (**mangled == '_') + { + (*mangled)++; + if (!ISDIGIT ((unsigned char)**mangled)) + return -1; + + idx = consume_count (mangled); + if (**mangled != '_') + /* The trailing underscore was missing. */ + return -1; + + (*mangled)++; + } + else + { + if (**mangled < '0' || **mangled > '9') + return -1; + + idx = **mangled - '0'; + (*mangled)++; + } + + return idx; +} + +/* C is the code for a type-qualifier. Return the TYPE_QUAL + corresponding to this qualifier. */ + +static int +code_for_qualifier (c) + int c; +{ + switch (c) + { + case 'C': + return TYPE_QUAL_CONST; + + case 'V': + return TYPE_QUAL_VOLATILE; + + case 'u': + return TYPE_QUAL_RESTRICT; + + default: + break; + } + + /* C was an invalid qualifier. */ + abort (); +} + +/* Return the string corresponding to the qualifiers given by + TYPE_QUALS. */ + +static const char* +qualifier_string (type_quals) + int type_quals; +{ + switch (type_quals) + { + case TYPE_UNQUALIFIED: + return ""; + + case TYPE_QUAL_CONST: + return "const"; + + case TYPE_QUAL_VOLATILE: + return "volatile"; + + case TYPE_QUAL_RESTRICT: + return "__restrict"; + + case TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE: + return "const volatile"; + + case TYPE_QUAL_CONST | TYPE_QUAL_RESTRICT: + return "const __restrict"; + + case TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT: + return "volatile __restrict"; + + case TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE | TYPE_QUAL_RESTRICT: + return "const volatile __restrict"; + + default: + break; + } + + /* TYPE_QUALS was an invalid qualifier set. */ + abort (); +} + +/* C is the code for a type-qualifier. Return the string + corresponding to this qualifier. This function should only be + called with a valid qualifier code. */ + +static const char* +demangle_qualifier (c) + int c; +{ + return qualifier_string (code_for_qualifier (c)); +} + +#if 0 +int +cplus_demangle_opname (opname, result, options) + const char *opname; + char *result; + int options; +{ + int len, len1, ret; + string type; + struct work_stuff work[1]; + const char *tem; + + len = strlen(opname); + result[0] = '\0'; + ret = 0; + memset ((char *) work, 0, sizeof (work)); + work->options = options; + + if (opname[0] == '_' && opname[1] == '_' + && opname[2] == 'o' && opname[3] == 'p') + { + /* ANSI. */ + /* type conversion operator. */ + tem = opname + 4; + if (do_type (work, &tem, &type)) + { + strcat (result, "operator "); + strncat (result, type.b, type.p - type.b); + string_delete (&type); + ret = 1; + } + } + else if (opname[0] == '_' && opname[1] == '_' + && ISLOWER((unsigned char)opname[2]) + && ISLOWER((unsigned char)opname[3])) + { + if (opname[4] == '\0') + { + /* Operator. */ + size_t i; + for (i = 0; i < ARRAY_SIZE (optable); i++) + { + if (strlen (optable[i].in) == 2 + && memcmp (optable[i].in, opname + 2, 2) == 0) + { + strcat (result, "operator"); + strcat (result, optable[i].out); + ret = 1; + break; + } + } + } + else + { + if (opname[2] == 'a' && opname[5] == '\0') + { + /* Assignment. */ + size_t i; + for (i = 0; i < ARRAY_SIZE (optable); i++) + { + if (strlen (optable[i].in) == 3 + && memcmp (optable[i].in, opname + 2, 3) == 0) + { + strcat (result, "operator"); + strcat (result, optable[i].out); + ret = 1; + break; + } + } + } + } + } + else if (len >= 3 + && opname[0] == 'o' + && opname[1] == 'p' + && strchr (cplus_markers, opname[2]) != NULL) + { + /* see if it's an assignment expression */ + if (len >= 10 /* op$assign_ */ + && memcmp (opname + 3, "assign_", 7) == 0) + { + size_t i; + for (i = 0; i < ARRAY_SIZE (optable); i++) + { + len1 = len - 10; + if ((int) strlen (optable[i].in) == len1 + && memcmp (optable[i].in, opname + 10, len1) == 0) + { + strcat (result, "operator"); + strcat (result, optable[i].out); + strcat (result, "="); + ret = 1; + break; + } + } + } + else + { + size_t i; + for (i = 0; i < ARRAY_SIZE (optable); i++) + { + len1 = len - 3; + if ((int) strlen (optable[i].in) == len1 + && memcmp (optable[i].in, opname + 3, len1) == 0) + { + strcat (result, "operator"); + strcat (result, optable[i].out); + ret = 1; + break; + } + } + } + } + else if (len >= 5 && memcmp (opname, "type", 4) == 0 + && strchr (cplus_markers, opname[4]) != NULL) + { + /* type conversion operator */ + tem = opname + 5; + if (do_type (work, &tem, &type)) + { + strcat (result, "operator "); + strncat (result, type.b, type.p - type.b); + string_delete (&type); + ret = 1; + } + } + squangle_mop_up (work); + return ret; + +} +#endif /* 0 */ + +/* Takes operator name as e.g. "++" and returns mangled + operator name (e.g. "postincrement_expr"), or NULL if not found. + + If OPTIONS & DMGL_ANSI == 1, return the ANSI name; + if OPTIONS & DMGL_ANSI == 0, return the old GNU name. */ + +/* +const char * +cplus_mangle_opname (opname, options) + const char *opname; + int options; +{ + size_t i; + int len; + + len = strlen (opname); + for (i = 0; i < ARRAY_SIZE (optable); i++) + { + if ((int) strlen (optable[i].out) == len + && (options & DMGL_ANSI) == (optable[i].flags & DMGL_ANSI) + && memcmp (optable[i].out, opname, len) == 0) + return optable[i].in; + } + return (0); +} +*/ + +/* Add a routine to set the demangling style to be sure it is valid and + allow for any demangler initialization that maybe necessary. */ + +/* +enum demangling_styles +cplus_demangle_set_style (style) + enum demangling_styles style; +{ + const struct demangler_engine *demangler = libiberty_demanglers; + + for (; demangler->demangling_style != unknown_demangling; ++demangler) + if (style == demangler->demangling_style) + { + current_demangling_style = style; + return current_demangling_style; + } + + return unknown_demangling; +} +*/ + +/* Do string name to style translation */ + +/* +enum demangling_styles +cplus_demangle_name_to_style (name) + const char *name; +{ + const struct demangler_engine *demangler = libiberty_demanglers; + + for (; demangler->demangling_style != unknown_demangling; ++demangler) + if (strcmp (name, demangler->demangling_style_name) == 0) + return demangler->demangling_style; + + return unknown_demangling; +} +*/ + +/* char *cplus_demangle (const char *mangled, int options) + + If MANGLED is a mangled function name produced by GNU C++, then + a pointer to a @code{malloc}ed string giving a C++ representation + of the name will be returned; otherwise NULL will be returned. + It is the caller's responsibility to free the string which + is returned. + + The OPTIONS arg may contain one or more of the following bits: + + DMGL_ANSI ANSI qualifiers such as `const' and `void' are + included. + DMGL_PARAMS Function parameters are included. + + For example, + + cplus_demangle ("foo__1Ai", DMGL_PARAMS) => "A::foo(int)" + cplus_demangle ("foo__1Ai", DMGL_PARAMS | DMGL_ANSI) => "A::foo(int)" + cplus_demangle ("foo__1Ai", 0) => "A::foo" + + cplus_demangle ("foo__1Afe", DMGL_PARAMS) => "A::foo(float,...)" + cplus_demangle ("foo__1Afe", DMGL_PARAMS | DMGL_ANSI)=> "A::foo(float,...)" + cplus_demangle ("foo__1Afe", 0) => "A::foo" + + Note that any leading underscores, or other such characters prepended by + the compilation system, are presumed to have already been stripped from + MANGLED. */ + +char * +VG_(cplus_demangle) (mangled, options) + const char *mangled; + int options; +{ + char *ret; + struct work_stuff work[1]; + + if (current_demangling_style == no_demangling) + return xstrdup (mangled); + + memset ((char *) work, 0, sizeof (work)); + work->options = options; + if ((work->options & DMGL_STYLE_MASK) == 0) + work->options |= (int) current_demangling_style & DMGL_STYLE_MASK; + + /* The V3 ABI demangling is implemented elsewhere. */ + if (GNU_V3_DEMANGLING || AUTO_DEMANGLING) + { + ret = VG_(cplus_demangle_v3) (mangled/*, work->options*/); + if (ret || GNU_V3_DEMANGLING) + return ret; + } + + if (JAVA_DEMANGLING) + { + ret = VG_(java_demangle_v3) (mangled); + if (ret) + return ret; + } + + if (GNAT_DEMANGLING) + return ada_demangle(mangled,options); + + ret = internal_cplus_demangle (work, mangled); + squangle_mop_up (work); + return (ret); +} + + +/* Assuming *OLD_VECT points to an array of *SIZE objects of size + ELEMENT_SIZE, grow it to contain at least MIN_SIZE objects, + updating *OLD_VECT and *SIZE as necessary. */ + +static void +grow_vect (old_vect, size, min_size, element_size) + void **old_vect; + size_t *size; + size_t min_size; + int element_size; +{ + if (*size < min_size) + { + *size *= 2; + if (*size < min_size) + *size = min_size; + *old_vect = xrealloc (*old_vect, *size * element_size); + } +} + +/* Demangle ada names: + 1. Discard final __{DIGIT}+ or ${DIGIT}+ + 2. Convert other instances of embedded "__" to `.'. + 3. Discard leading _ada_. + 4. Remove everything after first ___ if it is followed by 'X'. + 5. Put symbols that should be suppressed in <...> brackets. + The resulting string is valid until the next call of ada_demangle. */ + +static char * +ada_demangle (mangled, option) + const char *mangled; + int option ATTRIBUTE_UNUSED; +{ + int i, j; + int len0; + const char* p; + char *demangled = NULL; + int at_start_name; + int changed; + char *demangling_buffer = NULL; + size_t demangling_buffer_size = 0; + + changed = 0; + + if (strncmp (mangled, "_ada_", 5) == 0) + { + mangled += 5; + changed = 1; + } + + if (mangled[0] == '_' || mangled[0] == '<') + goto Suppress; + + p = strstr (mangled, "___"); + if (p == NULL) + len0 = strlen (mangled); + else + { + if (p[3] == 'X') + { + len0 = p - mangled; + changed = 1; + } + else + goto Suppress; + } + + /* Make demangled big enough for possible expansion by operator name. */ + grow_vect ((void **) &(demangling_buffer), + &demangling_buffer_size, 2 * len0 + 1, + sizeof (char)); + demangled = demangling_buffer; + + if (ISDIGIT ((unsigned char) mangled[len0 - 1])) { + for (i = len0 - 2; i >= 0 && ISDIGIT ((unsigned char) mangled[i]); i -= 1) + ; + if (i > 1 && mangled[i] == '_' && mangled[i - 1] == '_') + { + len0 = i - 1; + changed = 1; + } + else if (mangled[i] == '$') + { + len0 = i; + changed = 1; + } + } + + for (i = 0, j = 0; i < len0 && ! ISALPHA ((unsigned char)mangled[i]); + i += 1, j += 1) + demangled[j] = mangled[i]; + + at_start_name = 1; + while (i < len0) + { + at_start_name = 0; + + if (i < len0 - 2 && mangled[i] == '_' && mangled[i + 1] == '_') + { + demangled[j] = '.'; + changed = at_start_name = 1; + i += 2; j += 1; + } + else + { + demangled[j] = mangled[i]; + i += 1; j += 1; + } + } + demangled[j] = '\000'; + + for (i = 0; demangled[i] != '\0'; i += 1) + if (ISUPPER ((unsigned char)demangled[i]) || demangled[i] == ' ') + goto Suppress; + + if (! changed) + return NULL; + else + return demangled; + + Suppress: + grow_vect ((void **) &(demangling_buffer), + &demangling_buffer_size, strlen (mangled) + 3, + sizeof (char)); + demangled = demangling_buffer; + if (mangled[0] == '<') + strcpy (demangled, mangled); + else + sprintf (demangled, "<%s>", mangled); + + return demangled; +} + +/* This function performs most of what cplus_demangle use to do, but + to be able to demangle a name with a B, K or n code, we need to + have a longer term memory of what types have been seen. The original + now initializes and cleans up the squangle code info, while internal + calls go directly to this routine to avoid resetting that info. */ + +static char * +internal_cplus_demangle (work, mangled) + struct work_stuff *work; + const char *mangled; +{ + + string decl; + int success = 0; + char *demangled = NULL; + int s1, s2, s3, s4; + s1 = work->constructor; + s2 = work->destructor; + s3 = work->static_type; + s4 = work->type_quals; + work->constructor = work->destructor = 0; + work->type_quals = TYPE_UNQUALIFIED; + work->dllimported = 0; + + if ((mangled != NULL) && (*mangled != '\0')) + { + string_init (&decl); + + /* First check to see if gnu style demangling is active and if the + string to be demangled contains a CPLUS_MARKER. If so, attempt to + recognize one of the gnu special forms rather than looking for a + standard prefix. In particular, don't worry about whether there + is a "__" string in the mangled string. Consider "_$_5__foo" for + example. */ + + if ((AUTO_DEMANGLING || GNU_DEMANGLING)) + { + success = gnu_special (work, &mangled, &decl); + } + if (!success) + { + success = demangle_prefix (work, &mangled, &decl); + } + if (success && (*mangled != '\0')) + { + success = demangle_signature (work, &mangled, &decl); + } + if (work->constructor == 2) + { + string_prepend (&decl, "global constructors keyed to "); + work->constructor = 0; + } + else if (work->destructor == 2) + { + string_prepend (&decl, "global destructors keyed to "); + work->destructor = 0; + } + else if (work->dllimported == 1) + { + string_prepend (&decl, "import stub for "); + work->dllimported = 0; + } + demangled = mop_up (work, &decl, success); + } + work->constructor = s1; + work->destructor = s2; + work->static_type = s3; + work->type_quals = s4; + return demangled; +} + + +/* Clear out and squangling related storage */ +static void +squangle_mop_up (work) + struct work_stuff *work; +{ + /* clean up the B and K type mangling types. */ + forget_B_and_K_types (work); + if (work -> btypevec != NULL) + { + free ((char *) work -> btypevec); + } + if (work -> ktypevec != NULL) + { + free ((char *) work -> ktypevec); + } +} + + +/* Copy the work state and storage. */ + +static void +work_stuff_copy_to_from (to, from) + struct work_stuff *to; + struct work_stuff *from; +{ + int i; + + delete_work_stuff (to); + + /* Shallow-copy scalars. */ + memcpy (to, from, sizeof (*to)); + + /* Deep-copy dynamic storage. */ + if (from->typevec_size) + to->typevec + = (char **) xmalloc (from->typevec_size * sizeof (to->typevec[0])); + + for (i = 0; i < from->ntypes; i++) + { + int len = strlen (from->typevec[i]) + 1; + + to->typevec[i] = xmalloc (len); + memcpy (to->typevec[i], from->typevec[i], len); + } + + if (from->ksize) + to->ktypevec + = (char **) xmalloc (from->ksize * sizeof (to->ktypevec[0])); + + for (i = 0; i < from->numk; i++) + { + int len = strlen (from->ktypevec[i]) + 1; + + to->ktypevec[i] = xmalloc (len); + memcpy (to->ktypevec[i], from->ktypevec[i], len); + } + + if (from->bsize) + to->btypevec + = (char **) xmalloc (from->bsize * sizeof (to->btypevec[0])); + + for (i = 0; i < from->numb; i++) + { + int len = strlen (from->btypevec[i]) + 1; + + to->btypevec[i] = xmalloc (len); + memcpy (to->btypevec[i], from->btypevec[i], len); + } + + if (from->ntmpl_args) + to->tmpl_argvec + = xmalloc (from->ntmpl_args * sizeof (to->tmpl_argvec[0])); + + for (i = 0; i < from->ntmpl_args; i++) + { + int len = strlen (from->tmpl_argvec[i]) + 1; + + to->tmpl_argvec[i] = xmalloc (len); + memcpy (to->tmpl_argvec[i], from->tmpl_argvec[i], len); + } + + if (from->previous_argument) + { + to->previous_argument = (string*) xmalloc (sizeof (string)); + string_init (to->previous_argument); + string_appends (to->previous_argument, from->previous_argument); + } +} + + +/* Delete dynamic stuff in work_stuff that is not to be re-used. */ + +static void +delete_non_B_K_work_stuff (work) + struct work_stuff *work; +{ + /* Discard the remembered types, if any. */ + + forget_types (work); + if (work -> typevec != NULL) + { + free ((char *) work -> typevec); + work -> typevec = NULL; + work -> typevec_size = 0; + } + if (work->tmpl_argvec) + { + int i; + + for (i = 0; i < work->ntmpl_args; i++) + if (work->tmpl_argvec[i]) + free ((char*) work->tmpl_argvec[i]); + + free ((char*) work->tmpl_argvec); + work->tmpl_argvec = NULL; + } + if (work->previous_argument) + { + string_delete (work->previous_argument); + free ((char*) work->previous_argument); + work->previous_argument = NULL; + } +} + + +/* Delete all dynamic storage in work_stuff. */ +static void +delete_work_stuff (work) + struct work_stuff *work; +{ + delete_non_B_K_work_stuff (work); + squangle_mop_up (work); +} + + +/* Clear out any mangled storage */ + +static char * +mop_up (work, declp, success) + struct work_stuff *work; + string *declp; + int success; +{ + char *demangled = NULL; + + delete_non_B_K_work_stuff (work); + + /* If demangling was successful, ensure that the demangled string is null + terminated and return it. Otherwise, free the demangling decl. */ + + if (!success) + { + string_delete (declp); + } + else + { + string_appendn (declp, "", 1); + demangled = declp->b; + } + return (demangled); +} + +/* + +LOCAL FUNCTION + + demangle_signature -- demangle the signature part of a mangled name + +SYNOPSIS + + static int + demangle_signature (struct work_stuff *work, const char **mangled, + string *declp); + +DESCRIPTION + + Consume and demangle the signature portion of the mangled name. + + DECLP is the string where demangled output is being built. At + entry it contains the demangled root name from the mangled name + prefix. I.E. either a demangled operator name or the root function + name. In some special cases, it may contain nothing. + + *MANGLED points to the current unconsumed location in the mangled + name. As tokens are consumed and demangling is performed, the + pointer is updated to continuously point at the next token to + be consumed. + + Demangling GNU style mangled names is nasty because there is no + explicit token that marks the start of the outermost function + argument list. */ + +static int +demangle_signature (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int success = 1; + int func_done = 0; + int expect_func = 0; + int expect_return_type = 0; + const char *oldmangled = NULL; + string trawname; + string tname; + + while (success && (**mangled != '\0')) + { + switch (**mangled) + { + case 'Q': + oldmangled = *mangled; + success = demangle_qualified (work, mangled, declp, 1, 0); + if (success) + remember_type (work, oldmangled, *mangled - oldmangled); + if (AUTO_DEMANGLING || GNU_DEMANGLING) + expect_func = 1; + oldmangled = NULL; + break; + + case 'K': + oldmangled = *mangled; + success = demangle_qualified (work, mangled, declp, 1, 0); + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + expect_func = 1; + } + oldmangled = NULL; + break; + + case 'S': + /* Static member function */ + if (oldmangled == NULL) + { + oldmangled = *mangled; + } + (*mangled)++; + work -> static_type = 1; + break; + + case 'C': + case 'V': + case 'u': + work->type_quals |= code_for_qualifier (**mangled); + + /* a qualified member function */ + if (oldmangled == NULL) + oldmangled = *mangled; + (*mangled)++; + break; + + case 'L': + /* Local class name follows after "Lnnn_" */ + if (HP_DEMANGLING) + { + while (**mangled && (**mangled != '_')) + (*mangled)++; + if (!**mangled) + success = 0; + else + (*mangled)++; + } + else + success = 0; + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (oldmangled == NULL) + { + oldmangled = *mangled; + } + work->temp_start = -1; /* uppermost call to demangle_class */ + success = demangle_class (work, mangled, declp); + if (success) + { + remember_type (work, oldmangled, *mangled - oldmangled); + } + if (AUTO_DEMANGLING || GNU_DEMANGLING || EDG_DEMANGLING) + { + /* EDG and others will have the "F", so we let the loop cycle + if we are looking at one. */ + if (**mangled != 'F') + expect_func = 1; + } + oldmangled = NULL; + break; + + case 'B': + { + string s; + success = do_type (work, mangled, &s); + if (success) + { + string_append (&s, SCOPE_STRING (work)); + string_prepends (declp, &s); + } + oldmangled = NULL; + expect_func = 1; + } + break; + + case 'F': + /* Function */ + /* ARM/HP style demangling includes a specific 'F' character after + the class name. For GNU style, it is just implied. So we can + safely just consume any 'F' at this point and be compatible + with either style. */ + + oldmangled = NULL; + func_done = 1; + (*mangled)++; + + /* For lucid/ARM/HP style we have to forget any types we might + have remembered up to this point, since they were not argument + types. GNU style considers all types seen as available for + back references. See comment in demangle_args() */ + + if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) + { + forget_types (work); + } + success = demangle_args (work, mangled, declp); + /* After picking off the function args, we expect to either + find the function return type (preceded by an '_') or the + end of the string. */ + if (success && (AUTO_DEMANGLING || EDG_DEMANGLING) && **mangled == '_') + { + ++(*mangled); + /* At this level, we do not care about the return type. */ + success = do_type (work, mangled, &tname); + string_delete (&tname); + } + + break; + + case 't': + /* G++ Template */ + string_init(&trawname); + string_init(&tname); + if (oldmangled == NULL) + { + oldmangled = *mangled; + } + success = demangle_template (work, mangled, &tname, + &trawname, 1, 1); + if (success) + { + remember_type (work, oldmangled, *mangled - oldmangled); + } + string_append (&tname, SCOPE_STRING (work)); + + string_prepends(declp, &tname); + if (work -> destructor & 1) + { + string_prepend (&trawname, "~"); + string_appends (declp, &trawname); + work->destructor -= 1; + } + if ((work->constructor & 1) || (work->destructor & 1)) + { + string_appends (declp, &trawname); + work->constructor -= 1; + } + string_delete(&trawname); + string_delete(&tname); + oldmangled = NULL; + expect_func = 1; + break; + + case '_': + if ((AUTO_DEMANGLING || GNU_DEMANGLING) && expect_return_type) + { + /* Read the return type. */ + string return_type; + string_init (&return_type); + + (*mangled)++; + success = do_type (work, mangled, &return_type); + APPEND_BLANK (&return_type); + + string_prepends (declp, &return_type); + string_delete (&return_type); + break; + } + else + /* At the outermost level, we cannot have a return type specified, + so if we run into another '_' at this point we are dealing with + a mangled name that is either bogus, or has been mangled by + some algorithm we don't know how to deal with. So just + reject the entire demangling. */ + /* However, "_nnn" is an expected suffix for alternate entry point + numbered nnn for a function, with HP aCC, so skip over that + without reporting failure. pai/1997-09-04 */ + if (HP_DEMANGLING) + { + (*mangled)++; + while (**mangled && ISDIGIT ((unsigned char)**mangled)) + (*mangled)++; + } + else + success = 0; + break; + + case 'H': + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + /* A G++ template function. Read the template arguments. */ + success = demangle_template (work, mangled, declp, 0, 0, + 0); + if (!(work->constructor & 1)) + expect_return_type = 1; + (*mangled)++; + break; + } + else + /* fall through */ + {;} + + default: + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + /* Assume we have stumbled onto the first outermost function + argument token, and start processing args. */ + func_done = 1; + success = demangle_args (work, mangled, declp); + } + else + { + /* Non-GNU demanglers use a specific token to mark the start + of the outermost function argument tokens. Typically 'F', + for ARM/HP-demangling, for example. So if we find something + we are not prepared for, it must be an error. */ + success = 0; + } + break; + } + /* + if (AUTO_DEMANGLING || GNU_DEMANGLING) + */ + { + if (success && expect_func) + { + func_done = 1; + if (LUCID_DEMANGLING || ARM_DEMANGLING || EDG_DEMANGLING) + { + forget_types (work); + } + success = demangle_args (work, mangled, declp); + /* Since template include the mangling of their return types, + we must set expect_func to 0 so that we don't try do + demangle more arguments the next time we get here. */ + expect_func = 0; + } + } + } + if (success && !func_done) + { + if (AUTO_DEMANGLING || GNU_DEMANGLING) + { + /* With GNU style demangling, bar__3foo is 'foo::bar(void)', and + bar__3fooi is 'foo::bar(int)'. We get here when we find the + first case, and need to ensure that the '(void)' gets added to + the current declp. Note that with ARM/HP, the first case + represents the name of a static data member 'foo::bar', + which is in the current declp, so we leave it alone. */ + success = demangle_args (work, mangled, declp); + } + } + if (success && PRINT_ARG_TYPES) + { + if (work->static_type) + string_append (declp, " static"); + if (work->type_quals != TYPE_UNQUALIFIED) + { + APPEND_BLANK (declp); + string_append (declp, qualifier_string (work->type_quals)); + } + } + + return (success); +} + +#if 0 + +static int +demangle_method_args (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int success = 0; + + if (work -> static_type) + { + string_append (declp, *mangled + 1); + *mangled += strlen (*mangled); + success = 1; + } + else + { + success = demangle_args (work, mangled, declp); + } + return (success); +} + +#endif + +static int +demangle_template_template_parm (work, mangled, tname) + struct work_stuff *work; + const char **mangled; + string *tname; +{ + int i; + int r; + int need_comma = 0; + int success = 1; + string temp; + + string_append (tname, "template <"); + /* get size of template parameter list */ + if (get_count (mangled, &r)) + { + for (i = 0; i < r; i++) + { + if (need_comma) + { + string_append (tname, ", "); + } + + /* Z for type parameters */ + if (**mangled == 'Z') + { + (*mangled)++; + string_append (tname, "class"); + } + /* z for template parameters */ + else if (**mangled == 'z') + { + (*mangled)++; + success = + demangle_template_template_parm (work, mangled, tname); + if (!success) + { + break; + } + } + else + { + /* temp is initialized in do_type */ + success = do_type (work, mangled, &temp); + if (success) + { + string_appends (tname, &temp); + } + string_delete(&temp); + if (!success) + { + break; + } + } + need_comma = 1; + } + + } + if (tname->p[-1] == '>') + string_append (tname, " "); + string_append (tname, "> class"); + return (success); +} + +static int +demangle_expression (work, mangled, s, tk) + struct work_stuff *work; + const char** mangled; + string* s; + type_kind_t tk; +{ + int need_operator = 0; + int success; + + success = 1; + string_appendn (s, "(", 1); + (*mangled)++; + while (success && **mangled != 'W' && **mangled != '\0') + { + if (need_operator) + { + size_t i; + size_t len; + + success = 0; + + len = strlen (*mangled); + + for (i = 0; i < (size_t)ARRAY_SIZE (optable); ++i) + { + size_t l = strlen (optable[i].in); + + if (l <= len + && memcmp (optable[i].in, *mangled, l) == 0) + { + string_appendn (s, " ", 1); + string_append (s, optable[i].out); + string_appendn (s, " ", 1); + success = 1; + (*mangled) += l; + break; + } + } + + if (!success) + break; + } + else + need_operator = 1; + + success = demangle_template_value_parm (work, mangled, s, tk); + } + + if (**mangled != 'W') + success = 0; + else + { + string_appendn (s, ")", 1); + (*mangled)++; + } + + return success; +} + +static int +demangle_integral_value (work, mangled, s) + struct work_stuff *work; + const char** mangled; + string* s; +{ + int success; + + if (**mangled == 'E') + success = demangle_expression (work, mangled, s, tk_integral); + else if (**mangled == 'Q' || **mangled == 'K') + success = demangle_qualified (work, mangled, s, 0, 1); + else + { + int value; + + /* By default, we let the number decide whether we shall consume an + underscore. */ + int consume_following_underscore = 0; + int leave_following_underscore = 0; + + success = 0; + + /* Negative numbers are indicated with a leading `m'. */ + if (**mangled == 'm') + { + string_appendn (s, "-", 1); + (*mangled)++; + } + else if (mangled[0][0] == '_' && mangled[0][1] == 'm') + { + /* Since consume_count_with_underscores does not handle the + `m'-prefix we must do it here, using consume_count and + adjusting underscores: we have to consume the underscore + matching the prepended one. */ + consume_following_underscore = 1; + string_appendn (s, "-", 1); + (*mangled) += 2; + } + else if (**mangled == '_') + { + /* Do not consume a following underscore; + consume_following_underscore will consume what should be + consumed. */ + leave_following_underscore = 1; + } + + /* We must call consume_count if we expect to remove a trailing + underscore, since consume_count_with_underscores expects + the leading underscore (that we consumed) if it is to handle + multi-digit numbers. */ + if (consume_following_underscore) + value = consume_count (mangled); + else + value = consume_count_with_underscores (mangled); + + if (value != -1) + { + char buf[INTBUF_SIZE]; + sprintf (buf, "%d", value); + string_append (s, buf); + + /* Numbers not otherwise delimited, might have an underscore + appended as a delimeter, which we should skip. + + ??? This used to always remove a following underscore, which + is wrong. If other (arbitrary) cases are followed by an + underscore, we need to do something more radical. */ + + if ((value > 9 || consume_following_underscore) + && ! leave_following_underscore + && **mangled == '_') + (*mangled)++; + + /* All is well. */ + success = 1; + } + } + + return success; +} + +/* Demangle the real value in MANGLED. */ + +static int +demangle_real_value (work, mangled, s) + struct work_stuff *work; + const char **mangled; + string* s; +{ + if (**mangled == 'E') + return demangle_expression (work, mangled, s, tk_real); + + if (**mangled == 'm') + { + string_appendn (s, "-", 1); + (*mangled)++; + } + while (ISDIGIT ((unsigned char)**mangled)) + { + string_appendn (s, *mangled, 1); + (*mangled)++; + } + if (**mangled == '.') /* fraction */ + { + string_appendn (s, ".", 1); + (*mangled)++; + while (ISDIGIT ((unsigned char)**mangled)) + { + string_appendn (s, *mangled, 1); + (*mangled)++; + } + } + if (**mangled == 'e') /* exponent */ + { + string_appendn (s, "e", 1); + (*mangled)++; + while (ISDIGIT ((unsigned char)**mangled)) + { + string_appendn (s, *mangled, 1); + (*mangled)++; + } + } + + return 1; +} + +static int +demangle_template_value_parm (work, mangled, s, tk) + struct work_stuff *work; + const char **mangled; + string* s; + type_kind_t tk; +{ + int success = 1; + + if (**mangled == 'Y') + { + /* The next argument is a template parameter. */ + int idx; + + (*mangled)++; + idx = consume_count_with_underscores (mangled); + if (idx == -1 + || (work->tmpl_argvec && idx >= work->ntmpl_args) + || consume_count_with_underscores (mangled) == -1) + return -1; + if (work->tmpl_argvec) + string_append (s, work->tmpl_argvec[idx]); + else + string_append_template_idx (s, idx); + } + else if (tk == tk_integral) + success = demangle_integral_value (work, mangled, s); + else if (tk == tk_char) + { + char tmp[2]; + int val; + if (**mangled == 'm') + { + string_appendn (s, "-", 1); + (*mangled)++; + } + string_appendn (s, "'", 1); + val = consume_count(mangled); + if (val <= 0) + success = 0; + else + { + tmp[0] = (char)val; + tmp[1] = '\0'; + string_appendn (s, &tmp[0], 1); + string_appendn (s, "'", 1); + } + } + else if (tk == tk_bool) + { + int val = consume_count (mangled); + if (val == 0) + string_appendn (s, "false", 5); + else if (val == 1) + string_appendn (s, "true", 4); + else + success = 0; + } + else if (tk == tk_real) + success = demangle_real_value (work, mangled, s); + else if (tk == tk_pointer || tk == tk_reference) + { + if (**mangled == 'Q') + success = demangle_qualified (work, mangled, s, + /*isfuncname=*/0, + /*append=*/1); + else + { + int symbol_len = consume_count (mangled); + if (symbol_len == -1) + return -1; + if (symbol_len == 0) + string_appendn (s, "0", 1); + else + { + char *p = xmalloc (symbol_len + 1), *q; + strncpy (p, *mangled, symbol_len); + p [symbol_len] = '\0'; + /* We use cplus_demangle here, rather than + internal_cplus_demangle, because the name of the entity + mangled here does not make use of any of the squangling + or type-code information we have built up thus far; it is + mangled independently. */ + q = VG_(cplus_demangle) (p, work->options); + if (tk == tk_pointer) + string_appendn (s, "&", 1); + /* FIXME: Pointer-to-member constants should get a + qualifying class name here. */ + if (q) + { + string_append (s, q); + free (q); + } + else + string_append (s, p); + free (p); + } + *mangled += symbol_len; + } + } + + return success; +} + +/* Demangle the template name in MANGLED. The full name of the + template (e.g., S) is placed in TNAME. The name without the + template parameters (e.g. S) is placed in TRAWNAME if TRAWNAME is + non-NULL. If IS_TYPE is nonzero, this template is a type template, + not a function template. If both IS_TYPE and REMEMBER are nonzero, + the template is remembered in the list of back-referenceable + types. */ + +static int +demangle_template (work, mangled, tname, trawname, is_type, remember) + struct work_stuff *work; + const char **mangled; + string *tname; + string *trawname; + int is_type; + int remember; +{ + int i; + int r; + int need_comma = 0; + int success = 0; + const char *start; + int is_java_array = 0; + string temp; + int bindex = 0; + + (*mangled)++; + if (is_type) + { + if (remember) + bindex = register_Btype (work); + start = *mangled; + /* get template name */ + if (**mangled == 'z') + { + int idx; + (*mangled)++; + (*mangled)++; + + idx = consume_count_with_underscores (mangled); + if (idx == -1 + || (work->tmpl_argvec && idx >= work->ntmpl_args) + || consume_count_with_underscores (mangled) == -1) + return (0); + + if (work->tmpl_argvec) + { + string_append (tname, work->tmpl_argvec[idx]); + if (trawname) + string_append (trawname, work->tmpl_argvec[idx]); + } + else + { + string_append_template_idx (tname, idx); + if (trawname) + string_append_template_idx (trawname, idx); + } + } + else + { + if ((r = consume_count (mangled)) <= 0 + || (int) strlen (*mangled) < r) + { + return (0); + } + is_java_array = (work -> options & DMGL_JAVA) + && strncmp (*mangled, "JArray1Z", 8) == 0; + if (! is_java_array) + { + string_appendn (tname, *mangled, r); + } + if (trawname) + string_appendn (trawname, *mangled, r); + *mangled += r; + } + } + if (!is_java_array) + string_append (tname, "<"); + /* get size of template parameter list */ + if (!get_count (mangled, &r)) + { + return (0); + } + if (!is_type) + { + /* Create an array for saving the template argument values. */ + work->tmpl_argvec = (char**) xmalloc (r * sizeof (char *)); + work->ntmpl_args = r; + for (i = 0; i < r; i++) + work->tmpl_argvec[i] = 0; + } + for (i = 0; i < r; i++) + { + if (need_comma) + { + string_append (tname, ", "); + } + /* Z for type parameters */ + if (**mangled == 'Z') + { + (*mangled)++; + /* temp is initialized in do_type */ + success = do_type (work, mangled, &temp); + if (success) + { + string_appends (tname, &temp); + + if (!is_type) + { + /* Save the template argument. */ + int len = temp.p - temp.b; + work->tmpl_argvec[i] = xmalloc (len + 1); + memcpy (work->tmpl_argvec[i], temp.b, len); + work->tmpl_argvec[i][len] = '\0'; + } + } + string_delete(&temp); + if (!success) + { + break; + } + } + /* z for template parameters */ + else if (**mangled == 'z') + { + int r2; + (*mangled)++; + success = demangle_template_template_parm (work, mangled, tname); + + if (success + && (r2 = consume_count (mangled)) > 0 + && (int) strlen (*mangled) >= r2) + { + string_append (tname, " "); + string_appendn (tname, *mangled, r2); + if (!is_type) + { + /* Save the template argument. */ + int len = r2; + work->tmpl_argvec[i] = xmalloc (len + 1); + memcpy (work->tmpl_argvec[i], *mangled, len); + work->tmpl_argvec[i][len] = '\0'; + } + *mangled += r2; + } + if (!success) + { + break; + } + } + else + { + string param; + string* s; + + /* otherwise, value parameter */ + + /* temp is initialized in do_type */ + success = do_type (work, mangled, &temp); + string_delete(&temp); + if (!success) + break; + + if (!is_type) + { + s = ¶m; + string_init (s); + } + else + s = tname; + + success = demangle_template_value_parm (work, mangled, s, + (type_kind_t) success); + + if (!success) + { + if (!is_type) + string_delete (s); + success = 0; + break; + } + + if (!is_type) + { + int len = s->p - s->b; + work->tmpl_argvec[i] = xmalloc (len + 1); + memcpy (work->tmpl_argvec[i], s->b, len); + work->tmpl_argvec[i][len] = '\0'; + + string_appends (tname, s); + string_delete (s); + } + } + need_comma = 1; + } + if (is_java_array) + { + string_append (tname, "[]"); + } + else + { + if (tname->p[-1] == '>') + string_append (tname, " "); + string_append (tname, ">"); + } + + if (is_type && remember) + remember_Btype (work, tname->b, LEN_STRING (tname), bindex); + + /* + if (work -> static_type) + { + string_append (declp, *mangled + 1); + *mangled += strlen (*mangled); + success = 1; + } + else + { + success = demangle_args (work, mangled, declp); + } + } + */ + return (success); +} + +static int +arm_pt (work, mangled, n, anchor, args) + struct work_stuff *work; + const char *mangled; + int n; + const char **anchor, **args; +{ + /* Check if ARM template with "__pt__" in it ("parameterized type") */ + /* Allow HP also here, because HP's cfront compiler follows ARM to some extent */ + if ((ARM_DEMANGLING || HP_DEMANGLING) && (*anchor = strstr (mangled, "__pt__"))) + { + int len; + *args = *anchor + 6; + len = consume_count (args); + if (len == -1) + return 0; + if (*args + len == mangled + n && **args == '_') + { + ++*args; + return 1; + } + } + if (AUTO_DEMANGLING || EDG_DEMANGLING) + { + if ((*anchor = strstr (mangled, "__tm__")) + || (*anchor = strstr (mangled, "__ps__")) + || (*anchor = strstr (mangled, "__pt__"))) + { + int len; + *args = *anchor + 6; + len = consume_count (args); + if (len == -1) + return 0; + if (*args + len == mangled + n && **args == '_') + { + ++*args; + return 1; + } + } + else if ((*anchor = strstr (mangled, "__S"))) + { + int len; + *args = *anchor + 3; + len = consume_count (args); + if (len == -1) + return 0; + if (*args + len == mangled + n && **args == '_') + { + ++*args; + return 1; + } + } + } + + return 0; +} + +static void +demangle_arm_hp_template (work, mangled, n, declp) + struct work_stuff *work; + const char **mangled; + int n; + string *declp; +{ + const char *p; + const char *args; + const char *e = *mangled + n; + string arg; + + /* Check for HP aCC template spec: classXt1t2 where t1, t2 are + template args */ + if (HP_DEMANGLING && ((*mangled)[n] == 'X')) + { + char *start_spec_args = NULL; + + /* First check for and omit template specialization pseudo-arguments, + such as in "Spec<#1,#1.*>" */ + start_spec_args = strchr (*mangled, '<'); + if (start_spec_args && (start_spec_args - *mangled < n)) + string_appendn (declp, *mangled, start_spec_args - *mangled); + else + string_appendn (declp, *mangled, n); + (*mangled) += n + 1; + string_init (&arg); + if (work->temp_start == -1) /* non-recursive call */ + work->temp_start = declp->p - declp->b; + string_append (declp, "<"); + while (1) + { + string_clear (&arg); + switch (**mangled) + { + case 'T': + /* 'T' signals a type parameter */ + (*mangled)++; + if (!do_type (work, mangled, &arg)) + goto hpacc_template_args_done; + break; + + case 'U': + case 'S': + /* 'U' or 'S' signals an integral value */ + if (!do_hpacc_template_const_value (work, mangled, &arg)) + goto hpacc_template_args_done; + break; + + case 'A': + /* 'A' signals a named constant expression (literal) */ + if (!do_hpacc_template_literal (work, mangled, &arg)) + goto hpacc_template_args_done; + break; + + default: + /* Today, 1997-09-03, we have only the above types + of template parameters */ + /* FIXME: maybe this should fail and return null */ + goto hpacc_template_args_done; + } + string_appends (declp, &arg); + /* Check if we're at the end of template args. + 0 if at end of static member of template class, + _ if done with template args for a function */ + if ((**mangled == '\000') || (**mangled == '_')) + break; + else + string_append (declp, ","); + } + hpacc_template_args_done: + string_append (declp, ">"); + string_delete (&arg); + if (**mangled == '_') + (*mangled)++; + return; + } + /* ARM template? (Also handles HP cfront extensions) */ + else if (arm_pt (work, *mangled, n, &p, &args)) + { + string type_str; + + string_init (&arg); + string_appendn (declp, *mangled, p - *mangled); + if (work->temp_start == -1) /* non-recursive call */ + work->temp_start = declp->p - declp->b; + string_append (declp, "<"); + /* should do error checking here */ + while (args < e) { + string_clear (&arg); + + /* Check for type or literal here */ + switch (*args) + { + /* HP cfront extensions to ARM for template args */ + /* spec: Xt1Lv1 where t1 is a type, v1 is a literal value */ + /* FIXME: We handle only numeric literals for HP cfront */ + case 'X': + /* A typed constant value follows */ + args++; + if (!do_type (work, &args, &type_str)) + goto cfront_template_args_done; + string_append (&arg, "("); + string_appends (&arg, &type_str); + string_append (&arg, ")"); + if (*args != 'L') + goto cfront_template_args_done; + args++; + /* Now snarf a literal value following 'L' */ + if (!snarf_numeric_literal (&args, &arg)) + goto cfront_template_args_done; + break; + + case 'L': + /* Snarf a literal following 'L' */ + args++; + if (!snarf_numeric_literal (&args, &arg)) + goto cfront_template_args_done; + break; + default: + /* Not handling other HP cfront stuff */ + if (!do_type (work, &args, &arg)) + goto cfront_template_args_done; + } + string_appends (declp, &arg); + string_append (declp, ","); + } + cfront_template_args_done: + string_delete (&arg); + if (args >= e) + --declp->p; /* remove extra comma */ + string_append (declp, ">"); + } + else if (n>10 && strncmp (*mangled, "_GLOBAL_", 8) == 0 + && (*mangled)[9] == 'N' + && (*mangled)[8] == (*mangled)[10] + && strchr (cplus_markers, (*mangled)[8])) + { + /* A member of the anonymous namespace. */ + string_append (declp, "{anonymous}"); + } + else + { + if (work->temp_start == -1) /* non-recursive call only */ + work->temp_start = 0; /* disable in recursive calls */ + string_appendn (declp, *mangled, n); + } + *mangled += n; +} + +/* Extract a class name, possibly a template with arguments, from the + mangled string; qualifiers, local class indicators, etc. have + already been dealt with */ + +static int +demangle_class_name (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int n; + int success = 0; + + n = consume_count (mangled); + if (n == -1) + return 0; + if ((int) strlen (*mangled) >= n) + { + demangle_arm_hp_template (work, mangled, n, declp); + success = 1; + } + + return (success); +} + +/* + +LOCAL FUNCTION + + demangle_class -- demangle a mangled class sequence + +SYNOPSIS + + static int + demangle_class (struct work_stuff *work, const char **mangled, + strint *declp) + +DESCRIPTION + + DECLP points to the buffer into which demangling is being done. + + *MANGLED points to the current token to be demangled. On input, + it points to a mangled class (I.E. "3foo", "13verylongclass", etc.) + On exit, it points to the next token after the mangled class on + success, or the first unconsumed token on failure. + + If the CONSTRUCTOR or DESTRUCTOR flags are set in WORK, then + we are demangling a constructor or destructor. In this case + we prepend "class::class" or "class::~class" to DECLP. + + Otherwise, we prepend "class::" to the current DECLP. + + Reset the constructor/destructor flags once they have been + "consumed". This allows demangle_class to be called later during + the same demangling, to do normal class demangling. + + Returns 1 if demangling is successful, 0 otherwise. + +*/ + +static int +demangle_class (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int success = 0; + int btype; + string class_name; + char *save_class_name_end = 0; + + string_init (&class_name); + btype = register_Btype (work); + if (demangle_class_name (work, mangled, &class_name)) + { + save_class_name_end = class_name.p; + if ((work->constructor & 1) || (work->destructor & 1)) + { + /* adjust so we don't include template args */ + if (work->temp_start && (work->temp_start != -1)) + { + class_name.p = class_name.b + work->temp_start; + } + string_prepends (declp, &class_name); + if (work -> destructor & 1) + { + string_prepend (declp, "~"); + work -> destructor -= 1; + } + else + { + work -> constructor -= 1; + } + } + class_name.p = save_class_name_end; + remember_Ktype (work, class_name.b, LEN_STRING(&class_name)); + remember_Btype (work, class_name.b, LEN_STRING(&class_name), btype); + string_prepend (declp, SCOPE_STRING (work)); + string_prepends (declp, &class_name); + success = 1; + } + string_delete (&class_name); + return (success); +} + + +/* Called when there's a "__" in the mangled name, with `scan' pointing to + the rightmost guess. + + Find the correct "__"-sequence where the function name ends and the + signature starts, which is ambiguous with GNU mangling. + Call demangle_signature here, so we can make sure we found the right + one; *mangled will be consumed so caller will not make further calls to + demangle_signature. */ + +static int +iterate_demangle_function (work, mangled, declp, scan) + struct work_stuff *work; + const char **mangled; + string *declp; + const char *scan; +{ + const char *mangle_init = *mangled; + int success = 0; + string decl_init; + struct work_stuff work_init; + + if (*(scan + 2) == '\0') + return 0; + + /* Do not iterate for some demangling modes, or if there's only one + "__"-sequence. This is the normal case. */ + if (ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING + || strstr (scan + 2, "__") == NULL) + { + demangle_function_name (work, mangled, declp, scan); + return 1; + } + + /* Save state so we can restart if the guess at the correct "__" was + wrong. */ + string_init (&decl_init); + string_appends (&decl_init, declp); + memset (&work_init, 0, sizeof work_init); + work_stuff_copy_to_from (&work_init, work); + + /* Iterate over occurrences of __, allowing names and types to have a + "__" sequence in them. We must start with the first (not the last) + occurrence, since "__" most often occur between independent mangled + parts, hence starting at the last occurrence inside a signature + might get us a "successful" demangling of the signature. */ + + while (scan[2]) + { + demangle_function_name (work, mangled, declp, scan); + success = demangle_signature (work, mangled, declp); + if (success) + break; + + /* Reset demangle state for the next round. */ + *mangled = mangle_init; + string_clear (declp); + string_appends (declp, &decl_init); + work_stuff_copy_to_from (work, &work_init); + + /* Leave this underscore-sequence. */ + scan += 2; + + /* Scan for the next "__" sequence. */ + while (*scan && (scan[0] != '_' || scan[1] != '_')) + scan++; + + /* Move to last "__" in this sequence. */ + while (*scan && *scan == '_') + scan++; + scan -= 2; + } + + /* Delete saved state. */ + delete_work_stuff (&work_init); + string_delete (&decl_init); + + return success; +} + +/* + +LOCAL FUNCTION + + demangle_prefix -- consume the mangled name prefix and find signature + +SYNOPSIS + + static int + demangle_prefix (struct work_stuff *work, const char **mangled, + string *declp); + +DESCRIPTION + + Consume and demangle the prefix of the mangled name. + While processing the function name root, arrange to call + demangle_signature if the root is ambiguous. + + DECLP points to the string buffer into which demangled output is + placed. On entry, the buffer is empty. On exit it contains + the root function name, the demangled operator name, or in some + special cases either nothing or the completely demangled result. + + MANGLED points to the current pointer into the mangled name. As each + token of the mangled name is consumed, it is updated. Upon entry + the current mangled name pointer points to the first character of + the mangled name. Upon exit, it should point to the first character + of the signature if demangling was successful, or to the first + unconsumed character if demangling of the prefix was unsuccessful. + + Returns 1 on success, 0 otherwise. + */ + +static int +demangle_prefix (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int success = 1; + const char *scan; + int i; + + if (strlen(*mangled) > 6 + && (strncmp(*mangled, "_imp__", 6) == 0 + || strncmp(*mangled, "__imp_", 6) == 0)) + { + /* it's a symbol imported from a PE dynamic library. Check for both + new style prefix _imp__ and legacy __imp_ used by older versions + of dlltool. */ + (*mangled) += 6; + work->dllimported = 1; + } + else if (strlen(*mangled) >= 11 && strncmp(*mangled, "_GLOBAL_", 8) == 0) + { + char *marker = strchr (cplus_markers, (*mangled)[8]); + if (marker != NULL && *marker == (*mangled)[10]) + { + if ((*mangled)[9] == 'D') + { + /* it's a GNU global destructor to be executed at program exit */ + (*mangled) += 11; + work->destructor = 2; + if (gnu_special (work, mangled, declp)) + return success; + } + else if ((*mangled)[9] == 'I') + { + /* it's a GNU global constructor to be executed at program init */ + (*mangled) += 11; + work->constructor = 2; + if (gnu_special (work, mangled, declp)) + return success; + } + } + } + else if ((ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) && strncmp(*mangled, "__std__", 7) == 0) + { + /* it's a ARM global destructor to be executed at program exit */ + (*mangled) += 7; + work->destructor = 2; + } + else if ((ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) && strncmp(*mangled, "__sti__", 7) == 0) + { + /* it's a ARM global constructor to be executed at program initial */ + (*mangled) += 7; + work->constructor = 2; + } + + /* This block of code is a reduction in strength time optimization + of: + scan = strstr (*mangled, "__"); */ + + { + scan = *mangled; + + do { + scan = strchr (scan, '_'); + } while (scan != NULL && *++scan != '_'); + + if (scan != NULL) --scan; + } + + if (scan != NULL) + { + /* We found a sequence of two or more '_', ensure that we start at + the last pair in the sequence. */ + /* i = strspn (scan, "_"); */ + i = 0; + while (scan[i] == '_') i++; + if (i > 2) + { + scan += (i - 2); + } + } + + if (scan == NULL) + { + success = 0; + } + else if (work -> static_type) + { + if (!ISDIGIT ((unsigned char)scan[0]) && (scan[0] != 't')) + { + success = 0; + } + } + else if ((scan == *mangled) + && (ISDIGIT ((unsigned char)scan[2]) || (scan[2] == 'Q') + || (scan[2] == 't') || (scan[2] == 'K') || (scan[2] == 'H'))) + { + /* The ARM says nothing about the mangling of local variables. + But cfront mangles local variables by prepending __ + to them. As an extension to ARM demangling we handle this case. */ + if ((LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING) + && ISDIGIT ((unsigned char)scan[2])) + { + *mangled = scan + 2; + consume_count (mangled); + string_append (declp, *mangled); + *mangled += strlen (*mangled); + success = 1; + } + else + { + /* A GNU style constructor starts with __[0-9Qt]. But cfront uses + names like __Q2_3foo3bar for nested type names. So don't accept + this style of constructor for cfront demangling. A GNU + style member-template constructor starts with 'H'. */ + if (!(LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING)) + work -> constructor += 1; + *mangled = scan + 2; + } + } + else if (ARM_DEMANGLING && scan[2] == 'p' && scan[3] == 't') + { + /* Cfront-style parameterized type. Handled later as a signature. */ + success = 1; + + /* ARM template? */ + demangle_arm_hp_template (work, mangled, strlen (*mangled), declp); + } + else if (EDG_DEMANGLING && ((scan[2] == 't' && scan[3] == 'm') + || (scan[2] == 'p' && scan[3] == 's') + || (scan[2] == 'p' && scan[3] == 't'))) + { + /* EDG-style parameterized type. Handled later as a signature. */ + success = 1; + + /* EDG template? */ + demangle_arm_hp_template (work, mangled, strlen (*mangled), declp); + } + else if ((scan == *mangled) && !ISDIGIT ((unsigned char)scan[2]) + && (scan[2] != 't')) + { + /* Mangled name starts with "__". Skip over any leading '_' characters, + then find the next "__" that separates the prefix from the signature. + */ + if (!(ARM_DEMANGLING || LUCID_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) + || (arm_special (mangled, declp) == 0)) + { + while (*scan == '_') + { + scan++; + } + if ((scan = strstr (scan, "__")) == NULL || (*(scan + 2) == '\0')) + { + /* No separator (I.E. "__not_mangled"), or empty signature + (I.E. "__not_mangled_either__") */ + success = 0; + } + else + return iterate_demangle_function (work, mangled, declp, scan); + } + } + else if (*(scan + 2) != '\0') + { + /* Mangled name does not start with "__" but does have one somewhere + in there with non empty stuff after it. Looks like a global + function name. Iterate over all "__":s until the right + one is found. */ + return iterate_demangle_function (work, mangled, declp, scan); + } + else + { + /* Doesn't look like a mangled name */ + success = 0; + } + + if (!success && (work->constructor == 2 || work->destructor == 2)) + { + string_append (declp, *mangled); + *mangled += strlen (*mangled); + success = 1; + } + return (success); +} + +/* + +LOCAL FUNCTION + + gnu_special -- special handling of gnu mangled strings + +SYNOPSIS + + static int + gnu_special (struct work_stuff *work, const char **mangled, + string *declp); + + +DESCRIPTION + + Process some special GNU style mangling forms that don't fit + the normal pattern. For example: + + _$_3foo (destructor for class foo) + _vt$foo (foo virtual table) + _vt$foo$bar (foo::bar virtual table) + __vt_foo (foo virtual table, new style with thunks) + _3foo$varname (static data member) + _Q22rs2tu$vw (static data member) + __t6vector1Zii (constructor with template) + __thunk_4__$_7ostream (virtual function thunk) + */ + +static int +gnu_special (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + int n; + int success = 1; + const char *p; + + if ((*mangled)[0] == '_' + && strchr (cplus_markers, (*mangled)[1]) != NULL + && (*mangled)[2] == '_') + { + /* Found a GNU style destructor, get past "__" */ + (*mangled) += 3; + work -> destructor += 1; + } + else if ((*mangled)[0] == '_' + && (((*mangled)[1] == '_' + && (*mangled)[2] == 'v' + && (*mangled)[3] == 't' + && (*mangled)[4] == '_') + || ((*mangled)[1] == 'v' + && (*mangled)[2] == 't' + && strchr (cplus_markers, (*mangled)[3]) != NULL))) + { + /* Found a GNU style virtual table, get past "_vt" + and create the decl. Note that we consume the entire mangled + input string, which means that demangle_signature has no work + to do. */ + if ((*mangled)[2] == 'v') + (*mangled) += 5; /* New style, with thunks: "__vt_" */ + else + (*mangled) += 4; /* Old style, no thunks: "_vt" */ + while (**mangled != '\0') + { + switch (**mangled) + { + case 'Q': + case 'K': + success = demangle_qualified (work, mangled, declp, 0, 1); + break; + case 't': + success = demangle_template (work, mangled, declp, 0, 1, + 1); + break; + default: + if (ISDIGIT((unsigned char)*mangled[0])) + { + n = consume_count(mangled); + /* We may be seeing a too-large size, or else a + "." indicating a static local symbol. In + any case, declare victory and move on; *don't* try + to use n to allocate. */ + if (n > (int) strlen (*mangled)) + { + success = 1; + break; + } + } + else + { + /*n = strcspn (*mangled, cplus_markers);*/ + const char *check = *mangled; + n = 0; + while (*check) + if (strchr (cplus_markers, *check++) == NULL) + n++; + else + break; + } + string_appendn (declp, *mangled, n); + (*mangled) += n; + } + + p = strpbrk (*mangled, cplus_markers); + if (success && ((p == NULL) || (p == *mangled))) + { + if (p != NULL) + { + string_append (declp, SCOPE_STRING (work)); + (*mangled)++; + } + } + else + { + success = 0; + break; + } + } + if (success) + string_append (declp, " virtual table"); + } + else if ((*mangled)[0] == '_' + && (strchr("0123456789Qt", (*mangled)[1]) != NULL) + && (p = strpbrk (*mangled, cplus_markers)) != NULL) + { + /* static data member, "_3foo$varname" for example */ + (*mangled)++; + switch (**mangled) + { + case 'Q': + case 'K': + success = demangle_qualified (work, mangled, declp, 0, 1); + break; + case 't': + success = demangle_template (work, mangled, declp, 0, 1, 1); + break; + default: + n = consume_count (mangled); + if (n < 0 || n > (long) strlen (*mangled)) + { + success = 0; + break; + } + + if (n > 10 && strncmp (*mangled, "_GLOBAL_", 8) == 0 + && (*mangled)[9] == 'N' + && (*mangled)[8] == (*mangled)[10] + && strchr (cplus_markers, (*mangled)[8])) + { + /* A member of the anonymous namespace. There's information + about what identifier or filename it was keyed to, but + it's just there to make the mangled name unique; we just + step over it. */ + string_append (declp, "{anonymous}"); + (*mangled) += n; + + /* Now p points to the marker before the N, so we need to + update it to the first marker after what we consumed. */ + p = strpbrk (*mangled, cplus_markers); + break; + } + + string_appendn (declp, *mangled, n); + (*mangled) += n; + } + if (success && (p == *mangled)) + { + /* Consumed everything up to the cplus_marker, append the + variable name. */ + (*mangled)++; + string_append (declp, SCOPE_STRING (work)); + n = strlen (*mangled); + string_appendn (declp, *mangled, n); + (*mangled) += n; + } + else + { + success = 0; + } + } + else if (strncmp (*mangled, "__thunk_", 8) == 0) + { + int delta; + + (*mangled) += 8; + delta = consume_count (mangled); + if (delta == -1) + success = 0; + else + { + char *method = internal_cplus_demangle (work, ++*mangled); + + if (method) + { + char buf[50]; + sprintf (buf, "virtual function thunk (delta:%d) for ", -delta); + string_append (declp, buf); + string_append (declp, method); + free (method); + n = strlen (*mangled); + (*mangled) += n; + } + else + { + success = 0; + } + } + } + else if (strncmp (*mangled, "__t", 3) == 0 + && ((*mangled)[3] == 'i' || (*mangled)[3] == 'f')) + { + p = (*mangled)[3] == 'i' ? " type_info node" : " type_info function"; + (*mangled) += 4; + switch (**mangled) + { + case 'Q': + case 'K': + success = demangle_qualified (work, mangled, declp, 0, 1); + break; + case 't': + success = demangle_template (work, mangled, declp, 0, 1, 1); + break; + default: + success = do_type (work, mangled, declp); + break; + } + if (success && **mangled != '\0') + success = 0; + if (success) + string_append (declp, p); + } + else + { + success = 0; + } + return (success); +} + +static void +recursively_demangle(work, mangled, result, namelength) + struct work_stuff *work; + const char **mangled; + string *result; + int namelength; +{ + char * recurse = (char *)NULL; + char * recurse_dem = (char *)NULL; + + recurse = (char *) xmalloc (namelength + 1); + memcpy (recurse, *mangled, namelength); + recurse[namelength] = '\000'; + + recurse_dem = VG_(cplus_demangle) (recurse, work->options); + + if (recurse_dem) + { + string_append (result, recurse_dem); + free (recurse_dem); + } + else + { + string_appendn (result, *mangled, namelength); + } + free (recurse); + *mangled += namelength; +} + +/* + +LOCAL FUNCTION + + arm_special -- special handling of ARM/lucid mangled strings + +SYNOPSIS + + static int + arm_special (const char **mangled, + string *declp); + + +DESCRIPTION + + Process some special ARM style mangling forms that don't fit + the normal pattern. For example: + + __vtbl__3foo (foo virtual table) + __vtbl__3foo__3bar (bar::foo virtual table) + + */ + +static int +arm_special (mangled, declp) + const char **mangled; + string *declp; +{ + int n; + int success = 1; + const char *scan; + + if (strncmp (*mangled, ARM_VTABLE_STRING, ARM_VTABLE_STRLEN) == 0) + { + /* Found a ARM style virtual table, get past ARM_VTABLE_STRING + and create the decl. Note that we consume the entire mangled + input string, which means that demangle_signature has no work + to do. */ + scan = *mangled + ARM_VTABLE_STRLEN; + while (*scan != '\0') /* first check it can be demangled */ + { + n = consume_count (&scan); + if (n == -1) + { + return (0); /* no good */ + } + scan += n; + if (scan[0] == '_' && scan[1] == '_') + { + scan += 2; + } + } + (*mangled) += ARM_VTABLE_STRLEN; + while (**mangled != '\0') + { + n = consume_count (mangled); + if (n == -1 + || n > (long) strlen (*mangled)) + return 0; + string_prependn (declp, *mangled, n); + (*mangled) += n; + if ((*mangled)[0] == '_' && (*mangled)[1] == '_') + { + string_prepend (declp, "::"); + (*mangled) += 2; + } + } + string_append (declp, " virtual table"); + } + else + { + success = 0; + } + return (success); +} + +/* + +LOCAL FUNCTION + + demangle_qualified -- demangle 'Q' qualified name strings + +SYNOPSIS + + static int + demangle_qualified (struct work_stuff *, const char *mangled, + string *result, int isfuncname, int append); + +DESCRIPTION + + Demangle a qualified name, such as "Q25Outer5Inner" which is + the mangled form of "Outer::Inner". The demangled output is + prepended or appended to the result string according to the + state of the append flag. + + If isfuncname is nonzero, then the qualified name we are building + is going to be used as a member function name, so if it is a + constructor or destructor function, append an appropriate + constructor or destructor name. I.E. for the above example, + the result for use as a constructor is "Outer::Inner::Inner" + and the result for use as a destructor is "Outer::Inner::~Inner". + +BUGS + + Numeric conversion is ASCII dependent (FIXME). + + */ + +static int +demangle_qualified (work, mangled, result, isfuncname, append) + struct work_stuff *work; + const char **mangled; + string *result; + int isfuncname; + int append; +{ + int qualifiers = 0; + int success = 1; + string temp; + string last_name; + int bindex = register_Btype (work); + + /* We only make use of ISFUNCNAME if the entity is a constructor or + destructor. */ + isfuncname = (isfuncname + && ((work->constructor & 1) || (work->destructor & 1))); + + string_init (&temp); + string_init (&last_name); + + if ((*mangled)[0] == 'K') + { + /* Squangling qualified name reuse */ + int idx; + (*mangled)++; + idx = consume_count_with_underscores (mangled); + if (idx == -1 || idx >= work -> numk) + success = 0; + else + string_append (&temp, work -> ktypevec[idx]); + } + else + switch ((*mangled)[1]) + { + case '_': + /* GNU mangled name with more than 9 classes. The count is preceded + by an underscore (to distinguish it from the <= 9 case) and followed + by an underscore. */ + (*mangled)++; + qualifiers = consume_count_with_underscores (mangled); + if (qualifiers == -1) + success = 0; + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* The count is in a single digit. */ + qualifiers = (*mangled)[1] - '0'; + + /* If there is an underscore after the digit, skip it. This is + said to be for ARM-qualified names, but the ARM makes no + mention of such an underscore. Perhaps cfront uses one. */ + if ((*mangled)[2] == '_') + { + (*mangled)++; + } + (*mangled) += 2; + break; + + case '0': + default: + success = 0; + } + + if (!success) + { + string_delete (&last_name); + string_delete (&temp); + return success; + } + + /* Pick off the names and collect them in the temp buffer in the order + in which they are found, separated by '::'. */ + + while (qualifiers-- > 0) + { + int remember_K = 1; + string_clear (&last_name); + + if (*mangled[0] == '_') + (*mangled)++; + + if (*mangled[0] == 't') + { + /* Here we always append to TEMP since we will want to use + the template name without the template parameters as a + constructor or destructor name. The appropriate + (parameter-less) value is returned by demangle_template + in LAST_NAME. We do not remember the template type here, + in order to match the G++ mangling algorithm. */ + success = demangle_template(work, mangled, &temp, + &last_name, 1, 0); + if (!success) + break; + } + else if (*mangled[0] == 'K') + { + int idx; + (*mangled)++; + idx = consume_count_with_underscores (mangled); + if (idx == -1 || idx >= work->numk) + success = 0; + else + string_append (&temp, work->ktypevec[idx]); + remember_K = 0; + + if (!success) break; + } + else + { + if (EDG_DEMANGLING) + { + int namelength; + /* Now recursively demangle the qualifier + * This is necessary to deal with templates in + * mangling styles like EDG */ + namelength = consume_count (mangled); + if (namelength == -1) + { + success = 0; + break; + } + recursively_demangle(work, mangled, &temp, namelength); + } + else + { + string temp_last_name; + string_init (&temp_last_name); + success = do_type (work, mangled, &temp_last_name); + if (!success) + { + string_delete (&temp_last_name); + break; + } + string_appends (&temp, &temp_last_name); + string_appends (&last_name, &temp_last_name); + string_delete (&temp_last_name); + } + } + + if (remember_K) + remember_Ktype (work, temp.b, LEN_STRING (&temp)); + + if (qualifiers > 0) + string_append (&temp, SCOPE_STRING (work)); + } + + remember_Btype (work, temp.b, LEN_STRING (&temp), bindex); + + /* If we are using the result as a function name, we need to append + the appropriate '::' separated constructor or destructor name. + We do this here because this is the most convenient place, where + we already have a pointer to the name and the length of the name. */ + + if (isfuncname) + { + string_append (&temp, SCOPE_STRING (work)); + if (work -> destructor & 1) + string_append (&temp, "~"); + string_appends (&temp, &last_name); + } + + /* Now either prepend the temp buffer to the result, or append it, + depending upon the state of the append flag. */ + + if (append) + string_appends (result, &temp); + else + { + if (!STRING_EMPTY (result)) + string_append (&temp, SCOPE_STRING (work)); + string_prepends (result, &temp); + } + + string_delete (&last_name); + string_delete (&temp); + return (success); +} + +/* + +LOCAL FUNCTION + + get_count -- convert an ascii count to integer, consuming tokens + +SYNOPSIS + + static int + get_count (const char **type, int *count) + +DESCRIPTION + + Assume that *type points at a count in a mangled name; set + *count to its value, and set *type to the next character after + the count. There are some weird rules in effect here. + + If *type does not point at a string of digits, return zero. + + If *type points at a string of digits followed by an + underscore, set *count to their value as an integer, advance + *type to point *after the underscore, and return 1. + + If *type points at a string of digits not followed by an + underscore, consume only the first digit. Set *count to its + value as an integer, leave *type pointing after that digit, + and return 1. + + The excuse for this odd behavior: in the ARM and HP demangling + styles, a type can be followed by a repeat count of the form + `Nxy', where: + + `x' is a single digit specifying how many additional copies + of the type to append to the argument list, and + + `y' is one or more digits, specifying the zero-based index of + the first repeated argument in the list. Yes, as you're + unmangling the name you can figure this out yourself, but + it's there anyway. + + So, for example, in `bar__3fooFPiN51', the first argument is a + pointer to an integer (`Pi'), and then the next five arguments + are the same (`N5'), and the first repeat is the function's + second argument (`1'). +*/ + +static int +get_count (type, count) + const char **type; + int *count; +{ + const char *p; + int n; + + if (!ISDIGIT ((unsigned char)**type)) + return (0); + else + { + *count = **type - '0'; + (*type)++; + if (ISDIGIT ((unsigned char)**type)) + { + p = *type; + n = *count; + do + { + n *= 10; + n += *p - '0'; + p++; + } + while (ISDIGIT ((unsigned char)*p)); + if (*p == '_') + { + *type = p + 1; + *count = n; + } + } + } + return (1); +} + +/* RESULT will be initialised here; it will be freed on failure. The + value returned is really a type_kind_t. */ + +static int +do_type (work, mangled, result) + struct work_stuff *work; + const char **mangled; + string *result; +{ + int n; + int done; + int success; + string decl; + const char *remembered_type; + int type_quals; + string btype; + type_kind_t tk = tk_none; + + string_init (&btype); + string_init (&decl); + string_init (result); + + done = 0; + success = 1; + while (success && !done) + { + int member; + switch (**mangled) + { + + /* A pointer type */ + case 'P': + case 'p': + (*mangled)++; + if (! (work -> options & DMGL_JAVA)) + string_prepend (&decl, "*"); + if (tk == tk_none) + tk = tk_pointer; + break; + + /* A reference type */ + case 'R': + (*mangled)++; + string_prepend (&decl, "&"); + if (tk == tk_none) + tk = tk_reference; + break; + + /* An array */ + case 'A': + { + ++(*mangled); + if (!STRING_EMPTY (&decl) + && (decl.b[0] == '*' || decl.b[0] == '&')) + { + string_prepend (&decl, "("); + string_append (&decl, ")"); + } + string_append (&decl, "["); + if (**mangled != '_') + success = demangle_template_value_parm (work, mangled, &decl, + tk_integral); + if (**mangled == '_') + ++(*mangled); + string_append (&decl, "]"); + break; + } + + /* A back reference to a previously seen type */ + case 'T': + (*mangled)++; + if (!get_count (mangled, &n) || n >= work -> ntypes) + { + success = 0; + } + else + { + remembered_type = work -> typevec[n]; + mangled = &remembered_type; + } + break; + + /* A function */ + case 'F': + (*mangled)++; + if (!STRING_EMPTY (&decl) + && (decl.b[0] == '*' || decl.b[0] == '&')) + { + string_prepend (&decl, "("); + string_append (&decl, ")"); + } + /* After picking off the function args, we expect to either find the + function return type (preceded by an '_') or the end of the + string. */ + if (!demangle_nested_args (work, mangled, &decl) + || (**mangled != '_' && **mangled != '\0')) + { + success = 0; + break; + } + if (success && (**mangled == '_')) + (*mangled)++; + break; + + case 'M': + case 'O': + { + type_quals = TYPE_UNQUALIFIED; + + member = **mangled == 'M'; + (*mangled)++; + + string_append (&decl, ")"); + + /* We don't need to prepend `::' for a qualified name; + demangle_qualified will do that for us. */ + if (**mangled != 'Q') + string_prepend (&decl, SCOPE_STRING (work)); + + if (ISDIGIT ((unsigned char)**mangled)) + { + n = consume_count (mangled); + if (n == -1 + || (int) strlen (*mangled) < n) + { + success = 0; + break; + } + string_prependn (&decl, *mangled, n); + *mangled += n; + } + else if (**mangled == 'X' || **mangled == 'Y') + { + string temp; + do_type (work, mangled, &temp); + string_prepends (&decl, &temp); + } + else if (**mangled == 't') + { + string temp; + string_init (&temp); + success = demangle_template (work, mangled, &temp, + NULL, 1, 1); + if (success) + { + string_prependn (&decl, temp.b, temp.p - temp.b); + string_clear (&temp); + } + else + break; + } + else if (**mangled == 'Q') + { + success = demangle_qualified (work, mangled, &decl, + /*isfuncnam=*/0, + /*append=*/0); + if (!success) + break; + } + else + { + success = 0; + break; + } + + string_prepend (&decl, "("); + if (member) + { + switch (**mangled) + { + case 'C': + case 'V': + case 'u': + type_quals |= code_for_qualifier (**mangled); + (*mangled)++; + break; + + default: + break; + } + + if (*(*mangled)++ != 'F') + { + success = 0; + break; + } + } + if ((member && !demangle_nested_args (work, mangled, &decl)) + || **mangled != '_') + { + success = 0; + break; + } + (*mangled)++; + if (! PRINT_ANSI_QUALIFIERS) + { + break; + } + if (type_quals != TYPE_UNQUALIFIED) + { + APPEND_BLANK (&decl); + string_append (&decl, qualifier_string (type_quals)); + } + break; + } + case 'G': + (*mangled)++; + break; + + case 'C': + case 'V': + case 'u': + if (PRINT_ANSI_QUALIFIERS) + { + if (!STRING_EMPTY (&decl)) + string_prepend (&decl, " "); + + string_prepend (&decl, demangle_qualifier (**mangled)); + } + (*mangled)++; + break; + /* + } + */ + + /* fall through */ + default: + done = 1; + break; + } + } + + if (success) switch (**mangled) + { + /* A qualified name, such as "Outer::Inner". */ + case 'Q': + case 'K': + { + success = demangle_qualified (work, mangled, result, 0, 1); + break; + } + + /* A back reference to a previously seen squangled type */ + case 'B': + (*mangled)++; + if (!get_count (mangled, &n) || n >= work -> numb) + success = 0; + else + string_append (result, work->btypevec[n]); + break; + + case 'X': + case 'Y': + /* A template parm. We substitute the corresponding argument. */ + { + int idx; + + (*mangled)++; + idx = consume_count_with_underscores (mangled); + + if (idx == -1 + || (work->tmpl_argvec && idx >= work->ntmpl_args) + || consume_count_with_underscores (mangled) == -1) + { + success = 0; + break; + } + + if (work->tmpl_argvec) + string_append (result, work->tmpl_argvec[idx]); + else + string_append_template_idx (result, idx); + + success = 1; + } + break; + + default: + success = demangle_fund_type (work, mangled, result); + if (tk == tk_none) + tk = (type_kind_t) success; + break; + } + + if (success) + { + if (!STRING_EMPTY (&decl)) + { + string_append (result, " "); + string_appends (result, &decl); + } + } + else + string_delete (result); + string_delete (&decl); + + if (success) + /* Assume an integral type, if we're not sure. */ + return (int) ((tk == tk_none) ? tk_integral : tk); + else + return 0; +} + +/* Given a pointer to a type string that represents a fundamental type + argument (int, long, unsigned int, etc) in TYPE, a pointer to the + string in which the demangled output is being built in RESULT, and + the WORK structure, decode the types and add them to the result. + + For example: + + "Ci" => "const int" + "Sl" => "signed long" + "CUs" => "const unsigned short" + + The value returned is really a type_kind_t. */ + +static int +demangle_fund_type (work, mangled, result) + struct work_stuff *work; + const char **mangled; + string *result; +{ + int done = 0; + int success = 1; + char buf[10]; + unsigned int dec = 0; + string btype; + type_kind_t tk = tk_integral; + + string_init (&btype); + + /* First pick off any type qualifiers. There can be more than one. */ + + while (!done) + { + switch (**mangled) + { + case 'C': + case 'V': + case 'u': + if (PRINT_ANSI_QUALIFIERS) + { + if (!STRING_EMPTY (result)) + string_prepend (result, " "); + string_prepend (result, demangle_qualifier (**mangled)); + } + (*mangled)++; + break; + case 'U': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "unsigned"); + break; + case 'S': /* signed char only */ + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "signed"); + break; + case 'J': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "__complex"); + break; + default: + done = 1; + break; + } + } + + /* Now pick off the fundamental type. There can be only one. */ + + switch (**mangled) + { + case '\0': + case '_': + break; + case 'v': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "void"); + break; + case 'x': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "long long"); + break; + case 'l': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "long"); + break; + case 'i': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "int"); + break; + case 's': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "short"); + break; + case 'b': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "bool"); + tk = tk_bool; + break; + case 'c': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "char"); + tk = tk_char; + break; + case 'w': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "wchar_t"); + tk = tk_char; + break; + case 'r': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "long double"); + tk = tk_real; + break; + case 'd': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "double"); + tk = tk_real; + break; + case 'f': + (*mangled)++; + APPEND_BLANK (result); + string_append (result, "float"); + tk = tk_real; + break; + case 'G': + (*mangled)++; + if (!ISDIGIT ((unsigned char)**mangled)) + { + success = 0; + break; + } + case 'I': + (*mangled)++; + if (**mangled == '_') + { + int i; + (*mangled)++; + for (i = 0; + i < (long) sizeof (buf) - 1 && **mangled && **mangled != '_'; + (*mangled)++, i++) + buf[i] = **mangled; + if (**mangled != '_') + { + success = 0; + break; + } + buf[i] = '\0'; + (*mangled)++; + } + else + { + strncpy (buf, *mangled, 2); + buf[2] = '\0'; + *mangled += min (strlen (*mangled), 2); + } + /*sscanf (buf, "%x", &dec); + sprintf (buf, "int%u_t", dec);*/ + sprintf (buf, "i_xx_t"); + APPEND_BLANK (result); + string_append (result, buf); + break; + + /* fall through */ + /* An explicit type, such as "6mytype" or "7integer" */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + int bindex = register_Btype (work); + string loc_btype; + string_init (&loc_btype); + if (demangle_class_name (work, mangled, &loc_btype)) { + remember_Btype (work, loc_btype.b, LEN_STRING (&loc_btype), bindex); + APPEND_BLANK (result); + string_appends (result, &loc_btype); + } + else + success = 0; + string_delete (&loc_btype); + break; + } + case 't': + { + success = demangle_template (work, mangled, &btype, 0, 1, 1); + string_appends (result, &btype); + break; + } + default: + success = 0; + break; + } + + string_delete (&btype); + + return success ? ((int) tk) : 0; +} + + +/* Handle a template's value parameter for HP aCC (extension from ARM) + **mangled points to 'S' or 'U' */ + +static int +do_hpacc_template_const_value (work, mangled, result) + struct work_stuff *work ATTRIBUTE_UNUSED; + const char **mangled; + string *result; +{ + int unsigned_const; + + if (**mangled != 'U' && **mangled != 'S') + return 0; + + unsigned_const = (**mangled == 'U'); + + (*mangled)++; + + switch (**mangled) + { + case 'N': + string_append (result, "-"); + /* fall through */ + case 'P': + (*mangled)++; + break; + case 'M': + /* special case for -2^31 */ + string_append (result, "-2147483648"); + (*mangled)++; + return 1; + default: + return 0; + } + + /* We have to be looking at an integer now */ + if (!(ISDIGIT ((unsigned char)**mangled))) + return 0; + + /* We only deal with integral values for template + parameters -- so it's OK to look only for digits */ + while (ISDIGIT ((unsigned char)**mangled)) + { + char_str[0] = **mangled; + string_append (result, char_str); + (*mangled)++; + } + + if (unsigned_const) + string_append (result, "U"); + + /* FIXME? Some day we may have 64-bit (or larger :-) ) constants + with L or LL suffixes. pai/1997-09-03 */ + + return 1; /* success */ +} + +/* Handle a template's literal parameter for HP aCC (extension from ARM) + **mangled is pointing to the 'A' */ + +static int +do_hpacc_template_literal (work, mangled, result) + struct work_stuff *work; + const char **mangled; + string *result; +{ + int literal_len = 0; + char * recurse; + char * recurse_dem; + + if (**mangled != 'A') + return 0; + + (*mangled)++; + + literal_len = consume_count (mangled); + + if (literal_len <= 0) + return 0; + + /* Literal parameters are names of arrays, functions, etc. and the + canonical representation uses the address operator */ + string_append (result, "&"); + + /* Now recursively demangle the literal name */ + recurse = (char *) xmalloc (literal_len + 1); + memcpy (recurse, *mangled, literal_len); + recurse[literal_len] = '\000'; + + recurse_dem = VG_(cplus_demangle) (recurse, work->options); + + if (recurse_dem) + { + string_append (result, recurse_dem); + free (recurse_dem); + } + else + { + string_appendn (result, *mangled, literal_len); + } + (*mangled) += literal_len; + free (recurse); + + return 1; +} + +static int +snarf_numeric_literal (args, arg) + const char ** args; + string * arg; +{ + if (**args == '-') + { + char_str[0] = '-'; + string_append (arg, char_str); + (*args)++; + } + else if (**args == '+') + (*args)++; + + if (!ISDIGIT ((unsigned char)**args)) + return 0; + + while (ISDIGIT ((unsigned char)**args)) + { + char_str[0] = **args; + string_append (arg, char_str); + (*args)++; + } + + return 1; +} + +/* Demangle the next argument, given by MANGLED into RESULT, which + *should be an uninitialized* string. It will be initialized here, + and free'd should anything go wrong. */ + +static int +do_arg (work, mangled, result) + struct work_stuff *work; + const char **mangled; + string *result; +{ + /* Remember where we started so that we can record the type, for + non-squangling type remembering. */ + const char *start = *mangled; + string temp_result; + + string_init (result); + string_init (&temp_result); + + if (work->nrepeats > 0) + { + --work->nrepeats; + + if (work->previous_argument == 0) + return 0; + + /* We want to reissue the previous type in this argument list. */ + string_appends (result, work->previous_argument); + return 1; + } + + if (**mangled == 'n') + { + /* A squangling-style repeat. */ + (*mangled)++; + work->nrepeats = consume_count(mangled); + + if (work->nrepeats <= 0) + /* This was not a repeat count after all. */ + return 0; + + if (work->nrepeats > 9) + { + if (**mangled != '_') + /* The repeat count should be followed by an '_' in this + case. */ + return 0; + else + (*mangled)++; + } + + /* Now, the repeat is all set up. */ + return do_arg (work, mangled, result); + } + + /* Save the result in WORK->previous_argument so that we can find it + if it's repeated. Note that saving START is not good enough: we + do not want to add additional types to the back-referenceable + type vector when processing a repeated type. */ + if (work->previous_argument) + string_clear (work->previous_argument); + else + { + work->previous_argument = (string*) xmalloc (sizeof (string)); + string_init (work->previous_argument); + } + + if (!do_type (work, mangled, &temp_result)) + { + string_delete (&temp_result); + return 0; + } + string_appends (work->previous_argument, &temp_result); + string_delete (&temp_result); + + string_appends (result, work->previous_argument); + + remember_type (work, start, *mangled - start); + return 1; +} + +static void +remember_type (work, start, len) + struct work_stuff *work; + const char *start; + int len; +{ + char *tem; + + if (work->forgetting_types) + return; + + if (work -> ntypes >= work -> typevec_size) + { + if (work -> typevec_size == 0) + { + work -> typevec_size = 3; + work -> typevec + = (char **) xmalloc (sizeof (char *) * work -> typevec_size); + } + else + { + work -> typevec_size *= 2; + work -> typevec + = (char **) xrealloc ((char *)work -> typevec, + sizeof (char *) * work -> typevec_size); + } + } + tem = xmalloc (len + 1); + memcpy (tem, start, len); + tem[len] = '\0'; + work -> typevec[work -> ntypes++] = tem; +} + + +/* Remember a K type class qualifier. */ +static void +remember_Ktype (work, start, len) + struct work_stuff *work; + const char *start; + int len; +{ + char *tem; + + if (work -> numk >= work -> ksize) + { + if (work -> ksize == 0) + { + work -> ksize = 5; + work -> ktypevec + = (char **) xmalloc (sizeof (char *) * work -> ksize); + } + else + { + work -> ksize *= 2; + work -> ktypevec + = (char **) xrealloc ((char *)work -> ktypevec, + sizeof (char *) * work -> ksize); + } + } + tem = xmalloc (len + 1); + memcpy (tem, start, len); + tem[len] = '\0'; + work -> ktypevec[work -> numk++] = tem; +} + +/* Register a B code, and get an index for it. B codes are registered + as they are seen, rather than as they are completed, so map > + registers map > as B0, and temp as B1 */ + +static int +register_Btype (work) + struct work_stuff *work; +{ + int ret; + + if (work -> numb >= work -> bsize) + { + if (work -> bsize == 0) + { + work -> bsize = 5; + work -> btypevec + = (char **) xmalloc (sizeof (char *) * work -> bsize); + } + else + { + work -> bsize *= 2; + work -> btypevec + = (char **) xrealloc ((char *)work -> btypevec, + sizeof (char *) * work -> bsize); + } + } + ret = work -> numb++; + work -> btypevec[ret] = NULL; + return(ret); +} + +/* Store a value into a previously registered B code type. */ + +static void +remember_Btype (work, start, len, ind) + struct work_stuff *work; + const char *start; + int len, ind; +{ + char *tem; + + tem = xmalloc (len + 1); + memcpy (tem, start, len); + tem[len] = '\0'; + work -> btypevec[ind] = tem; +} + +/* Lose all the info related to B and K type codes. */ +static void +forget_B_and_K_types (work) + struct work_stuff *work; +{ + int i; + + while (work -> numk > 0) + { + i = --(work -> numk); + if (work -> ktypevec[i] != NULL) + { + free (work -> ktypevec[i]); + work -> ktypevec[i] = NULL; + } + } + + while (work -> numb > 0) + { + i = --(work -> numb); + if (work -> btypevec[i] != NULL) + { + free (work -> btypevec[i]); + work -> btypevec[i] = NULL; + } + } +} +/* Forget the remembered types, but not the type vector itself. */ + +static void +forget_types (work) + struct work_stuff *work; +{ + int i; + + while (work -> ntypes > 0) + { + i = --(work -> ntypes); + if (work -> typevec[i] != NULL) + { + free (work -> typevec[i]); + work -> typevec[i] = NULL; + } + } +} + +/* Process the argument list part of the signature, after any class spec + has been consumed, as well as the first 'F' character (if any). For + example: + + "__als__3fooRT0" => process "RT0" + "complexfunc5__FPFPc_PFl_i" => process "PFPc_PFl_i" + + DECLP must be already initialised, usually non-empty. It won't be freed + on failure. + + Note that g++ differs significantly from ARM and lucid style mangling + with regards to references to previously seen types. For example, given + the source fragment: + + class foo { + public: + foo::foo (int, foo &ia, int, foo &ib, int, foo &ic); + }; + + foo::foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; } + void foo (int, foo &ia, int, foo &ib, int, foo &ic) { ia = ib = ic; } + + g++ produces the names: + + __3fooiRT0iT2iT2 + foo__FiR3fooiT1iT1 + + while lcc (and presumably other ARM style compilers as well) produces: + + foo__FiR3fooT1T2T1T2 + __ct__3fooFiR3fooT1T2T1T2 + + Note that g++ bases its type numbers starting at zero and counts all + previously seen types, while lucid/ARM bases its type numbers starting + at one and only considers types after it has seen the 'F' character + indicating the start of the function args. For lucid/ARM style, we + account for this difference by discarding any previously seen types when + we see the 'F' character, and subtracting one from the type number + reference. + + */ + +static int +demangle_args (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + string arg; + int need_comma = 0; + int r; + int t; + const char *tem; + char temptype; + + if (PRINT_ARG_TYPES) + { + string_append (declp, "("); + if (**mangled == '\0') + { + string_append (declp, "void"); + } + } + + while ((**mangled != '_' && **mangled != '\0' && **mangled != 'e') + || work->nrepeats > 0) + { + if ((**mangled == 'N') || (**mangled == 'T')) + { + temptype = *(*mangled)++; + + if (temptype == 'N') + { + if (!get_count (mangled, &r)) + { + return (0); + } + } + else + { + r = 1; + } + if ((HP_DEMANGLING || ARM_DEMANGLING || EDG_DEMANGLING) && work -> ntypes >= 10) + { + /* If we have 10 or more types we might have more than a 1 digit + index so we'll have to consume the whole count here. This + will lose if the next thing is a type name preceded by a + count but it's impossible to demangle that case properly + anyway. Eg if we already have 12 types is T12Pc "(..., type1, + Pc, ...)" or "(..., type12, char *, ...)" */ + if ((t = consume_count(mangled)) <= 0) + { + return (0); + } + } + else + { + if (!get_count (mangled, &t)) + { + return (0); + } + } + if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) + { + t--; + } + /* Validate the type index. Protect against illegal indices from + malformed type strings. */ + if ((t < 0) || (t >= work -> ntypes)) + { + return (0); + } + while (work->nrepeats > 0 || --r >= 0) + { + tem = work -> typevec[t]; + if (need_comma && PRINT_ARG_TYPES) + { + string_append (declp, ", "); + } + if (!do_arg (work, &tem, &arg)) + { + return (0); + } + if (PRINT_ARG_TYPES) + { + string_appends (declp, &arg); + } + string_delete (&arg); + need_comma = 1; + } + } + else + { + if (need_comma && PRINT_ARG_TYPES) + string_append (declp, ", "); + if (!do_arg (work, mangled, &arg)) + { + string_delete (&arg); + return (0); + } + if (PRINT_ARG_TYPES) + string_appends (declp, &arg); + string_delete (&arg); + need_comma = 1; + } + } + + if (**mangled == 'e') + { + (*mangled)++; + if (PRINT_ARG_TYPES) + { + if (need_comma) + { + string_append (declp, ","); + } + string_append (declp, "..."); + } + } + + if (PRINT_ARG_TYPES) + { + string_append (declp, ")"); + } + return (1); +} + +/* Like demangle_args, but for demangling the argument lists of function + and method pointers or references, not top-level declarations. */ + +static int +demangle_nested_args (work, mangled, declp) + struct work_stuff *work; + const char **mangled; + string *declp; +{ + string* saved_previous_argument; + int result; + int saved_nrepeats; + + /* The G++ name-mangling algorithm does not remember types on nested + argument lists, unless -fsquangling is used, and in that case the + type vector updated by remember_type is not used. So, we turn + off remembering of types here. */ + ++work->forgetting_types; + + /* For the repeat codes used with -fsquangling, we must keep track of + the last argument. */ + saved_previous_argument = work->previous_argument; + saved_nrepeats = work->nrepeats; + work->previous_argument = 0; + work->nrepeats = 0; + + /* Actually demangle the arguments. */ + result = demangle_args (work, mangled, declp); + + /* Restore the previous_argument field. */ + if (work->previous_argument) + { + string_delete (work->previous_argument); + free ((char*) work->previous_argument); + } + work->previous_argument = saved_previous_argument; + --work->forgetting_types; + work->nrepeats = saved_nrepeats; + + return result; +} + +static void +demangle_function_name (work, mangled, declp, scan) + struct work_stuff *work; + const char **mangled; + string *declp; + const char *scan; +{ + size_t i; + string type; + const char *tem; + + string_appendn (declp, (*mangled), scan - (*mangled)); + string_need (declp, 1); + *(declp -> p) = '\0'; + + /* Consume the function name, including the "__" separating the name + from the signature. We are guaranteed that SCAN points to the + separator. */ + + (*mangled) = scan + 2; + /* We may be looking at an instantiation of a template function: + foo__Xt1t2_Ft3t4, where t1, t2, ... are template arguments and a + following _F marks the start of the function arguments. Handle + the template arguments first. */ + + if (HP_DEMANGLING && (**mangled == 'X')) + { + demangle_arm_hp_template (work, mangled, 0, declp); + /* This leaves MANGLED pointing to the 'F' marking func args */ + } + + if (LUCID_DEMANGLING || ARM_DEMANGLING || HP_DEMANGLING || EDG_DEMANGLING) + { + + /* See if we have an ARM style constructor or destructor operator. + If so, then just record it, clear the decl, and return. + We can't build the actual constructor/destructor decl until later, + when we recover the class name from the signature. */ + + if (strcmp (declp -> b, "__ct") == 0) + { + work -> constructor += 1; + string_clear (declp); + return; + } + else if (strcmp (declp -> b, "__dt") == 0) + { + work -> destructor += 1; + string_clear (declp); + return; + } + } + + if (declp->p - declp->b >= 3 + && declp->b[0] == 'o' + && declp->b[1] == 'p' + && strchr (cplus_markers, declp->b[2]) != NULL) + { + /* see if it's an assignment expression */ + if (declp->p - declp->b >= 10 /* op$assign_ */ + && memcmp (declp->b + 3, "assign_", 7) == 0) + { + for (i = 0; i < (size_t)ARRAY_SIZE (optable); i++) + { + int len = declp->p - declp->b - 10; + if ((int) strlen (optable[i].in) == len + && memcmp (optable[i].in, declp->b + 10, len) == 0) + { + string_clear (declp); + string_append (declp, "operator"); + string_append (declp, optable[i].out); + string_append (declp, "="); + break; + } + } + } + else + { + for (i = 0; i < (size_t)ARRAY_SIZE (optable); i++) + { + int len = declp->p - declp->b - 3; + if ((int) strlen (optable[i].in) == len + && memcmp (optable[i].in, declp->b + 3, len) == 0) + { + string_clear (declp); + string_append (declp, "operator"); + string_append (declp, optable[i].out); + break; + } + } + } + } + else if (declp->p - declp->b >= 5 && memcmp (declp->b, "type", 4) == 0 + && strchr (cplus_markers, declp->b[4]) != NULL) + { + /* type conversion operator */ + tem = declp->b + 5; + if (do_type (work, &tem, &type)) + { + string_clear (declp); + string_append (declp, "operator "); + string_appends (declp, &type); + string_delete (&type); + } + } + else if (declp->b[0] == '_' && declp->b[1] == '_' + && declp->b[2] == 'o' && declp->b[3] == 'p') + { + /* ANSI. */ + /* type conversion operator. */ + tem = declp->b + 4; + if (do_type (work, &tem, &type)) + { + string_clear (declp); + string_append (declp, "operator "); + string_appends (declp, &type); + string_delete (&type); + } + } + else if (declp->b[0] == '_' && declp->b[1] == '_' + && ISLOWER((unsigned char)declp->b[2]) + && ISLOWER((unsigned char)declp->b[3])) + { + if (declp->b[4] == '\0') + { + /* Operator. */ + for (i = 0; i < (size_t)ARRAY_SIZE (optable); i++) + { + if (strlen (optable[i].in) == 2 + && memcmp (optable[i].in, declp->b + 2, 2) == 0) + { + string_clear (declp); + string_append (declp, "operator"); + string_append (declp, optable[i].out); + break; + } + } + } + else + { + if (declp->b[2] == 'a' && declp->b[5] == '\0') + { + /* Assignment. */ + for (i = 0; i < (size_t)ARRAY_SIZE (optable); i++) + { + if (strlen (optable[i].in) == 3 + && memcmp (optable[i].in, declp->b + 2, 3) == 0) + { + string_clear (declp); + string_append (declp, "operator"); + string_append (declp, optable[i].out); + break; + } + } + } + } + } +} + +/* a mini string-handling package */ + +static void +string_need (s, n) + string *s; + int n; +{ + int tem; + + if (s->b == NULL) + { + if (n < 32) + { + n = 32; + } + s->p = s->b = xmalloc (n); + s->e = s->b + n; + } + else if (s->e - s->p < n) + { + tem = s->p - s->b; + n += tem; + n *= 2; + s->b = xrealloc (s->b, n); + s->p = s->b + tem; + s->e = s->b + n; + } +} + +static void +string_delete (s) + string *s; +{ + if (s->b != NULL) + { + free (s->b); + s->b = s->e = s->p = NULL; + } +} + +static void +string_init (s) + string *s; +{ + s->b = s->p = s->e = NULL; +} + +static void +string_clear (s) + string *s; +{ + s->p = s->b; +} + +#if 0 + +static int +string_empty (s) + string *s; +{ + return (s->b == s->p); +} + +#endif + +static void +string_append (p, s) + string *p; + const char *s; +{ + int n; + if (s == NULL || *s == '\0') + return; + n = strlen (s); + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; +} + +static void +string_appends (p, s) + string *p, *s; +{ + int n; + + if (s->b != s->p) + { + n = s->p - s->b; + string_need (p, n); + memcpy (p->p, s->b, n); + p->p += n; + } +} + +static void +string_appendn (p, s, n) + string *p; + const char *s; + int n; +{ + if (n != 0) + { + string_need (p, n); + memcpy (p->p, s, n); + p->p += n; + } +} + +static void +string_prepend (p, s) + string *p; + const char *s; +{ + if (s != NULL && *s != '\0') + { + string_prependn (p, s, strlen (s)); + } +} + +static void +string_prepends (p, s) + string *p, *s; +{ + if (s->b != s->p) + { + string_prependn (p, s->b, s->p - s->b); + } +} + +static void +string_prependn (p, s, n) + string *p; + const char *s; + int n; +{ + char *q; + + if (n != 0) + { + string_need (p, n); + for (q = p->p - 1; q >= p->b; q--) + { + q[n] = q[0]; + } + memcpy (p->b, s, n); + p->p += n; + } +} + +static void +string_append_template_idx (s, idx) + string *s; + int idx; +{ + char buf[INTBUF_SIZE + 1 /* 'T' */]; + sprintf(buf, "T%d", idx); + string_append (s, buf); +} + +/* To generate a standalone demangler program for testing purposes, + just compile and link this file with -DMAIN and libiberty.a. When + run, it demangles each command line arg, or each stdin string, and + prints the result on stdout. */ + +#ifdef MAIN + +#include "getopt.h" + +static const char *program_name; +static const char *program_version = VERSION; +static int flags = DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE; + +static void demangle_it PARAMS ((char *)); +static void usage PARAMS ((FILE *, int)) ATTRIBUTE_NORETURN; +static void fatal PARAMS ((const char *)) ATTRIBUTE_NORETURN; +static void print_demangler_list PARAMS ((FILE *)); + +static void +demangle_it (mangled_name) + char *mangled_name; +{ + char *result; + + /* For command line args, also try to demangle type encodings. */ + result = cplus_demangle (mangled_name, flags | DMGL_TYPES); + if (result == NULL) + { + printf ("%s\n", mangled_name); + } + else + { + printf ("%s\n", result); + free (result); + } +} + +static void +print_demangler_list (stream) + FILE *stream; +{ + const struct demangler_engine *demangler; + + fprintf (stream, "{%s", libiberty_demanglers->demangling_style_name); + + for (demangler = libiberty_demanglers + 1; + demangler->demangling_style != unknown_demangling; + ++demangler) + fprintf (stream, ",%s", demangler->demangling_style_name); + + fprintf (stream, "}"); +} + +static void +usage (stream, status) + FILE *stream; + int status; +{ + fprintf (stream, "\ +Usage: %s [-_] [-n] [--strip-underscores] [--no-strip-underscores] \n", + program_name); + + fprintf (stream, "\ + [-s "); + print_demangler_list (stream); + fprintf (stream, "]\n"); + + fprintf (stream, "\ + [--format "); + print_demangler_list (stream); + fprintf (stream, "]\n"); + + fprintf (stream, "\ + [--help] [--version] [arg...]\n"); + exit (status); +} + +#define MBUF_SIZE 32767 +char mbuffer[MBUF_SIZE]; + +/* Defined in the automatically-generated underscore.c. */ +extern int prepends_underscore; + +int strip_underscore = 0; + +static const struct option long_options[] = { + {"strip-underscores", no_argument, 0, '_'}, + {"format", required_argument, 0, 's'}, + {"help", no_argument, 0, 'h'}, + {"no-strip-underscores", no_argument, 0, 'n'}, + {"version", no_argument, 0, 'v'}, + {0, no_argument, 0, 0} +}; + +/* More 'friendly' abort that prints the line and file. + config.h can #define abort fancy_abort if you like that sort of thing. */ + +void +fancy_abort () +{ + fatal ("Internal gcc abort."); +} + + +static const char * +standard_symbol_characters PARAMS ((void)); + +static const char * +hp_symbol_characters PARAMS ((void)); + +static const char * +gnu_v3_symbol_characters PARAMS ((void)); + +/* Return the string of non-alnum characters that may occur + as a valid symbol component, in the standard assembler symbol + syntax. */ + +static const char * +standard_symbol_characters () +{ + return "_$."; +} + + +/* Return the string of non-alnum characters that may occur + as a valid symbol name component in an HP object file. + + Note that, since HP's compiler generates object code straight from + C++ source, without going through an assembler, its mangled + identifiers can use all sorts of characters that no assembler would + tolerate, so the alphabet this function creates is a little odd. + Here are some sample mangled identifiers offered by HP: + + typeid*__XT24AddressIndExpClassMember_ + [Vftptr]key:__dt__32OrdinaryCompareIndExpClassMemberFv + __ct__Q2_9Elf64_Dyn18{unnamed.union.#1}Fv + + This still seems really weird to me, since nowhere else in this + file is there anything to recognize curly brackets, parens, etc. + I've talked with Srikanth , and he assures me + this is right, but I still strongly suspect that there's a + misunderstanding here. + + If we decide it's better for c++filt to use HP's assembler syntax + to scrape identifiers out of its input, here's the definition of + the symbol name syntax from the HP assembler manual: + + Symbols are composed of uppercase and lowercase letters, decimal + digits, dollar symbol, period (.), ampersand (&), pound sign(#) and + underscore (_). A symbol can begin with a letter, digit underscore or + dollar sign. If a symbol begins with a digit, it must contain a + non-digit character. + + So have fun. */ +static const char * +hp_symbol_characters () +{ + return "_$.<>#,*&[]:(){}"; +} + + +/* Return the string of non-alnum characters that may occur + as a valid symbol component in the GNU C++ V3 ABI mangling + scheme. */ + +static const char * +gnu_v3_symbol_characters () +{ + return "_$."; +} + + +extern int main PARAMS ((int, char **)); + +int +main (argc, argv) + int argc; + char **argv; +{ + char *result; + int c; + const char *valid_symbols; + enum demangling_styles style = auto_demangling; + + program_name = argv[0]; + + strip_underscore = prepends_underscore; + + while ((c = getopt_long (argc, argv, "_ns:", long_options, (int *) 0)) != EOF) + { + switch (c) + { + case '?': + usage (stderr, 1); + break; + case 'h': + usage (stdout, 0); + case 'n': + strip_underscore = 0; + break; + case 'v': + printf ("GNU %s (C++ demangler), version %s\n", program_name, program_version); + return (0); + case '_': + strip_underscore = 1; + break; + case 's': + { + style = cplus_demangle_name_to_style (optarg); + if (style == unknown_demangling) + { + fprintf (stderr, "%s: unknown demangling style `%s'\n", + program_name, optarg); + return (1); + } + else + cplus_demangle_set_style (style); + } + break; + } + } + + if (optind < argc) + { + for ( ; optind < argc; optind++) + { + demangle_it (argv[optind]); + } + } + else + { + switch (current_demangling_style) + { + case gnu_demangling: + case lucid_demangling: + case arm_demangling: + case java_demangling: + case edg_demangling: + case gnat_demangling: + case auto_demangling: + valid_symbols = standard_symbol_characters (); + break; + case hp_demangling: + valid_symbols = hp_symbol_characters (); + break; + case gnu_v3_demangling: + valid_symbols = gnu_v3_symbol_characters (); + break; + default: + /* Folks should explicitly indicate the appropriate alphabet for + each demangling. Providing a default would allow the + question to go unconsidered. */ + abort (); + } + + for (;;) + { + int i = 0; + c = getchar (); + /* Try to read a label. */ + while (c != EOF && (ISALNUM (c) || strchr (valid_symbols, c))) + { + if (i >= MBUF_SIZE-1) + break; + mbuffer[i++] = c; + c = getchar (); + } + if (i > 0) + { + int skip_first = 0; + + if (mbuffer[0] == '.' || mbuffer[0] == '$') + ++skip_first; + if (strip_underscore && mbuffer[skip_first] == '_') + ++skip_first; + + if (skip_first > i) + skip_first = i; + + mbuffer[i] = 0; + flags |= (int) style; + result = cplus_demangle (mbuffer + skip_first, flags); + if (result) + { + if (mbuffer[0] == '.') + putc ('.', stdout); + fputs (result, stdout); + free (result); + } + else + fputs (mbuffer, stdout); + + fflush (stdout); + } + if (c == EOF) + break; + putchar (c); + fflush (stdout); + } + } + + return (0); +} + +static void +fatal (str) + const char *str; +{ + fprintf (stderr, "%s: %s\n", program_name, str); + exit (1); +} + +PTR +xmalloc (size) + size_t size; +{ + register PTR value = (PTR) malloc (size); + if (value == 0) + fatal ("virtual memory exhausted"); + return value; +} + +PTR +xrealloc (ptr, size) + PTR ptr; + size_t size; +{ + register PTR value = (PTR) realloc (ptr, size); + if (value == 0) + fatal ("virtual memory exhausted"); + return value; +} +#endif /* main */ diff --git a/VEX/head20041019/coregrind/demangle/demangle.h b/VEX/head20041019/coregrind/demangle/demangle.h new file mode 100644 index 0000000000..238ae3398a --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/demangle.h @@ -0,0 +1,177 @@ +/* Defs for interface to demanglers. + Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000, 2001 + Free Software Foundation, Inc. + + 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 2, 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + + +#if !defined (DEMANGLE_H) +#define DEMANGLE_H + +#include + +#define current_demangling_style VG_(current_demangling_style) + +/* Options passed to cplus_demangle (in 2nd parameter). */ + +#define DMGL_NO_OPTS 0 /* For readability... */ +#define DMGL_PARAMS (1 << 0) /* Include function args */ +#define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ +#define DMGL_JAVA (1 << 2) /* Demangle as Java rather than C++. */ + +#define DMGL_AUTO (1 << 8) +#define DMGL_GNU (1 << 9) +#define DMGL_LUCID (1 << 10) +#define DMGL_ARM (1 << 11) +#define DMGL_HP (1 << 12) /* For the HP aCC compiler; + same as ARM except for + template arguments, etc. */ +#define DMGL_EDG (1 << 13) +#define DMGL_GNU_V3 (1 << 14) +#define DMGL_GNAT (1 << 15) + +/* If none of these are set, use 'current_demangling_style' as the default. */ +#define DMGL_STYLE_MASK (DMGL_AUTO|DMGL_GNU|DMGL_LUCID|DMGL_ARM|DMGL_HP|DMGL_EDG|DMGL_GNU_V3|DMGL_JAVA|DMGL_GNAT) + +/* Enumeration of possible demangling styles. + + Lucid and ARM styles are still kept logically distinct, even though + they now both behave identically. The resulting style is actual the + union of both. I.E. either style recognizes both "__pt__" and "__rf__" + for operator "->", even though the first is lucid style and the second + is ARM style. (FIXME?) */ + +extern enum demangling_styles +{ + no_demangling = -1, + unknown_demangling = 0, + auto_demangling = DMGL_AUTO, + gnu_demangling = DMGL_GNU, + lucid_demangling = DMGL_LUCID, + arm_demangling = DMGL_ARM, + hp_demangling = DMGL_HP, + edg_demangling = DMGL_EDG, + gnu_v3_demangling = DMGL_GNU_V3, + java_demangling = DMGL_JAVA, + gnat_demangling = DMGL_GNAT +} current_demangling_style; + +/* Define string names for the various demangling styles. */ + +#define NO_DEMANGLING_STYLE_STRING "none" +#define AUTO_DEMANGLING_STYLE_STRING "auto" +#define GNU_DEMANGLING_STYLE_STRING "gnu" +#define LUCID_DEMANGLING_STYLE_STRING "lucid" +#define ARM_DEMANGLING_STYLE_STRING "arm" +#define HP_DEMANGLING_STYLE_STRING "hp" +#define EDG_DEMANGLING_STYLE_STRING "edg" +#define GNU_V3_DEMANGLING_STYLE_STRING "gnu-v3" +#define JAVA_DEMANGLING_STYLE_STRING "java" +#define GNAT_DEMANGLING_STYLE_STRING "gnat" + +/* Some macros to test what demangling style is active. */ + +#define CURRENT_DEMANGLING_STYLE current_demangling_style +#define AUTO_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_AUTO) +#define GNU_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU) +#define LUCID_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_LUCID) +#define ARM_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_ARM) +#define HP_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_HP) +#define EDG_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_EDG) +#define GNU_V3_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNU_V3) +#define JAVA_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_JAVA) +#define GNAT_DEMANGLING (((int) CURRENT_DEMANGLING_STYLE) & DMGL_GNAT) + +/* Provide information about the available demangle styles. This code is + pulled from gdb into libiberty because it is useful to binutils also. */ + +extern const struct demangler_engine +{ + const char *const demangling_style_name; + const enum demangling_styles demangling_style; + const char *const demangling_style_doc; +} libiberty_demanglers[]; + +extern char * +VG_(cplus_demangle) PARAMS ((const char *mangled, int options)); + +/* +extern int +cplus_demangle_opname PARAMS ((const char *opname, char *result, int options)); +*/ + +/* +extern const char * +cplus_mangle_opname PARAMS ((const char *opname, int options)); +*/ + +/* Note: This sets global state. FIXME if you care about multi-threading. */ + +/* +extern void +set_cplus_marker_for_demangling PARAMS ((int ch)); +*/ + +/* +extern enum demangling_styles +cplus_demangle_set_style PARAMS ((enum demangling_styles style)); +*/ + +/* +extern enum demangling_styles +cplus_demangle_name_to_style PARAMS ((const char *name)); +*/ + +/* V3 ABI demangling entry points, defined in cp-demangle.c. */ +extern char* +VG_(cplus_demangle_v3) PARAMS ((const char* mangled)); + +extern char* +VG_(java_demangle_v3) PARAMS ((const char* mangled)); + + +enum gnu_v3_ctor_kinds { + gnu_v3_complete_object_ctor = 1, + gnu_v3_base_object_ctor, + gnu_v3_complete_object_allocating_ctor +}; + +/* Return non-zero iff NAME is the mangled form of a constructor name + in the G++ V3 ABI demangling style. Specifically, return an `enum + gnu_v3_ctor_kinds' value indicating what kind of constructor + it is. */ +/* +extern enum gnu_v3_ctor_kinds + is_gnu_v3_mangled_ctor PARAMS ((const char *name)); +*/ + + +enum gnu_v3_dtor_kinds { + gnu_v3_deleting_dtor = 1, + gnu_v3_complete_object_dtor, + gnu_v3_base_object_dtor +}; + +/* Return non-zero iff NAME is the mangled form of a destructor name + in the G++ V3 ABI demangling style. Specifically, return an `enum + gnu_v3_dtor_kinds' value, indicating what kind of destructor + it is. */ +/* +extern enum gnu_v3_dtor_kinds + is_gnu_v3_mangled_dtor PARAMS ((const char *name)); +*/ + +#endif /* DEMANGLE_H */ diff --git a/VEX/head20041019/coregrind/demangle/dyn-string.c b/VEX/head20041019/coregrind/demangle/dyn-string.c new file mode 100644 index 0000000000..96a2f7dba9 --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/dyn-string.c @@ -0,0 +1,439 @@ +/* An abstract string datatype. + Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + Contributed by Mark Mitchell (mark@markmitchell.com). + +This file is part of GNU CC. + +GNU CC 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 2, or (at your option) +any later version. + +GNU CC 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 GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/*#ifdef HAVE_STRING_H +#include +#endif*/ + +/*#ifdef HAVE_STDLIB_H +#include +#endif*/ + +#include "core.h" +#include "ansidecl.h" +#include "dyn-string.h" + +#ifndef STANDALONE +#define malloc(s) VG_(arena_malloc) (VG_AR_DEMANGLE, s) +#define free(p) VG_(arena_free) (VG_AR_DEMANGLE, p) +#define realloc(p,s) VG_(arena_realloc)(VG_AR_DEMANGLE, p, VG_MIN_MALLOC_SZB, s) +#endif + +/* If this file is being compiled for inclusion in the C++ runtime + library, as part of the demangler implementation, we don't want to + abort if an allocation fails. Instead, percolate an error code up + through the call chain. */ + +#ifdef IN_LIBGCC2 +#define RETURN_ON_ALLOCATION_FAILURE +#endif + +/* Performs in-place initialization of a dyn_string struct. This + function can be used with a dyn_string struct on the stack or + embedded in another object. The contents of of the string itself + are still dynamically allocated. The string initially is capable + of holding at least SPACE characeters, including the terminating + NUL. If SPACE is 0, it will silently be increated to 1. + + If RETURN_ON_ALLOCATION_FAILURE is defined and memory allocation + fails, returns 0. Otherwise returns 1. */ + +int +dyn_string_init (ds_struct_ptr, space) + struct dyn_string *ds_struct_ptr; + int space; +{ + /* We need at least one byte in which to store the terminating NUL. */ + if (space == 0) + space = 1; + +#ifdef RETURN_ON_ALLOCATION_FAILURE + ds_struct_ptr->s = (char *) malloc (space); + if (ds_struct_ptr->s == NULL) + return 0; +#else + ds_struct_ptr->s = (char *) malloc (space); +#endif + ds_struct_ptr->allocated = space; + ds_struct_ptr->length = 0; + ds_struct_ptr->s[0] = '\0'; + + return 1; +} + +/* Create a new dynamic string capable of holding at least SPACE + characters, including the terminating NUL. If SPACE is 0, it will + be silently increased to 1. If RETURN_ON_ALLOCATION_FAILURE is + defined and memory allocation fails, returns NULL. Otherwise + returns the newly allocated string. */ + +dyn_string_t +dyn_string_new (space) + int space; +{ + dyn_string_t result; +#ifdef RETURN_ON_ALLOCATION_FAILURE + result = (dyn_string_t) malloc (sizeof (struct dyn_string)); + if (result == NULL) + return NULL; + if (!dyn_string_init (result, space)) + { + free (result); + return NULL; + } +#else + result = (dyn_string_t) malloc (sizeof (struct dyn_string)); + dyn_string_init (result, space); +#endif + return result; +} + +/* Free the memory used by DS. */ + +void +dyn_string_delete (ds) + dyn_string_t ds; +{ + free (ds->s); + free (ds); +} + +/* Returns the contents of DS in a buffer allocated with malloc. It + is the caller's responsibility to deallocate the buffer using free. + DS is then set to the empty string. Deletes DS itself. */ + +char* +dyn_string_release (ds) + dyn_string_t ds; +{ + /* Store the old buffer. */ + char* result = ds->s; + /* The buffer is no longer owned by DS. */ + ds->s = NULL; + /* Delete DS. */ + free (ds); + /* Return the old buffer. */ + return result; +} + +/* Increase the capacity of DS so it can hold at least SPACE + characters, plus the terminating NUL. This function will not (at + present) reduce the capacity of DS. Returns DS on success. + + If RETURN_ON_ALLOCATION_FAILURE is defined and a memory allocation + operation fails, deletes DS and returns NULL. */ + +dyn_string_t +dyn_string_resize (ds, space) + dyn_string_t ds; + int space; +{ + int new_allocated = ds->allocated; + + /* Increase SPACE to hold the NUL termination. */ + ++space; + + /* Increase allocation by factors of two. */ + while (space > new_allocated) + new_allocated *= 2; + + if (new_allocated != ds->allocated) + { + ds->allocated = new_allocated; + /* We actually need more space. */ +#ifdef RETURN_ON_ALLOCATION_FAILURE + ds->s = (char *) realloc (ds->s, ds->allocated); + if (ds->s == NULL) + { + free (ds); + return NULL; + } +#else + ds->s = (char *) realloc (ds->s, ds->allocated); +#endif + } + + return ds; +} + +/* Sets the contents of DS to the empty string. */ + +void +dyn_string_clear (ds) + dyn_string_t ds; +{ + /* A dyn_string always has room for at least the NUL terminator. */ + ds->s[0] = '\0'; + ds->length = 0; +} + +/* Makes the contents of DEST the same as the contents of SRC. DEST + and SRC must be distinct. Returns 1 on success. On failure, if + RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ + +int +dyn_string_copy (dest, src) + dyn_string_t dest; + dyn_string_t src; +{ + if (dest == src) + VG_(core_panic) ("dyn_string_copy: src==dest"); + + /* Make room in DEST. */ + if (dyn_string_resize (dest, src->length) == NULL) + return 0; + /* Copy DEST into SRC. */ + VG_(strcpy) (dest->s, src->s); + /* Update the size of DEST. */ + dest->length = src->length; + return 1; +} + +/* Copies SRC, a NUL-terminated string, into DEST. Returns 1 on + success. On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST + and returns 0. */ + +int +dyn_string_copy_cstr (dest, src) + dyn_string_t dest; + const char *src; +{ + int length = VG_(strlen) (src); + /* Make room in DEST. */ + if (dyn_string_resize (dest, length) == NULL) + return 0; + /* Copy DEST into SRC. */ + VG_(strcpy) (dest->s, src); + /* Update the size of DEST. */ + dest->length = length; + return 1; +} + +/* Inserts SRC at the beginning of DEST. DEST is expanded as + necessary. SRC and DEST must be distinct. Returns 1 on success. + On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and + returns 0. */ + +int +dyn_string_prepend (dest, src) + dyn_string_t dest; + dyn_string_t src; +{ + return dyn_string_insert (dest, 0, src); +} + +/* Inserts SRC, a NUL-terminated string, at the beginning of DEST. + DEST is expanded as necessary. Returns 1 on success. On failure, + if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ + +int +dyn_string_prepend_cstr (dest, src) + dyn_string_t dest; + const char *src; +{ + return dyn_string_insert_cstr (dest, 0, src); +} + +/* Inserts SRC into DEST starting at position POS. DEST is expanded + as necessary. SRC and DEST must be distinct. Returns 1 on + success. On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST + and returns 0. */ + +int +dyn_string_insert (dest, pos, src) + dyn_string_t dest; + int pos; + dyn_string_t src; +{ + int i; + + if (src == dest) + VG_(core_panic)( "dyn_string_insert: src==dest" ); + + if (dyn_string_resize (dest, dest->length + src->length) == NULL) + return 0; + /* Make room for the insertion. Be sure to copy the NUL. */ + for (i = dest->length; i >= pos; --i) + dest->s[i + src->length] = dest->s[i]; + /* Splice in the new stuff. */ + VG_(strncpy) (dest->s + pos, src->s, src->length); + /* Compute the new length. */ + dest->length += src->length; + return 1; +} + +/* Inserts SRC, a NUL-terminated string, into DEST starting at + position POS. DEST is expanded as necessary. Returns 1 on + success. On failure, RETURN_ON_ALLOCATION_FAILURE, deletes DEST + and returns 0. */ + +int +dyn_string_insert_cstr (dest, pos, src) + dyn_string_t dest; + int pos; + const char *src; +{ + int i; + int length = VG_(strlen) (src); + + if (dyn_string_resize (dest, dest->length + length) == NULL) + return 0; + /* Make room for the insertion. Be sure to copy the NUL. */ + for (i = dest->length; i >= pos; --i) + dest->s[i + length] = dest->s[i]; + /* Splice in the new stuff. */ + VG_(strncpy) (dest->s + pos, src, length); + /* Compute the new length. */ + dest->length += length; + return 1; +} + +/* Inserts character C into DEST starting at position POS. DEST is + expanded as necessary. Returns 1 on success. On failure, + RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ + +int +dyn_string_insert_char (dest, pos, c) + dyn_string_t dest; + int pos; + int c; +{ + int i; + + if (dyn_string_resize (dest, dest->length + 1) == NULL) + return 0; + /* Make room for the insertion. Be sure to copy the NUL. */ + for (i = dest->length; i >= pos; --i) + dest->s[i + 1] = dest->s[i]; + /* Add the new character. */ + dest->s[pos] = c; + /* Compute the new length. */ + ++dest->length; + return 1; +} + +/* Append S to DS, resizing DS if necessary. Returns 1 on success. + On failure, if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and + returns 0. */ + +int +dyn_string_append (dest, s) + dyn_string_t dest; + dyn_string_t s; +{ + if (dyn_string_resize (dest, dest->length + s->length) == 0) + return 0; + VG_(strcpy) (dest->s + dest->length, s->s); + dest->length += s->length; + return 1; +} + +/* Append the NUL-terminated string S to DS, resizing DS if necessary. + Returns 1 on success. On failure, if RETURN_ON_ALLOCATION_FAILURE, + deletes DEST and returns 0. */ + +int +dyn_string_append_cstr (dest, s) + dyn_string_t dest; + const char *s; +{ + int len = VG_(strlen) (s); + + /* The new length is the old length plus the size of our string, plus + one for the null at the end. */ + if (dyn_string_resize (dest, dest->length + len) == NULL) + return 0; + VG_(strcpy) (dest->s + dest->length, s); + dest->length += len; + return 1; +} + +/* Appends C to the end of DEST. Returns 1 on success. On failiure, + if RETURN_ON_ALLOCATION_FAILURE, deletes DEST and returns 0. */ + +int +dyn_string_append_char (dest, c) + dyn_string_t dest; + int c; +{ + /* Make room for the extra character. */ + if (dyn_string_resize (dest, dest->length + 1) == NULL) + return 0; + /* Append the character; it will overwrite the old NUL. */ + dest->s[dest->length] = c; + /* Add a new NUL at the end. */ + dest->s[dest->length + 1] = '\0'; + /* Update the length. */ + ++(dest->length); + return 1; +} + +/* Sets the contents of DEST to the substring of SRC starting at START + and ending before END. START must be less than or equal to END, + and both must be between zero and the length of SRC, inclusive. + Returns 1 on success. On failure, if RETURN_ON_ALLOCATION_FAILURE, + deletes DEST and returns 0. */ + +int +dyn_string_substring (dest, src, start, end) + dyn_string_t dest; + dyn_string_t src; + int start; + int end; +{ + int i; + int length = end - start; + + /* + vg_assert (start > end || start > src->length || end > src->length); + */ + + /* Make room for the substring. */ + if (dyn_string_resize (dest, length) == NULL) + return 0; + /* Copy the characters in the substring, */ + for (i = length; --i >= 0; ) + dest->s[i] = src->s[start + i]; + /* NUL-terimate the result. */ + dest->s[length] = '\0'; + /* Record the length of the substring. */ + dest->length = length; + + return 1; +} + +/* Returns non-zero if DS1 and DS2 have the same contents. */ + +int +dyn_string_eq (ds1, ds2) + dyn_string_t ds1; + dyn_string_t ds2; +{ + /* If DS1 and DS2 have different lengths, they must not be the same. */ + if (ds1->length != ds2->length) + return 0; + else + return !VG_(strcmp) (ds1->s, ds2->s); +} diff --git a/VEX/head20041019/coregrind/demangle/dyn-string.h b/VEX/head20041019/coregrind/demangle/dyn-string.h new file mode 100644 index 0000000000..9615cd64ee --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/dyn-string.h @@ -0,0 +1,96 @@ +/* An abstract string datatype. + Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc. + Contributed by Mark Mitchell (mark@markmitchell.com). + +This file is part of GCC. + +GCC 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 2, or (at your option) +any later version. + +GCC 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 GCC; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ +#ifndef __DYN_STRING_H +#define __DYN_STRING_H + + +typedef struct dyn_string +{ + int allocated; /* The amount of space allocated for the string. */ + int length; /* The actual length of the string. */ + char *s; /* The string itself, NUL-terminated. */ +}* dyn_string_t; + +/* The length STR, in bytes, not including the terminating NUL. */ +#define dyn_string_length(STR) \ + ((STR)->length) + +/* The NTBS in which the contents of STR are stored. */ +#define dyn_string_buf(STR) \ + ((STR)->s) + +/* Compare DS1 to DS2 with strcmp. */ +#define dyn_string_compare(DS1, DS2) \ + (VG_(strcmp) ((DS1)->s, (DS2)->s)) + + +/* dyn_string functions are used in the demangling implementation + included in the G++ runtime library. To prevent collisions with + names in user programs, the functions that are used in the + demangler are given implementation-reserved names. */ + +#if 1 /* def IN_LIBGCC2 */ + +#define dyn_string_init VG_(__cxa_dyn_string_init) +#define dyn_string_new VG_(__cxa_dyn_string_new) +#define dyn_string_delete VG_(__cxa_dyn_string_delete) +#define dyn_string_release VG_(__cxa_dyn_string_release) +#define dyn_string_resize VG_(__cxa_dyn_string_resize) +#define dyn_string_clear VG_(__cxa_dyn_string_clear) +#define dyn_string_copy VG_(__cxa_dyn_string_copy) +#define dyn_string_copy_cstr VG_(__cxa_dyn_string_copy_cstr) +#define dyn_string_prepend VG_(__cxa_dyn_string_prepend) +#define dyn_string_prepend_cstr VG_(__cxa_dyn_string_prepend_cstr) +#define dyn_string_insert VG_(__cxa_dyn_string_insert) +#define dyn_string_insert_cstr VG_(__cxa_dyn_string_insert_cstr) +#define dyn_string_insert_char VG_(__cxa_dyn_string_insert_char) +#define dyn_string_append VG_(__cxa_dyn_string_append) +#define dyn_string_append_cstr VG_(__cxa_dyn_string_append_cstr) +#define dyn_string_append_char VG_(__cxa_dyn_string_append_char) +#define dyn_string_substring VG_(__cxa_dyn_string_substring) +#define dyn_string_eq VG_(__cxa_dyn_string_eq) + +#endif /* IN_LIBGCC2 */ + + +extern int dyn_string_init PARAMS ((struct dyn_string *, int)); +extern dyn_string_t dyn_string_new PARAMS ((int)); +extern void dyn_string_delete PARAMS ((dyn_string_t)); +extern char *dyn_string_release PARAMS ((dyn_string_t)); +extern dyn_string_t dyn_string_resize PARAMS ((dyn_string_t, int)); +extern void dyn_string_clear PARAMS ((dyn_string_t)); +extern int dyn_string_copy PARAMS ((dyn_string_t, dyn_string_t)); +extern int dyn_string_copy_cstr PARAMS ((dyn_string_t, const char *)); +extern int dyn_string_prepend PARAMS ((dyn_string_t, dyn_string_t)); +extern int dyn_string_prepend_cstr PARAMS ((dyn_string_t, const char *)); +extern int dyn_string_insert PARAMS ((dyn_string_t, int, + dyn_string_t)); +extern int dyn_string_insert_cstr PARAMS ((dyn_string_t, int, + const char *)); +extern int dyn_string_insert_char PARAMS ((dyn_string_t, int, int)); +extern int dyn_string_append PARAMS ((dyn_string_t, dyn_string_t)); +extern int dyn_string_append_cstr PARAMS ((dyn_string_t, const char *)); +extern int dyn_string_append_char PARAMS ((dyn_string_t, int)); +extern int dyn_string_substring PARAMS ((dyn_string_t, + dyn_string_t, int, int)); +extern int dyn_string_eq PARAMS ((dyn_string_t, dyn_string_t)); + +#endif diff --git a/VEX/head20041019/coregrind/demangle/safe-ctype.c b/VEX/head20041019/coregrind/demangle/safe-ctype.c new file mode 100644 index 0000000000..340abba2ba --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/safe-ctype.c @@ -0,0 +1,163 @@ +/* replacement macros. + + Copyright (C) 2000 Free Software Foundation, Inc. + Contributed by Zack Weinberg . + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* This is a compatible replacement of the standard C library's + with the following properties: + + - Implements all isxxx() macros required by C99. + - Also implements some character classes useful when + parsing C-like languages. + - Does not change behavior depending on the current locale. + - Behaves properly for all values in the range of a signed or + unsigned char. */ + +#include "ansidecl.h" +#include +/*#include */ /* for EOF */ + +/* Shorthand */ +#define bl _sch_isblank +#define cn _sch_iscntrl +#define di _sch_isdigit +#define is _sch_isidst +#define lo _sch_islower +#define nv _sch_isnvsp +#define pn _sch_ispunct +#define pr _sch_isprint +#define sp _sch_isspace +#define up _sch_isupper +#define vs _sch_isvsp +#define xd _sch_isxdigit + +/* Masks. */ +#define L lo|is |pr /* lower case letter */ +#define XL lo|is|xd|pr /* lowercase hex digit */ +#define U up|is |pr /* upper case letter */ +#define XU up|is|xd|pr /* uppercase hex digit */ +#define D di |xd|pr /* decimal digit */ +#define P pn |pr /* punctuation */ +#define _ pn|is |pr /* underscore */ + +#define C cn /* control character */ +#define Z nv |cn /* NUL */ +#define M nv|sp |cn /* cursor movement: \f \v */ +#define V vs|sp |cn /* vertical space: \r \n */ +#define T nv|sp|bl|cn /* tab */ +#define S nv|sp|bl|pr /* space */ + +/* Are we ASCII? */ +#if '\n' == 0x0A && ' ' == 0x20 && '0' == 0x30 \ + && 'A' == 0x41 && 'a' == 0x61 && '!' == 0x21 \ +/* && EOF == -1*/ + +const unsigned short _sch_istable[256] = +{ + Z, C, C, C, C, C, C, C, /* NUL SOH STX ETX EOT ENQ ACK BEL */ + C, T, V, M, M, V, C, C, /* BS HT LF VT FF CR SO SI */ + C, C, C, C, C, C, C, C, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + C, C, C, C, C, C, C, C, /* CAN EM SUB ESC FS GS RS US */ + S, P, P, P, P, P, P, P, /* SP ! " # $ % & ' */ + P, P, P, P, P, P, P, P, /* ( ) * + , - . / */ + D, D, D, D, D, D, D, D, /* 0 1 2 3 4 5 6 7 */ + D, D, P, P, P, P, P, P, /* 8 9 : ; < = > ? */ + P, XU, XU, XU, XU, XU, XU, U, /* @ A B C D E F G */ + U, U, U, U, U, U, U, U, /* H I J K L M N O */ + U, U, U, U, U, U, U, U, /* P Q R S T U V W */ + U, U, U, P, P, P, P, _, /* X Y Z [ \ ] ^ _ */ + P, XL, XL, XL, XL, XL, XL, L, /* ` a b c d e f g */ + L, L, L, L, L, L, L, L, /* h i j k l m n o */ + L, L, L, L, L, L, L, L, /* p q r s t u v w */ + L, L, L, P, P, P, P, C, /* x y z { | } ~ DEL */ + + /* high half of unsigned char is locale-specific, so all tests are + false in "C" locale */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +const unsigned char _sch_tolower[256] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, + + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + + 91, 92, 93, 94, 95, 96, + + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + + 123,124,125,126,127, + + 128,129,130,131, 132,133,134,135, 136,137,138,139, 140,141,142,143, + 144,145,146,147, 148,149,150,151, 152,153,154,155, 156,157,158,159, + 160,161,162,163, 164,165,166,167, 168,169,170,171, 172,173,174,175, + 176,177,178,179, 180,181,182,183, 184,185,186,187, 188,189,190,191, + + 192,193,194,195, 196,197,198,199, 200,201,202,203, 204,205,206,207, + 208,209,210,211, 212,213,214,215, 216,217,218,219, 220,221,222,223, + 224,225,226,227, 228,229,230,231, 232,233,234,235, 236,237,238,239, + 240,241,242,243, 244,245,246,247, 248,249,250,251, 252,253,254,255, +}; + +const unsigned char _sch_toupper[256] = +{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, + + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + + 91, 92, 93, 94, 95, 96, + + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + + 123,124,125,126,127, + + 128,129,130,131, 132,133,134,135, 136,137,138,139, 140,141,142,143, + 144,145,146,147, 148,149,150,151, 152,153,154,155, 156,157,158,159, + 160,161,162,163, 164,165,166,167, 168,169,170,171, 172,173,174,175, + 176,177,178,179, 180,181,182,183, 184,185,186,187, 188,189,190,191, + + 192,193,194,195, 196,197,198,199, 200,201,202,203, 204,205,206,207, + 208,209,210,211, 212,213,214,215, 216,217,218,219, 220,221,222,223, + 224,225,226,227, 228,229,230,231, 232,233,234,235, 236,237,238,239, + 240,241,242,243, 244,245,246,247, 248,249,250,251, 252,253,254,255, +}; + +#else + #error "Unsupported host character set" +#endif /* not ASCII */ diff --git a/VEX/head20041019/coregrind/demangle/safe-ctype.h b/VEX/head20041019/coregrind/demangle/safe-ctype.h new file mode 100644 index 0000000000..b2ad8490bd --- /dev/null +++ b/VEX/head20041019/coregrind/demangle/safe-ctype.h @@ -0,0 +1,103 @@ +/* replacement macros. + + Copyright (C) 2000, 2001 Free Software Foundation, Inc. + Contributed by Zack Weinberg . + +This file is part of the libiberty library. +Libiberty is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +Libiberty 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with libiberty; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +/* This is a compatible replacement of the standard C library's + with the following properties: + + - Implements all isxxx() macros required by C99. + - Also implements some character classes useful when + parsing C-like languages. + - Does not change behavior depending on the current locale. + - Behaves properly for all values in the range of a signed or + unsigned char. + + To avoid conflicts, this header defines the isxxx functions in upper + case, e.g. ISALPHA not isalpha. */ + +#ifndef SAFE_CTYPE_H +#define SAFE_CTYPE_H + +#ifdef isalpha + #error "safe-ctype.h and ctype.h may not be used simultaneously" +#else + +/* Categories. */ + +enum { + /* In C99 */ + _sch_isblank = 0x0001, /* space \t */ + _sch_iscntrl = 0x0002, /* nonprinting characters */ + _sch_isdigit = 0x0004, /* 0-9 */ + _sch_islower = 0x0008, /* a-z */ + _sch_isprint = 0x0010, /* any printing character including ' ' */ + _sch_ispunct = 0x0020, /* all punctuation */ + _sch_isspace = 0x0040, /* space \t \n \r \f \v */ + _sch_isupper = 0x0080, /* A-Z */ + _sch_isxdigit = 0x0100, /* 0-9A-Fa-f */ + + /* Extra categories useful to cpplib. */ + _sch_isidst = 0x0200, /* A-Za-z_ */ + _sch_isvsp = 0x0400, /* \n \r */ + _sch_isnvsp = 0x0800, /* space \t \f \v \0 */ + + /* Combinations of the above. */ + _sch_isalpha = _sch_isupper|_sch_islower, /* A-Za-z */ + _sch_isalnum = _sch_isalpha|_sch_isdigit, /* A-Za-z0-9 */ + _sch_isidnum = _sch_isidst|_sch_isdigit, /* A-Za-z0-9_ */ + _sch_isgraph = _sch_isalnum|_sch_ispunct, /* isprint and not space */ + _sch_iscppsp = _sch_isvsp|_sch_isnvsp, /* isspace + \0 */ + _sch_isbasic = _sch_isprint|_sch_iscppsp /* basic charset of ISO C + (plus ` and @) */ +}; + +/* Character classification. */ +extern const unsigned short _sch_istable[256]; + +#define _sch_test(c, bit) (_sch_istable[(c) & 0xff] & (unsigned short)(bit)) + +#define ISALPHA(c) _sch_test(c, _sch_isalpha) +#define ISALNUM(c) _sch_test(c, _sch_isalnum) +#define ISBLANK(c) _sch_test(c, _sch_isblank) +#define ISCNTRL(c) _sch_test(c, _sch_iscntrl) +#define ISDIGIT(c) _sch_test(c, _sch_isdigit) +#define ISGRAPH(c) _sch_test(c, _sch_isgraph) +#define ISLOWER(c) _sch_test(c, _sch_islower) +#define ISPRINT(c) _sch_test(c, _sch_isprint) +#define ISPUNCT(c) _sch_test(c, _sch_ispunct) +#define ISSPACE(c) _sch_test(c, _sch_isspace) +#define ISUPPER(c) _sch_test(c, _sch_isupper) +#define ISXDIGIT(c) _sch_test(c, _sch_isxdigit) + +#define ISIDNUM(c) _sch_test(c, _sch_isidnum) +#define ISIDST(c) _sch_test(c, _sch_isidst) +#define IS_ISOBASIC(c) _sch_test(c, _sch_isbasic) +#define IS_VSPACE(c) _sch_test(c, _sch_isvsp) +#define IS_NVSPACE(c) _sch_test(c, _sch_isnvsp) +#define IS_SPACE_OR_NUL(c) _sch_test(c, _sch_iscppsp) + +/* Character transformation. */ +extern const unsigned char _sch_toupper[256]; +extern const unsigned char _sch_tolower[256]; +#define TOUPPER(c) _sch_toupper[(c) & 0xff] +#define TOLOWER(c) _sch_tolower[(c) & 0xff] + +#endif /* no ctype.h */ +#endif /* SAFE_CTYPE_H */ diff --git a/VEX/head20041019/coregrind/docs/.cvsignore b/VEX/head20041019/coregrind/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/coregrind/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/coregrind/docs/CVS/Entries b/VEX/head20041019/coregrind/docs/CVS/Entries new file mode 100644 index 0000000000..5e7eb926dd --- /dev/null +++ b/VEX/head20041019/coregrind/docs/CVS/Entries @@ -0,0 +1,6 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:29 2002// +/Makefile.am/1.5/Wed Aug 25 11:40:05 2004// +/coregrind_core.html/1.35/Thu Sep 2 08:10:13 2004// +/coregrind_intro.html/1.8/Wed Jan 21 13:59:23 2004// +/coregrind_tools.html/1.3/Thu Sep 2 08:51:41 2004// +D diff --git a/VEX/head20041019/coregrind/docs/CVS/Repository b/VEX/head20041019/coregrind/docs/CVS/Repository new file mode 100644 index 0000000000..898d609abb --- /dev/null +++ b/VEX/head20041019/coregrind/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/coregrind/docs diff --git a/VEX/head20041019/coregrind/docs/CVS/Root b/VEX/head20041019/coregrind/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/coregrind/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/coregrind/docs/CVS/Template b/VEX/head20041019/coregrind/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/coregrind/docs/Makefile.am b/VEX/head20041019/coregrind/docs/Makefile.am new file mode 100644 index 0000000000..27a9e9bf50 --- /dev/null +++ b/VEX/head20041019/coregrind/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = coregrind_core.html coregrind_intro.html coregrind_tools.html diff --git a/VEX/head20041019/coregrind/docs/coregrind_core.html b/VEX/head20041019/coregrind/docs/coregrind_core.html new file mode 100644 index 0000000000..feaf757fcc --- /dev/null +++ b/VEX/head20041019/coregrind/docs/coregrind_core.html @@ -0,0 +1,1552 @@ + + + +

2  Using and understanding the Valgrind core

+ +This section describes the Valgrind core services, flags and behaviours. That +means it is relevant regardless of what particular tool you are using. +A point of terminology: most references to "valgrind" in the rest of +this section (Section 2) refer to the valgrind core services. + + + +

2.1  What it does with your program

+ +Valgrind is designed to be as non-intrusive as possible. It works +directly with existing executables. You don't need to recompile, +relink, or otherwise modify, the program to be checked. + +Simply put valgrind --tool=tool_name at the start of +the command line normally used to run the program. For example, +if want to run the command ls -l +using the heavyweight memory-checking tool Memcheck, issue the command: + +
+ valgrind --tool=memcheck ls -l. +
+ +

Regardless of which tool is in use, Valgrind takes control of your +program before it starts. Debugging information is read from the +executable and associated libraries, so that error messages and other +outputs can be phrased in terms of source code locations (if that is +appropriate) + +

+Your program is then run on a synthetic x86 CPU provided by the +Valgrind core. As new code is executed for the first time, the core +hands the code to the selected tool. The tool adds its own +instrumentation code to this and hands the result back to the core, +which coordinates the continued execution of this instrumented code. + +

+The amount of instrumentation code added varies widely between tools. +At one end of the scale, Memcheck adds code to check every +memory access and every value computed, increasing the size of the +code at least 12 times, and making it run 25-50 times slower than +natively. At the other end of the spectrum, the ultra-trivial "none" +tool (a.k.a. Nulgrind) adds no instrumentation at all and causes in total +"only" about a 4 times slowdown. + +

+Valgrind simulates every single instruction your program executes. +Because of this, the active tool checks, or profiles, not only the +code in your application but also in all supporting dynamically-linked +(.so-format) libraries, including the GNU C library, the +X client libraries, Qt, if you work with KDE, and so on. + +

+If you're using one of the error-detection tools, Valgrind will often +detect errors in libraries, for example the GNU C or X11 libraries, +which you have to use. You might not be interested in these errors, +since you probably have noo control over that code. Therefore, Valgrind +allows you to selectively suppress errors, by recording them in a +suppressions file which is read when Valgrind starts up. The build +mechanism attempts to select suppressions which give reasonable +behaviour for the libc and XFree86 versions detected on your machine. +To make it easier to write suppressions, you can use the +--gen-suppressions=yes option which tells Valgrind to print +out a suppression for each error that appears, which you can then copy +into a suppressions file. + +

+Different error-checking tools report different kinds of errors. The +suppression mechanism therefore allows you to say which tool or tool(s) +each suppression applies to. + + + +

2.2  Getting started

+ +First off, consider whether it might be beneficial to recompile your +application and supporting libraries with debugging info enabled (the +-g flag). Without debugging info, the best Valgrind tools +will be able to do is guess which function a particular piece of code +belongs to, which makes both error messages and profiling output +nearly useless. With -g, you'll hopefully get messages +which point directly to the relevant source code lines. + +

+Another flag you might like to consider, if you are working with +C++, is -fno-inline. That makes it easier to see the +function-call chain, which can help reduce confusion when navigating +around large C++ apps. For whatever it's worth, debugging +OpenOffice.org with Memcheck is a bit easier when using this flag. + +

+You don't have to do this, but doing so helps Valgrind produce more +accurate and less confusing error reports. Chances are you're set up +like this already, if you intended to debug your program with GNU gdb, +or some other debugger. + +

+This paragraph applies only if you plan to use Memcheck: +On rare occasions, optimisation levels +at -O2 and above have been observed to generate code which +fools Memcheck into wrongly reporting uninitialised value +errors. We have looked in detail into fixing this, and unfortunately +the result is that doing so would give a further significant slowdown +in what is already a slow tool. So the best solution is to turn off +optimisation altogether. Since this often makes things unmanagably +slow, a plausible compromise is to use -O. This gets +you the majority of the benefits of higher optimisation levels whilst +keeping relatively small the chances of false complaints from Memcheck. +All other tools (as far as we know) are unaffected by optimisation +level. + +

+Valgrind understands both the older "stabs" debugging format, used by +gcc versions prior to 3.1, and the newer DWARF2 format used by gcc 3.1 +and later. We continue to refine and debug our debug-info readers, +although the majority of effort will naturally enough go into the +newer DWARF2 reader. + +

+When you're ready to roll, just run your application as you would +normally, but place valgrind --tool=tool_name in +front of your usual command-line invocation. Note that you should run +the real (machine-code) executable here. If your application is +started by, for example, a shell or perl script, you'll need to modify +it to invoke Valgrind on the real executables. Running such scripts +directly under Valgrind will result in you getting error reports +pertaining to /bin/sh, /usr/bin/perl, or +whatever interpreter you're using. This may not be what you want and +can be confusing. You can force the issue by giving the flag +--trace-children=yes, but confusion is still likely. + + + +

2.3  The commentary

+ +Valgrind tools write a commentary, a stream of text, detailing error +reports and other significant events. All lines in the commentary +have following form:
+
+  ==12345== some-message-from-Valgrind
+
+ +

The 12345 is the process ID. This scheme makes it easy +to distinguish program output from Valgrind commentary, and also easy +to differentiate commentaries from different processes which have +become merged together, for whatever reason. + +

By default, Valgrind tools write only essential messages to the commentary, +so as to avoid flooding you with information of secondary importance. +If you want more information about what is happening, re-run, passing +the -v flag to Valgrind. + +

+You can direct the commentary to three different places: + +

    +
  • The default: send it to a file descriptor, which is by default 2 + (stderr). So, if you give the core no options, it will write + commentary to the standard error stream. If you want to send + it to some other file descriptor, for example number 9, + you can specify --log-fd=9. +

    +

  • A less intrusive option is to write the commentary to a file, + which you specify by --log-file=filename. Note + carefully that the commentary is not written to the file + you specify, but instead to one called + filename.pid12345, if for example the pid of the + traced process is 12345. This is helpful when valgrinding a whole + tree of processes at once, since it means that each process writes + to its own logfile, rather than the result being jumbled up in one + big logfile. +

    +

  • The least intrusive option is to send the commentary to a network + socket. The socket is specified as an IP address and port number + pair, like this: --log-socket=192.168.0.1:12345 if you + want to send the output to host IP 192.168.0.1 port 12345 (I have + no idea if 12345 is a port of pre-existing significance). You can + also omit the port number: --log-socket=192.168.0.1, + in which case a default port of 1500 is used. This default is + defined by the constant VG_CLO_DEFAULT_LOGPORT + in the sources. +

    + Note, unfortunately, that you have to use an IP address here, rather + than a hostname. +

    + Writing to a network socket is pretty useless if you don't have + something listening at the other end. We provide a simple + listener program, valgrind-listener, which accepts + connections on the specified port and copies whatever it is sent + to stdout. Probably someone will tell us this is a horrible + security risk. It seems likely that people will write more + sophisticated listeners in the fullness of time. +

    + valgrind-listener can accept simultaneous connections from up to 50 + valgrinded processes. In front of each line of output it prints + the current number of active connections in round brackets. +

    + valgrind-listener accepts two command-line flags: +

      +
    • -e or --exit-at-zero: when the + number of connected processes falls back to zero, exit. + Without this, it will run forever, that is, until you send it + Control-C. +

      +

    • portnumber: changes the port it listens on from + the default (1500). The specified port must be in the range + 1024 to 65535. The same restriction applies to port numbers + specified by a --log-socket= to Valgrind itself. +
    +

    + If a valgrinded process fails to connect to a listener, for + whatever reason (the listener isn't running, invalid or + unreachable host or port, etc), Valgrind switches back to writing + the commentary to stderr. The same goes for any process which + loses an established connection to a listener. In other words, + killing the listener doesn't kill the processes sending data to + it. +

+

+Here is an important point about the relationship between the +commentary and profiling output from tools. The commentary contains a +mix of messages from the Valgrind core and the selected tool. If the +tool reports errors, it will report them to the commentary. However, +if the tool does profiling, the profile data will be written to a file +of some kind, depending on the tool, and independent of what +--log-* options are in force. The commentary is intended +to be a low-bandwidth, human-readable channel. Profiling data, on the +other hand, is usually voluminous and not meaningful without further +processing, which is why we have chosen this arrangement. + + + +

2.4  Reporting of errors

+ +When one of the error-checking tools (Memcheck, Addrcheck, Helgrind) +detects something bad happening in the program, an error message is +written to the commentary. For example:
+
+  ==25832== Invalid read of size 4
+  ==25832==    at 0x8048724: BandMatrix::ReSize(int, int, int) (bogon.cpp:45)
+  ==25832==    by 0x80487AF: main (bogon.cpp:66)
+  ==25832==    by 0x40371E5E: __libc_start_main (libc-start.c:129)
+  ==25832==    by 0x80485D1: (within /home/sewardj/newmat10/bogon)
+  ==25832==    Address 0xBFFFF74C is not stack'd, malloc'd or free'd
+
+ +

+This message says that the program did an illegal 4-byte read of +address 0xBFFFF74C, which, as far as Memcheck can tell, is not a valid +stack address, nor corresponds to any currently malloc'd or free'd +blocks. The read is happening at line 45 of bogon.cpp, +called from line 66 of the same file, etc. For errors associated with +an identified malloc'd/free'd block, for example reading free'd +memory, Valgrind reports not only the location where the error +happened, but also where the associated block was malloc'd/free'd. + +

+Valgrind remembers all error reports. When an error is detected, +it is compared against old reports, to see if it is a duplicate. If +so, the error is noted, but no further commentary is emitted. This +avoids you being swamped with bazillions of duplicate error reports. + +

+If you want to know how many times each error occurred, run with the +-v option. When execution finishes, all the reports are +printed out, along with, and sorted by, their occurrence counts. This +makes it easy to see which errors have occurred most frequently. + +

+Errors are reported before the associated operation actually happens. +If you're using a tool (Memcheck, Addrcheck) which does address +checking, and your program attempts to read from address zero, the +tool will emit a message to this effect, and the program will then +duly die with a segmentation fault. + +

+In general, you should try and fix errors in the order that they are +reported. Not doing so can be confusing. For example, a program +which copies uninitialised values to several memory locations, and +later uses them, will generate several error messages, when run on +Memcheck. The first such error message may well give the most direct +clue to the root cause of the problem. + +

+The process of detecting duplicate errors is quite an expensive one +and can become a significant performance overhead if your program +generates huge quantities of errors. To avoid serious problems here, +Valgrind will simply stop collecting errors after 300 different errors +have been seen, or 30000 errors in total have been seen. In this +situation you might as well stop your program and fix it, because +Valgrind won't tell you anything else useful after this. Note that +the 300/30000 limits apply after suppressed errors are removed. These +limits are defined in core.h and can be increased +if necessary. + +

+To avoid this cutoff you can use the --error-limit=no +flag. Then Valgrind will always show errors, regardless of how many +there are. Use this flag carefully, since it may have a dire effect +on performance. + + + +

2.5  Suppressing errors

+ +The error-checking tools detect numerous problems in the base +libraries, such as the GNU C library, and the XFree86 client +libraries, which come pre-installed on your GNU/Linux system. You +can't easily fix these, but you don't want to see these errors (and +yes, there are many!) So Valgrind reads a list of errors to suppress +at startup. A default suppression file is cooked up by the +./configure script when the system is built. + +

+You can modify and add to the suppressions file at your leisure, +or, better, write your own. Multiple suppression files are allowed. +This is useful if part of your project contains errors you can't or +don't want to fix, yet you don't want to continuously be reminded of +them. + +

+Note: By far the easiest way to add suppressions is to use the +--gen-suppressions=yes flag described in this +section. + +

+Each error to be suppressed is described very specifically, to +minimise the possibility that a suppression-directive inadvertantly +suppresses a bunch of similar errors which you did want to see. The +suppression mechanism is designed to allow precise yet flexible +specification of errors to suppress. + +

+If you use the -v flag, at the end of execution, Valgrind +prints out one line for each used suppression, giving its name and the +number of times it got used. Here's the suppressions used by a run of +valgrind --tool=memcheck ls -l: +

+  --27579-- supp: 1 socketcall.connect(serv_addr)/__libc_connect/__nscd_getgrgid_r
+  --27579-- supp: 1 socketcall.connect(serv_addr)/__libc_connect/__nscd_getpwuid_r
+  --27579-- supp: 6 strrchr/_dl_map_object_from_fd/_dl_map_object
+
+ +

+Multiple suppressions files are allowed. By default, Valgrind uses +$PREFIX/lib/valgrind/default.supp. You can ask to add +suppressions from another file, by specifying +--suppressions=/path/to/file.supp. + +

+If you want to understand more about suppressions, look at an existing +suppressions file whilst reading the following documentation. The file +glibc-2.2.supp, in the source distribution, provides some good +examples. + +

Each suppression has the following components:
+

    +
  • First line: its name. This merely gives a handy name to the suppression, + by which it is referred to in the summary of used suppressions printed + out when a program finishes. It's not important what the name is; any + identifying string will do. +
  • +

    + +

  • Second line: name of the tool(s) that the suppression is for (if more + than one, comma-separated), and the name of the suppression itself, + separated by a colon, eg: +
    +      tool_name1,tool_name2:suppression_name
    +      
    + (Nb: no spaces are allowed). +

    + Recall that Valgrind-2.0.X is a modular system, in which + different instrumentation tools can observe your program whilst + it is running. Since different tools detect different kinds of + errors, it is necessary to say which tool(s) the suppression is + meaningful to. +

    + Tools will complain, at startup, if a tool does not understand + any suppression directed to it. Tools ignore suppressions which + are not directed to them. As a result, it is quite practical to + put suppressions for all tools into the same suppression file. +

    + Valgrind's core can detect certain PThreads API errors, for which this + line reads: +

    +      core:PThread
    +      
    + +
  • Next line: a small number of suppression types have extra information + after the second line (eg. the Param suppression for + Memcheck)

    + +

  • Remaining lines: This is the calling context for the error -- the chain + of function calls that led to it. There can be up to four of these lines. +

    + Locations may be either names of shared objects/executables or wildcards + matching function names. They begin obj: and + fun: respectively. Function and object names to match + against may use the wildcard characters * and + ?. +

    + Important note: C++ function names must be mangled. If + you are writing suppressions by hand, use the --demangle=no + option to get the mangled names in your error messages. +

    + +

  • Finally, the entire suppression must be between curly braces. Each + brace must be the first character on its own line. +
+ +

+ +A suppression only suppresses an error when the error matches all the +details in the suppression. Here's an example: +

+  {
+    __gconv_transform_ascii_internal/__mbrtowc/mbtowc
+    Memcheck:Value4
+    fun:__gconv_transform_ascii_internal
+    fun:__mbr*toc
+    fun:mbtowc
+  }
+
+ +

What is means is: for Memcheck only, suppress a +use-of-uninitialised-value error, when the data size is 4, when it +occurs in the function __gconv_transform_ascii_internal, +when that is called from any function of name matching +__mbr*toc, when that is called from mbtowc. +It doesn't apply under any other circumstances. The string by which +this suppression is identified to the user is +__gconv_transform_ascii_internal/__mbrtowc/mbtowc. +

+(See this section for more details on +the specifics of Memcheck's suppression kinds.) + +

Another example, again for the Memcheck tool: +

+  {
+    libX11.so.6.2/libX11.so.6.2/libXaw.so.7.0
+    Memcheck:Value4
+    obj:/usr/X11R6/lib/libX11.so.6.2
+    obj:/usr/X11R6/lib/libX11.so.6.2
+    obj:/usr/X11R6/lib/libXaw.so.7.0
+  }
+
+ +

Suppress any size 4 uninitialised-value error which occurs anywhere +in libX11.so.6.2, when called from anywhere in the same +library, when called from anywhere in libXaw.so.7.0. The +inexact specification of locations is regrettable, but is about all +you can hope for, given that the X11 libraries shipped with Red Hat +7.2 have had their symbol tables removed. + +

Note -- since the above two examples did not make it clear -- that +you can freely mix the obj: and fun: +styles of description within a single suppression record. +

+ + +

2.6  Command-line flags for the Valgrind core

+ + +As mentioned above, Valgrind's core accepts a common set of flags. +The tools also accept tool-specific flags, which are documented +seperately for each tool. + +You invoke Valgrind like this: +
+  valgrind --tool=tool_name [options-for-Valgrind] your-prog [options for your-prog]
+
+ +

Valgrind's default settings succeed in giving reasonable behaviour +in most cases. We group the available options by rough categories. + +

Tool-selection option

+The single most important option. +
    +
  • --tool=name
    +

    Run the Valgrind tool called name, e.g. Memcheck, Addrcheck, + Cachegrind, etc. +


  • +

+ +

Basic Options

+These options work with all tools. + +
    +
  • --help
    +

    Show help for all options, both for the core and for the + selected tool.


  • + +

  • --help-debug
    +

    Same as --help, but also lists debugging options which + usually are only of use to developers.


  • + +

  • --version

    Show the version number of the + Valgrind core. Tools can have their own version numbers. There + is a scheme in place to ensure that tools only execute when the + core version is one they are known to work with. This was done + to minimise the chances of strange problems arising from + tool-vs-core version incompatibilities.


  • + +

  • -v --verbose

    Be more verbose. Gives extra + information on various aspects of your program, such as: the + shared objects loaded, the suppressions used, the progress of + the instrumentation and execution engines, and warnings about + unusual behaviour. Repeating the flag increases the verbosity + level.


  • + +

  • -q --quiet
    +

    Run silently, and only print error messages. Useful if you + are running regression tests or have some other automated test + machinery. +


  • + +

  • --trace-children=no [default]
    + --trace-children=yes +

    When enabled, Valgrind will trace into child processes. This + is confusing and often not what you want, so is disabled by + default. + +

    Note that the name of this option is slightly misleading. + It actually controls whether programs started with + exec() are run under Valgrind's control. If your + program calls fork(), both the parent and the child + will run under Valgrind's control. +


  • + +

  • --log-fd=<number> [default: 2, stderr] +

    Specifies that Valgrind should send all of its + messages to the specified file descriptor. The default, 2, is + the standard error channel (stderr). Note that this may + interfere with the client's own use of stderr. +


  • + +

  • --log-file=<filename> +

    Specifies that Valgrind should send all of its + messages to the specified file. In fact, the file name used + is created by concatenating the text filename, + ".pid" and the process ID, so as to create a file per process. + The specified file name may not be the empty string. +


  • + +

  • --log-socket=<ip-address:port-number> +

    Specifies that Valgrind should send all of its messages to + the specified port at the specified IP address. The port may be + omitted, in which case port 1500 is used. If a connection + cannot be made to the specified socket, Valgrind falls back to + writing output to the standard error (stderr). This option is + intended to be used in conjunction with the + valgrind-listener program. For further details, + see section 2.3. +


  • + +

  • --time-stamp=no [default]
    + --time-stamp=yes +

    Specifies that valgrind should output a timestamp before + each message that it outputs. +


  • +

+ +

Error-related options

+These options are used by all tools that can report errors, e.g. Memcheck, but +not Cachegrind. +
    +
  • --demangle=no
    + --demangle=yes [default] +

    Disable/enable automatic demangling (decoding) of C++ names. + Enabled by default. When enabled, Valgrind will attempt to + translate encoded C++ procedure names back to something + approaching the original. The demangler handles symbols mangled + by g++ versions 2.X and 3.X. + +

    An important fact about demangling is that function + names mentioned in suppressions files should be in their mangled + form. Valgrind does not demangle function names when searching + for applicable suppressions, because to do otherwise would make + suppressions file contents dependent on the state of Valgrind's + demangling machinery, and would also be slow and pointless. +


  • + +

  • --num-callers=<number> [default=4]
    +

    By default, Valgrind shows four levels of function call names + to help you identify program locations. You can change that + number with this option. This can help in determining the + program's location in deeply-nested call chains. Note that errors + are commoned up using only the top three function locations (the + place in the current function, and that of its two immediate + callers). So this doesn't affect the total number of errors + reported. +

    + The maximum value for this is 50. Note that higher settings + will make Valgrind run a bit more slowly and take a bit more + memory, but can be useful when working with programs with + deeply-nested call chains. +


  • + +

  • --error-limit=yes [default]
    + --error-limit=no

    When enabled, Valgrind stops + reporting errors after 30000 in total, or 300 different ones, + have been seen. This is to stop the error tracking machinery + from becoming a huge performance overhead in programs with many + errors. +


  • + +

  • --show-below-main=yes
    + --show-below-main=no [default] +

    By default, stack traces for errors do not show any functions that + appear beneath main(); most of the time it's uninteresting + C library stuff. If this option is enabled, these entries below + main() will be shown. +


  • + +

  • --suppressions=<filename> + [default: $PREFIX/lib/valgrind/default.supp] +

    Specifies an extra + file from which to read descriptions of errors to suppress. You + may use as many extra suppressions files as you + like. +


  • + +

  • --gen-suppressions=no [default]
    + --gen-suppressions=yes +

    When enabled, Valgrind will pause after every error shown, + and print the line +
    + ---- Print suppression ? --- [Return/N/n/Y/y/C/c] ---- +

    + The prompt's behaviour is the same as for the --db-attach + option. +

    + If you choose to, Valgrind will print out a suppression for this error. + You can then cut and paste it into a suppression file if you don't want + to hear about the error in the future. +

    + This option is particularly useful with C++ programs, as it prints out + the suppressions with mangled names, as required. +

    + Note that the suppressions printed are as specific as possible. You + may want to common up similar ones, eg. by adding wildcards to function + names. Also, sometimes two different errors are suppressed by the same + suppression, in which case Valgrind will output the suppression more than + once, but you only need to have one copy in your suppression file (but + having more than one won't cause problems). Also, the suppression + name is given as <insert a suppression name here>; + the name doesn't really matter, it's only used with the + -v option which prints out all used suppression records. +


  • + +

  • --track-fds=no [default]
    + --track-fds=yes +

    When enabled, Valgrind will print out a list of open file + descriptors on exit. Along with each file descriptor, Valgrind + prints out a stack backtrace of where the file was opened and any + details relating to the file descriptor such as the file name or + socket details. +

    + +

  • --db-attach=no [default]
    + --db-attach=yes +

    When enabled, Valgrind will pause after every error shown, + and print the line +
    + ---- Attach to debugger ? --- [Return/N/n/Y/y/C/c] ---- +

    + Pressing Ret, or N Ret + or n Ret, causes Valgrind not to + start a debugger for this error. +

    + Y Ret + or y Ret causes Valgrind to + start a debugger, for the program at this point. When you have + finished with the debugger, quit from it, and the program will continue. + Trying to continue from inside the debugger doesn't work. +

    + C Ret + or c Ret causes Valgrind not to + start a debugger, and not to ask again. +

    + --db-attach=yes conflicts with + --trace-children=yes. You can't use them together. + Valgrind refuses to start up in this situation. 1 May 2002: + this is a historical relic which could be easily fixed if it + gets in your way. Mail me and complain if this is a problem for + you. +

    + Nov 2002: if you're sending output to a logfile or to a network + socket, I guess this option doesn't make any sense. Caveat emptor. +


  • + +

  • --db-command=<command> [default: gdb -nw %f %p]
    +

    This specifies how Valgrind will invoke the debugger. By + default it will use whatever GDB is detected at build time, + which is usually /usr/bin/gdb. Using this command, + you can specify some alternative command to invoke the debugger + you want to use. +

    + The command string given can include one or instances of the + %p and %f expansions. Each instance of %p expands to the PID of + the process to be debugged and each instance of %f expands to + the path to the executable for the process to be debugged. +


  • + +

  • --input-fd=<number> [default=0, stdin]
    +

    When using --db-attach=yes and + --gen-suppressions=yes, Valgrind will stop + so as to read keyboard input from you, when each error occurs. + By default it reads from the standard input (stdin), which is + problematic for programs which close stdin. This option + allows you to specify an alternative file descriptor from + which to read input. +


  • +

+ +

malloc()-related options

+For tools that use their own version of malloc() (e.g. Memcheck +and Addrcheck), the following options apply. +
    +
  • --alignment=<number> [default: 8]

    By + default Valgrind's malloc, realloc, + etc, return 4-byte aligned addresses. These are suitable for + any accesses on x86 processors. + Some programs might however assume that malloc et + al return 8- or more aligned memory. The supplied value must be + between 4 and 4096 inclusive, and must be a power of two.


  • + +

  • --sloppy-malloc=no [default]
    + --sloppy-malloc=yes +

    When enabled, all requests for malloc/calloc are rounded up + to a multiple of 4 bytes. For example, a request for 17 bytes of space + would result in a 20-byte area being made available. This works + around bugs in sloppy libraries which assume that they can + safely rely on malloc/calloc requests being rounded up in this + fashion. Without the workaround, these libraries tend to + generate large numbers of errors when they access the ends of + these areas. +

    + Valgrind snapshots dated 17 Feb 2002 and later are + cleverer about this problem, and you should no longer need to + use this flag. To put it bluntly, if you do need to use this + flag, your program violates the ANSI C semantics defined for + malloc and free, even if it appears to + work correctly, and you should fix it, at least if you hope for + maximum portability. +


  • +

+ +

Rare options

+These options apply to all tools, as they affect certain obscure workings of +the Valgrind core. Most people won't need to use these. +
    +
  • --run-libc-freeres=yes [default]
    + --run-libc-freeres=no +

    The GNU C library (libc.so), which is used by + all programs, may allocate memory for its own uses. Usually it + doesn't bother to free that memory when the program ends - there + would be no point, since the Linux kernel reclaims all process + resources when a process exits anyway, so it would just slow + things down. +

    + The glibc authors realised that this behaviour causes leak + checkers, such as Valgrind, to falsely report leaks in glibc, + when a leak check is done at exit. In order to avoid this, they + provided a routine called __libc_freeres + specifically to make glibc release all memory it has allocated. + Memcheck and Addrcheck therefore try and run + __libc_freeres at exit. +

    + Unfortunately, in some versions of glibc, + __libc_freeres is sufficiently buggy to cause + segmentation faults. This is particularly noticeable on Red Hat + 7.1. So this flag is provided in order to inhibit the run of + __libc_freeres. If your program seems to run fine + on Valgrind, but segfaults at exit, you may find that + --run-libc-freeres=no fixes that, although at the + cost of possibly falsely reporting space leaks in + libc.so. +


  • + +

  • --weird-hacks=hack1,hack2,... + Pass miscellaneous hints to Valgrind which slightly modify the + simulated behaviour in nonstandard or dangerous ways, possibly + to help the simulation of strange features. By default no hacks + are enabled. Use with caution! Currently known hacks are: +

    +

      +
    • lax-ioctls Be very lax about ioctl handling; the only + assumption is that the size is correct. Doesn't require the full + buffer to be initialized when writing. Without this, using some + device drivers with a large number of strange ioctl commands becomes + very tiresome. +
    +

  • + +

  • --signal-polltime=<time> [default=50]
    +

    How often to poll for signals (in milliseconds). Only applies for + older kernels that need signal routing. +


  • + +

  • --lowlat-signals=no [default]
    + --lowlat-signals=yes
    +

    Improve wake-up latency when a thread receives a signal. +


  • + +

  • --lowlat-syscalls=no [default]
    + --lowlat-syscalls=yes
    +

    Improve wake-up latency when a thread's syscall completes. +


  • + +

+ +There are also some options for debugging Valgrind itself. You +shouldn't need to use them in the normal run of things. Nevertheless: + +
    + +
  • --single-step=no [default]
    + --single-step=yes +

    When enabled, each x86 insn is translated separately into + instrumented code. When disabled, translation is done on a + per-basic-block basis, giving much better translations.


  • +

    + +

  • --optimise=no
    + --optimise=yes [default] +

    When enabled, various improvements are applied to the + intermediate code, mainly aimed at allowing the simulated CPU's + registers to be cached in the real CPU's registers over several + simulated instructions.


  • +

    + +

  • --profile=no
    + --profile=yes [default] +

    When enabled, does crude internal profiling of Valgrind + itself. This is not for profiling your programs. Rather it is + to allow the developers to assess where Valgrind is spending + its time. The tools must be built for profiling for this to + work. +


  • + +

  • --trace-syscalls=no [default]
    + --trace-syscalls=yes +

    Enable/disable tracing of system call intercepts.


  • +

    + +

  • --trace-signals=no [default]
    + --trace-signals=yes +

    Enable/disable tracing of signal handling.


  • +

    + +

  • --trace-sched=no [default]
    + --trace-sched=yes +

    Enable/disable tracing of thread scheduling events.


  • +

    + +

  • --trace-pthread=none [default]
    + --trace-pthread=some
    + --trace-pthread=all +

    Specifies amount of trace detail for pthread-related events.


  • +

    + +

  • --trace-symtab=no [default]
    + --trace-symtab=yes +

    Enable/disable tracing of symbol table reading.


  • +

    + +

  • --trace-malloc=no [default]
    + --trace-malloc=yes +

    Enable/disable tracing of malloc/free (et al) intercepts. +


  • +

    + +

  • --trace-codegen=XXXXX [default: 00000] +

    Enable/disable tracing of code generation. Code can be printed + at five different stages of translation; each X element + must be 0 or 1. +


  • +

    + +

  • --dump-error=<number> [default: inactive] +

    After the program has exited, show gory details of the + translation of the basic block containing the <number>'th + error context. When used with --single-step=yes, + can show the exact x86 instruction causing an error. This is + all fairly dodgy and doesn't work at all if threads are + involved.


  • +

    +

+ +

Setting default options

+ +

Note that Valgrind also reads options from three places: +

    +
  • The file ~/.valgrindrc +
  • The environment variable $VALGRIND_OPTS +
  • The file ./.valgrindrc +
+These are processed in the given order, before the command-line options. +Options processed later override those processed earlier; for example, +options in ./.valgrindrc will take precedence over those in +~/.valgrindrc. The first two are particularly useful for +setting the default tool to use. +

+Any tool-specific options put in $VALGRIND_OPTS or the +.valgrindrc files should be prefixed with the tool name and +a colon. For example, if you want Memcheck to always do leak checking, +you can put the following entry in ~/.valgrindrc: + +

+    --memcheck:leak-check=yes
+
+ +This will be ignored if any tool other than Memcheck is run. +Without the memcheck: part, this will cause problems if you +select other tools that don't understand --leak-check=yes. + + + +

2.7  The Client Request mechanism

+ +Valgrind has a trapdoor mechanism via which the client program can +pass all manner of requests and queries to Valgrind and the current tool. +Internally, this is used extensively to make malloc, free, signals, threads, +etc, work, although you don't see that. +

+For your convenience, a subset of these so-called client requests is +provided to allow you to tell Valgrind facts about the behaviour of +your program, and conversely to make queries. In particular, your +program can tell Valgrind about changes in memory range permissions +that Valgrind would not otherwise know about, and so allows clients to +get Valgrind to do arbitrary custom checks. +

+Clients need to include a header file to make this work. Which header file +depends on which client requests you use. Some client requests are handled by +the core, and are defined in the header file valgrind.h. +Tool-specific header files are named after the tool, e.g. +memcheck.h. All header files can be found in the +include directory of wherever Valgrind was installed. +

+The macros in these header files have the magical property that +they generate code in-line which Valgrind can spot. However, the code +does nothing when not run on Valgrind, so you are not forced to run +your program on Valgrind just because you use the macros in this file. +Also, you are not required to link your program with any extra +supporting libraries. +

+Here is a brief description of the macros available in +valgrind.h, which work with more than one tool (see the +tool-specific documentation for explanations of the tool-specific macros). +

    +
  • RUNNING_ON_VALGRIND: returns 1 if running on + Valgrind, 0 if running on the real CPU. +

    +

  • VALGRIND_DISCARD_TRANSLATIONS: discard translations + of code in the specified address range. Useful if you are + debugging a JITter or some other dynamic code generation system. + After this call, attempts to execute code in the invalidated + address range will cause Valgrind to make new translations of that + code, which is probably the semantics you want. Note that this is + implemented naively, and involves checking all 200191 entries in + the translation table to see if any of them overlap the specified + address range. So try not to call it often, or performance will + nosedive. Note that you can be clever about this: you only need + to call it when an area which previously contained code is + overwritten with new code. You can choose to write code into + fresh memory, and just call this occasionally to discard large + chunks of old code all at once. +

    + Warning: minimally tested, especially for tools other than Memcheck. +

    +

  • VALGRIND_COUNT_ERRORS: returns the number of errors + found so far by Valgrind. Can be useful in test harness code when + combined with the --log-fd=-1 option; this runs + Valgrind silently, but the client program can detect when errors + occur. Only useful for tools that report errors, e.g. it's useful for + Memcheck, but for Cachegrind it will always return zero because + Cachegrind doesn't report errors. +

    +

  • VALGRIND_MALLOCLIKE_BLOCK: If your program manages its own + memory instead of using the standard + malloc()/new/new[], tools that track + information about heap blocks will not do nearly as good a + job. For example, Memcheck won't detect nearly as many errors, and the + error messages won't be as informative. To improve this situation, use + this macro just after your custom allocator allocates some new memory. See + the comments in valgrind.h for information on how to use it. +

    +

  • VALGRIND_FREELIKE_BLOCK: This should be used in conjunction + with VALGRIND_MALLOCLIKE_BLOCK. Again, see + memcheck/memcheck.h for information on how to use it. +

    +

  • VALGRIND_CREATE_MEMPOOL: This is similar to + VALGRIND_MALLOCLIKE_BLOCK, but is tailored towards code + that uses memory pools. See the comments in valgrind.h + for information on how to use it. +

    +

  • VALGRIND_DESTROY_MEMPOOL: This should be used in + conjunction with VALGRIND_CREATE_MEMPOOL Again, see the + comments in valgrind.h for information on how to use it. +

    +

  • VALGRIND_MEMPOOL_ALLOC: This should be used in + conjunction with VALGRIND_CREATE_MEMPOOL Again, see the + comments in valgrind.h for information on how to use it. +

    +

  • VALGRIND_MEMPOOL_FREE: This should be used in + conjunction with VALGRIND_CREATE_MEMPOOL Again, see the + comments in valgrind.h for information on how to use it. +

    +

  • VALGRIND_NON_SIMD_CALL[0123]: executes a function of 0, 1, 2 + or 3 args in the client program on the real CPU, not the virtual + CPU that Valgrind normally runs code on. These are used in various ways + internally to Valgrind. They might be useful to client programs. + Warning: Only use these if you really know what you are + doing. +

    +

  • VALGRIND_PRINTF(format, ...): printf a message to the + log file when running under Valgrind. Nothing is output if not + running under Valgrind. Returns the number of characters output. +

    +

  • VALGRIND_PRINTF_BACKTRACE(format, ...): printf a message + to the log file along with a stack backtrace when running under + Valgrind. Nothing is output if not running under Valgrind. + Returns the number of characters output. +

    +

+Note that valgrind.h is included by all the tool-specific header +files (such as memcheck.h), so you don't need to include it in +your client if you include a tool-specific header. +

+ + + +

2.8  Support for POSIX Pthreads

+ +Valgrind supports programs which use POSIX pthreads. Getting this to work was +technically challenging but it all works well enough for significant threaded +applications to work. +

+It works as follows: threaded apps are (dynamically) linked against +libpthread.so. Usually this is the one installed with +your Linux distribution. Valgrind, however, supplies its own +libpthread.so and automatically connects your program to +it instead. +

+The fake libpthread.so and Valgrind cooperate to +implement a user-space pthreads package. This approach avoids the +horrible implementation problems of implementing a truly +multiprocessor version of Valgrind, but it does mean that threaded +apps run only on one CPU, even if you have a multiprocessor machine. +

+Valgrind schedules your threads in a round-robin fashion, with all +threads having equal priority. It switches threads every 50000 basic +blocks (typically around 300000 x86 instructions), which means you'll +get a much finer interleaving of thread executions than when run +natively. This in itself may cause your program to behave differently +if you have some kind of concurrency, critical race, locking, or +similar, bugs. +

+As of the Valgrind-1.0 release, the state of pthread support was as follows: +

    +
  • Mutexes, condition variables, thread-specific data, + pthread_once, reader-writer locks, semaphores, + cleanup stacks, cancellation and thread detaching currently work. + Various attribute-like calls are handled but ignored; you get a + warning message. +

    +

  • Currently the following syscalls are thread-safe (nonblocking): + write read nanosleep + sleep select poll + recvmsg and + accept. +

    +

  • Signals in pthreads are now handled properly(ish): + pthread_sigmask, pthread_kill, + sigwait and raise are now implemented. + Each thread has its own signal mask, as POSIX requires. + It's a bit kludgey -- there's a system-wide pending signal set, + rather than one for each thread. But hey. +
+ +As of 18 May 02, the following threaded programs now work fine on my +RedHat 7.2 box: Opera 6.0Beta2, KNode in KDE 3.0, Mozilla-0.9.2.1 and +Galeon-0.11.3, both as supplied with RedHat 7.2. Also Mozilla 1.0RC2. +OpenOffice 1.0. MySQL 3.something (the current stable release). + + + + +

2.9  Handling of signals

+ +Valgrind provides suitable handling of signals, so, provided you stick +to POSIX stuff, you should be ok. Basic sigaction() and sigprocmask() +are handled. Signal handlers may return in the normal way or do +longjmp(); both should work ok. As specified by POSIX, a signal is +blocked in its own handler. Default actions for signals should work +as before. Etc, etc. + +

Under the hood, dealing with signals is a real pain, and Valgrind's +simulation leaves much to be desired. If your program does +way-strange stuff with signals, bad things may happen. If so, let me +know. I don't promise to fix it, but I'd at least like to be aware of +it. + + + + +

2.10  Building and installing

+ +We now use the standard Unix ./configure, +make, make install mechanism, and I have +attempted to ensure that it works on machines with kernel 2.4 or 2.6 +and glibc 2.2.X or 2.3.X. I don't think there is much else to say. +There are no options apart from the usual --prefix that +you should give to ./configure. + +

+The configure script tests the version of the X server +currently indicated by the current $DISPLAY. This is a +known bug. The intention was to detect the version of the current +XFree86 client libraries, so that correct suppressions could be +selected for them, but instead the test checks the server version. +This is just plain wrong. + +

+If you are building a binary package of Valgrind for distribution, +please read README_PACKAGERS. It contains some important +information. + +

+Apart from that there is no excitement here. Let me know if you have +build problems. + + + + +

2.11  If you have problems

+Contact us at valgrind.kde.org. + +

See this section for the known limitations of +Valgrind, and for a list of programs which are known not to work on +it. + +

The translator/instrumentor has a lot of assertions in it. They +are permanently enabled, and I have no plans to disable them. If one +of these breaks, please mail us! + +

If you get an assertion failure on the expression +chunkSane(ch) in vg_free() in +vg_malloc.c, this may have happened because your program +wrote off the end of a malloc'd block, or before its beginning. +Valgrind should have emitted a proper message to that effect before +dying in this way. This is a known problem which I should fix. + +

+Read the file FAQ.txt in the source distribution, for +more advice about common problems, crashes, etc. + + +

2.12  Limitations

+ +The following list of limitations seems depressingly long. However, +most programs actually work fine. + +

Valgrind will run x86-GNU/Linux ELF dynamically linked binaries, on +a kernel 2.4.X or 2.6.X system, subject to the following constraints: + +

    +
  • No support for 3DNow instructions. If the translator encounters + these, Valgrind will generate a SIGILL when the instruction is + executed.
  • +

    + +

  • Pthreads support is improving, but there are still significant + limitations in that department. See the section above on + Pthreads. Note that your program must be dynamically linked + against libpthread.so, so that Valgrind can + substitute its own implementation at program startup time. If + you're statically linked against it, things will fail + badly.
  • +

    + +

  • Memcheck assumes that the floating point registers are + not used as intermediaries in memory-to-memory copies, so it + immediately checks definedness of values loaded from memory by + floating-point loads. If you want to write code which copies + around possibly-uninitialised values, you must ensure these + travel through the integer registers, not the FPU.
  • +

    + +

  • If your program does its own memory management, rather than + using malloc/new/free/delete, it should still work, but + Valgrind's error checking won't be so effective. + If you describe your program's memory management scheme + using "client requests" (Section 3.7 of this manual), + Memcheck can do better. Nevertheless, using malloc/new + and free/delete is still the best approach. +
  • +

    + +

  • Valgrind's signal simulation is not as robust as it could be. + Basic POSIX-compliant sigaction and sigprocmask functionality is + supplied, but it's conceivable that things could go badly awry + if you do weird things with signals. Workaround: don't. + Programs that do non-POSIX signal tricks are in any case + inherently unportable, so should be avoided if + possible.
  • +

    + +

  • Programs which switch stacks are not well handled. Valgrind + does have support for this, but I don't have great faith in it. + It's difficult -- there's no cast-iron way to decide whether a + large change in %esp is as a result of the program switching + stacks, or merely allocating a large object temporarily on the + current stack -- yet Valgrind needs to handle the two situations + differently.
  • +

    + +

  • x86 instructions, and system calls, have been implemented on + demand. So it's possible, although unlikely, that a program + will fall over with a message to that effect. If this happens, + please report ALL the details printed out, so we can try and + implement the missing feature.
  • +

    + +

  • x86 floating point works correctly, but floating-point code may + run even more slowly than integer code, due to my simplistic + approach to FPU emulation.
  • +

    + +

  • Memory consumption of your program is majorly increased whilst + running under Valgrind. This is due to the large amount of + administrative information maintained behind the scenes. Another + cause is that Valgrind dynamically translates the original + executable. Translated, instrumented code is 14-16 times larger + than the original (!) so you can easily end up with 30+ MB of + translations when running (eg) a web browser. +
  • +

    + +

  • Valgrind can handle dynamically-generated code just fine. + However, if you regenerate code over the top of old code + (ie. at the same memory addresses) Valgrind will not realise the + code has changed, and will run its old translations, which will + be out-of-date. You need to use the VALGRIND_DISCARD_TRANSLATIONS + client request in that case. For the same reason gcc's + + trampolines for nested functions are currently + unsupported, see + bug 69511. +
  • +

    + +

+ +Programs which are known not to work are: + +
    +
  • emacs starts up but immediately concludes it is out of memory + and aborts. Emacs has it's own memory-management scheme, but I + don't understand why this should interact so badly with + Valgrind. Emacs works fine if you build it to use the standard + malloc/free routines.

  • +

    +

+ +Known platform-specific limitations, as of release 1.0.0: + +
    +
  • On Red Hat 7.3, there have been reports of link errors (at + program start time) for threaded programs using + __pthread_clock_gettime and + __pthread_clock_settime. This appears to be due to + /lib/librt-2.2.5.so needing them. Unfortunately I + do not understand enough about this problem to fix it properly, + and I can't reproduce it on my test RedHat 7.3 system. Please + mail me if you have more information / understanding.

  • +

    +

+ + + + +

2.13  How it works -- a rough overview

+Some gory details, for those with a passion for gory details. You +don't need to read this section if all you want to do is use Valgrind. +What follows is an outline of the machinery. A more detailed +(and somewhat out of date) description is to be found +here. + + +

2.13.1  Getting started

+ +Valgrind is compiled into a shared object, valgrind.so. The shell +script valgrind sets the LD_PRELOAD environment variable to point to +valgrind.so. This causes the .so to be loaded as an extra library to +any subsequently executed dynamically-linked ELF binary, viz, the +program you want to debug. + +

The dynamic linker allows each .so in the process image to have an +initialisation function which is run before main(). It also allows +each .so to have a finalisation function run after main() exits. + +

When valgrind.so's initialisation function is called by the dynamic +linker, the synthetic CPU to starts up. The real CPU remains locked +in valgrind.so for the entire rest of the program, but the synthetic +CPU returns from the initialisation function. Startup of the program +now continues as usual -- the dynamic linker calls all the other .so's +initialisation routines, and eventually runs main(). This all runs on +the synthetic CPU, not the real one, but the client program cannot +tell the difference. + +

Eventually main() exits, so the synthetic CPU calls valgrind.so's +finalisation function. Valgrind detects this, and uses it as its cue +to exit. It prints summaries of all errors detected, possibly checks +for memory leaks, and then exits the finalisation routine, but now on +the real CPU. The synthetic CPU has now lost control -- permanently +-- so the program exits back to the OS on the real CPU, just as it +would have done anyway. + +

On entry, Valgrind switches stacks, so it runs on its own stack. +On exit, it switches back. This means that the client program +continues to run on its own stack, so we can switch back and forth +between running it on the simulated and real CPUs without difficulty. +This was an important design decision, because it makes it easy (well, +significantly less difficult) to debug the synthetic CPU. + + + +

2.13.2  The translation/instrumentation engine

+ +Valgrind does not directly run any of the original program's code. Only +instrumented translations are run. Valgrind maintains a translation +table, which allows it to find the translation quickly for any branch +target (code address). If no translation has yet been made, the +translator - a just-in-time translator - is summoned. This makes an +instrumented translation, which is added to the collection of +translations. Subsequent jumps to that address will use this +translation. + +

Valgrind no longer directly supports detection of self-modifying +code. Such checking is expensive, and in practice (fortunately) +almost no applications need it. However, to help people who are +debugging dynamic code generation systems, there is a Client Request +(basically a macro you can put in your program) which directs Valgrind +to discard translations in a given address range. So Valgrind can +still work in this situation provided the client tells it when +code has become out-of-date and needs to be retranslated. + +

The JITter translates basic blocks -- blocks of straight-line-code +-- as single entities. To minimise the considerable difficulties of +dealing with the x86 instruction set, x86 instructions are first +translated to a RISC-like intermediate code, similar to sparc code, +but with an infinite number of virtual integer registers. Initially +each insn is translated seperately, and there is no attempt at +instrumentation. + +

The intermediate code is improved, mostly so as to try and cache +the simulated machine's registers in the real machine's registers over +several simulated instructions. This is often very effective. Also, +we try to remove redundant updates of the simulated machines's +condition-code register. + +

The intermediate code is then instrumented, giving more +intermediate code. There are a few extra intermediate-code operations +to support instrumentation; it is all refreshingly simple. After +instrumentation there is a cleanup pass to remove redundant value +checks. + +

This gives instrumented intermediate code which mentions arbitrary +numbers of virtual registers. A linear-scan register allocator is +used to assign real registers and possibly generate spill code. All +of this is still phrased in terms of the intermediate code. This +machinery is inspired by the work of Reuben Thomas (Mite). + +

Then, and only then, is the final x86 code emitted. The +intermediate code is carefully designed so that x86 code can be +generated from it without need for spare registers or other +inconveniences. + +

The translations are managed using a traditional LRU-based caching +scheme. The translation cache has a default size of about 14MB. + + + +

2.13.3  Tracking the status of memory

Each byte in the +process' address space has nine bits associated with it: one A bit and +eight V bits. The A and V bits for each byte are stored using a +sparse array, which flexibly and efficiently covers arbitrary parts of +the 32-bit address space without imposing significant space or +performance overheads for the parts of the address space never +visited. The scheme used, and speedup hacks, are described in detail +at the top of the source file vg_memory.c, so you should read that for +the gory details. + + + +

2.13.4 System calls

+All system calls are intercepted. The memory status map is consulted +before and updated after each call. It's all rather tiresome. See +coregrind/vg_syscalls.c for details. + + + +

2.13.5  Signals

+All system calls to sigaction() and sigprocmask() are intercepted. If +the client program is trying to set a signal handler, Valgrind makes a +note of the handler address and which signal it is for. Valgrind then +arranges for the same signal to be delivered to its own handler. + +

When such a signal arrives, Valgrind's own handler catches it, and +notes the fact. At a convenient safe point in execution, Valgrind +builds a signal delivery frame on the client's stack and runs its +handler. If the handler longjmp()s, there is nothing more to be said. +If the handler returns, Valgrind notices this, zaps the delivery +frame, and carries on where it left off before delivering the signal. + +

The purpose of this nonsense is that setting signal handlers +essentially amounts to giving callback addresses to the Linux kernel. +We can't allow this to happen, because if it did, signal handlers +would run on the real CPU, not the simulated one. This means the +checking machinery would not operate during the handler run, and, +worse, memory permissions maps would not be updated, which could cause +spurious error reports once the handler had returned. + +

An even worse thing would happen if the signal handler longjmp'd +rather than returned: Valgrind would completely lose control of the +client program. + +

Upshot: we can't allow the client to install signal handlers +directly. Instead, Valgrind must catch, on behalf of the client, any +signal the client asks to catch, and must delivery it to the client on +the simulated CPU, not the real one. This involves considerable +gruesome fakery; see vg_signals.c for details. +

+ + + + +

2.14  An example run

+This is the log for a run of a small program using Memcheck +The program is in fact correct, and the reported error is as the +result of a potentially serious code generation bug in GNU g++ +(snapshot 20010527). +
+sewardj@phoenix:~/newmat10$
+~/Valgrind-6/valgrind -v ./bogon 
+==25832== Valgrind 0.10, a memory error detector for x86 RedHat 7.1.
+==25832== Copyright (C) 2000-2001, and GNU GPL'd, by Julian Seward.
+==25832== Startup, with flags:
+==25832== --suppressions=/home/sewardj/Valgrind/redhat71.supp
+==25832== reading syms from /lib/ld-linux.so.2
+==25832== reading syms from /lib/libc.so.6
+==25832== reading syms from /mnt/pima/jrs/Inst/lib/libgcc_s.so.0
+==25832== reading syms from /lib/libm.so.6
+==25832== reading syms from /mnt/pima/jrs/Inst/lib/libstdc++.so.3
+==25832== reading syms from /home/sewardj/Valgrind/valgrind.so
+==25832== reading syms from /proc/self/exe
+==25832== loaded 5950 symbols, 142333 line number locations
+==25832== 
+==25832== Invalid read of size 4
+==25832==    at 0x8048724: _ZN10BandMatrix6ReSizeEiii (bogon.cpp:45)
+==25832==    by 0x80487AF: main (bogon.cpp:66)
+==25832==    by 0x40371E5E: __libc_start_main (libc-start.c:129)
+==25832==    by 0x80485D1: (within /home/sewardj/newmat10/bogon)
+==25832==    Address 0xBFFFF74C is not stack'd, malloc'd or free'd
+==25832==
+==25832== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
+==25832== malloc/free: in use at exit: 0 bytes in 0 blocks.
+==25832== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
+==25832== For a detailed leak analysis, rerun with: --leak-check=yes
+==25832==
+==25832== exiting, did 1881 basic blocks, 0 misses.
+==25832== 223 translations, 3626 bytes in, 56801 bytes out.
+
+

The GCC folks fixed this about a week before gcc-3.0 shipped. +

+ + +

2.15  Warning messages you might see

+ +Most of these only appear if you run in verbose mode (enabled by +-v): +
    +
  • More than 50 errors detected. Subsequent errors + will still be recorded, but in less detail than before. +
    + After 50 different errors have been shown, Valgrind becomes + more conservative about collecting them. It then requires only + the program counters in the top two stack frames to match when + deciding whether or not two errors are really the same one. + Prior to this point, the PCs in the top four frames are required + to match. This hack has the effect of slowing down the + appearance of new errors after the first 50. The 50 constant can + be changed by recompiling Valgrind. +

    +

  • More than 300 errors detected. I'm not reporting any more. + Final error counts may be inaccurate. Go fix your + program! +
    + After 300 different errors have been detected, Valgrind ignores + any more. It seems unlikely that collecting even more different + ones would be of practical help to anybody, and it avoids the + danger that Valgrind spends more and more of its time comparing + new errors against an ever-growing collection. As above, the 300 + number is a compile-time constant. +

    +

  • Warning: client switching stacks? +
    + Valgrind spotted such a large change in the stack pointer, %esp, + that it guesses the client is switching to a different stack. + At this point it makes a kludgey guess where the base of the new + stack is, and sets memory permissions accordingly. You may get + many bogus error messages following this, if Valgrind guesses + wrong. At the moment "large change" is defined as a change of + more that 2000000 in the value of the %esp (stack pointer) + register. +

    +

  • Warning: client attempted to close Valgrind's logfile fd <number> + +
    + Valgrind doesn't allow the client + to close the logfile, because you'd never see any diagnostic + information after that point. If you see this message, + you may want to use the --log-fd=<number> + option to specify a different logfile file-descriptor number. + Or +

    +

  • Warning: noted but unhandled ioctl <number> +
    + Valgrind observed a call to one of the vast family of + ioctl system calls, but did not modify its + memory status info (because I have not yet got round to it). + The call will still have gone through, but you may get spurious + errors after this as a result of the non-update of the memory info. +

    +

  • Warning: set address range perms: large range <number> +
    + Diagnostic message, mostly for benefit of the valgrind + developers, to do with memory permissions. +
+ + + + + + diff --git a/VEX/head20041019/coregrind/docs/coregrind_intro.html b/VEX/head20041019/coregrind/docs/coregrind_intro.html new file mode 100644 index 0000000000..662e205fcd --- /dev/null +++ b/VEX/head20041019/coregrind/docs/coregrind_intro.html @@ -0,0 +1,162 @@ + + + +

1  Introduction

+ + +

1.1  An overview of Valgrind

+ +Valgrind is a flexible system for debugging and profiling Linux-x86 +executables. The system consists of a core, which provides a synthetic +x86 CPU in software, and a series of tools, each of which performs some +kind of debugging, profiling, or similar task. The architecture is +modular, so that new tools can be created easily and without disturbing +the existing structure. + +

+A number of useful tools are supplied as standard. In summary, these +are: + +

    +
  • Memcheck detects memory-management problems in your programs. + All reads and writes of memory are checked, and calls to + malloc/new/free/delete are intercepted. As a result, Memcheck can + detect the following problems: +
      +
    • Use of uninitialised memory
    • +
    • Reading/writing memory after it has been free'd
    • +
    • Reading/writing off the end of malloc'd blocks
    • +
    • Reading/writing inappropriate areas on the stack
    • +
    • Memory leaks -- where pointers to malloc'd blocks are lost + forever
    • +
    • Mismatched use of malloc/new/new [] vs free/delete/delete []
    • +
    • Overlapping src and dst pointers in + memcpy() and related functions
    • +
    • Some misuses of the POSIX pthreads API
    • +
    +

    + Problems like these can be difficult to find by other means, often + lying undetected for long periods, then causing occasional, + difficult-to-diagnose crashes. +

    +

  • Addrcheck is a lightweight version of + Memcheck. It is identical to Memcheck except + for the single detail that it does not do any uninitialised-value + checks. All of the other checks -- primarily the fine-grained + address checking -- are still done. The downside of this is that + you don't catch the uninitialised-value errors that + Memcheck can find. +

    + But the upside is significant: programs run about twice as fast as + they do on Memcheck, and a lot less memory is used. It + still finds reads/writes of freed memory, memory off the end of + blocks and in other invalid places, bugs which you really want to + find before release! +

    + Because Addrcheck is lighter and faster than + Memcheck, you can run more programs for longer, and so you + may be able to cover more test scenarios. Addrcheck was + created because one of us (Julian) wanted to be able to + run a complete KDE desktop session with checking. As of early + November 2002, we have been able to run KDE-3.0.3 on a 1.7 GHz P4 + with 512 MB of memory, using Addrcheck. Although the + result is not stellar, it's quite usable, and it seems plausible + to run KDE for long periods at a time like this, collecting up + all the addressing errors that appear. +

    +

  • Cachegrind is a cache profiler. It performs detailed simulation of + the I1, D1 and L2 caches in your CPU and so can accurately + pinpoint the sources of cache misses in your code. If you desire, + it will show the number of cache misses, memory references and + instructions accruing to each line of source code, with + per-function, per-module and whole-program summaries. If you ask + really nicely it will even show counts for each individual x86 + instruction. +

    + Cachegrind auto-detects your machine's cache configuration + using the CPUID instruction, and so needs no further + configuration info, in most cases. +

    + Cachegrind is nicely complemented by Josef Weidendorfer's + amazing KCacheGrind visualisation tool ( + http://kcachegrind.sourceforge.net), a KDE application which + presents these profiling results in a graphical and + easier-to-understand form. +

    +

  • Helgrind finds data races in multithreaded programs. + Helgrind looks for + memory locations which are accessed by more than one (POSIX + p-)thread, but for which no consistently used (pthread_mutex_)lock + can be found. Such locations are indicative of missing + synchronisation between threads, and could cause hard-to-find + timing-dependent problems. +

    + Helgrind ("Hell's Gate", in Norse mythology) implements the + so-called "Eraser" data-race-detection algorithm, along with + various refinements (thread-segment lifetimes) which reduce the + number of false errors it reports. It is as yet somewhat of an + experimental tool, so your feedback is especially welcomed here. +

    + Helgrind has been hacked on extensively by Jeremy + Fitzhardinge, and we have him to thank for getting it to a + releasable state. +

+ +A number of minor tools (corecheck, lackey and +Nulgrind) are also supplied. These aren't particularly useful -- +they exist to illustrate how to create simple tools and to help the +valgrind developers in various ways. + + +

+Valgrind is closely tied to details of the CPU, operating system and +to a less extent, compiler and basic C libraries. This makes it +difficult to make it portable, so we have chosen at the outset to +concentrate on what we believe to be a widely used platform: Linux on +x86s. Valgrind uses the standard Unix ./configure, +make, make install mechanism, and we have +attempted to ensure that it works on machines with kernel 2.2 or 2.4 +and glibc 2.1.X, 2.2.X or 2.3.1. This should cover the vast majority +of modern Linux installations. Note that glibc-2.3.2+, with the +NPTL (Native Posix Threads Library) package won't work. We hope to +be able to fix this, but it won't be easy. + + +

+Valgrind is licensed under the GNU General Public License, version +2. Read the file LICENSE in the source distribution for details. Some +of the PThreads test cases, pth_*.c, are taken from +"Pthreads Programming" by Bradford Nichols, Dick Buttlar & +Jacqueline Proulx Farrell, ISBN 1-56592-115-1, published by O'Reilly +& Associates, Inc. + + + + + +

1.2  How to navigate this manual

+ +The Valgrind distribution consists of the Valgrind core, upon which are +built Valgrind tools, which do different kinds of debugging and +profiling. This manual is structured similarly. + +

+First, we describe the Valgrind core, how to use it, and the flags it +supports. Then, each tool has its own chapter in this manual. You only +need to read the documentation for the core and for the tool(s) you +actually use, although you may find it helpful to be at least a little +bit familar with what all tools do. If you're new to all this, you +probably want to run the Memcheck tool. If you want to write a new tool, +read this. + +

+Be aware that the core understands some command line flags, and the +tools have their own flags which they know about. This means +there is no central place describing all the flags that are accepted +-- you have to read the flags documentation both for +Valgrind's core +and for the tool you want to use. + +

+ diff --git a/VEX/head20041019/coregrind/docs/coregrind_tools.html b/VEX/head20041019/coregrind/docs/coregrind_tools.html new file mode 100644 index 0000000000..20b42a3604 --- /dev/null +++ b/VEX/head20041019/coregrind/docs/coregrind_tools.html @@ -0,0 +1,761 @@ + + + + Valgrind + + + + +  +

Valgrind Tools

+
+ A guide to writing new tools for Valgrind
+ This guide was last updated on 20030520 +
+

+ +

+njn25@cam.ac.uk
+Nick Nethercote +

+Valgrind is licensed under the GNU General Public License, +version 2
+An open-source tool for supervising execution of Linux-x86 executables. +

+ +

+ +


+ +

Contents of this manual

+ +

Introduction

+ 1.1  Supervised Execution
+ 1.2  Tools
+ 1.3  Execution Spaces
+ +

Writing a Tool

+ 2.1  Why write a tool?
+ 2.2  Suggested tools
+ 2.3  How tools work
+ 2.4  Getting the code
+ 2.5  Getting started
+ 2.6  Writing the code
+ 2.7  Initialisation
+ 2.8  Instrumentation
+ 2.9  Finalisation
+ 2.10  Other important information
+ 2.11  Words of advice
+ +

Advanced Topics

+ 3.1  Suppressions
+ 3.2  Documentation
+ 3.3  Regression tests
+ 3.4  Profiling
+ 3.5  Other makefile hackery
+ 3.6  Core/tool interface versions
+ +

Final Words

+ +
+ + +

1  Introduction

+ + +

1.1  Supervised Execution

+ +Valgrind provides a generic infrastructure for supervising the execution of +programs. This is done by providing a way to instrument programs in very +precise ways, making it relatively easy to support activities such as dynamic +error detection and profiling.

+ +Although writing a tool is not easy, and requires learning quite a few things +about Valgrind, it is much easier than instrumenting a program from scratch +yourself. + + +

1.2  Tools

+The key idea behind Valgrind's architecture is the division between its +``core'' and ``tools''. +

+The core provides the common low-level infrastructure to support program +instrumentation, including the x86-to-x86 JIT compiler, low-level memory +manager, signal handling and a scheduler (for pthreads). It also provides +certain services that are useful to some but not all tools, such as support +for error recording and suppression. +

+But the core leaves certain operations undefined, which must be filled by tools. +Most notably, tools define how program code should be instrumented. They can +also define certain variables to indicate to the core that they would like to +use certain services, or be notified when certain interesting events occur. +But the core takes care of all the hard work. +

+ + +

1.3  Execution Spaces

+An important concept to understand before writing a tool is that there are +three spaces in which program code executes: + +
    +
  1. User space: this covers most of the program's execution. The tool is + given the code and can instrument it any way it likes, providing (more or + less) total control over the code.

    + + Code executed in user space includes all the program code, almost all of + the C library (including things like the dynamic linker), and almost + all parts of all other libraries. +

  2. + +

  3. Core space: a small proportion of the program's execution takes place + entirely within Valgrind's core. This includes:

    + +

      +
    • Dynamic memory management (malloc() etc.)
    • + +
    • Pthread operations and scheduling
    • + +
    • Signal handling
    • +

    + + A tool has no control over these operations; it never ``sees'' the code + doing this work and thus cannot instrument it. However, the core + provides hooks so a tool can be notified when certain interesting events + happen, for example when when dynamic memory is allocated or freed, the + stack pointer is changed, or a pthread mutex is locked, etc.

    + + Note that these hooks only notify tools of events relevant to user + space. For example, when the core allocates some memory for its own use, + the tool is not notified of this, because it's not directly part of the + supervised program's execution. +

  4. + +

  5. Kernel space: execution in the kernel. Two kinds:

    + +

      +
    1. System calls: can't be directly observed by either the tool or the + core. But the core does have some idea of what happens to the + arguments, and it provides hooks for a tool to wrap system calls. +
    2. + +

    3. Other: all other kernel activity (e.g. process scheduling) is + totally opaque and irrelevant to the program. +
    4. +

    +
  6. + + It should be noted that a tool only has direct control over code executed in + user space. This is the vast majority of code executed, but it is not + absolutely all of it, so any profiling information recorded by a tool won't + be totally accurate. +

+ + + +

2  Writing a Tool

+ + +

2.1  Why write a tool?

+ +Before you write a tool, you should have some idea of what it should do. What +is it you want to know about your programs of interest? Consider some existing +tools: + +
    +
  • memcheck: among other things, performs fine-grained validity and + addressibility checks of every memory reference performed by the program +
  • + +

  • addrcheck: performs lighterweight addressibility checks of every memory + reference performed by the program
  • + +

  • cachegrind: tracks every instruction and memory reference to simulate + instruction and data caches, tracking cache accesses and misses that + occur on every line in the program
  • + +

  • helgrind: tracks every memory access and mutex lock/unlock to determine + if a program contains any data races
  • + +

  • lackey: does simple counting of various things: the number of calls to a + particular function (_dl_runtime_resolve()); the number of + basic blocks, x86 instruction, UCode instructions executed; the number + of branches executed and the proportion of those which were taken.
  • +

+ +These examples give a reasonable idea of what kinds of things Valgrind can be +used for. The instrumentation can range from very lightweight (e.g. counting +the number of times a particular function is called) to very intrusive (e.g. +memcheck's memory checking). + + + +

2.2  Suggested tools

+ +Here is a list of ideas we have had for tools that should not be too hard to +implement. + +
    +
  • branch profiler: A machine's branch prediction hardware could be + simulated, and each branch annotated with the number of predicted and + mispredicted branches. Would be implemented quite similarly to + Cachegrind, and could reuse the cg_annotate script to + annotate source code.

    + + The biggest difficulty with this is the simulation; the chip-makers + are very cagey about how their chips do branch prediction. But + implementing one or more of the basic algorithms could still give good + information. +

  • + +

  • coverage tool: Cachegrind can already be used for doing test coverage, + but it's massive overkill to use it just for that.

    + + It would be easy to write a coverage tool that records how many times + each basic block was recorded. Again, the cg_annotate + script could be used for annotating source code with the gathered + information. Although, cg_annotate is only designed for + working with single program runs. It could be extended relatively easily + to deal with multiple runs of a program, so that the coverage of a whole + test suite could be determined.

    + + In addition to the standard coverage information, such a tool could + record extra information that would help a user generate test cases to + exercise unexercised paths. For example, for each conditional branch, + the tool could record all inputs to the conditional test, and print these + out when annotating.

    + +

  • run-time type checking: A nice example of a dynamic checker is given + in this paper: + +
    + Debugging via Run-Time Type Checking
    + Alexey Loginov, Suan Hsi Yong, Susan Horwitz and Thomas Reps
    + Proceedings of Fundamental Approaches to Software Engineering
    + April 2001. +
    + + Similar is the tool described in this paper: + +
    + Run-Time Type Checking for Binary Programs
    + Michael Burrows, Stephen N. Freund, Janet L. Wiener
    + Proceedings of the 12th International Conference on Compiler Construction + (CC 2003)
    + April 2003. +
    + + These approach can find quite a range of bugs, particularly in C and C++ + programs, and could be implemented quite nicely as a Valgrind tool.

    + + Ways to speed up this run-time type checking are described in this paper: + +

    + Reducing the Overhead of Dynamic Analysis
    + Suan Hsi Yong and Susan Horwitz
    + Proceedings of Runtime Verification '02
    + July 2002. +
    + + Valgrind's client requests could be used to pass information to a tool + about which elements need instrumentation and which don't. +
  • +

+ +We would love to hear from anyone who implements these or other tools. + + +

2.3  How tools work

+ +Tools must define various functions for instrumenting programs that are called +by Valgrind's core, yet they must be implemented in such a way that they can be +written and compiled without touching Valgrind's core. This is important, +because one of our aims is to allow people to write and distribute their own +tools that can be plugged into Valgrind's core easily.

+ +This is achieved by packaging each tool into a separate shared object which is +then loaded ahead of the core shared object valgrind.so, using the +dynamic linker's LD_PRELOAD variable. Any functions defined in +the tool that share the name with a function defined in core (such as +the instrumentation function SK_(instrument)()) override the +core's definition. Thus the core can call the necessary tool functions.

+ +This magic is all done for you; the shared object used is chosen with the +--tool option to the valgrind startup script. The +default tool used is memcheck, Valgrind's original memory checker. + + +

2.4  Getting the code

+ +To write your own tool, you'll need to check out a copy of Valgrind from the +CVS repository, rather than using a packaged distribution. This is because it +contains several extra files needed for writing tools.

+ +To check out the code from the CVS repository, first login: +

+cvs -d:pserver:anonymous@cvs.valgrind.sourceforge.net:/cvsroot/valgrind login +
+ +Then checkout the code. To get a copy of the current development version +(recommended for the brave only): +
+cvs -z3 -d:pserver:anonymous@cvs.valgrind.sourceforge.net:/cvsroot/valgrind co valgrind +
+ +To get a copy of the stable released branch: +
+cvs -z3 -d:pserver:anonymous@cvs.valgrind.sourceforge.net:/cvsroot/valgrind co -r TAG valgrind +
+ +where TAG has the form VALGRIND_X_Y_Z for +version X.Y.Z. + + +

2.5  Getting started

+ +Valgrind uses GNU automake and autoconf for the +creation of Makefiles and configuration. But don't worry, these instructions +should be enough to get you started even if you know nothing about those +tools.

+ +In what follows, all filenames are relative to Valgrind's top-level directory +valgrind/. + +

    +
  1. Choose a name for the tool, and an abbreviation that can be used as a + short prefix. We'll use foobar and fb as an + example. +
  2. + +

  3. Make a new directory foobar/ which will hold the tool. +
  4. + +

  5. Copy none/Makefile.am into foobar/. + Edit it by replacing all occurrences of the string + ``none'' with ``foobar'' and the one + occurrence of the string ``nl_'' with ``fb_''. + It might be worth trying to understand this file, at least a little; you + might have to do more complicated things with it later on. In + particular, the name of the vgskin_foobar_so_SOURCES variable + determines the name of the tool's shared object, which determines what + name must be passed to the --tool option to use the tool. +
  6. + +

  7. Copy none/nl_main.c into + foobar/, renaming it as fb_main.c. + Edit it by changing the lines in SK_(pre_clo_init)() + to something appropriate for the tool. These fields are used in the + startup message, except for bug_reports_to which is used + if a tool assertion fails. +
  8. + +

  9. Edit Makefile.am, adding the new directory + foobar to the SUBDIRS variable. +
  10. + +

  11. Edit configure.in, adding foobar/Makefile to the + AC_OUTPUT list. +
  12. + +

  13. Run: +
    +    autogen.sh
    +    ./configure --prefix=`pwd`/inst
    +    make install
    + + It should automake, configure and compile without errors, putting copies + of the tool's shared object vgskin_foobar.so in + foobar/ and + inst/lib/valgrind/. +
  14. + +

  15. You can test it with a command like +
    +    inst/bin/valgrind --tool=foobar date
    + + (almost any program should work; date is just an example). + The output should be something like this: +
    +==738== foobar-0.0.1, a foobarring tool for x86-linux.
    +==738== Copyright (C) 1066AD, and GNU GPL'd, by J. Random Hacker.
    +==738== Built with valgrind-1.1.0, a program execution monitor.
    +==738== Copyright (C) 2000-2003, and GNU GPL'd, by Julian Seward.
    +==738== Estimated CPU clock rate is 1400 MHz
    +==738== For more details, rerun with: -v
    +==738== 
    +Wed Sep 25 10:31:54 BST 2002
    +==738==
    + + The tool does nothing except run the program uninstrumented. +
  16. +

+ +These steps don't have to be followed exactly - you can choose different names +for your source files, and use a different --prefix for +./configure.

+ +Now that we've setup, built and tested the simplest possible tool, onto the +interesting stuff... + + + +

2.6  Writing the code

+ +A tool must define at least these four functions: +
+    SK_(pre_clo_init)()
+    SK_(post_clo_init)()
+    SK_(instrument)()
+    SK_(fini)()
+
+ +Also, it must use the macro VG_DETERMINE_INTERFACE_VERSION +exactly once in its source code. If it doesn't, you will get a link error +involving VG_(skin_interface_major_version). This macro is +used to ensure the core/tool interface used by the core and a plugged-in +tool are binary compatible. + +In addition, if a tool wants to use some of the optional services provided by +the core, it may have to define other functions. + + +

2.7  Initialisation

+ +Most of the initialisation should be done in SK_(pre_clo_init)(). +Only use SK_(post_clo_init)() if a tool provides command line +options and must do some initialisation after option processing takes place +(``clo'' stands for ``command line options'').

+ +First of all, various ``details'' need to be set for a tool, using the +functions VG_(details_*)(). Some are all compulsory, some aren't. +Some are used when constructing the startup message, +detail_bug_reports_to is used if VG_(skin_panic)() is +ever called, or a tool assertion fails. Others have other uses.

+ +Second, various ``needs'' can be set for a tool, using the functions +VG_(needs_*)(). They are mostly booleans, and can be left +untouched (they default to False). They determine whether a tool +can do various things such as: record, report and suppress errors; process +command line options; wrap system calls; record extra information about +malloc'd blocks, etc.

+ +For example, if a tool wants the core's help in recording and reporting errors, +it must set the skin_errors need to True, and then +provide definitions of six functions for comparing errors, printing out errors, +reading suppressions from a suppressions file, etc. While writing these +functions requires some work, it's much less than doing error handling from +scratch because the core is doing most of the work. See the type +VgNeeds in include/tool.h for full details of all +the needs.

+ +Third, the tool can indicate which events in core it wants to be notified +about, using the functions VG_(track_*)(). These include things +such as blocks of memory being malloc'd, the stack pointer changing, a mutex +being locked, etc. If a tool wants to know about this, it should set the +relevant pointer in the structure to point to a function, which will be called +when that event happens.

+ +For example, if the tool want to be notified when a new block of memory is +malloc'd, it should call VG_(track_new_mem_heap)() with an +appropriate function pointer, and the assigned function will be called each +time this happens.

+ +More information about ``details'', ``needs'' and ``trackable events'' can be +found in include/tool.h.

+ + +

2.8  Instrumentation

+ +SK_(instrument)() is the interesting one. It allows you to +instrument UCode, which is Valgrind's RISC-like intermediate language. +UCode is described in the technical docs for +Memcheck. + +The easiest way to instrument UCode is to insert calls to C functions when +interesting things happen. See the tool ``Lackey'' +(lackey/lk_main.c) for a simple example of this, or +Cachegrind (cachegrind/cg_main.c) for a more complex +example.

+ +A much more complicated way to instrument UCode, albeit one that might result +in faster instrumented programs, is to extend UCode with new UCode +instructions. This is recommended for advanced Valgrind hackers only! See +Memcheck for an example. + + +

2.9  Finalisation

+ +This is where you can present the final results, such as a summary of the +information collected. Any log files should be written out at this point. + + +

2.10  Other important information

+ +Please note that the core/tool split infrastructure is quite complex and +not brilliantly documented. Here are some important points, but there are +undoubtedly many others that I should note but haven't thought of.

+ +The file include/tool.h contains all the types, +macros, functions, etc. that a tool should (hopefully) need, and is the only +.h file a tool should need to #include.

+ +In particular, you probably shouldn't use anything from the C library (there +are deep reasons for this, trust us). Valgrind provides an implementation of a +reasonable subset of the C library, details of which are in +tool.h.

+ +Similarly, when writing a tool, you shouldn't need to look at any of the code +in Valgrind's core. Although it might be useful sometimes to help understand +something.

+ +tool.h has a reasonable amount of documentation in it that +should hopefully be enough to get you going. But ultimately, the tools +distributed (Memcheck, Addrcheck, Cachegrind, Lackey, etc.) are probably the +best documentation of all, for the moment.

+ +Note that the VG_ and SK_ macros are used heavily. +These just prepend longer strings in front of names to avoid potential +namespace clashes. We strongly recommend using the SK_ macro for +any global functions and variables in your tool, or writing a similar macro.

+ + +

2.11  Words of Advice

+ +Writing and debugging tools is not trivial. Here are some suggestions for +solving common problems.

+ +If you are getting segmentation faults in C functions used by your tool, the +usual GDB command: +

gdb prog core
+usually gives the location of the segmentation fault.

+ +If you want to debug C functions used by your tool, you can attach GDB to +Valgrind with some effort: +

    +
  • Enable the following code in coregrind/vg_main.c by + changing if (0) into if (1): +
    +   /* Hook to delay things long enough so we can get the pid and
    +      attach GDB in another shell. */
    +   if (0) { 
    +      Int p, q;
    +      for (p = 0; p < 50000; p++)
    +         for (q = 0; q < 50000; q++) ;
    +   }
    +
    +
  • + and rebuild Valgrind. + +

  • Then run: +
    valgrind prog
    + + Valgrind starts the program, printing its process id, and then delays for + a few seconds (you may have to change the loop bounds to get a suitable + delay).
  • + +

  • In a second shell run: + +
    gdb prog pid
  • +

+ +GDB may be able to give you useful information. Note that by default +most of the system is built with -fomit-frame-pointer, +and you'll need to get rid of this to extract useful tracebacks from +GDB.

+ +If you just want to know whether a program point has been reached, using the +OINK macro (in include/tool.h) can be easier than +using GDB.

+ +If you are having problems with your UCode instrumentation, it's likely that +GDB won't be able to help at all. In this case, Valgrind's +--trace-codegen option is invaluable for observing the results of +instrumentation.

+ +The other debugging command line options can be useful too (run valgrind +-h for the list).

+ + +

3  Advanced Topics

+ +Once a tool becomes more complicated, there are some extra things you may +want/need to do. + + +

3.1  Suppressions

+ +If your tool reports errors and you want to suppress some common ones, you can +add suppressions to the suppression files. The relevant files are +valgrind/*.supp; the final suppression file is aggregated from +these files by combining the relevant .supp files depending on the +versions of linux, X and glibc on a system. +

+Suppression types have the form tool_name:suppression_name. The +tool_name here is the name you specify for the tool during +initialisation with VG_(details_name)(). + + +

3.2  Documentation

+ +If you are feeling conscientious and want to write some HTML documentation for +your tool, follow these steps (using foobar as the example tool +name again): + +
    +
  1. Make a directory foobar/docs/. +
  2. + +

  3. Edit foobar/Makefile.am, adding docs to + the SUBDIRS variable. +
  4. + +

  5. Edit configure.in, adding + foobar/docs/Makefile to the AC_OUTPUT list. +
  6. + +

  7. Write foobar/docs/Makefile.am. Use + memcheck/docs/Makefile.am as an example. +
  8. + +

  9. Write the documentation, putting it in foobar/docs/. +
  10. +

+ + +

3.3  Regression tests

+ +Valgrind has some support for regression tests. If you want to write +regression tests for your tool: + +
    +
  1. Make a directory foobar/tests/. +
  2. + +

  3. Edit foobar/Makefile.am, adding tests to + the SUBDIRS variable. +
  4. + +

  5. Edit configure.in, adding + foobar/tests/Makefile to the AC_OUTPUT list. +
  6. + +

  7. Write foobar/tests/Makefile.am. Use + memcheck/tests/Makefile.am as an example. +
  8. + +

  9. Write the tests, .vgtest test description files, + .stdout.exp and .stderr.exp expected output + files. (Note that Valgrind's output goes to stderr.) Some details + on writing and running tests are given in the comments at the top of the + testing script tests/vg_regtest. +
  10. + +

  11. Write a filter for stderr results foobar/tests/filter_stderr. + It can call the existing filters in tests/. See + memcheck/tests/filter_stderr for an example; in particular + note the $dir trick that ensures the filter works correctly + from any directory. +
  12. +

+ + +

3.4  Profiling

+ +To do simple tick-based profiling of a tool, include the line +
+#include "vg_profile.c" +
+in the tool somewhere, and rebuild (you may have to make clean +first). Then run Valgrind with the --profile=yes option.

+ +The profiler is stack-based; you can register a profiling event with +VGP_(register_profile_event)() and then use the +VGP_PUSHCC and VGP_POPCC macros to record time spent +doing certain things. New profiling event numbers must not overlap with the +core profiling event numbers. See include/tool.h for details +and Memcheck for an example. + + + +

3.5  Other makefile hackery

+ +If you add any directories under valgrind/foobar/, you will +need to add an appropriate Makefile.am to it, and add a +corresponding entry to the AC_OUTPUT list in +valgrind/configure.in.

+ +If you add any scripts to your tool (see Cachegrind for an example) you need to +add them to the bin_SCRIPTS variable in +valgrind/foobar/Makefile.am.

+ + + +

3.5  Core/tool interface versions

+ +In order to allow for the core/tool interface to evolve over time, Valgrind +uses a basic interface versioning system. All a tool has to do is use the +VG_DETERMINE_INTERFACE_VERSION macro exactly once in its code. +If not, a link error will occur when the tool is built. +

+The interface version number has the form X.Y. Changes in Y indicate binary +compatible changes. Changes in X indicate binary incompatible changes. If +the core and tool has the same major version number X they should work +together. If X doesn't match, Valgrind will abort execution with an +explanation of the problem. +

+This approach was chosen so that if the interface changes in the future, +old tools won't work and the reason will be clearly explained, instead of +possibly crashing mysteriously. We have attempted to minimise the potential +for binary incompatible changes by means such as minimising the use of naked +structs in the interface. + + +

4  Final Words

+ +This whole core/tool business under active development, although it's slowly +maturing.

+ +The first consequence of this is that the core/tool interface will continue +to change in the future; we have no intention of freezing it and then +regretting the inevitable stupidities. Hopefully most of the future changes +will be to add new features, hooks, functions, etc, rather than to change old +ones, which should cause a minimum of trouble for existing tools, and we've put +some effort into future-proofing the interface to avoid binary incompatibility. +But we can't guarantee anything. The versioning system should catch any +incompatibilities. Just something to be aware of.

+ +The second consequence of this is that we'd love to hear your feedback about +it: + +

    +
  • If you love it or hate it
  • +

  • If you find bugs
  • +

  • If you write a tool
  • +

  • If you have suggestions for new features, needs, trackable events, + functions
  • +

  • If you have suggestions for making tools easier to write
  • +

  • If you have suggestions for improving this documentation
  • +

  • If you don't understand something
  • +

+ +or anything else!

+ +Happy programming. + diff --git a/VEX/head20041019/coregrind/dosyms b/VEX/head20041019/coregrind/dosyms new file mode 100755 index 0000000000..b7ba8503ec --- /dev/null +++ b/VEX/head20041019/coregrind/dosyms @@ -0,0 +1,31 @@ +#!/bin/sh + +# A simple script to help me ensure that my libpthread.so looks +# from the outside, to the linker, identical to the original. + +nm /lib/libpthread.so.0 | grep " T " | cut -c 10- > orig-T +nm /lib/libpthread.so.0 | grep " D " | cut -c 10- > orig-D +nm /lib/libpthread.so.0 | grep " W " | cut -c 10- > orig-W +nm /lib/libpthread.so.0 | grep " U " | cut -c 10- > orig-U + +nm ./libpthread.so | grep " T " | cut -c 10- > mine-T +nm ./libpthread.so | grep " D " | cut -c 10- > mine-D +nm ./libpthread.so | grep " W " | cut -c 10- > mine-W +nm ./libpthread.so | grep " U " | cut -c 10- > mine-U + +echo ========================== TEXT orig vs mine ========================= +sdiff -w 80 orig-T mine-T +echo + +echo ========================== WEAK orig vs mine ========================= +sdiff -w 80 orig-W mine-W +echo + +echo ========================== DATA orig vs mine ========================= +sdiff -w 80 orig-D mine-D +echo + +echo ========================== UNDF orig vs mine ========================= +sdiff -w 80 orig-U mine-U +echo + diff --git a/VEX/head20041019/coregrind/gen_intercepts.pl b/VEX/head20041019/coregrind/gen_intercepts.pl new file mode 100644 index 0000000000..ef23d4e59a --- /dev/null +++ b/VEX/head20041019/coregrind/gen_intercepts.pl @@ -0,0 +1,48 @@ +#!/usr/bin/perl + +# This file is part of Valgrind, an extensible x86 protected-mode +# emulator for monitoring program execution on x86-Unixes. +# +# Copyright (C) 2000-2004 Julian Seward +# jseward@acm.org +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA. +# +# The GNU General Public License is contained in the file COPYING. + +use strict; +use warnings; + +while(<>) { + if(/VG_INTERCEPT\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/) { + my $ver = $1 . ":" . $2; + $ver =~ s/\*/\$2A/g; + $ver =~ s/\+/\$2B/g; + $ver =~ s/\-/\$2D/g; + $ver =~ s/\./\$2E/g; + $ver =~ s/\:/\$3A/g; + s/VG_INTERCEPT\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/VG_INTERCEPT($ver)/g; + } elsif(/VG_INTERCEPT_ALIAS\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/) { + my $ver = $1 . ":" . $2; + $ver =~ s/\*/\$2A/g; + $ver =~ s/\+/\$2B/g; + $ver =~ s/\-/\$2D/g; + $ver =~ s/\./\$2E/g; + $ver =~ s/\:/\$3A/g; + s/VG_INTERCEPT_ALIAS\s*\(\s*(.*?)\s*,\s*(.*?)\s*\)/VG_INTERCEPT_ALIAS($ver)/g; + } + print $_; +} diff --git a/VEX/head20041019/coregrind/gen_toolint.pl b/VEX/head20041019/coregrind/gen_toolint.pl new file mode 100644 index 0000000000..edd716de9a --- /dev/null +++ b/VEX/head20041019/coregrind/gen_toolint.pl @@ -0,0 +1,292 @@ +#!/usr/bin/perl + +# This file is part of Valgrind, an extensible x86 protected-mode +# emulator for monitoring program execution on x86-Unixes. +# +# Copyright (C) 2000-2004 Julian Seward +# jseward@acm.org +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +# 02111-1307, USA. +# +# The GNU General Public License is contained in the file COPYING. + +use strict; +use warnings; + +my $output = shift @ARGV; +my $indent = ""; +my $headerguard; +my $include; +my $passcomment = 1; +my $pre; +my $post; +my $generate; + +my $struct = "VG_(tool_interface)"; + +my %pfxmap = ("track" => "SK_", + "tool" => "SK_", + "malloc"=> "SK_", + ); + +sub getargnames(@) { + my @args = @_; + my @ret; + + foreach my $a (@args) { + my @pieces = split /\s+/, $a; + my $name = pop @pieces; + push @ret, $name unless $name eq "void"; + } + return @ret; +} + +sub getargtypes(@) { + my @args = @_; + my @ret; + + foreach my $a (@args) { + my @pieces = split /\s+/, $a; + pop @pieces; + push @ret, (join " ", @pieces); + } + @ret = "void" if ($#ret == -1); + return @ret; +} + +# Different output modes +if ($output eq "callwrap") { + $include = "core.h"; + $generate = sub ($$$@) { + my ($pfx, $ret, $func, @args) = @_; + my $args = join ", ", @args; + my $argnames = join ", ", getargnames(@args); + print "$ret $pfxmap{$pfx}($func)($args)\n{\n"; + print " return (*$struct.${pfx}_$func)($argnames);\n"; + print "}\n"; + } +} elsif ($output eq "proto") { + $include = "core.h"; + $generate = sub ($$$@) { + my ($pfx, $ret, $func, @args) = @_; + my $args = join ', ', @args; + + print "$ret $pfxmap{$pfx}($func)($args);\n"; + print "Bool VG_(defined_$func)(void);\n"; + } +} elsif ($output eq "toolproto") { + $generate = sub ($$$@) { + my ($pfx, $ret, $func, @args) = @_; + my $args = join ', ', @args; + + print "$ret $pfxmap{$pfx}($func)($args);\n"; + } +} elsif ($output eq "missingfuncs") { + $include = "core.h"; + $generate = sub ($$$@) { + my ($pfx, $ret, $func, @args) = @_; + my $args = join ", ", @args; + + print "static $ret missing_${pfx}_$func($args) {\n"; + print " VG_(missing_tool_func)(\"${pfx}_$func\");\n"; + print "}\n"; + print "Bool VG_(defined_$func)(void) {\n"; + print " return $struct.${pfx}_$func != missing_${pfx}_$func;\n"; + print "}\n\n"; + }; + $indent = " "; +} elsif ($output eq "struct") { + $include = "core.h"; + $pre = sub () { + print "typedef struct {\n"; + }; + $post = sub () { + print "} VgToolInterface;\n\n"; + print "extern VgToolInterface $struct;\n" + }; + $generate = sub ($$$@) { + my ($pfx, $ret, $func, @args) = @_; + my $args = join ", ", @args; + + print "$indent$ret (*${pfx}_$func)($args);\n"; + }; + $indent = " "; + $headerguard=$output; +} elsif ($output eq "structdef") { + $include = "vg_toolint.h"; + $pre = sub () { + print "VgToolInterface $struct = {\n"; + }; + $post = sub () { + print "};\n"; + }; + $generate = sub ($$$@) { + my ($pfx, $ret, $func, @args) = @_; + + print "$indent.${pfx}_$func = missing_${pfx}_$func,\n" + }; + $indent = " "; +} elsif ($output eq "initfunc") { + $include = "tool.h"; + $generate = sub ($$$@) { + my ($pfx, $ret, $func, @args) = @_; + my $args = join ", ", @args; + my $argnames = join ", ", getargnames(@args); + + print < +void VG_(tool_init_dlsym)(void *dlhandle) +{ + void *ret; + +EOF + }; + $post = sub () { + print "}\n"; + }; + $generate = sub ($$$@) { + my ($pfx, $ret, $func, @args) = @_; + my $args = join ", ", getargtypes(@args); + + print <) { + # skip simple comments + next if (/^#[^#]/); + + if (/^:/) { + s/^://; + chomp; + $prefix=$_; + next; + } + + # look for inserted comments + if (/^##/) { + if ($state eq "idle") { + $state = "comment"; + $lines = 1; + $_ =~ s,^## ,/* ,; + $buf = $_; + next; + } elsif ($state eq "comment") { + $lines++; + $_ =~ s,^## , ,; + print $indent.$buf if $passcomment; + $buf = $_; + next; + } + next; + } + + # blank lines in a comment are part of the comment + if (/^\s*$/) { + if ($state eq "comment") { + $lines++; + print $indent.$buf if $passcomment; + $buf = "\n"; + } else { + print "\n" if $passcomment; + } + next; + } + + # coming out of a comment + if ($state eq "comment") { + chomp $buf; + + if ($passcomment) { + if ($lines == 1) { + print "$indent$buf */\n"; + } else { + print "$indent$buf\n$indent */\n"; + } + } + $buf = ""; + $state = "idle"; + } + + chomp; + my @func = split /,\s*/; + + my $rettype = shift @func; + my $funcname = shift @func; + + @func = "void" if scalar @func == 0; + + &$generate ($prefix, $rettype, $funcname, @func); +} + +&$post() if defined $post; # postamble + +print < +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "ume.h" + +static int stack[SIGSTKSZ*4]; + +// Initial stack pointer, which points to argc. +static void* init_sp; + +/* Where we expect to find all our aux files (namely, stage2) */ +static const char *valgrind_lib = VG_LIBDIR; + +/* stage2's name */ +static const char stage2[] = "stage2"; + +/*------------------------------------------------------------*/ +/*--- Auxv modification ---*/ +/*------------------------------------------------------------*/ + +/* Modify the auxv the kernel gave us to make it look like we were + execed as the shared object. + + This also inserts a new entry into the auxv table so we can + communicate some extra information to stage2 (namely, the fd of the + padding file, so it can identiry and remove the padding later). +*/ +static void *fix_auxv(void *v_init_esp, const struct exeinfo *info, + int padfile) +{ + struct ume_auxv *auxv; + int *newesp; + int seen; + int delta; + int i; + static const int new_entries = 2; + + /* make sure we're running on the private stack */ + assert(&delta >= stack && &delta < &stack[sizeof(stack)/sizeof(*stack)]); + + /* find the beginning of the AUXV table */ + auxv = find_auxv(v_init_esp); + + /* Work out how we should move things to make space for the new + auxv entry. It seems that ld.so wants a 16-byte aligned stack on + entry, so make sure that's the case. */ + newesp = (int *)(((unsigned long)v_init_esp - new_entries * sizeof(*auxv)) & ~0xf); + delta = (char *)v_init_esp - (char *)newesp; + + memmove(newesp, v_init_esp, (char *)auxv - (char *)v_init_esp); + + v_init_esp = (void *)newesp; + auxv -= delta/sizeof(*auxv); + + /* stage2 needs this so it can clean up the padding we leave in + place when we start it */ + auxv[0].a_type = AT_UME_PADFD; + auxv[0].u.a_val = padfile; + + /* This will be needed by valgrind itself so that it can + subsequently execve() children. This needs to be done here + because /proc/self/exe will go away once we unmap stage1. */ + auxv[1].a_type = AT_UME_EXECFD; + auxv[1].u.a_val = open("/proc/self/exe", O_RDONLY); + + /* make sure the rest are sane */ + for(i = new_entries; i < delta/sizeof(*auxv); i++) { + auxv[i].a_type = AT_IGNORE; + auxv[i].u.a_val = 0; + } + + /* OK, go through and patch up the auxv entries to match the new + executable */ + seen = 0; + for(; auxv->a_type != AT_NULL; auxv++) { + if (0) + printf("doing auxv %p %4x: %d %p\n", auxv, auxv->a_type, auxv->u.a_val, auxv->u.a_ptr); + + switch(auxv->a_type) { + case AT_PHDR: + seen |= 1; + auxv->u.a_val = info->phdr; + break; + + case AT_PHNUM: + seen |= 2; + auxv->u.a_val = info->phnum; + break; + + case AT_BASE: + seen |= 4; + auxv->u.a_val = info->interp_base; + break; + + case AT_ENTRY: + seen |= 8; + auxv->u.a_val = info->entry; + break; + +#if (defined(AT_SYSINFO) || defined(AT_SYSINFO_EHDR)) +#ifdef AT_SYSINFO + case AT_SYSINFO: +#endif +#ifdef AT_SYSINFO_EHDR + case AT_SYSINFO_EHDR: +#endif + auxv->a_type = AT_IGNORE; + break; +#endif + } + } + + /* If we didn't see all the entries we need to fix up, then we + can't make the new executable viable. */ + if (seen != 0xf) { + fprintf(stderr, "valgrind: we didn't see enough auxv entries (seen=%x)\n", seen); + exit(1); + } + + return v_init_esp; +} + + +/*------------------------------------------------------------*/ +/*--- Address space padding ---*/ +/*------------------------------------------------------------*/ + +static void check_mmap(void* res, void* base, int len) +{ + if ((void*)-1 == res) { + fprintf(stderr, "valgrind: padding mmap(%p, %d) failed during startup.\n" + "valgrind: is there a hard virtual memory limit set?\n", + base, len); + exit(1); + } +} + +typedef struct { + char* fillgap_start; + char* fillgap_end; + int fillgap_padfile; +} fillgap_extra; + +static int fillgap(char *segstart, char *segend, const char *perm, off_t off, + int maj, int min, int ino, void* e) +{ + fillgap_extra* extra = e; + + if (segstart >= extra->fillgap_end) + return 0; + + if (segstart > extra->fillgap_start) { + void* res = mmap(extra->fillgap_start, segstart - extra->fillgap_start, + PROT_NONE, MAP_FIXED|MAP_PRIVATE, + extra->fillgap_padfile, 0); + check_mmap(res, extra->fillgap_start, segstart - extra->fillgap_start); + } + extra->fillgap_start = segend; + + return 1; +} + +// Choose a name for the padfile, open it. +int as_openpadfile(void) +{ + char buf[256]; + int padfile; + int seq = 1; + do { + snprintf(buf, 256, "/tmp/.pad.%d.%d", getpid(), seq++); + padfile = open(buf, O_RDWR|O_CREAT|O_EXCL, 0); + unlink(buf); + if (padfile == -1 && errno != EEXIST) { + fprintf(stderr, "valgrind: couldn't open padfile\n"); + exit(44); + } + } while(padfile == -1); + + return padfile; +} + +// Pad all the empty spaces in a range of address space to stop interlopers. +void as_pad(void *start, void *end, int padfile) +{ + fillgap_extra extra; + extra.fillgap_start = start; + extra.fillgap_end = end; + extra.fillgap_padfile = padfile; + + foreach_map(fillgap, &extra); + + if (extra.fillgap_start < extra.fillgap_end) { + void* res = mmap(extra.fillgap_start, + extra.fillgap_end - extra.fillgap_start, + PROT_NONE, MAP_FIXED|MAP_PRIVATE, padfile, 0); + check_mmap(res, extra.fillgap_start, + extra.fillgap_end - extra.fillgap_start); + } +} + + +/*------------------------------------------------------------*/ +/*--- main() and related pieces ---*/ +/*------------------------------------------------------------*/ + +static int prmap(char *start, char *end, const char *perm, off_t off, int maj, + int min, int ino, void* dummy) { + printf("mapping %10p-%10p %s %02x:%02x %d\n", + start, end, perm, maj, min, ino); + return 1; +} + +static void main2(void) +{ + int err, padfile; + struct exeinfo info; + extern char _end; + int *esp; + char buf[strlen(valgrind_lib) + sizeof(stage2) + 16]; + + info.exe_base = PGROUNDUP(&_end); + info.exe_end = PGROUNDDN(init_sp); + + /* XXX FIXME: how can stage1 know where stage2 wants things placed? + Options: + - we could look for a symbol + - it could have a special PHDR (v. ELF specific) + - something else? + */ + info.map_base = KICKSTART_BASE + 0x01000000; + info.argv = NULL; + + snprintf(buf, sizeof(buf), "%s/%s", valgrind_lib, stage2); + + err = do_exec(buf, &info); + + if (err != 0) { + fprintf(stderr, "valgrind: failed to load %s: %s\n", + buf, strerror(err)); + exit(1); + } + + /* Make sure stage2's dynamic linker can't tromp on the lower part + of the address space. */ + padfile = as_openpadfile(); + as_pad(0, (void *)info.map_base, padfile); + + esp = fix_auxv(init_sp, &info, padfile); + + if (0) { + printf("---------- launch stage 2 ----------\n"); + printf("eip=%p esp=%p\n", (void *)info.init_eip, esp); + foreach_map(prmap, /*dummy*/NULL); + } + + jmp_with_stack(info.init_eip, (addr_t)esp); +} + +int main(int argc, char** argv) +{ + struct rlimit rlim; + const char *cp = getenv(VALGRINDLIB); + + if (cp != NULL) + valgrind_lib = cp; + + // Initial stack pointer is to argc, which is immediately before argv[0] + // on the stack. Nb: Assumes argc is word-aligned. + init_sp = argv - 1; + + /* Set the address space limit as high as it will go, since we make + a lot of very large mappings. */ + getrlimit(RLIMIT_AS, &rlim); + rlim.rlim_cur = rlim.rlim_max; + setrlimit(RLIMIT_AS, &rlim); + + /* move onto another stack so we can play with the main one */ + jmp_with_stack((addr_t)main2, (addr_t)stack + sizeof(stack)); +} + +/*--------------------------------------------------------------------*/ +/*--- end stage1.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/toolfuncs.def b/VEX/head20041019/coregrind/toolfuncs.def new file mode 100644 index 0000000000..d2f4cfbcd1 --- /dev/null +++ b/VEX/head20041019/coregrind/toolfuncs.def @@ -0,0 +1,301 @@ +# Tool interface functions +# The format for an interface function definition is: +# return_type, func_name, type arg, type arg +# If the function has no arguments, specify no arguments (rather than void) +# +# Comments starting with "##" are turned into C comments in the output +# +# Lines starting with : set the prefix + +## These are the parameterised functions in the core. The default definitions +## are overridden by LD_PRELOADed tool version. At the very least, a tool +## must define the fundamental template functions. Depending on what needs +## are set, extra template functions will be used too. Functions are +## grouped under the needs that govern their use. + +:tool +## ------------------------------------------------------------------ +## Fundamental template functions + +## Do initialisation that can only be done after command line processing. +void, post_clo_init + +## Instrument a basic block. Must be a true function, ie. the same input +## always results in the same output, because basic blocks can be +## retranslated. Unless you're doing something really strange... +## 'orig_addr' is the address of the first instruction in the block. +UCodeBlock*, instrument, UCodeBlock* cb, Addr orig_addr + +## Finish up, print out any results, etc. `exitcode' is program's exit +## code. The shadow (if the `shadow_regs' need is set) can be found with +## VG_(get_exit_status_shadow)(). +void, fini, Int exitcode + + +## ------------------------------------------------------------------ +## VG_(needs).core_errors + +## (none needed) + +## ------------------------------------------------------------------ +## VG_(needs).skin_errors + +## Identify if two errors are equal, or equal enough. `res' indicates how +## close is "close enough". `res' should be passed on as necessary, eg. if +## the Error's `extra' part contains an ExeContext, `res' should be +## passed to VG_(eq_ExeContext)() if the ExeContexts are considered. Other +## than that, probably don't worry about it unless you have lots of very +## similar errors occurring. +Bool, eq_SkinError, VgRes res, Error* e1, Error* e2 + +## Print error context. +void, pp_SkinError, Error* err + +## Should fill in any details that could be postponed until after the +## decision whether to ignore the error (ie. details not affecting the +## result of SK_(eq_SkinError)()). This saves time when errors are ignored. +## Yuk. + +## Return value: must be the size of the `extra' part in bytes -- used by +## the core to make a copy. +UInt, update_extra, Error* err + +## Return value indicates recognition. If recognised, must set skind using +## VG_(set_supp_kind)(). +Bool, recognised_suppression, Char* name, Supp* su + +## Read any extra info for this suppression kind. Most likely for filling +## in the `extra' and `string' parts (with VG_(set_supp_{extra, string})()) +## of a suppression if necessary. Should return False if a syntax error +## occurred, True otherwise. +Bool, read_extra_suppression_info, Int fd, Char* buf, Int nBuf, Supp* su + +## This should just check the kinds match and maybe some stuff in the +## `string' and `extra' field if appropriate (using VG_(get_supp_*)() to +## get the relevant suppression parts). +Bool, error_matches_suppression, Error* err, Supp* su + +## This should return the suppression name, for --gen-suppressions, or NULL +## if that error type cannot be suppressed. This is the inverse of +## SK_(recognised_suppression)(). +Char*, get_error_name, Error* err + +## This should print any extra info for the error, for --gen-suppressions, +## including the newline. This is the inverse of +## SK_(read_extra_suppression_info)(). +void, print_extra_suppression_info, Error* err + + +## ------------------------------------------------------------------ +## VG_(needs).basic_block_discards + +## Should discard any information that pertains to specific basic blocks +## or instructions within the address range given. +void, discard_basic_block_info, Addr a, UInt size + + +## ------------------------------------------------------------------ +## VG_(needs).shadow_regs + +## No functions must be defined, but the post_reg[s]_write_* events should +## be tracked. + +## ------------------------------------------------------------------ +## VG_(needs).command_line_options + +## Return True if option was recognised. Presumably sets some state to +## record the option as well. +Bool, process_cmd_line_option, Char* argv + +## Print out command line usage for options for normal tool operation. +void, print_usage + +## Print out command line usage for options for debugging the tool. +void, print_debug_usage + +## ------------------------------------------------------------------ +## VG_(needs).client_requests + +## If using client requests, the number of the first request should be equal +## to VG_USERREQ_SKIN_BASE('X', 'Y'), where 'X' and 'Y' form a suitable two +## character identification for the string. The second and subsequent +## requests should follow. + +## This function should use the VG_IS_SKIN_USERREQ macro (in +## include/valgrind.h) to first check if it's a request for this tool. Then +## should handle it if it's recognised (and return True), or return False if +## not recognised. arg_block[0] holds the request number, any further args +## from the request are in arg_block[1..]. 'ret' is for the return value... +## it should probably be filled, if only with 0. +Bool, handle_client_request, ThreadId tid, UInt* arg_block, UInt* ret + + +## ------------------------------------------------------------------ +## VG_(needs).extends_UCode + +## 'X' prefix indicates eXtended UCode. +Int, get_Xreg_usage, UInstr* u, Tag tag, Int* regs, Bool* isWrites +void, emit_XUInstr, UInstr* u, RRegSet regs_live_before +Bool, sane_XUInstr, Bool beforeRA, Bool beforeLiveness, UInstr* u +Char *, name_XUOpcode, Opcode opc +void, pp_XUInstr, UInstr* u + + +## ------------------------------------------------------------------ +## VG_(needs).syscall_wrapper + +## If either of the pre_ functions malloc() something to return, the +## corresponding post_ function had better free() it! + +void *, pre_syscall, ThreadId tid, UInt syscallno, Bool is_blocking +void, post_syscall, ThreadId tid, UInt syscallno, void* pre_result, Int res, Bool is_blocking + + +## --------------------------------------------------------------------- +## VG_(needs).sanity_checks + +## Can be useful for ensuring a tool's correctness. SK_(cheap_sanity_check) +## is called very frequently; SK_(expensive_sanity_check) is called less +## frequently and can be more involved. +Bool, cheap_sanity_check +Bool, expensive_sanity_check + + +## ================================================================================ +## Event tracking functions +:track + +## Events happening in core to track. To be notified, pass a callback +## function to the appropriate function. To ignore an event, don't do +## anything (default is for events to be ignored). + +## Note that most events aren't passed a ThreadId. To find out the ThreadId +## of the affected thread, use VG_(get_current_or_recent_tid)(). For the +## ones passed a ThreadId, use that instead, since +## VG_(get_current_or_recent_tid)() might not give the right ThreadId in +## that case. + +## Memory events (Nb: to track heap allocation/freeing, a tool must replace +## malloc() et al. See above how to do this.) + +## These ones occur at startup, upon some signals, and upon some syscalls +void, new_mem_startup, Addr a, UInt len, Bool rr, Bool ww, Bool xx +void, new_mem_stack_signal, Addr a, UInt len +void, new_mem_brk, Addr a, UInt len +void, new_mem_mmap, Addr a, UInt len, Bool rr, Bool ww, Bool xx + +void, copy_mem_remap, Addr from, Addr to, UInt len +void, change_mem_mprotect, Addr a, UInt len, Bool rr, Bool ww, Bool xx +void, die_mem_stack_signal, Addr a, UInt len +void, die_mem_brk, Addr a, UInt len +void, die_mem_munmap, Addr a, UInt len + +## These ones are called when %esp changes. A tool could track these itself +## (except for ban_mem_stack) but it's much easier to use the core's help. + +## The specialised ones are called in preference to the general one, if they +## are defined. These functions are called a lot if they are used, so +## specialising can optimise things significantly. If any of the +## specialised cases are defined, the general case must be defined too. + +## Nb: they must all use the REGPARM(n) attribute. +void, new_mem_stack_4, Addr new_ESP +void, new_mem_stack_8, Addr new_ESP +void, new_mem_stack_12, Addr new_ESP +void, new_mem_stack_16, Addr new_ESP +void, new_mem_stack_32, Addr new_ESP +void, new_mem_stack, Addr a, UInt len + +void, die_mem_stack_4, Addr die_ESP +void, die_mem_stack_8, Addr die_ESP +void, die_mem_stack_12, Addr die_ESP +void, die_mem_stack_16, Addr die_ESP +void, die_mem_stack_32, Addr die_ESP +void, die_mem_stack, Addr a, UInt len + +## Used for redzone at end of thread stacks +void, ban_mem_stack, Addr a, UInt len + +## These ones occur around syscalls, signal handling, etc +void, pre_mem_read, CorePart part, ThreadId tid, Char* s, Addr a, UInt size +void, pre_mem_read_asciiz, CorePart part, ThreadId tid, Char* s, Addr a +void, pre_mem_write, CorePart part, ThreadId tid, Char* s, Addr a, UInt size +## Not implemented yet -- have to add in lots of places, which is a +## pain. Won't bother unless/until there's a need. +## void (*post_mem_read) ( ThreadState* tst, Char* s, Addr a, UInt size ); +void, post_mem_write, Addr a, UInt size + + +## Register events -- if `shadow_regs' need is set, all should probably be +## used. Use VG_(set_thread_shadow_archreg)() to set the shadow of the +## changed register. + +## Use VG_(set_shadow_archreg)() to set the eight general purpose regs, +## and use VG_(set_shadow_eflags)() to set eflags. +void, post_regs_write_init, void + +## Use VG_(set_thread_shadow_archreg)() to set the shadow regs for these +## events. +void, post_reg_write_syscall_return, ThreadId tid, UInt reg +void, post_reg_write_deliver_signal, ThreadId tid, UInt reg +void, post_reg_write_pthread_return, ThreadId tid, UInt reg +void, post_reg_write_clientreq_return, ThreadId tid, UInt reg +## This one is called for malloc() et al if they are replaced by a tool. +void, post_reg_write_clientcall_return, ThreadId tid, UInt reg, Addr f + + +## Scheduler events (not exhaustive) +void, thread_run, ThreadId tid + + +## Thread events (not exhaustive) + +## Called during thread create, before the new thread has run any +## instructions (or touched any memory). +void, post_thread_create, ThreadId tid, ThreadId child +void, post_thread_join, ThreadId joiner, ThreadId joinee + + +## Mutex events (not exhaustive) +## "void *mutex" is really a pthread_mutex * + +## Called before a thread can block while waiting for a mutex (called +## regardless of whether the thread will block or not). +void, pre_mutex_lock, ThreadId tid, void* mutex +## Called once the thread actually holds the mutex (always paired with +## pre_mutex_lock). +void, post_mutex_lock, ThreadId tid, void* mutex +## Called after a thread has released a mutex (no need for a corresponding +## pre_mutex_unlock, because unlocking can't block). +void, post_mutex_unlock, ThreadId tid, void* mutex + +## Signal events (not exhaustive) + +## ... pre_send_signal, post_send_signal ... + +## Called before a signal is delivered; `alt_stack' indicates if it is +## delivered on an alternative stack. +void, pre_deliver_signal, ThreadId tid, Int sigNo, Bool alt_stack +## Called after a signal is delivered. Nb: unfortunately, if the signal +## handler longjmps, this won't be called. +void, post_deliver_signal, ThreadId tid, Int sigNo + + +## Others... condition variable... +## ... + +## Shadow memory management +void, init_shadow_page, Addr p + +## ================================================================================ +## malloc and friends +:malloc +void*, malloc, Int n +void*, __builtin_new, Int n +void*, __builtin_vec_new, Int n +void*, memalign, Int align, Int n +void*, calloc, Int nmemb, Int n +void, free, void* p +void, __builtin_delete, void* p +void, __builtin_vec_delete, void* p +void*, realloc, void* p, Int size diff --git a/VEX/head20041019/coregrind/ume.c b/VEX/head20041019/coregrind/ume.c new file mode 100644 index 0000000000..7fe3146d27 --- /dev/null +++ b/VEX/head20041019/coregrind/ume.c @@ -0,0 +1,608 @@ + +/*--------------------------------------------------------------------*/ +/*--- User-mode execve(), and other stuff shared between stage1 ---*/ +/*--- and stage2. ume.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + + +#define _GNU_SOURCE +#define _FILE_OFFSET_BITS 64 + +#include "core.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ume.h" + +struct elfinfo +{ + ESZ(Ehdr) e; + ESZ(Phdr) *p; + int fd; +}; + +static void check_mmap(void* res, void* base, int len) +{ + if ((void*)-1 == res) { + fprintf(stderr, "valgrind: mmap(%p, %d) failed in UME.\n", base, len); + exit(1); + } +} + +// 'extra' allows the caller to pass in extra args to 'fn', like free +// variables to a closure. +void foreach_map(int (*fn)(char *start, char *end, + const char *perm, off_t offset, + int maj, int min, int ino, void* extra), + void* extra) +{ + static char buf[10240]; + char *bufptr = buf; + int ret, fd; + + fd = open("/proc/self/maps", O_RDONLY); + + if (fd == -1) { + perror("open /proc/self/maps"); + return; + } + + ret = read(fd, buf, sizeof(buf)); + + if (ret == -1) { + perror("read /proc/self/maps"); + close(fd); + return; + } + close(fd); + + if (ret == sizeof(buf)) { + fprintf(stderr, "buf too small\n"); + return; + } + + while(bufptr && bufptr < buf+ret) { + char perm[5]; + off_t offset; + int maj, min; + int ino; + void *segstart, *segend; + + sscanf(bufptr, "%p-%p %s %Lx %x:%x %d", + &segstart, &segend, perm, &offset, &maj, &min, &ino); + bufptr = strchr(bufptr, '\n'); + if (bufptr != NULL) + bufptr++; /* skip \n */ + + if (!(*fn)(segstart, segend, perm, offset, maj, min, ino, extra)) + break; + } +} + +/*------------------------------------------------------------*/ +/*--- Finding auxv on the stack ---*/ +/*------------------------------------------------------------*/ + +struct ume_auxv *find_auxv(int *esp) +{ + esp++; /* skip argc */ + + while(*esp != 0) /* skip argv */ + esp++; + esp++; + + while(*esp != 0) /* skip env */ + esp++; + esp++; + + return (struct ume_auxv *)esp; +} + +/*------------------------------------------------------------*/ +/*--- Loading ELF files ---*/ +/*------------------------------------------------------------*/ + +struct elfinfo *readelf(int fd, const char *filename) +{ + struct elfinfo *e = malloc(sizeof(*e)); + int phsz; + + assert(e); + e->fd = fd; + + if (pread(fd, &e->e, sizeof(e->e), 0) != sizeof(e->e)) { + fprintf(stderr, "valgrind: %s: can't read elf header: %s\n", + filename, strerror(errno)); + return NULL; + } + + if (memcmp(&e->e.e_ident[0], ELFMAG, SELFMAG) != 0) { + fprintf(stderr, "valgrind: %s: bad ELF magic\n", filename); + return NULL; + } + if (e->e.e_ident[EI_CLASS] != VG_ELF_CLASS) { + fprintf(stderr, "valgrind: wrong executable class (eg. 32-bit instead\n" + "valgrind: of 64-bit)\n"); + return NULL; + } + if (e->e.e_ident[EI_DATA] != VG_ELF_ENDIANNESS) { + fprintf(stderr, "valgrind: wrong endian-ness\n"); + return NULL; + } + if (!(e->e.e_type == ET_EXEC || e->e.e_type == ET_DYN)) { + fprintf(stderr, "valgrind: need executable\n"); + return NULL; + } + + if (e->e.e_machine != VG_ELF_MACHINE) { + fprintf(stderr, "valgrind: wrong architecture\n"); + return NULL; + } + + if (e->e.e_phentsize != sizeof(ESZ(Phdr))) { + fprintf(stderr, "valgrind: sizeof Phdr wrong\n"); + return NULL; + } + + phsz = sizeof(ESZ(Phdr)) * e->e.e_phnum; + e->p = malloc(phsz); + assert(e->p); + + if (pread(fd, e->p, phsz, e->e.e_phoff) != phsz) { + fprintf(stderr, "valgrind: can't read phdr: %s\n", strerror(errno)); + return NULL; + } + + return e; +} + +/* Map an ELF file. Returns the brk address. */ +ESZ(Addr) mapelf(struct elfinfo *e, ESZ(Addr) base) +{ + int i; + void* res; + ESZ(Addr) elfbrk = 0; + + for(i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + ESZ(Addr) addr, brkaddr; + ESZ(Word) memsz; + + if (ph->p_type != PT_LOAD) + continue; + + addr = ph->p_vaddr+base; + memsz = ph->p_memsz; + brkaddr = addr+memsz; + + if (brkaddr > elfbrk) + elfbrk = brkaddr; + } + + for(i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + ESZ(Addr) addr, bss, brkaddr; + ESZ(Off) off; + ESZ(Word) filesz; + ESZ(Word) memsz; + ESZ(Word) align; + unsigned prot = 0; + + if (ph->p_type != PT_LOAD) + continue; + + if (ph->p_flags & PF_X) + prot |= PROT_EXEC; + if (ph->p_flags & PF_W) + prot |= PROT_WRITE; + if (ph->p_flags & PF_R) + prot |= PROT_READ; + + align = ph->p_align; + + addr = ph->p_vaddr+base; + off = ph->p_offset; + filesz = ph->p_filesz; + bss = addr+filesz; + memsz = ph->p_memsz; + brkaddr = addr+memsz; + + res = mmap((char *)ROUNDDN(addr, align), + ROUNDUP(bss, align)-ROUNDDN(addr, align), + prot, MAP_FIXED|MAP_PRIVATE, e->fd, ROUNDDN(off, align)); + check_mmap(res, (char*)ROUNDDN(addr,align), + ROUNDUP(bss, align)-ROUNDDN(addr, align)); + + /* if memsz > filesz, then we need to fill the remainder with zeroed pages */ + if (memsz > filesz) { + UInt bytes; + + bytes = ROUNDUP(brkaddr, align)-ROUNDUP(bss, align); + if (bytes > 0) { + res = mmap((char *)ROUNDUP(bss, align), bytes, + prot, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + check_mmap(res, (char*)ROUNDUP(bss,align), bytes); + } + + bytes = bss & (VKI_BYTES_PER_PAGE - 1); + if (bytes > 0) { + bytes = VKI_BYTES_PER_PAGE - bytes; + memset((char *)bss, 0, bytes); + } + } + } + + return elfbrk; +} + +// Forward declaration. +static int do_exec_inner(const char *exe, struct exeinfo *info); + +static int match_ELF(const char *hdr, int len) +{ + ESZ(Ehdr) *e = (ESZ(Ehdr) *)hdr; + return (len > sizeof(*e)) && memcmp(&e->e_ident[0], ELFMAG, SELFMAG) == 0; +} + +static int load_ELF(char *hdr, int len, int fd, const char *name, + struct exeinfo *info) +{ + struct elfinfo *e; + struct elfinfo *interp = NULL; + ESZ(Addr) exeoff = 0; /* offset between link address and load address */ + ESZ(Addr) minaddr = ~0; /* lowest mapped address */ + ESZ(Addr) maxaddr = 0; /* highest mapped address */ + ESZ(Addr) interp_addr = 0; /* interpreter (ld.so) address */ + ESZ(Word) interp_size = 0; /* interpreter size */ + int i; + void *entry; + + e = readelf(fd, name); + + if (e == NULL) + return ENOEXEC; + + info->phnum = e->e.e_phnum; + info->entry = e->e.e_entry; + + for(i = 0; i < e->e.e_phnum; i++) { + ESZ(Phdr) *ph = &e->p[i]; + + switch(ph->p_type) { + case PT_PHDR: + info->phdr = ph->p_vaddr; + break; + + case PT_LOAD: + if (ph->p_vaddr < minaddr) + minaddr = ph->p_vaddr; + if (ph->p_vaddr+ph->p_memsz > maxaddr) + maxaddr = ph->p_vaddr+ph->p_memsz; + break; + + case PT_INTERP: { + char *buf = malloc(ph->p_filesz+1); + int j; + int intfd; + int baseaddr_set; + + assert(buf); + pread(fd, buf, ph->p_filesz, ph->p_offset); + buf[ph->p_filesz] = '\0'; + + intfd = open(buf, O_RDONLY); + if (intfd == -1) { + perror("open interp"); + exit(1); + } + + interp = readelf(intfd, buf); + if (interp == NULL) { + fprintf(stderr, "Can't read interpreter\n"); + return 1; + } + free(buf); + + baseaddr_set = 0; + for(j = 0; j < interp->e.e_phnum; j++) { + ESZ(Phdr) *iph = &interp->p[j]; + ESZ(Addr) end; + + if (iph->p_type != PT_LOAD) + continue; + + if (!baseaddr_set) { + interp_addr = iph->p_vaddr; + baseaddr_set = 1; + } + + /* assumes that all segments in the interp are close */ + end = (iph->p_vaddr - interp_addr) + iph->p_memsz; + + if (end > interp_size) + interp_size = end; + } + break; + + default: + // do nothing + break; + } + } + } + + if (e->e.e_type == ET_DYN) { + /* PIE executable */ + exeoff = info->exe_base - minaddr; + } + + minaddr += exeoff; + maxaddr += exeoff; + info->phdr += exeoff; + info->entry += exeoff; + + if (info->exe_base != info->exe_end) { + if (minaddr >= maxaddr || + (minaddr < info->exe_base || + maxaddr > info->exe_end)) { + fprintf(stderr, "Executable range %p-%p is outside the\n" + "acceptable range %p-%p\n", + (void *)minaddr, (void *)maxaddr, + (void *)info->exe_base, (void *)info->exe_end); + return ENOMEM; + } + } + + info->brkbase = mapelf(e, exeoff); /* map the executable */ + + if (info->brkbase == 0) + return ENOMEM; + + if (interp != NULL) { + /* reserve a chunk of address space for interpreter */ + void* res; + char* base = (char *)info->exe_base; + char* baseoff; + int flags = MAP_PRIVATE|MAP_ANONYMOUS; + + if (info->map_base != 0) { + base = (char *)info->map_base; + flags |= MAP_FIXED; + } + + res = mmap(base, interp_size, PROT_NONE, flags, -1, 0); + check_mmap(res, base, interp_size); + base = res; + + baseoff = base - interp_addr; + + mapelf(interp, (ESZ(Addr))baseoff); + + close(interp->fd); + + entry = baseoff + interp->e.e_entry; + info->interp_base = (ESZ(Addr))base; + + free(interp); + } else + entry = (void *)e->e.e_entry + exeoff; + + info->exe_base = minaddr; + info->exe_end = maxaddr; + + info->init_eip = (addr_t)entry; + + free(e); + + return 0; +} + + +static int match_script(const char *hdr, Int len) +{ + return (len > 2) && memcmp(hdr, "#!", 2) == 0; +} + +static int load_script(char *hdr, int len, int fd, const char *name, + struct exeinfo *info) +{ + char *interp; + char *const end = hdr+len; + char *cp; + char *arg = NULL; + int eol; + + interp = hdr + 2; + while(interp < end && (*interp == ' ' || *interp == '\t')) + interp++; + + if (*interp != '/') + return ENOEXEC; /* absolute path only for interpreter */ + + /* skip over interpreter name */ + for(cp = interp; cp < end && *cp != ' ' && *cp != '\t' && *cp != '\n'; cp++) + ; + + eol = (*cp == '\n'); + + *cp++ = '\0'; + + if (!eol && cp < end) { + /* skip space before arg */ + while (cp < end && (*cp == '\t' || *cp == ' ')) + cp++; + + /* arg is from here to eol */ + arg = cp; + while (cp < end && *cp != '\n') + cp++; + *cp = '\0'; + } + + info->interp_name = strdup(interp); + assert(NULL != info->interp_name); + if (arg != NULL && *arg != '\0') { + info->interp_args = strdup(arg); + assert(NULL != info->interp_args); + } + + if (info->argv && info->argv[0] != NULL) + info->argv[0] = (char *)name; + + if (0) + printf("#! script: interp_name=\"%s\" interp_args=\"%s\"\n", + info->interp_name, info->interp_args); + + return do_exec_inner(interp, info); +} + +/* + Emulate the normal Unix permissions checking algorithm. + + If owner matches, then use the owner permissions, else + if group matches, then use the group permissions, else + use other permissions. + + Note that we can't deal with SUID/SGID, so we refuse to run them + (otherwise the executable may misbehave if it doesn't have the + permissions it thinks it does). +*/ +static int check_perms(int fd) +{ + struct stat st; + + if (fstat(fd, &st) == -1) + return errno; + + if (st.st_mode & (S_ISUID | S_ISGID)) { + //fprintf(stderr, "Can't execute suid/sgid executable %s\n", exe); + return EACCES; + } + + if (geteuid() == st.st_uid) { + if (!(st.st_mode & S_IXUSR)) + return EACCES; + } else { + int grpmatch = 0; + + if (getegid() == st.st_gid) + grpmatch = 1; + else { + gid_t groups[32]; + int ngrp = getgroups(32, groups); + int i; + + for(i = 0; i < ngrp; i++) + if (groups[i] == st.st_gid) { + grpmatch = 1; + break; + } + } + + if (grpmatch) { + if (!(st.st_mode & S_IXGRP)) + return EACCES; + } else if (!(st.st_mode & S_IXOTH)) + return EACCES; + } + + return 0; +} + +static int do_exec_inner(const char *exe, struct exeinfo *info) +{ + int fd; + int err; + char buf[VKI_BYTES_PER_PAGE]; + int bufsz; + int i; + int ret; + static const struct { + int (*match)(const char *hdr, int len); + int (*load) ( char *hdr, int len, int fd2, const char *name, + struct exeinfo *); + } formats[] = { + { match_ELF, load_ELF }, + { match_script, load_script }, + }; + + fd = open(exe, O_RDONLY); + if (fd == -1) { + if (0) + fprintf(stderr, "Can't open executable %s: %s\n", + exe, strerror(errno)); + return errno; + } + + err = check_perms(fd); + if (err != 0) { + close(fd); + return err; + } + + bufsz = pread(fd, buf, sizeof(buf), 0); + if (bufsz < 0) { + fprintf(stderr, "Can't read executable header: %s\n", + strerror(errno)); + close(fd); + return errno; + } + + ret = ENOEXEC; + for(i = 0; i < sizeof(formats)/sizeof(*formats); i++) { + if ((formats[i].match)(buf, bufsz)) { + ret = (formats[i].load)(buf, bufsz, fd, exe, info); + break; + } + } + + close(fd); + + return ret; +} + +// See ume.h for an indication of which entries of 'info' are inputs, which +// are outputs, and which are both. +int do_exec(const char *exe, struct exeinfo *info) +{ + info->interp_name = NULL; + info->interp_args = NULL; + + return do_exec_inner(exe, info); +} + +/*--------------------------------------------------------------------*/ +/*--- end ume.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/ume.h b/VEX/head20041019/coregrind/ume.h new file mode 100644 index 0000000000..a015ff07d8 --- /dev/null +++ b/VEX/head20041019/coregrind/ume.h @@ -0,0 +1,117 @@ + +/*--------------------------------------------------------------------*/ +/*--- A header file used by both stage1 and stage2. ---*/ +/*--- ume.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef _COREGRIND_UME_H +#define _COREGRIND_UME_H + +#include +#include + +/*------------------------------------------------------------*/ +/*--- General stuff ---*/ +/*------------------------------------------------------------*/ + +void foreach_map(int (*fn)(char *start, char *end, + const char *perm, off_t offset, + int maj, int min, int ino, void* extra), + void* extra); + +#if ELFSZ == 64 +#define ESZ(x) Elf64_##x +#elif ELFSZ == 32 +#define ESZ(x) Elf32_##x +#else +#error ELFSZ needs to ==32 or ==64 +#endif + +/* Integer type the same size as a pointer */ +typedef ESZ(Addr) addr_t; + +// Jump to a new 'ip' with the stack 'sp'. +void jmp_with_stack(addr_t ip, addr_t sp) __attribute__((noreturn)); + +/*------------------------------------------------------------*/ +/*--- Loading ELF files ---*/ +/*------------------------------------------------------------*/ + +// Info needed to load and run a program. IN/INOUT/OUT refers to the +// inputs/outputs of do_exec(). +struct exeinfo +{ + addr_t map_base; // IN: if non-zero, base address of mappings + char** argv; // IN: the original argv + + addr_t exe_base; // INOUT: lowest (allowed) address of exe + addr_t exe_end; // INOUT: highest (allowed) address + + addr_t phdr; // OUT: address phdr was mapped at + int phnum; // OUT: number of phdrs + addr_t interp_base; // OUT: where interpreter (ld.so) was mapped + addr_t entry; // OUT: entrypoint in main executable + addr_t init_eip; // OUT: initial eip + addr_t brkbase; // OUT: base address of brk segment + + // These are the extra args added by #! scripts + char* interp_name; // OUT: the interpreter name + char* interp_args; // OUT: the args for the interpreter +}; + +// Does everything short of actually running 'exe': finds the file, +// checks execute permissions, sets up interpreter if program is a script, +// reads headers, maps file into memory, and returns important info about +// the program. +int do_exec(const char *exe, struct exeinfo *info); + +/*------------------------------------------------------------*/ +/*--- Finding and dealing with auxv ---*/ +/*------------------------------------------------------------*/ + +struct ume_auxv +{ + int a_type; + union { + void *a_ptr; + int a_val; + void (*a_fcn)(void); + } u; +}; + +struct ume_auxv *find_auxv(int *orig_esp); + +/* Our private auxv entries */ +#define AT_UME_PADFD 0xff01 /* padding file fd */ +#define AT_UME_EXECFD 0xff02 /* stage1 executable fd */ + +#endif /* _COREGRIND_UME_H */ + +/*--------------------------------------------------------------------*/ +/*--- end ume.h ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/valgrind.vs b/VEX/head20041019/coregrind/valgrind.vs new file mode 100644 index 0000000000..a246684b12 --- /dev/null +++ b/VEX/head20041019/coregrind/valgrind.vs @@ -0,0 +1,11 @@ +VALGRIND_2.1 { + global: + vgPlain_*; + vgSkin_*; + vgProf_*; + vgOff_*; + vgArch_*; + + local: + *; # default to hidden +}; diff --git a/VEX/head20041019/coregrind/vg_cpuid.S b/VEX/head20041019/coregrind/vg_cpuid.S new file mode 100644 index 0000000000..439e6e1258 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_cpuid.S @@ -0,0 +1,84 @@ + +##--------------------------------------------------------------------## +##--- Support for determining CPU characteristics. ---## +##--- vg_cpuid.S ---## +##--------------------------------------------------------------------## + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core_asm.h" +#include "vki_unistd.h" + +/* + int VG_(cpuid)(UInt eax, + UInt *eax_ret, UInt *ebx_ret, UInt *ecx_ret, UInt *edx_ret) + */ +.globl VG_(cpuid) +VG_(cpuid): + pushl %ebp + movl %esp, %ebp + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + movl 8(%ebp), %eax + cpuid + movl 12(%ebp), %esi + testl %esi, %esi + jz 1f + movl %eax, (%esi) +1: + movl 16(%ebp), %esi + testl %esi, %esi + jz 2f + movl %ebx, (%esi) +2: + movl 20(%ebp), %esi + testl %esi, %esi + jz 3f + movl %ecx, (%esi) +3: + movl 24(%ebp), %esi + testl %esi, %esi + jz 4f + movl %edx, (%esi) +4: + popl %esi + popl %edx + popl %ecx + popl %ebx + popl %eax + movl %ebp, %esp + popl %ebp + ret + +/* Let the linker know we don't need an executable stack */ +.section .note.GNU-stack,"",@progbits + +##--------------------------------------------------------------------## +##--- end vg_cpuid.S ---## +##--------------------------------------------------------------------## diff --git a/VEX/head20041019/coregrind/vg_default.c b/VEX/head20041019/coregrind/vg_default.c new file mode 100644 index 0000000000..ed425e24b1 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_default.c @@ -0,0 +1,102 @@ + +/*--------------------------------------------------------------------*/ +/*--- Default panicky definitions of template functions that tools ---*/ +/*--- should override. ---*/ +/*--- vg_defaults.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + + +#include "core.h" + +/* --------------------------------------------------------------------- + Error messages (for malformed tools) + ------------------------------------------------------------------ */ + +/* If the tool fails to define one or more of the required functions, + * make it very clear what went wrong! */ + +__attribute__ ((noreturn)) +void VG_(missing_tool_func) ( const Char* fn ) +{ + VG_(printf)( + "\nTool error:\n" + " The tool you have selected is missing the function `%s',\n" + " which is required.\n\n", + fn); + VG_(skin_panic)("Missing tool function"); +} + +static __attribute__ ((noreturn)) +void malloc_panic ( const Char* fn ) +{ + VG_(printf)( + "\nTool error:\n" + " The tool you have selected is missing the function `%s'\n" + " required because it is replacing malloc() et al.\n\n", + fn); + VG_(skin_panic)("Missing tool function"); +} + +/*------------------------------------------------------------*/ +/*--- Replacing malloc et al ---*/ +/*------------------------------------------------------------*/ + +/* Default redzone size for CLIENT arena of Valgrind's malloc() */ +__attribute__ ((weak)) +UInt VG_(vg_malloc_redzone_szB) = 8; + +Bool VG_(sk_malloc_called_by_scheduler) = False; + +/* If the tool hasn't replaced malloc(), this one can be called from the + scheduler, for the USERREQ__MALLOC user request used by vg_libpthread.c. + (Nb: it cannot call glibc's malloc().) The lock variable ensures that the + scheduler is the only place this can be called from; this ensures that a + malloc()-replacing tool cannot forget to implement SK_(malloc)() or + SK_(free)(). */ +__attribute__ ((weak)) +void* SK_(malloc)( Int size ) +{ + if (VG_(sk_malloc_called_by_scheduler)) + return VG_(cli_malloc)(VG_MIN_MALLOC_SZB, size); + else + malloc_panic(__PRETTY_FUNCTION__); +} + +__attribute__ ((weak)) +void SK_(free)( void* p ) +{ + /* see comment for SK_(malloc)() above */ + if (VG_(sk_malloc_called_by_scheduler)) + VG_(cli_free)(p); + else + malloc_panic(__PRETTY_FUNCTION__); +} + +/*--------------------------------------------------------------------*/ +/*--- end vg_defaults.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_demangle.c b/VEX/head20041019/coregrind/vg_demangle.c new file mode 100644 index 0000000000..f5696aa1dd --- /dev/null +++ b/VEX/head20041019/coregrind/vg_demangle.c @@ -0,0 +1,77 @@ + +/*--------------------------------------------------------------------*/ +/*--- Demangling of C++ mangled names. ---*/ +/*--- vg_demangle.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core.h" +#include "demangle.h" + +#define ADD_TO_RESULT(zzstr,zzn) \ +{ \ + Char* zz = (zzstr); \ + Int nn = (zzn); \ + Int ii; \ + for (ii = 0; ii < nn; ii++) { \ + result[n_result] = zz[ii]; \ + if (n_result < result_size-1) n_result++; \ + result[n_result] = 0; \ + } \ +} + +void VG_(demangle) ( Char* orig, Char* result, Int result_size ) +{ + Int n_result = 0; + Char* demangled = NULL; + + VGP_PUSHCC(VgpDemangle); + + if (VG_(clo_demangle)) + demangled = VG_(cplus_demangle) ( orig, DMGL_ANSI | DMGL_PARAMS ); + + if (demangled) { + ADD_TO_RESULT(demangled, VG_(strlen)(demangled)); + VG_(arena_free) (VG_AR_DEMANGLE, demangled); + } else { + ADD_TO_RESULT(orig, VG_(strlen)(orig)); + } + + /* Check that the demangler isn't leaking. */ + /* 15 Feb 02: if this assertion fails, this is not a disaster. + Comment it out, and let me know. (jseward@acm.org). */ + vg_assert(VG_(is_empty_arena)(VG_AR_DEMANGLE)); + + /* VG_(show_all_arena_stats)(); */ + + VGP_POPCC(VgpDemangle); +} + + +/*--------------------------------------------------------------------*/ +/*--- end vg_demangle.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_dispatch.S b/VEX/head20041019/coregrind/vg_dispatch.S new file mode 100644 index 0000000000..111a160e1b --- /dev/null +++ b/VEX/head20041019/coregrind/vg_dispatch.S @@ -0,0 +1,231 @@ + +##--------------------------------------------------------------------## +##--- The core dispatch loop, for jumping to a code address. ---## +##--- vg_dispatch.S ---## +##--------------------------------------------------------------------## + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core_asm.h" + + +/*------------------------------------------------------------*/ +/*--- The normal-case dispatch machinery. ---*/ +/*------------------------------------------------------------*/ + +/* To transfer to an (original) code address, load it into %eax and + jump to vg_dispatch. This fragment of code tries to find the + address of the corresponding translation by searching the translation + table. If it fails, a new translation is made, added to the + translation table, and then jumped to. Almost all the hard + work is done by C routines; this code simply handles the + common case fast -- when the translation address is found in + the translation cache. + + At entry, %eax is the only live (real-machine) register; the + entire simulated state is tidily saved in vg_m_state. +*/ + + +#define TT_LOOKUP(reg, fail) \ + movl %eax, reg; \ + andl $VG_TT_FAST_MASK, reg; \ + movl VG_(tt_fast)(,reg,4), reg; \ + cmpl %eax, (reg); \ + jnz fail + +/* The C world needs a way to get started simulating. So we provide + a function void vg_run_innerloop ( void ), which starts running + from vg_m_eip, and exits when the counter reaches zero. This loop + can also exit if vg_oursignalhandler() catches a non-resumable + signal, for example SIGSEGV. It then longjmp()s back past here. +*/ + +.globl VG_(run_innerloop) +VG_(run_innerloop): + /* OYNK(1000) */ + + /* ----- entry point to VG_(run_innerloop) ----- */ + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + pushl %edi + pushl %ebp + + /* check to see if we're doing pointer checking */ + movb VG_(clo_pointercheck), %al + testb %al,%al + jz 1f + + pushl %fs /* save %fs */ + mov $(VG_POINTERCHECK_SEGIDX << 3) + 7, %eax /* load new %fs */ + movw %ax,%fs + +1: + /* Set up the baseBlock pointer */ + movl $VG_(baseBlock), %ebp + + /* fetch m_eip into %eax */ + movl VGOFF_(m_eip), %esi + movl (%ebp, %esi, 4), %eax + +dispatch_main: + /* Jump here to do a new dispatch. + %eax holds destination (original) address. + %ebp indicates further details of the control transfer + requested to the address in %eax. + + If ebp == & VG_(baseBlock), just jump next to %eax. + + If ebp == VG_EBP_JMP_SYSCALL, do a system call before + continuing at eax. + + If ebp == VG_EBP_JMP_CLIENTREQ, do a client request before + continuing at eax. + + If %ebp has any other value, we panic. + */ + /*cmpl $VG_(baseBlock), %ebp*/ + /*jnz dispatch_exceptional*/ + /* fall into main loop */ + + +dispatch_boring: + /* save the jump address at VG_(baseBlock)[VGOFF_(m_eip)] */ + movl VGOFF_(m_eip), %esi + movl %eax, (%ebp, %esi, 4) + + /* Are we out of timeslice? If yes, defer to scheduler. */ + cmpl $0, VG_(dispatch_ctr) + jz counter_is_zero + /* try a fast lookup in the translation cache */ + TT_LOOKUP(%ebx, fast_lookup_failed) + + /* Found a match. Call the tce.payload field (+VG_CODE_OFFSET) */ + addl $VG_CODE_OFFSET, %ebx + incl VG_(unchained_jumps_done) /* update stats */ + call *%ebx + + cmpl $VG_(baseBlock), %ebp + jz dispatch_boring + + jmp dispatch_exceptional + + +fast_lookup_failed: + /* %EIP is up to date here since dispatch_boring dominates */ + movl $VG_TRC_INNER_FASTMISS, %eax + jmp run_innerloop_exit + +counter_is_zero: + /* %EIP is up to date here since dispatch_boring dominates */ + movl $VG_TRC_INNER_COUNTERZERO, %eax + jmp run_innerloop_exit + +run_innerloop_exit: + movb VG_(clo_pointercheck), %bl + testb %bl,%bl + jz 1f + + /* restore %fs */ + popl %fs + +1: popl %ebp + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + ret + + + +/* Other ways of getting out of the inner loop. Placed out-of-line to + make it look cleaner. +*/ +dispatch_exceptional: + /* this is jumped to only, not fallen-through from above */ + cmpl $VG_TRC_INNER_COUNTERZERO, %ebp + jz counter_is_zero + + /* save %eax in %EIP and defer to sched */ + movl VGOFF_(m_eip), %esi + movl %eax, VG_(baseBlock)(,%esi, 4) + movl %ebp, %eax + jmp run_innerloop_exit + + +/* + This is the translation chainer, our run-time linker, if you like. + + VG_(patch_me) patches the call instruction in the jump site + with a jump to the generated code for the branch target. %eax + contains the original program's EIP - if we get a hit in + tt_fast, then the call is patched into a jump; otherwise it + simply drops back into the dispatch loop for normal + processing. + + The callsite is expected to look like: + call VG_(patch_me) + it will be transformed into + jmp $TARGETADDR + + The environment we're expecting on entry is: + %eax = branch target address (original code EIP) + *(%esp) = just after call +*/ +.globl VG_(patch_me) +VG_(patch_me): + /* try a fast lookup in the translation cache */ + TT_LOOKUP(%ebx, 1f) + + /* Patch call instruction at callsite into a chained jmp */ + popl %eax /* eax = just after (VG_PATCHME_CALLSZ byte) call */ + addl $VG_CODE_OFFSET, %ebx /* ebx = target eip */ + subl %eax, %ebx /* ebx = delta */ + movb $0xE9, -(VG_PATCHME_CALLSZ-0)(%eax) /* 0xe9 = jmp */ + movl %ebx, -(VG_PATCHME_CALLSZ-1)(%eax) /* store delta */ + addl %eax, %ebx + incl VG_(bb_enchain_count) /* update stats */ + jmp *%ebx /* jmp to dest */ + + /* tt_fast miss: return into main dispatch loop */ +1: addl $4, %esp /* remove our call address */ + ret /* return into main dispatch loop above */ + +.data +panic_msg_ebp: +.ascii "vg_dispatch: %ebp has invalid value!" +.byte 0 +.text + +/* Let the linker know we don't need an executable stack */ +.section .note.GNU-stack,"",@progbits + +##--------------------------------------------------------------------## +##--- end vg_dispatch.S ---## +##--------------------------------------------------------------------## diff --git a/VEX/head20041019/coregrind/vg_dummy_profile.c b/VEX/head20041019/coregrind/vg_dummy_profile.c new file mode 100644 index 0000000000..a44133e1ca --- /dev/null +++ b/VEX/head20041019/coregrind/vg_dummy_profile.c @@ -0,0 +1,71 @@ + +/*--------------------------------------------------------------------*/ +/*--- Dummy profiling machinery -- overridden by tools when they ---*/ +/*--- want profiling. ---*/ +/*--- vg_dummy_profile.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core.h" + +static void vgp_die(void) +{ + VG_(printf)( + "\nProfiling error:\n" + " The --profile=yes option was specified, but the tool\n" + " wasn't built for profiling. #include \"vg_profile.c\"\n" + " into the tool and rebuild to allow profiling.\n\n"); + VG_(exit)(1); +} + +void VGP_(register_profile_event) ( Int n, Char* name ) +{ +} + +void VGP_(init_profiling) ( void ) +{ + vgp_die(); +} + +void VGP_(done_profiling) ( void ) +{ + VG_(core_panic)("done_profiling(), but not compiled for profiling??"); +} + +void VGP_(pushcc) ( UInt cc ) +{ + vgp_die(); +} + +void VGP_(popcc) ( UInt cc ) +{ + vgp_die(); +} + +/*--------------------------------------------------------------------*/ +/*--- end vg_dummy_profile.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_dwarf.c b/VEX/head20041019/coregrind/vg_dwarf.c new file mode 100644 index 0000000000..aff343f8d1 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_dwarf.c @@ -0,0 +1,834 @@ +/*--------------------------------------------------------------------*/ +/*--- Read DWARF2 debug info. vg_dwarf.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core.h" +#include "vg_symtab2.h" + + +/* Structure found in the .debug_line section. */ +typedef struct +{ + UChar li_length [4]; + UChar li_version [2]; + UChar li_prologue_length [4]; + UChar li_min_insn_length [1]; + UChar li_default_is_stmt [1]; + UChar li_line_base [1]; + UChar li_line_range [1]; + UChar li_opcode_base [1]; +} +DWARF2_External_LineInfo; + +typedef struct +{ + UInt li_length; + UShort li_version; + UInt li_prologue_length; + UChar li_min_insn_length; + UChar li_default_is_stmt; + Int li_line_base; + UChar li_line_range; + UChar li_opcode_base; +} +DWARF2_Internal_LineInfo; + +/* Line number opcodes. */ +enum dwarf_line_number_ops + { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + /* DWARF 3. */ + DW_LNS_set_prologue_end = 10, + DW_LNS_set_epilogue_begin = 11, + DW_LNS_set_isa = 12 + }; + +/* Line number extended opcodes. */ +enum dwarf_line_number_x_ops + { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3 + }; + +typedef struct State_Machine_Registers +{ + /* Information for the last statement boundary. + * Needed to calculate statement lengths. */ + Addr last_address; + UInt last_file; + UInt last_line; + + Addr address; + UInt file; + UInt line; + UInt column; + Int is_stmt; + Int basic_block; + Int end_sequence; + /* This variable hold the number of the last entry seen + in the File Table. */ + UInt last_file_entry; +} SMR; + + +static +UInt read_leb128 ( UChar* data, Int* length_return, Int sign ) +{ + UInt result = 0; + UInt num_read = 0; + Int shift = 0; + UChar byte; + + do + { + byte = * data ++; + num_read ++; + + result |= (byte & 0x7f) << shift; + + shift += 7; + + } + while (byte & 0x80); + + if (length_return != NULL) + * length_return = num_read; + + if (sign && (shift < 32) && (byte & 0x40)) + result |= -1 << shift; + + return result; +} + + +static SMR state_machine_regs; + +static +void reset_state_machine ( Int is_stmt ) +{ + if (0) VG_(printf)("smr.a := %p (reset)\n", 0 ); + state_machine_regs.last_address = 0; + state_machine_regs.last_file = 1; + state_machine_regs.last_line = 1; + state_machine_regs.address = 0; + state_machine_regs.file = 1; + state_machine_regs.line = 1; + state_machine_regs.column = 0; + state_machine_regs.is_stmt = is_stmt; + state_machine_regs.basic_block = 0; + state_machine_regs.end_sequence = 0; + state_machine_regs.last_file_entry = 0; +} + +/* Handled an extend line op. Returns true if this is the end + of sequence. */ +static +int process_extended_line_op( SegInfo *si, Char*** fnames, + UChar* data, Int is_stmt, Int pointer_size) +{ + UChar op_code; + Int bytes_read; + UInt len; + UChar * name; + Addr adr; + + len = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + + if (len == 0) + { + VG_(message)(Vg_UserMsg, + "badly formed extended line op encountered!\n"); + return bytes_read; + } + + len += bytes_read; + op_code = * data ++; + + if (0) VG_(printf)("dwarf2: ext OPC: %d\n", op_code); + + switch (op_code) + { + case DW_LNE_end_sequence: + if (0) VG_(printf)("1001: si->o %p, smr.a %p\n", + si->offset, state_machine_regs.address ); + state_machine_regs.end_sequence = 1; /* JRS: added for compliance + with spec; is pointless due to reset_state_machine below + */ + if (state_machine_regs.is_stmt) { + if (state_machine_regs.last_address) + VG_(addLineInfo) (si, (*fnames)[state_machine_regs.last_file], + si->offset + state_machine_regs.last_address, + si->offset + state_machine_regs.address, + state_machine_regs.last_line, 0); + } + reset_state_machine (is_stmt); + break; + + case DW_LNE_set_address: + /* XXX: Pointer size could be 8 */ + vg_assert(pointer_size == 4); + adr = *((Addr *)data); + if (0) VG_(printf)("smr.a := %p\n", adr ); + state_machine_regs.address = adr; + break; + + case DW_LNE_define_file: + ++ state_machine_regs.last_file_entry; + name = data; + if (*fnames == NULL) + *fnames = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2); + else + *fnames = VG_(arena_realloc)( + VG_AR_SYMTAB, *fnames, VG_MIN_MALLOC_SZB, + sizeof(UInt) + * (state_machine_regs.last_file_entry + 1)); + (*fnames)[state_machine_regs.last_file_entry] = VG_(addStr) (si,name, -1); + data += VG_(strlen) ((char *) data) + 1; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + break; + + default: + break; + } + + return len; +} + + +void VG_(read_debuginfo_dwarf2) ( SegInfo* si, UChar* dwarf2, Int dwarf2_sz ) +{ + DWARF2_External_LineInfo * external; + DWARF2_Internal_LineInfo info; + UChar * standard_opcodes; + UChar * data = dwarf2; + UChar * end = dwarf2 + dwarf2_sz; + UChar * end_of_sequence; + Char ** fnames = NULL; + + /* Fails due to gcc padding ... + vg_assert(sizeof(DWARF2_External_LineInfo) + == sizeof(DWARF2_Internal_LineInfo)); + */ + + while (data < end) + { + external = (DWARF2_External_LineInfo *) data; + + /* Check the length of the block. */ + info.li_length = * ((UInt *)(external->li_length)); + + if (info.li_length == 0xffffffff) + { + VG_(symerr)("64-bit DWARF line info is not supported yet."); + break; + } + + if (info.li_length + sizeof (external->li_length) > dwarf2_sz) + { + VG_(symerr)("DWARF line info appears to be corrupt " + "- the section is too small"); + return; + } + + /* Check its version number. */ + info.li_version = * ((UShort *) (external->li_version)); + if (info.li_version != 2) + { + VG_(symerr)("Only DWARF version 2 line info " + "is currently supported."); + return; + } + + info.li_prologue_length = * ((UInt *) (external->li_prologue_length)); + info.li_min_insn_length = * ((UChar *)(external->li_min_insn_length)); + + info.li_default_is_stmt = True; + /* WAS: = * ((UChar *)(external->li_default_is_stmt)); */ + /* Josef Weidendorfer (20021021) writes: + + It seems to me that the Intel Fortran compiler generates + bad DWARF2 line info code: It sets "is_stmt" of the state + machine in the the line info reader to be always + false. Thus, there is never a statement boundary generated + and therefore never a instruction range/line number + mapping generated for valgrind. + + Please have a look at the DWARF2 specification, Ch. 6.2 + (x86.ddj.com/ftp/manuals/tools/dwarf.pdf). Perhaps I + understand this wrong, but I don't think so. + + I just had a look at the GDB DWARF2 reader... They + completely ignore "is_stmt" when recording line info ;-) + That's the reason "objdump -S" works on files from the the + intel fortran compiler. + */ + + + /* JRS: changed (UInt*) to (UChar*) */ + info.li_line_base = * ((UChar *)(external->li_line_base)); + + info.li_line_range = * ((UChar *)(external->li_line_range)); + info.li_opcode_base = * ((UChar *)(external->li_opcode_base)); + + if (0) VG_(printf)("dwarf2: line base: %d, range %d, opc base: %d\n", + info.li_line_base, info.li_line_range, info.li_opcode_base); + + /* Sign extend the line base field. */ + info.li_line_base <<= 24; + info.li_line_base >>= 24; + + end_of_sequence = data + info.li_length + + sizeof (external->li_length); + + reset_state_machine (info.li_default_is_stmt); + + /* Read the contents of the Opcodes table. */ + standard_opcodes = data + sizeof (* external); + + /* Read the contents of the Directory table. */ + data = standard_opcodes + info.li_opcode_base - 1; + + if (* data == 0) + { + } + else + { + /* We ignore the directory table, since gcc gives the entire + path as part of the filename */ + while (* data != 0) + { + data += VG_(strlen) ((char *) data) + 1; + } + } + + /* Skip the NUL at the end of the table. */ + if (*data != 0) { + VG_(symerr)("can't find NUL at end of DWARF2 directory table"); + return; + } + data ++; + + /* Read the contents of the File Name table. */ + if (* data == 0) + { + } + else + { + while (* data != 0) + { + UChar * name; + Int bytes_read; + + ++ state_machine_regs.last_file_entry; + name = data; + /* Since we don't have realloc (0, ....) == malloc (...) + semantics, we need to malloc the first time. */ + + if (fnames == NULL) + fnames = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2); + else + fnames = VG_(arena_realloc)(VG_AR_SYMTAB, fnames, + VG_MIN_MALLOC_SZB, + sizeof(UInt) + * (state_machine_regs.last_file_entry + 1)); + data += VG_(strlen) ((Char *) data) + 1; + fnames[state_machine_regs.last_file_entry] = VG_(addStr) (si,name, -1); + + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + } + } + + /* Skip the NUL at the end of the table. */ + if (*data != 0) { + VG_(symerr)("can't find NUL at end of DWARF2 file name table"); + return; + } + data ++; + + /* Now display the statements. */ + + while (data < end_of_sequence) + { + UChar op_code; + Int adv; + Int bytes_read; + + op_code = * data ++; + + if (0) VG_(printf)("dwarf2: OPC: %d\n", op_code); + + if (op_code >= info.li_opcode_base) + { + Int advAddr; + op_code -= info.li_opcode_base; + adv = (op_code / info.li_line_range) + * info.li_min_insn_length; + advAddr = adv; + state_machine_regs.address += adv; + if (0) VG_(printf)("smr.a += %p\n", adv ); + adv = (op_code % info.li_line_range) + info.li_line_base; + if (0) VG_(printf)("1002: si->o %p, smr.a %p\n", + si->offset, state_machine_regs.address ); + state_machine_regs.line += adv; + + if (state_machine_regs.is_stmt) { + /* only add a statement if there was a previous boundary */ + if (state_machine_regs.last_address) + VG_(addLineInfo) (si, fnames[state_machine_regs.last_file], + si->offset + state_machine_regs.last_address, + si->offset + state_machine_regs.address, + state_machine_regs.last_line, 0); + state_machine_regs.last_address = state_machine_regs.address; + state_machine_regs.last_file = state_machine_regs.file; + state_machine_regs.last_line = state_machine_regs.line; + } + } + else switch (op_code) + { + case DW_LNS_extended_op: + data += process_extended_line_op ( + si, &fnames, data, + info.li_default_is_stmt, sizeof (Addr)); + break; + + case DW_LNS_copy: + if (0) VG_(printf)("1002: si->o %p, smr.a %p\n", + si->offset, state_machine_regs.address ); + if (state_machine_regs.is_stmt) { + /* only add a statement if there was a previous boundary */ + if (state_machine_regs.last_address) + VG_(addLineInfo) (si, fnames[state_machine_regs.last_file], + si->offset + state_machine_regs.last_address, + si->offset + state_machine_regs.address, + state_machine_regs.last_line, 0); + state_machine_regs.last_address = state_machine_regs.address; + state_machine_regs.last_file = state_machine_regs.file; + state_machine_regs.last_line = state_machine_regs.line; + } + state_machine_regs.basic_block = 0; /* JRS added */ + break; + + case DW_LNS_advance_pc: + adv = info.li_min_insn_length + * read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.address += adv; + if (0) VG_(printf)("smr.a += %p\n", adv ); + break; + + case DW_LNS_advance_line: + adv = read_leb128 (data, & bytes_read, 1); + data += bytes_read; + state_machine_regs.line += adv; + break; + + case DW_LNS_set_file: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.file = adv; + break; + + case DW_LNS_set_column: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.column = adv; + break; + + case DW_LNS_negate_stmt: + adv = state_machine_regs.is_stmt; + adv = ! adv; + state_machine_regs.is_stmt = adv; + break; + + case DW_LNS_set_basic_block: + state_machine_regs.basic_block = 1; + break; + + case DW_LNS_const_add_pc: + adv = (((255 - info.li_opcode_base) / info.li_line_range) + * info.li_min_insn_length); + state_machine_regs.address += adv; + if (0) VG_(printf)("smr.a += %p\n", adv ); + break; + + case DW_LNS_fixed_advance_pc: + /* XXX: Need something to get 2 bytes */ + adv = *((UShort *)data); + data += 2; + state_machine_regs.address += adv; + if (0) VG_(printf)("smr.a += %p\n", adv ); + break; + + case DW_LNS_set_prologue_end: + break; + + case DW_LNS_set_epilogue_begin: + break; + + case DW_LNS_set_isa: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + break; + + default: + { + int j; + for (j = standard_opcodes[op_code - 1]; j > 0 ; --j) + { + read_leb128 (data, &bytes_read, 0); + data += bytes_read; + } + } + break; + } + } + VG_(arena_free)(VG_AR_SYMTAB, fnames); + fnames = NULL; + } +} + + +/*------------------------------------------------------------*/ +/*--- Read DWARF1 format line number info. ---*/ +/*------------------------------------------------------------*/ + +/* DWARF1 appears to be redundant, but nevertheless the Lahey Fortran + compiler generates it. +*/ + +/* The following three enums (dwarf_tag, dwarf_form, dwarf_attribute) + are taken from the file include/elf/dwarf.h in the GNU gdb-6.0 + sources, which are Copyright 1992, 1993, 1995, 1999 Free Software + Foundation, Inc and naturally licensed under the GNU General Public + License version 2 or later. +*/ + +/* Tag names and codes. */ + +enum dwarf_tag { + TAG_padding = 0x0000, + TAG_array_type = 0x0001, + TAG_class_type = 0x0002, + TAG_entry_point = 0x0003, + TAG_enumeration_type = 0x0004, + TAG_formal_parameter = 0x0005, + TAG_global_subroutine = 0x0006, + TAG_global_variable = 0x0007, + /* 0x0008 -- reserved */ + /* 0x0009 -- reserved */ + TAG_label = 0x000a, + TAG_lexical_block = 0x000b, + TAG_local_variable = 0x000c, + TAG_member = 0x000d, + /* 0x000e -- reserved */ + TAG_pointer_type = 0x000f, + TAG_reference_type = 0x0010, + TAG_compile_unit = 0x0011, + TAG_string_type = 0x0012, + TAG_structure_type = 0x0013, + TAG_subroutine = 0x0014, + TAG_subroutine_type = 0x0015, + TAG_typedef = 0x0016, + TAG_union_type = 0x0017, + TAG_unspecified_parameters = 0x0018, + TAG_variant = 0x0019, + TAG_common_block = 0x001a, + TAG_common_inclusion = 0x001b, + TAG_inheritance = 0x001c, + TAG_inlined_subroutine = 0x001d, + TAG_module = 0x001e, + TAG_ptr_to_member_type = 0x001f, + TAG_set_type = 0x0020, + TAG_subrange_type = 0x0021, + TAG_with_stmt = 0x0022, + + /* GNU extensions */ + + TAG_format_label = 0x8000, /* for FORTRAN 77 and Fortran 90 */ + TAG_namelist = 0x8001, /* For Fortran 90 */ + TAG_function_template = 0x8002, /* for C++ */ + TAG_class_template = 0x8003 /* for C++ */ +}; + +/* Form names and codes. */ + +enum dwarf_form { + FORM_ADDR = 0x1, + FORM_REF = 0x2, + FORM_BLOCK2 = 0x3, + FORM_BLOCK4 = 0x4, + FORM_DATA2 = 0x5, + FORM_DATA4 = 0x6, + FORM_DATA8 = 0x7, + FORM_STRING = 0x8 +}; + +/* Attribute names and codes. */ + +enum dwarf_attribute { + AT_sibling = (0x0010|FORM_REF), + AT_location = (0x0020|FORM_BLOCK2), + AT_name = (0x0030|FORM_STRING), + AT_fund_type = (0x0050|FORM_DATA2), + AT_mod_fund_type = (0x0060|FORM_BLOCK2), + AT_user_def_type = (0x0070|FORM_REF), + AT_mod_u_d_type = (0x0080|FORM_BLOCK2), + AT_ordering = (0x0090|FORM_DATA2), + AT_subscr_data = (0x00a0|FORM_BLOCK2), + AT_byte_size = (0x00b0|FORM_DATA4), + AT_bit_offset = (0x00c0|FORM_DATA2), + AT_bit_size = (0x00d0|FORM_DATA4), + /* (0x00e0|FORM_xxxx) -- reserved */ + AT_element_list = (0x00f0|FORM_BLOCK4), + AT_stmt_list = (0x0100|FORM_DATA4), + AT_low_pc = (0x0110|FORM_ADDR), + AT_high_pc = (0x0120|FORM_ADDR), + AT_language = (0x0130|FORM_DATA4), + AT_member = (0x0140|FORM_REF), + AT_discr = (0x0150|FORM_REF), + AT_discr_value = (0x0160|FORM_BLOCK2), + /* (0x0170|FORM_xxxx) -- reserved */ + /* (0x0180|FORM_xxxx) -- reserved */ + AT_string_length = (0x0190|FORM_BLOCK2), + AT_common_reference = (0x01a0|FORM_REF), + AT_comp_dir = (0x01b0|FORM_STRING), + AT_const_value_string = (0x01c0|FORM_STRING), + AT_const_value_data2 = (0x01c0|FORM_DATA2), + AT_const_value_data4 = (0x01c0|FORM_DATA4), + AT_const_value_data8 = (0x01c0|FORM_DATA8), + AT_const_value_block2 = (0x01c0|FORM_BLOCK2), + AT_const_value_block4 = (0x01c0|FORM_BLOCK4), + AT_containing_type = (0x01d0|FORM_REF), + AT_default_value_addr = (0x01e0|FORM_ADDR), + AT_default_value_data2 = (0x01e0|FORM_DATA2), + AT_default_value_data4 = (0x01e0|FORM_DATA4), + AT_default_value_data8 = (0x01e0|FORM_DATA8), + AT_default_value_string = (0x01e0|FORM_STRING), + AT_friends = (0x01f0|FORM_BLOCK2), + AT_inline = (0x0200|FORM_STRING), + AT_is_optional = (0x0210|FORM_STRING), + AT_lower_bound_ref = (0x0220|FORM_REF), + AT_lower_bound_data2 = (0x0220|FORM_DATA2), + AT_lower_bound_data4 = (0x0220|FORM_DATA4), + AT_lower_bound_data8 = (0x0220|FORM_DATA8), + AT_private = (0x0240|FORM_STRING), + AT_producer = (0x0250|FORM_STRING), + AT_program = (0x0230|FORM_STRING), + AT_protected = (0x0260|FORM_STRING), + AT_prototyped = (0x0270|FORM_STRING), + AT_public = (0x0280|FORM_STRING), + AT_pure_virtual = (0x0290|FORM_STRING), + AT_return_addr = (0x02a0|FORM_BLOCK2), + AT_abstract_origin = (0x02b0|FORM_REF), + AT_start_scope = (0x02c0|FORM_DATA4), + AT_stride_size = (0x02e0|FORM_DATA4), + AT_upper_bound_ref = (0x02f0|FORM_REF), + AT_upper_bound_data2 = (0x02f0|FORM_DATA2), + AT_upper_bound_data4 = (0x02f0|FORM_DATA4), + AT_upper_bound_data8 = (0x02f0|FORM_DATA8), + AT_virtual = (0x0300|FORM_STRING), + + /* GNU extensions. */ + + AT_sf_names = (0x8000|FORM_DATA4), + AT_src_info = (0x8010|FORM_DATA4), + AT_mac_info = (0x8020|FORM_DATA4), + AT_src_coords = (0x8030|FORM_DATA4), + AT_body_begin = (0x8040|FORM_ADDR), + AT_body_end = (0x8050|FORM_ADDR) +}; + +/* end of enums taken from gdb-6.0 sources */ + +void VG_(read_debuginfo_dwarf1) ( + SegInfo* si, + UChar* dwarf1d, Int dwarf1d_sz, + UChar* dwarf1l, Int dwarf1l_sz ) +{ + UInt stmt_list; + Bool stmt_list_found; + Int die_offset, die_szb, at_offset; + UShort die_kind, at_kind; + UChar* at_base; + UChar* src_filename; + + if (0) + VG_(printf)("read_debuginfo_dwarf1 ( %p, %d, %p, %d )\n", + dwarf1d, dwarf1d_sz, dwarf1l, dwarf1l_sz ); + + /* This loop scans the DIEs. */ + die_offset = 0; + while (True) { + if (die_offset >= dwarf1d_sz) break; + + die_szb = *(Int*)(dwarf1d + die_offset); + die_kind = *(UShort*)(dwarf1d + die_offset + 4); + + /* We're only interested in compile_unit DIEs; ignore others. */ + if (die_kind != TAG_compile_unit) { + die_offset += die_szb; + continue; + } + + if (0) + VG_(printf)("compile-unit DIE: offset %d, tag 0x%x, size %d\n", + die_offset, (Int)die_kind, die_szb ); + + /* We've got a compile_unit DIE starting at (dwarf1d + + die_offset+6). Try and find the AT_name and AT_stmt_list + attributes. Then, finally, we can read the line number info + for this source file. */ + + /* The next 3 are set as we find the relevant attrs. */ + src_filename = NULL; + stmt_list_found = False; + stmt_list = 0; + + /* This loop scans the Attrs inside compile_unit DIEs. */ + at_base = dwarf1d + die_offset + 6; + at_offset = 0; + while (True) { + if (at_offset >= die_szb-6) break; + + at_kind = *(UShort*)(at_base + at_offset); + if (0) VG_(printf)("atoffset %d, attag 0x%x\n", + at_offset, (Int)at_kind ); + at_offset += 2; /* step over the attribute itself */ + /* We have to examine the attribute to figure out its + length. */ + switch (at_kind) { + case AT_stmt_list: + case AT_language: + case AT_sibling: + if (at_kind == AT_stmt_list) { + stmt_list_found = True; + stmt_list = *(Int*)(at_base+at_offset); + } + at_offset += 4; break; + case AT_high_pc: + case AT_low_pc: + at_offset += sizeof(void*); break; + case AT_name: + case AT_producer: + case AT_comp_dir: + /* Zero terminated string, step over it. */ + if (at_kind == AT_name) + src_filename = at_base + at_offset; + while (at_offset < die_szb-6 && at_base[at_offset] != 0) + at_offset++; + at_offset++; + break; + default: + VG_(printf)("Unhandled DWARF-1 attribute 0x%x\n", + (Int)at_kind ); + VG_(core_panic)("Unhandled DWARF-1 attribute"); + } /* switch (at_kind) */ + } /* looping over attributes */ + + /* So, did we find the required stuff for a line number table in + this DIE? If yes, read it. */ + if (stmt_list_found /* there is a line number table */ + && src_filename != NULL /* we know the source filename */ + ) { + /* Table starts: + Length: + 4 bytes, includes the entire table + Base address: + unclear (4? 8?), assuming native pointer size here. + Then a sequence of triples + (source line number -- 32 bits + source line column -- 16 bits + address delta -- 32 bits) + */ + Addr base; + Int len; + Char* curr_filenm; + UChar* ptr; + UInt prev_line, prev_delta; + + curr_filenm = VG_(addStr) ( si, src_filename, -1 ); + prev_line = prev_delta = 0; + + ptr = dwarf1l + stmt_list; + len = *(Int*)ptr; ptr += sizeof(Int); + base = (Addr)(*(void**)ptr); ptr += sizeof(void*); + len -= (sizeof(Int) + sizeof(void*)); + while (len > 0) { + UInt line; + UShort col; + UInt delta; + line = *(UInt*)ptr; ptr += sizeof(UInt); + col = *(UShort*)ptr; ptr += sizeof(UShort); + delta = *(UShort*)ptr; ptr += sizeof(UInt); + if (0) VG_(printf)("line %d, col %d, delta %d\n", + line, (Int)col, delta ); + len -= (sizeof(UInt) + sizeof(UShort) + sizeof(UInt)); + + if (delta > 0 && prev_line > 0) { + if (0) VG_(printf) (" %d %d-%d\n", + prev_line, prev_delta, delta-1); + VG_(addLineInfo) ( si, curr_filenm, + base + prev_delta, base + delta, + prev_line, 0 ); + } + prev_line = line; + prev_delta = delta; + } + } + + /* Move on the the next DIE. */ + die_offset += die_szb; + + } /* Looping over DIEs */ + +} + + +/*--------------------------------------------------------------------*/ +/*--- end vg_dwarf.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_errcontext.c b/VEX/head20041019/coregrind/vg_errcontext.c new file mode 100644 index 0000000000..5a2274ec5b --- /dev/null +++ b/VEX/head20041019/coregrind/vg_errcontext.c @@ -0,0 +1,1021 @@ + +/*--------------------------------------------------------------------*/ +/*--- Management of error messages. vg_errcontext.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core.h" + +/*------------------------------------------------------------*/ +/*--- Globals ---*/ +/*------------------------------------------------------------*/ + +/* The list of error contexts found, both suppressed and unsuppressed. + Initially empty, and grows as errors are detected. */ +static Error* vg_errors = NULL; + +/* The list of suppression directives, as read from the specified + suppressions file. */ +static Supp* vg_suppressions = NULL; + +/* Running count of unsuppressed errors detected. */ +static UInt n_errs_found = 0; + +/* Running count of suppressed errors detected. */ +static UInt n_errs_suppressed = 0; + +/* forwards ... */ +static Supp* is_suppressible_error ( Error* err ); + + +/*------------------------------------------------------------*/ +/*--- Error type ---*/ +/*------------------------------------------------------------*/ + +/* Note: it is imperative this doesn't overlap with (0..) at all, as tools + * effectively extend it by defining their own enums in the (0..) range. */ +typedef + enum { + PThreadErr = -1, // Pthreading error + } + CoreErrorKind; + +/* Errors. Extensible (via the 'extra' field). Tools can use a normal + enum (with element values in the normal range (0..)) for `ekind'. + Functions for getting/setting the tool-relevant fields are in + include/tool.h. + + When errors are found and recorded with VG_(maybe_record_error)(), all + the tool must do is pass in the four parameters; core will + allocate/initialise the error record. +*/ +struct _Error { + struct _Error* next; + // NULL if unsuppressed; or ptr to suppression record. + Supp* supp; + Int count; + ThreadId tid; + + // The tool-specific part + ExeContext* where; // Initialised by core + Int ekind; // Used by ALL. Must be in the range (0..) + Addr addr; // Used frequently + Char* string; // Used frequently + void* extra; // For any tool-specific extras +}; + +ExeContext* VG_(get_error_where) ( Error* err ) +{ + return err->where; +} + +ErrorKind VG_(get_error_kind) ( Error* err ) +{ + return err->ekind; +} + +Addr VG_(get_error_address) ( Error* err ) +{ + return err->addr; +} + +Char* VG_(get_error_string) ( Error* err ) +{ + return err->string; +} + +void* VG_(get_error_extra) ( Error* err ) +{ + return err->extra; +} + +UInt VG_(get_n_errs_found)( void ) +{ + return n_errs_found; +} + +/*------------------------------------------------------------*/ +/*--- Suppression type ---*/ +/*------------------------------------------------------------*/ + +/* Note: it is imperative this doesn't overlap with (0..) at all, as tools + * effectively extend it by defining their own enums in the (0..) range. */ +typedef + enum { + PThreadSupp = -1, /* Matches PThreadErr */ + } + CoreSuppKind; + +/* For each caller specified for a suppression, record the nature of + the caller name. Not of interest to tools. */ +typedef + enum { + ObjName, /* Name is of an shared object file. */ + FunName /* Name is of a function. */ + } + SuppLocTy; + +/* Suppressions. Tools can get/set tool-relevant parts with functions + declared in include/tool.h. Extensible via the 'extra' field. + Tools can use a normal enum (with element values in the normal range + (0..)) for `skind'. */ +struct _Supp { + struct _Supp* next; + Int count; // The number of times this error has been suppressed. + Char* sname; // The name by which the suppression is referred to. + /* First two (name of fn where err occurs, and immediate caller) + * are mandatory; extra two are optional. */ + SuppLocTy caller_ty[VG_N_SUPP_CALLERS]; + Char* caller [VG_N_SUPP_CALLERS]; + + /* The tool-specific part */ + SuppKind skind; // What kind of suppression. Must use the range (0..). + Char* string; // String -- use is optional. NULL by default. + void* extra; // Anything else -- use is optional. NULL by default. +}; + +SuppKind VG_(get_supp_kind) ( Supp* su ) +{ + return su->skind; +} + +Char* VG_(get_supp_string) ( Supp* su ) +{ + return su->string; +} + +void* VG_(get_supp_extra) ( Supp* su ) +{ + return su->extra; +} + + +void VG_(set_supp_kind) ( Supp* su, SuppKind skind ) +{ + su->skind = skind; +} + +void VG_(set_supp_string) ( Supp* su, Char* string ) +{ + su->string = string; +} + +void VG_(set_supp_extra) ( Supp* su, void* extra ) +{ + su->extra = extra; +} + + +/*------------------------------------------------------------*/ +/*--- Helper fns ---*/ +/*------------------------------------------------------------*/ + +/* Compare error contexts, to detect duplicates. Note that if they + are otherwise the same, the faulting addrs and associated rwoffsets + are allowed to be different. */ +static Bool eq_Error ( VgRes res, Error* e1, Error* e2 ) +{ + if (e1->ekind != e2->ekind) + return False; + if (!VG_(eq_ExeContext)(res, e1->where, e2->where)) + return False; + + switch (e1->ekind) { + case PThreadErr: + vg_assert(VG_(needs).core_errors); + if (e1->string == e2->string) + return True; + if (0 == VG_(strcmp)(e1->string, e2->string)) + return True; + return False; + default: + if (VG_(needs).skin_errors) + return SK_(eq_SkinError)(res, e1, e2); + else { + VG_(printf)("\nUnhandled error type: %u. VG_(needs).skin_errors\n" + "probably needs to be set.\n", + e1->ekind); + VG_(skin_panic)("unhandled error type"); + } + } +} + +static void pp_Error ( Error* err, Bool printCount ) +{ + if (printCount) + VG_(message)(Vg_UserMsg, "Observed %d times:", err->count ); + if (err->tid > 1) + VG_(message)(Vg_UserMsg, "Thread %d:", err->tid ); + + switch (err->ekind) { + case PThreadErr: + vg_assert(VG_(needs).core_errors); + VG_(message)(Vg_UserMsg, "%s", err->string ); + VG_(pp_ExeContext)(err->where); + break; + default: + if (VG_(needs).skin_errors) + SK_(pp_SkinError)( err ); + else { + VG_(printf)("\nUnhandled error type: %u. VG_(needs).skin_errors\n" + "probably needs to be set?\n", + err->ekind); + VG_(skin_panic)("unhandled error type"); + } + } +} + +/* Figure out if we want to perform a given action for this error, possibly + by asking the user. */ +Bool VG_(is_action_requested) ( Char* action, Bool* clo ) +{ + Char ch, ch2; + Int res; + + if (*clo == False) + return False; + + VG_(message)(Vg_UserMsg, ""); + + again: + VG_(printf)( + "==%d== " + "---- %s ? --- [Return/N/n/Y/y/C/c] ---- ", + VG_(getpid)(), action + ); + + res = VG_(read)(VG_(clo_input_fd), &ch, 1); + if (res != 1) goto ioerror; + /* res == 1 */ + if (ch == '\n') return False; + if (ch != 'N' && ch != 'n' && ch != 'Y' && ch != 'y' + && ch != 'C' && ch != 'c') goto again; + + res = VG_(read)(VG_(clo_input_fd), &ch2, 1); + if (res != 1) goto ioerror; + if (ch2 != '\n') goto again; + + /* No, don't want to do action. */ + if (ch == 'n' || ch == 'N') return False; + /* Yes, want to do action. */ + if (ch == 'y' || ch == 'Y') return True; + /* No, don't want to do action, and don't ask again either. */ + vg_assert(ch == 'c' || ch == 'C'); + + ioerror: + *clo = False; + return False; +} + + +/* I've gone all object-oriented... initialisation depends on where the + error comes from: + + - If from generated code (tst == NULL), the %EIP/%EBP values that we + need in order to attach GDB are picked up out of VG_(baseBlock) rather + than from the thread table (vg_threads in vg_scheduler.c). + + - If not from generated code but in response to requests passed back to + the scheduler (tst != NULL), we pick up %EIP/%EBP values from the + stored thread state, not from VG_(baseBlock). +*/ +static __inline__ +void construct_error ( Error* err, ThreadId tid, ErrorKind ekind, Addr a, + Char* s, void* extra, ExeContext* where ) +{ + sk_assert(tid < VG_N_THREADS); + + /* Core-only parts */ + err->next = NULL; + err->supp = NULL; + err->count = 1; + err->tid = tid; + if (NULL == where) + err->where = VG_(get_ExeContext)( tid ); + else + err->where = where; + + /* Tool-relevant parts */ + err->ekind = ekind; + err->addr = a; + err->extra = extra; + err->string = s; + + /* sanity... */ + vg_assert( tid < VG_N_THREADS ); +} + +static void gen_suppression(Error* err) +{ + Int i; + static UChar buf[M_VG_ERRTXT]; + Bool main_done = False; + ExeContext* ec = VG_(get_error_where)(err); + Int stop_at = VG_(clo_backtrace_size); + + if (stop_at > 4) stop_at = 4; /* At most four names */ + vg_assert(stop_at > 0); + + VG_(printf)("{\n"); + VG_(printf)(" \n"); + + if (PThreadErr == err->ekind) { + VG_(printf)(" core:PThread\n"); + + } else { + Char* name = SK_(get_error_name)(err); + if (NULL == name) { + VG_(message)(Vg_UserMsg, + "(tool does not allow error to be suppressed)"); + return; + } + VG_(printf)(" %s:%s\n", VG_(details).name, name); + SK_(print_extra_suppression_info)(err); + } + + /* This loop condensed from VG_(mini_stack_dump)() */ + i = 0; + do { + Addr eip = ec->ips[i]; + if (i > 0) + eip -= MIN_INSTR_SIZE; // point to calling line + if ( VG_(get_fnname_nodemangle) (eip, buf, M_VG_ERRTXT) ) { + // Stop after "main"; if main() is recursive, stop after last main(). + + if ( ! VG_(clo_show_below_main)) { + if (VG_STREQ(buf, "main")) + main_done = True; + else if (main_done) + break; + } + VG_(printf)(" fun:%s\n", buf); + } else if ( VG_(get_objname)(eip, buf, M_VG_ERRTXT) ) { + VG_(printf)(" obj:%s\n", buf); + } else { + VG_(printf)(" ???:??? " + "# unknown, suppression will not work, sorry\n"); + } + i++; + } while (i < stop_at && ec->ips[i] != 0); + + VG_(printf)("}\n"); +} + +static +void do_actions_on_error(Error* err, Bool allow_db_attach) +{ + /* Perhaps we want a debugger attach at this point? */ + if (allow_db_attach && + VG_(is_action_requested)( "Attach to debugger", & VG_(clo_db_attach) )) + { + VG_(printf)("starting debugger\n"); + VG_(start_debugger)( err->tid ); + } + /* Or maybe we want to generate the error's suppression? */ + if (VG_(is_action_requested)( "Print suppression", + & VG_(clo_gen_suppressions) )) { + gen_suppression(err); + } +} + +/* Shared between VG_(maybe_record_error)() and VG_(unique_error)(), + just for pretty printing purposes. */ +static Bool is_first_shown_context = True; + +/* Top-level entry point to the error management subsystem. + All detected errors are notified here; this routine decides if/when the + user should see the error. */ +void VG_(maybe_record_error) ( ThreadId tid, + ErrorKind ekind, Addr a, Char* s, void* extra ) +{ + Error err; + Error* p; + Error* p_prev; + UInt extra_size; + VgRes exe_res = Vg_MedRes; + static Bool stopping_message = False; + static Bool slowdown_message = False; + static Int vg_n_errs_shown = 0; + + /* After M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN different errors have + been found, or M_VG_COLLECT_NO_ERRORS_AFTER_FOUND total errors + have been found, just refuse to collect any more. This stops + the burden of the error-management system becoming excessive in + extremely buggy programs, although it does make it pretty + pointless to continue the Valgrind run after this point. */ + if (VG_(clo_error_limit) + && (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN + || n_errs_found >= M_VG_COLLECT_NO_ERRORS_AFTER_FOUND)) { + if (!stopping_message) { + VG_(message)(Vg_UserMsg, ""); + + if (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN) { + VG_(message)(Vg_UserMsg, + "More than %d different errors detected. " + "I'm not reporting any more.", + M_VG_COLLECT_NO_ERRORS_AFTER_SHOWN ); + } else { + VG_(message)(Vg_UserMsg, + "More than %d total errors detected. " + "I'm not reporting any more.", + M_VG_COLLECT_NO_ERRORS_AFTER_FOUND ); + } + + VG_(message)(Vg_UserMsg, + "Final error counts will be inaccurate. Go fix your program!"); + VG_(message)(Vg_UserMsg, + "Rerun with --error-limit=no to disable this cutoff. Note"); + VG_(message)(Vg_UserMsg, + "that errors may occur in your program without prior warning from"); + VG_(message)(Vg_UserMsg, + "Valgrind, because errors are no longer being displayed."); + VG_(message)(Vg_UserMsg, ""); + stopping_message = True; + } + return; + } + + /* After M_VG_COLLECT_ERRORS_SLOWLY_AFTER different errors have + been found, be much more conservative about collecting new + ones. */ + if (vg_n_errs_shown >= M_VG_COLLECT_ERRORS_SLOWLY_AFTER) { + exe_res = Vg_LowRes; + if (!slowdown_message) { + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, + "More than %d errors detected. Subsequent errors", + M_VG_COLLECT_ERRORS_SLOWLY_AFTER); + VG_(message)(Vg_UserMsg, + "will still be recorded, but in less detail than before."); + slowdown_message = True; + } + } + + /* Build ourselves the error */ + construct_error ( &err, tid, ekind, a, s, extra, NULL ); + + /* First, see if we've got an error record matching this one. */ + p = vg_errors; + p_prev = NULL; + while (p != NULL) { + if (eq_Error(exe_res, p, &err)) { + /* Found it. */ + p->count++; + if (p->supp != NULL) { + /* Deal correctly with suppressed errors. */ + p->supp->count++; + n_errs_suppressed++; + } else { + n_errs_found++; + } + + /* Move p to the front of the list so that future searches + for it are faster. */ + if (p_prev != NULL) { + vg_assert(p_prev->next == p); + p_prev->next = p->next; + p->next = vg_errors; + vg_errors = p; + } + + return; + } + p_prev = p; + p = p->next; + } + + /* Didn't see it. Copy and add. */ + + /* OK, we're really going to collect it. The context is on the stack and + will disappear shortly, so we must copy it. First do the main + (non-`extra') part. + + Then SK_(update_extra) can update the `extra' part. This is for when + there are more details to fill in which take time to work out but + don't affect our earlier decision to include the error -- by + postponing those details until now, we avoid the extra work in the + case where we ignore the error. Ugly. + + Then, if there is an `extra' part, copy it too, using the size that + SK_(update_extra) returned. Also allow for people using the void* + extra field for a scalar value like an integer. + */ + + /* copy main part */ + p = VG_(arena_malloc)(VG_AR_ERRORS, sizeof(Error)); + *p = err; + + /* update `extra', for non-core errors (core ones don't use 'extra') */ + if (VG_(needs).skin_errors && PThreadErr != ekind) { + extra_size = SK_(update_extra)(p); + + /* copy block pointed to by `extra', if there is one */ + if (NULL != p->extra && 0 != extra_size) { + void* new_extra = VG_(malloc)(extra_size); + VG_(memcpy)(new_extra, p->extra, extra_size); + p->extra = new_extra; + } + } + + p->next = vg_errors; + p->supp = is_suppressible_error(&err); + vg_errors = p; + if (p->supp == NULL) { + n_errs_found++; + if (!is_first_shown_context) + VG_(message)(Vg_UserMsg, ""); + pp_Error(p, False); + is_first_shown_context = False; + vg_n_errs_shown++; + do_actions_on_error(p, /*allow_db_attach*/True); + } else { + n_errs_suppressed++; + p->supp->count++; + } +} + +/* Second top-level entry point to the error management subsystem, for + errors that the tool wants to report immediately, eg. because they're + guaranteed to only happen once. This avoids all the recording and + comparing stuff. But they can be suppressed; returns True if it is + suppressed. Bool `print_error' dictates whether to print the error. + Bool `count_error' dictates whether to count the error in n_errs_found. +*/ +Bool VG_(unique_error) ( ThreadId tid, ErrorKind ekind, Addr a, Char* s, + void* extra, ExeContext* where, Bool print_error, + Bool allow_db_attach, Bool count_error ) +{ + Error err; + + /* Build ourselves the error */ + construct_error ( &err, tid, ekind, a, s, extra, where ); + + /* Unless it's suppressed, we're going to show it. Don't need to make + a copy, because it's only temporary anyway. + + Then update the `extra' part with SK_(update_extra), because that can + have an affect on whether it's suppressed. Ignore the size return + value of SK_(update_extra), because we're not copying `extra'. */ + (void)SK_(update_extra)(&err); + + if (NULL == is_suppressible_error(&err)) { + if (count_error) + n_errs_found++; + + if (print_error) { + if (!is_first_shown_context) + VG_(message)(Vg_UserMsg, ""); + pp_Error(&err, False); + is_first_shown_context = False; + } + do_actions_on_error(&err, allow_db_attach); + + return False; + + } else { + n_errs_suppressed++; + return True; + } +} + + +/*------------------------------------------------------------*/ +/*--- Exported fns ---*/ +/*------------------------------------------------------------*/ + +/* These are called not from generated code but from the scheduler */ + +void VG_(record_pthread_error) ( ThreadId tid, Char* msg ) +{ + if (! VG_(needs).core_errors) return; + VG_(maybe_record_error)( tid, PThreadErr, /*addr*/0, msg, /*extra*/NULL ); +} + +void VG_(show_all_errors) ( void ) +{ + Int i, n_min; + Int n_err_contexts, n_supp_contexts; + Error *p, *p_min; + Supp *su; + Bool any_supp; + + if (VG_(clo_verbosity) == 0) + return; + + n_err_contexts = 0; + for (p = vg_errors; p != NULL; p = p->next) { + if (p->supp == NULL) + n_err_contexts++; + } + + n_supp_contexts = 0; + for (su = vg_suppressions; su != NULL; su = su->next) { + if (su->count > 0) + n_supp_contexts++; + } + VG_(message)(Vg_UserMsg, + "ERROR SUMMARY: " + "%d errors from %d contexts (suppressed: %d from %d)", + n_errs_found, n_err_contexts, + n_errs_suppressed, n_supp_contexts ); + + if (VG_(clo_verbosity) <= 1) + return; + + /* Print the contexts in order of increasing error count. */ + for (i = 0; i < n_err_contexts; i++) { + n_min = (1 << 30) - 1; + p_min = NULL; + for (p = vg_errors; p != NULL; p = p->next) { + if (p->supp != NULL) continue; + if (p->count < n_min) { + n_min = p->count; + p_min = p; + } + } + if (p_min == NULL) VG_(skin_panic)("show_all_errors()"); + + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, "%d errors in context %d of %d:", + p_min->count, + i+1, n_err_contexts); + pp_Error( p_min, False ); + + if ((i+1 == VG_(clo_dump_error))) { + VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to debugging*/, + p_min->where->ips[0], /*debugging*/True); + } + + p_min->count = 1 << 30; + } + + if (n_supp_contexts > 0) + VG_(message)(Vg_DebugMsg, ""); + any_supp = False; + for (su = vg_suppressions; su != NULL; su = su->next) { + if (su->count > 0) { + any_supp = True; + VG_(message)(Vg_DebugMsg, "supp: %4d %s", su->count, su->sname); + } + } + + if (n_err_contexts > 0) { + if (any_supp) + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, + "IN SUMMARY: " + "%d errors from %d contexts (suppressed: %d from %d)", + n_errs_found, n_err_contexts, n_errs_suppressed, + n_supp_contexts ); + VG_(message)(Vg_UserMsg, ""); + } +} + +/*------------------------------------------------------------*/ +/*--- Standard suppressions ---*/ +/*------------------------------------------------------------*/ + +/* Get a non-blank, non-comment line of at most nBuf chars from fd. + Skips leading spaces on the line. Return True if EOF was hit instead. +*/ + +#define VG_ISSPACE(ch) (((ch)==' ') || ((ch)=='\n') || ((ch)=='\t')) + +Bool VG_(get_line) ( Int fd, Char* buf, Int nBuf ) +{ + Char ch; + Int n, i; + while (True) { + /* First, read until a non-blank char appears. */ + while (True) { + n = VG_(read)(fd, &ch, 1); + if (n == 1 && !VG_ISSPACE(ch)) break; + if (n == 0) return True; + } + + /* Now, read the line into buf. */ + i = 0; + buf[i++] = ch; buf[i] = 0; + while (True) { + n = VG_(read)(fd, &ch, 1); + if (n == 0) return False; /* the next call will return True */ + if (ch == '\n') break; + if (i > 0 && i == nBuf-1) i--; + buf[i++] = ch; buf[i] = 0; + } + while (i > 1 && VG_ISSPACE(buf[i-1])) { + i--; buf[i] = 0; + }; + + /* VG_(printf)("The line is `%s'\n", buf); */ + /* Ok, we have a line. If a non-comment line, return. + If a comment line, start all over again. */ + if (buf[0] != '#') return False; + } +} + + +/* *p_caller contains the raw name of a caller, supposedly either + fun:some_function_name or + obj:some_object_name. + Set *p_ty accordingly and advance *p_caller over the descriptor + (fun: or obj:) part. + Returns False if failed. +*/ +static Bool setLocationTy ( Char** p_caller, SuppLocTy* p_ty ) +{ + if (VG_(strncmp)(*p_caller, "fun:", 4) == 0) { + (*p_caller) += 4; + *p_ty = FunName; + return True; + } + if (VG_(strncmp)(*p_caller, "obj:", 4) == 0) { + (*p_caller) += 4; + *p_ty = ObjName; + return True; + } + VG_(printf)("location should start with fun: or obj:\n"); + return False; +} + + +/* Look for "tool" in a string like "tool1,tool2,tool3" */ +static __inline__ +Bool tool_name_present(Char *name, Char *names) +{ + Bool found; + Char *s = NULL; /* Shut gcc up */ + Int len = VG_(strlen)(name); + + found = (NULL != (s = VG_(strstr)(names, name)) && + (s == names || *(s-1) == ',') && + (*(s+len) == ',' || *(s+len) == '\0') + ); + + return found; +} + +/* Read suppressions from the file specified in vg_clo_suppressions + and place them in the suppressions list. If there's any difficulty + doing this, just give up -- there's no point in trying to recover. +*/ +static void load_one_suppressions_file ( Char* filename ) +{ +# define N_BUF 200 + Int fd, i; + Bool eof; + Char buf[N_BUF+1]; + Char* tool_names; + Char* supp_name; + + fd = VG_(open)( filename, VKI_O_RDONLY, 0 ); + if (fd < 0) { + VG_(message)(Vg_UserMsg, "FATAL: can't open suppressions file `%s'", + filename ); + VG_(exit)(1); + } + + while (True) { + /* Assign and initialise the two suppression halves (core and tool) */ + Supp* supp; + supp = VG_(arena_malloc)(VG_AR_CORE, sizeof(Supp)); + supp->count = 0; + for (i = 0; i < VG_N_SUPP_CALLERS; i++) supp->caller[i] = NULL; + supp->string = supp->extra = NULL; + + eof = VG_(get_line) ( fd, buf, N_BUF ); + if (eof) break; + + if (!VG_STREQ(buf, "{")) goto syntax_error; + + eof = VG_(get_line) ( fd, buf, N_BUF ); + if (eof || VG_STREQ(buf, "}")) goto syntax_error; + supp->sname = VG_(arena_strdup)(VG_AR_CORE, buf); + + eof = VG_(get_line) ( fd, buf, N_BUF ); + + if (eof) goto syntax_error; + + /* Check it has the "skin1,skin2,...:supp" form (look for ':') */ + i = 0; + while (True) { + if (buf[i] == ':') break; + if (buf[i] == '\0') goto syntax_error; + i++; + } + buf[i] = '\0'; /* Replace ':', splitting into two strings */ + + tool_names = & buf[0]; + supp_name = & buf[i+1]; + + /* Is it a core suppression? */ + if (VG_(needs).core_errors && tool_name_present("core", tool_names)) + { + if (VG_STREQ(supp_name, "PThread")) + supp->skind = PThreadSupp; + else + goto syntax_error; + } + + /* Is it a tool suppression? */ + else if (VG_(needs).skin_errors && + tool_name_present(VG_(details).name, tool_names)) + { + if (SK_(recognised_suppression)(supp_name, supp)) + { + /* Do nothing, function fills in supp->skind */ + } else + goto syntax_error; + } + + else { + /* Ignore rest of suppression */ + while (True) { + eof = VG_(get_line) ( fd, buf, N_BUF ); + if (eof) goto syntax_error; + if (VG_STREQ(buf, "}")) + break; + } + continue; + } + + if (VG_(needs).skin_errors && + !SK_(read_extra_suppression_info)(fd, buf, N_BUF, supp)) + goto syntax_error; + + /* "i > 0" ensures at least one caller read. */ + for (i = 0; i <= VG_N_SUPP_CALLERS; i++) { + eof = VG_(get_line) ( fd, buf, N_BUF ); + if (eof) goto syntax_error; + if (i > 0 && VG_STREQ(buf, "}")) + break; + if (i == VG_N_SUPP_CALLERS) + break; + supp->caller[i] = VG_(arena_strdup)(VG_AR_CORE, buf); + if (!setLocationTy(&(supp->caller[i]), &(supp->caller_ty[i]))) + goto syntax_error; + } + + /* make sure to grab the '}' if the num callers is >= + VG_N_SUPP_CALLERS */ + if (!VG_STREQ(buf, "}")) { + do { + eof = VG_(get_line) ( fd, buf, N_BUF ); + } while (!eof && !VG_STREQ(buf, "}")); + } + + supp->next = vg_suppressions; + vg_suppressions = supp; + } + VG_(close)(fd); + return; + + syntax_error: + if (eof) { + VG_(message)(Vg_UserMsg, + "FATAL: in suppressions file `%s': unexpected EOF", + filename ); + } else { + VG_(message)(Vg_UserMsg, + "FATAL: in suppressions file: `%s': syntax error on: %s", + filename, buf ); + } + VG_(close)(fd); + VG_(message)(Vg_UserMsg, "exiting now."); + VG_(exit)(1); + +# undef N_BUF +} + + +void VG_(load_suppressions) ( void ) +{ + Int i; + vg_suppressions = NULL; + for (i = 0; i < VG_(clo_n_suppressions); i++) { + if (VG_(clo_verbosity) > 1) { + VG_(message)(Vg_UserMsg, "Reading suppressions file: %s", + VG_(clo_suppressions)[i] ); + } + load_one_suppressions_file( VG_(clo_suppressions)[i] ); + } +} + +/* Return the name of an erring fn in a way which is useful + for comparing against the contents of a suppressions file. + Doesn't demangle the fn name, because we want to refer to + mangled names in the suppressions file. +*/ +static void get_objname_fnname ( Addr a, Char* obj_buf, Int n_obj_buf, + Char* fun_buf, Int n_fun_buf ) +{ + (void)VG_(get_objname) ( a, obj_buf, n_obj_buf ); + (void)VG_(get_fnname_nodemangle)( a, fun_buf, n_fun_buf ); +} + +static __inline__ +Bool supp_matches_error(Supp* su, Error* err) +{ + switch (su->skind) { + case PThreadSupp: + return (err->ekind == PThreadErr); + default: + if (VG_(needs).skin_errors) { + return SK_(error_matches_suppression)(err, su); + } else { + VG_(printf)( + "\nUnhandled suppression type: %u. VG_(needs).skin_errors\n" + "probably needs to be set.\n", + err->ekind); + VG_(skin_panic)("unhandled suppression type"); + } + } +} + +static __inline__ +Bool supp_matches_callers(Supp* su, Char caller_obj[][M_VG_ERRTXT], + Char caller_fun[][M_VG_ERRTXT]) +{ + Int i; + + for (i = 0; i < VG_N_SUPP_CALLERS && su->caller[i] != NULL; i++) { + switch (su->caller_ty[i]) { + case ObjName: if (VG_(string_match)(su->caller[i], + caller_obj[i])) break; + return False; + case FunName: if (VG_(string_match)(su->caller[i], + caller_fun[i])) break; + return False; + default: VG_(skin_panic)("supp_matches_callers"); + } + } + + /* If we reach here, it's a match */ + return True; +} + +/* Does an error context match a suppression? ie is this a suppressible + error? If so, return a pointer to the Supp record, otherwise NULL. + Tries to minimise the number of symbol searches since they are expensive. +*/ +static Supp* is_suppressible_error ( Error* err ) +{ + Int i; + + static Char caller_obj[VG_N_SUPP_CALLERS][M_VG_ERRTXT]; + static Char caller_fun[VG_N_SUPP_CALLERS][M_VG_ERRTXT]; + + Supp* su; + + /* get_objname_fnname() writes the function name and object name if + it finds them in the debug info. So the strings in the suppression + file should match these. + */ + + /* Initialise these strs so they are always safe to compare, even + if get_objname_fnname doesn't write anything to them. */ + for (i = 0; i < VG_N_SUPP_CALLERS; i++) + caller_obj[i][0] = caller_fun[i][0] = 0; + + for (i = 0; i < VG_N_SUPP_CALLERS && i < VG_(clo_backtrace_size); i++) { + get_objname_fnname ( err->where->ips[i], caller_obj[i], M_VG_ERRTXT, + caller_fun[i], M_VG_ERRTXT ); + } + + /* See if the error context matches any suppression. */ + for (su = vg_suppressions; su != NULL; su = su->next) { + if (supp_matches_error(su, err) && + supp_matches_callers(su, caller_obj, caller_fun)) { + return su; + } + } + return NULL; /* no matches */ +} + +/*--------------------------------------------------------------------*/ +/*--- end vg_errcontext.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_execontext.c b/VEX/head20041019/coregrind/vg_execontext.c new file mode 100644 index 0000000000..0c3d2913be --- /dev/null +++ b/VEX/head20041019/coregrind/vg_execontext.c @@ -0,0 +1,380 @@ + +/*--------------------------------------------------------------------*/ +/*--- Storage, and equality on, execution contexts (backtraces). ---*/ +/*--- vg_execontext.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core.h" + + +/*------------------------------------------------------------*/ +/*--- Low-level ExeContext storage. ---*/ +/*------------------------------------------------------------*/ + +/* The idea is only to ever store any one context once, so as to save + space and make exact comparisons faster. */ + +static ExeContext* vg_ec_list[VG_N_EC_LISTS]; + +/* Stats only: the number of times the system was searched to locate a + context. */ +static UInt vg_ec_searchreqs; + +/* Stats only: the number of full context comparisons done. */ +static UInt vg_ec_searchcmps; + +/* Stats only: total number of stored contexts. */ +static UInt vg_ec_totstored; + +/* Number of 2, 4 and (fast) full cmps done. */ +static UInt vg_ec_cmp2s; +static UInt vg_ec_cmp4s; +static UInt vg_ec_cmpAlls; + + +/*------------------------------------------------------------*/ +/*--- Exported functions. ---*/ +/*------------------------------------------------------------*/ + + +/* Initialise this subsystem. */ +static void init_ExeContext_storage ( void ) +{ + Int i; + static Bool init_done = False; + if (init_done) + return; + vg_ec_searchreqs = 0; + vg_ec_searchcmps = 0; + vg_ec_totstored = 0; + vg_ec_cmp2s = 0; + vg_ec_cmp4s = 0; + vg_ec_cmpAlls = 0; + for (i = 0; i < VG_N_EC_LISTS; i++) + vg_ec_list[i] = NULL; + init_done = True; +} + + +/* Print stats. */ +void VG_(print_ExeContext_stats) ( void ) +{ + init_ExeContext_storage(); + VG_(message)(Vg_DebugMsg, + "exectx: %d lists, %d contexts (avg %d per list)", + VG_N_EC_LISTS, vg_ec_totstored, + vg_ec_totstored / VG_N_EC_LISTS + ); + VG_(message)(Vg_DebugMsg, + "exectx: %d searches, %d full compares (%d per 1000)", + vg_ec_searchreqs, vg_ec_searchcmps, + vg_ec_searchreqs == 0 + ? 0 + : (UInt)( (((ULong)vg_ec_searchcmps) * 1000) + / ((ULong)vg_ec_searchreqs )) + ); + VG_(message)(Vg_DebugMsg, + "exectx: %d cmp2, %d cmp4, %d cmpAll", + vg_ec_cmp2s, vg_ec_cmp4s, vg_ec_cmpAlls + ); +} + + +/* Print an ExeContext. */ +void VG_(pp_ExeContext) ( ExeContext* e ) +{ + init_ExeContext_storage(); + VG_(mini_stack_dump) ( e->ips, VG_(clo_backtrace_size) ); +} + + +/* Compare two ExeContexts, comparing all callers. */ +Bool VG_(eq_ExeContext) ( VgRes res, ExeContext* e1, ExeContext* e2 ) +{ + if (e1 == NULL || e2 == NULL) + return False; + switch (res) { + case Vg_LowRes: + /* Just compare the top two callers. */ + vg_ec_cmp2s++; + if (e1->ips[0] != e2->ips[0] + || e1->ips[1] != e2->ips[1]) return False; + return True; + + case Vg_MedRes: + /* Just compare the top four callers. */ + vg_ec_cmp4s++; + if (e1->ips[0] != e2->ips[0]) return False; + + if (VG_(clo_backtrace_size) < 2) return True; + if (e1->ips[1] != e2->ips[1]) return False; + + if (VG_(clo_backtrace_size) < 3) return True; + if (e1->ips[2] != e2->ips[2]) return False; + + if (VG_(clo_backtrace_size) < 4) return True; + if (e1->ips[3] != e2->ips[3]) return False; + return True; + + case Vg_HighRes: + vg_ec_cmpAlls++; + /* Compare them all -- just do pointer comparison. */ + if (e1 != e2) return False; + return True; + + default: + VG_(core_panic)("VG_(eq_ExeContext): unrecognised VgRes"); + } +} + + +/* Take a snapshot of the client's stack, putting the up to 'n_ips' IPs + into 'ips'. In order to be thread-safe, we pass in the thread's IP + and FP. Returns number of IPs put in 'ips'. */ +static UInt stack_snapshot2 ( Addr* ips, UInt n_ips, Addr ip, Addr fp, + Addr fp_min, Addr fp_max_orig ) +{ + Int i; + Addr fp_max; + UInt n_found = 0; + + VGP_PUSHCC(VgpExeContext); + + /* First snaffle IPs from the client's stack into ips[0 .. n_ips-1], + putting zeroes in when the trail goes cold, which we guess to be when + FP is not a reasonable stack location. We also assert that FP + increases down the chain. */ + + // Gives shorter stack trace for tests/badjump.c + // JRS 2002-aug-16: I don't think this is a big deal; looks ok for + // most "normal" backtraces. + // NJN 2002-sep-05: traces for pthreaded programs are particularly bad. + + // JRS 2002-sep-17: hack, to round up fp_max to the end of the + // current page, at least. Dunno if it helps. + // NJN 2002-sep-17: seems to -- stack traces look like 1.0.X again + fp_max = (fp_max_orig + VKI_BYTES_PER_PAGE - 1) + & ~(VKI_BYTES_PER_PAGE - 1); + fp_max -= sizeof(Addr); + + /* Assertion broken before main() is reached in pthreaded programs; the + * offending stack traces only have one item. --njn, 2002-aug-16 */ + /* vg_assert(fp_min <= fp_max);*/ + + if (fp_min + 4000000 <= fp_max) { + /* If the stack is ridiculously big, don't poke around ... but + don't bomb out either. Needed to make John Regehr's + user-space threads package work. JRS 20021001 */ + ips[0] = ip; + i = 1; + } else { + /* Get whatever we safely can ... */ + ips[0] = ip; + fp = FIRST_STACK_FRAME(fp); + for (i = 1; i < n_ips; i++) { + if (!(fp_min <= fp && fp <= fp_max)) { + //VG_(printf)("... out of range %p\n", fp); + break; /* fp gone baaaad */ + } + // NJN 2002-sep-17: monotonicity doesn't work -- gives wrong traces... + // if (fp >= ((UInt*)fp)[0]) { + // VG_(printf)("nonmonotonic\n"); + // break; /* fp gone nonmonotonic */ + // } + ips[i] = STACK_FRAME_RET(fp); /* ret addr */ + fp = STACK_FRAME_NEXT(fp); /* old fp */ + //VG_(printf)(" %p\n", ips[i]); + } + } + n_found = i; + + /* Put zeroes in the rest. */ + for (; i < n_ips; i++) { + ips[i] = 0; + } + VGP_POPCC(VgpExeContext); + + return n_found; +} + +/* This guy is the head honcho here. Take a snapshot of the client's + stack. Search our collection of ExeContexts to see if we already + have it, and if not, allocate a new one. Either way, return a + pointer to the context. If there is a matching context we + guarantee to not allocate a new one. Thus we never store + duplicates, and so exact equality can be quickly done as equality + on the returned ExeContext* values themselves. Inspired by Hugs's + Text type. +*/ +ExeContext* VG_(get_ExeContext2) ( Addr ip, Addr fp, + Addr fp_min, Addr fp_max_orig ) +{ + Int i; + Addr ips[VG_DEEPEST_BACKTRACE]; + Bool same; + UInt hash; + ExeContext* new_ec; + ExeContext* list; + + VGP_PUSHCC(VgpExeContext); + + init_ExeContext_storage(); + vg_assert(VG_(clo_backtrace_size) >= 1 + && VG_(clo_backtrace_size) <= VG_DEEPEST_BACKTRACE); + + stack_snapshot2( ips, VG_(clo_backtrace_size), + ip, fp, fp_min, fp_max_orig ); + + /* Now figure out if we've seen this one before. First hash it so + as to determine the list number. */ + + hash = 0; + for (i = 0; i < VG_(clo_backtrace_size); i++) { + hash ^= (UInt)ips[i]; + hash = (hash << 29) | (hash >> 3); + } + hash = hash % VG_N_EC_LISTS; + + /* And (the expensive bit) look a matching entry in the list. */ + + vg_ec_searchreqs++; + + list = vg_ec_list[hash]; + + while (True) { + if (list == NULL) break; + vg_ec_searchcmps++; + same = True; + for (i = 0; i < VG_(clo_backtrace_size); i++) { + if (list->ips[i] != ips[i]) { + same = False; + break; + } + } + if (same) break; + list = list->next; + } + + if (list != NULL) { + /* Yay! We found it. */ + VGP_POPCC(VgpExeContext); + return list; + } + + /* Bummer. We have to allocate a new context record. */ + vg_ec_totstored++; + + new_ec = VG_(arena_malloc)( VG_AR_EXECTXT, + sizeof(struct _ExeContext *) + + VG_(clo_backtrace_size) * sizeof(Addr) ); + + for (i = 0; i < VG_(clo_backtrace_size); i++) + new_ec->ips[i] = ips[i]; + + new_ec->next = vg_ec_list[hash]; + vg_ec_list[hash] = new_ec; + + VGP_POPCC(VgpExeContext); + return new_ec; +} + +void get_needed_regs(ThreadId tid, Addr* ip, Addr* fp, Addr* sp, + Addr* stack_highest_word) +{ + if (VG_(is_running_thread)(tid)) { + /* thread currently in baseblock */ + *ip = VG_(baseBlock)[VGOFF_INSTR_PTR]; + *fp = VG_(baseBlock)[VGOFF_FRAME_PTR]; + *sp = VG_(baseBlock)[VGOFF_STACK_PTR]; + *stack_highest_word = VG_(threads)[tid].stack_highest_word; + } else { + /* thread in thread table */ + ThreadState* tst = & VG_(threads)[ tid ]; + *ip = ARCH_INSTR_PTR(tst->arch); + *fp = ARCH_FRAME_PTR(tst->arch); + *sp = ARCH_STACK_PTR(tst->arch); + *stack_highest_word = tst->stack_highest_word; + } + + /* Nasty little hack to deal with sysinfo syscalls - if libc is + using the sysinfo page for syscalls (the TLS version does), then + ip will always appear to be in that page when doing a syscall, + not the actual libc function doing the syscall. This check sees + if IP is within the syscall code, and pops the return address + off the stack so that ip is placed within the library function + calling the syscall. This makes stack backtraces much more + useful. */ + if (*ip >= VG_(client_trampoline_code)+VG_(tramp_syscall_offset) && + *ip < VG_(client_trampoline_code)+VG_(trampoline_code_length) && + VG_(is_addressable)(*sp, sizeof(Addr))) { + *ip = *(Addr *)*sp; + *sp += sizeof(Addr); + } +} + +ExeContext* VG_(get_ExeContext) ( ThreadId tid ) +{ + Addr ip, fp, sp, stack_highest_word; + + get_needed_regs(tid, &ip, &fp, &sp, &stack_highest_word); + return VG_(get_ExeContext2)(ip, fp, sp, stack_highest_word); +} + +/* Take a snapshot of the client's stack, putting the up to 'n_ips' + instruction pointers into 'ips'. In order to be thread-safe, we pass in + the thread's IP and FP. Returns number of IPs put in 'ips'. */ +UInt VG_(stack_snapshot) ( ThreadId tid, Addr* ips, UInt n_ips ) +{ + Addr ip, fp, sp, stack_highest_word; + + get_needed_regs(tid, &ip, &fp, &sp, &stack_highest_word); + return stack_snapshot2(ips, n_ips, ip, fp, sp, stack_highest_word); +} + + +Addr VG_(get_EIP_from_ExeContext) ( ExeContext* e, UInt n ) +{ + if (n > VG_(clo_backtrace_size)) return 0; + return e->ips[n]; +} + +Addr VG_(get_EIP) ( ThreadId tid ) +{ + Addr ret; + + if (VG_(is_running_thread)(tid)) + ret = VG_(baseBlock)[VGOFF_INSTR_PTR]; + else + ret = ARCH_INSTR_PTR(VG_(threads)[ tid ].arch); + + return ret; +} + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_from_ucode.c b/VEX/head20041019/coregrind/vg_from_ucode.c new file mode 100644 index 0000000000..5f1fcc7601 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_from_ucode.c @@ -0,0 +1,4521 @@ + +/*--------------------------------------------------------------------*/ +/*--- The JITter: translate ucode back to x86 code. ---*/ +/*--- vg_from_ucode.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core.h" + + +/*------------------------------------------------------------*/ +/*--- Renamings of frequently-used global functions. ---*/ +/*------------------------------------------------------------*/ + +#define dis VG_(print_codegen) + +/*------------------------------------------------------------*/ +/*--- Instruction emission -- turning final uinstrs back ---*/ +/*--- into x86 code. ---*/ +/*------------------------------------------------------------*/ + +/* [2001-07-08 This comment is now somewhat out of date.] + + This is straightforward but for one thing: to facilitate generating + code in a single pass, we generate position-independent code. To + do this, calls and jmps to fixed addresses must specify the address + by first loading it into a register, and jump to/call that + register. Fortunately, the only jump to a literal is the jump back + to vg_dispatch, and only %eax is live then, conveniently. UCode + call insns may only have a register as target anyway, so there's no + need to do anything fancy for them. + + The emit_* routines constitute the lowest level of instruction + emission. They simply emit the sequence of bytes corresponding to + the relevant instruction, with no further ado. In particular there + is no checking about whether uses of byte registers makes sense, + nor whether shift insns have their first operand in %cl, etc. + + These issues are taken care of by the level above, the synth_* + routines. These detect impossible operand combinations and turn + them into sequences of legal instructions. Finally, emitUInstr is + phrased in terms of the synth_* abstraction layer. */ + +/* Static state for the current basic block */ +static UChar* emitted_code; +static Int emitted_code_used; +static Int emitted_code_size; + +/* offset (in bytes into the basic block) */ +static UShort jumps[VG_MAX_JUMPS]; +static Int jumpidx; + +static enum _eflags_state { + UPD_Simd, /* baseblock copy is up to date */ + UPD_Real, /* CPU copy is up to date */ + UPD_Both, /* both are current */ +} eflags_state; + +/* ia32 static prediction is very simple. Other implementations are + more complex, so we get the condition anyway. */ +static JumpPred static_pred(Condcode cond, Int forward) +{ + if (cond == CondAlways) + return JP_TAKEN; + + return forward ? JP_NOT_TAKEN : JP_TAKEN; +} + +static const Char *predstr(JumpPred p) +{ + if (!VG_(clo_branchpred)) + return ""; + + switch(p) { + default: + case JP_NONE: return ""; + case JP_TAKEN: return ",pt"; + case JP_NOT_TAKEN: return ",pn"; + } +} + +/* single site for resetting state */ +static void reset_state(void) +{ + emitted_code_used = 0; + emitted_code_size = 500; /* reasonable initial size */ + emitted_code = VG_(arena_malloc)(VG_AR_JITTER, emitted_code_size); + jumpidx = 0; + eflags_state = UPD_Simd; +} + + +/* Statistics about C functions called from generated code. */ +static UInt ccalls = 0; +static UInt ccall_reg_saves = 0; +static UInt ccall_args = 0; +static UInt ccall_arg_setup_instrs = 0; +static UInt ccall_stack_clears = 0; +static UInt ccall_retvals = 0; +static UInt ccall_retval_movs = 0; + +/* Statistics about frequency of each UInstr */ +typedef + struct { + UInt counts; + UInt size; + } Histogram; + +/* Automatically zeroed because it's static. */ +static Histogram histogram[100]; + +void VG_(print_ccall_stats)(void) +{ + VG_(message)(Vg_DebugMsg, + " ccalls: %u C calls, %u%% saves+restores avoided" + " (%d bytes)", + ccalls, + 100-(UInt)(ccall_reg_saves/(double)(ccalls*3)*100), + ((ccalls*3) - ccall_reg_saves)*2); + VG_(message)(Vg_DebugMsg, + " %u args, avg 0.%d setup instrs each (%d bytes)", + ccall_args, + (UInt)(ccall_arg_setup_instrs/(double)ccall_args*100), + (ccall_args - ccall_arg_setup_instrs)*2); + VG_(message)(Vg_DebugMsg, + " %d%% clear the stack (%d bytes)", + (UInt)(ccall_stack_clears/(double)ccalls*100), + (ccalls - ccall_stack_clears)*3); + VG_(message)(Vg_DebugMsg, + " %u retvals, %u%% of reg-reg movs avoided (%d bytes)", + ccall_retvals, + ( ccall_retvals == 0 + ? 100 + : 100-(UInt)(ccall_retval_movs / + (double)ccall_retvals*100)), + (ccall_retvals-ccall_retval_movs)*2); +} + +void VG_(print_UInstr_histogram)(void) +{ + Int i, j; + UInt total_counts = 0; + UInt total_size = 0; + + for (i = 0; i < 100; i++) { + total_counts += histogram[i].counts; + total_size += histogram[i].size; + } + + VG_(printf)("-- UInstr frequencies -----------\n"); + for (i = 0; i < 100; i++) { + if (0 != histogram[i].counts) { + + UInt count_pc = + (UInt)(histogram[i].counts/(double)total_counts*100 + 0.5); + UInt size_pc = + (UInt)(histogram[i].size /(double)total_size *100 + 0.5); + UInt avg_size = + (UInt)(histogram[i].size / (double)histogram[i].counts + 0.5); + + VG_(printf)("%-7s:%8u (%2u%%), avg %2dB (%2u%%) |", + VG_(name_UOpcode)(True, i), + histogram[i].counts, count_pc, + avg_size, size_pc); + + for (j = 0; j < (Int)size_pc; j++) VG_(printf)("O"); + VG_(printf)("\n"); + + } else { + vg_assert(0 == histogram[i].size); + } + } + + VG_(printf)("total UInstrs %u, total size %u\n", total_counts, total_size); +} + +static void expandEmittedCode ( void ) +{ + Int i; + UChar *tmp = VG_(arena_malloc)(VG_AR_JITTER, 2 * emitted_code_size); + /* VG_(printf)("expand to %d\n", 2 * emitted_code_size); */ + for (i = 0; i < emitted_code_size; i++) + tmp[i] = emitted_code[i]; + VG_(arena_free)(VG_AR_JITTER, emitted_code); + emitted_code = tmp; + emitted_code_size *= 2; +} + +/* Local calls will be inlined, cross-module ones not */ +__inline__ void VG_(emitB) ( UInt b ) +{ + if (dis) { + if (b < 16) VG_(printf)("0%x ", b); else VG_(printf)("%2x ", b); + } + if (emitted_code_used == emitted_code_size) + expandEmittedCode(); + + emitted_code[emitted_code_used] = (UChar)b; + emitted_code_used++; +} + +__inline__ void VG_(emitW) ( UInt l ) +{ + VG_(emitB) ( (l) & 0x000000FF ); + VG_(emitB) ( (l >> 8) & 0x000000FF ); +} + +/* __inline__ */ +void VG_(emitL) ( UInt l ) +{ + VG_(emitB) ( (l) & 0x000000FF ); + VG_(emitB) ( (l >> 8) & 0x000000FF ); + VG_(emitB) ( (l >> 16) & 0x000000FF ); + VG_(emitB) ( (l >> 24) & 0x000000FF ); +} + +/* This bit is ORd onto the size to indicate that it's a client + pointer which needs bounds checking. */ +#define DO_BOUNDSCHECK (1<<8) + +/* If the user asks for it, generate bounds checks on application + pointer dereferences, in the form of a segment override. */ +static __inline__ void boundscheck() +{ + if (VG_(clo_pointercheck)) + VG_(emitB)(0x64); /* %fs prefix - see vg_dispatch.S */ +} + + +static void emit_get_eflags ( void ) +{ + Int off = 4 * VGOFF_(m_eflags); + vg_assert(off >= 0 && off < 128); + + if (dis) + VG_(printf)("\t %4d: ", emitted_code_used ); + + VG_(emitB) ( 0xFF ); /* PUSHL off(%ebp) */ + VG_(emitB) ( 0x75 ); + VG_(emitB) ( off ); + VG_(emitB) ( 0x9D ); /* POPFL */ + if (dis) + VG_(printf)( "\n\t\tpushl %d(%%ebp) ; popfl\n", off ); +} + +static void emit_put_eflags ( void ) +{ + Int off = 4 * VGOFF_(m_eflags); + vg_assert(off >= 0 && off < 128); + + if (dis) + VG_(printf)("\t %4d: ", emitted_code_used ); + + VG_(emitB) ( 0x9C ); /* PUSHFL */ + VG_(emitB) ( 0x8F ); /* POPL vg_m_state.m_eflags */ + VG_(emitB) ( 0x45 ); + VG_(emitB) ( off ); + if (dis) + VG_(printf)( "\n\t\tpushfl ; popl %d(%%ebp)\n", off ); +} + +static void maybe_emit_put_eflags( void ) +{ + if (eflags_state == UPD_Real) { + eflags_state = UPD_Both; + emit_put_eflags(); + } +} + + +/* evidently unused */ +#if 0 +static void maybe_emit_get_eflags( void ) +{ + if (eflags_state == UPD_Simd) { + eflags_state = UPD_Both; + emit_get_eflags(); + } +} +#endif + + +#if 0 +/* begin UNUSED UNUSED UNUSED UNUSED UNUSED UNUSED UNUSED */ +/* An alternative implementation of new_emit in which the + state space is explicitly enumerated. */ +__inline__ +void VG_(new_emit) ( Bool upds_simd_flags, + FlagSet use_flags, FlagSet set_flags ) +{ + Bool simd = upds_simd_flags; + enum _eflags_state where = eflags_state; + + enum { WNone, WSome, WAll } ww; + Bool rr; + +#define DIS_HEADER \ + if (dis) \ + VG_(printf)("\t %4d: ", emitted_code_used ); + + if (use_flags == FlagsEmpty) { + rr = False; + } else { + rr = True; + } + + if (set_flags == FlagsEmpty) { + ww = WNone; + } else + if (set_flags == FlagsOSZACP) { + ww = WAll; + } else { + ww = WSome; + } + + /* If we're not wanting to interact with simd flags, and the simd + flags are not in the real flags, then do nothing. */ + if (simd == False && where == UPD_Simd) + goto noaction; + + if (simd == True && where == UPD_Simd && rr == False && ww == WAll) { + /* We're going to generate a complete new simd flag state without + consulting the old one first, so just deem this insn to create + the state in the real flags. */ + eflags_state = UPD_Real; + DIS_HEADER; + return; + } + + if (simd == True && where == UPD_Simd && rr == False && ww == WSome) { + /* Want to partially update the flags state, but is in simd. So + fetch it first, then declare that the real state is the most + recent. */ + emit_get_eflags(); + eflags_state = UPD_Real; + DIS_HEADER; + return; + } + + if (simd == True && where == UPD_Simd && rr == True && ww == WNone) { + /* want to read simd flags, but not in real -> copy to real. */ + emit_get_eflags(); + eflags_state = UPD_Both; + DIS_HEADER; + return; + } + + if (simd == True && where == UPD_Simd && rr == True && ww == WAll) { + /* want to read and write simd flags, but not in real -> copy to + real. State is then Real since they get updated. */ + emit_get_eflags(); + eflags_state = UPD_Real; + DIS_HEADER; + return; + } + + if (simd == True && where == UPD_Simd && rr == False && ww == WNone) { + /* Doesn't really make sense. Want to interact with simd flags, + but insn doesn't modify them. So don't do anything. ??? */ + goto noaction; + } + + if (simd == True && where == UPD_Real && rr == False && ww == WNone) { + /* Doesn't really make sense. Want to interact with simd flags, + but insn doesn't modify them. So don't do anything. ??? */ + goto noaction; + } + + if (simd == True && where == UPD_Real && rr == True && ww == WNone) { + /* simd is in real. Insn reads real but does not change. --> do + nothing. */ + goto noaction; + } + + if (simd == True && where == UPD_Real && rr == True && ww == WAll) { + /* simd is in real. we want to capture changes made by it. --> + do nothing */ + goto noaction; + } + + if (simd == True && where == UPD_Real && rr == False && ww == WAll) { + /* simd is in real. Insn creates new simd state. --> leave in + real */ + goto noaction; + } + + if (simd == True && where == UPD_Both && rr == False && ww == WAll) { + /* simd is in both. Insn creates new simd state. --> change + state to Real. */ + narrow_Both_to_Real: + eflags_state = UPD_Real; + DIS_HEADER; + return; + } + + if (simd == True && where == UPD_Both && rr == False && ww == WSome) { + /* simd is in both. Insn creates partial new simd state. --> + change state to Real. No need to get, since Both holds. */ + goto narrow_Both_to_Real; + } + + if (simd == True && where == UPD_Real && rr == False && ww == WSome) { + /* simd is in real. Insn creates new simd state. --> leave in + real */ + goto noaction; + } + + if (simd == True && where == UPD_Both && rr == True && ww == WNone) + /* want to read the simd flags, but already have a copy in real, + and not planning to modify it --> do nothing. */ + goto noaction; + + //////////////// + + if (simd == False && where == UPD_Real && rr == False && ww == WNone) + /* simd state is in real, but insn doesn't touch it --> do nothing */ + goto noaction; + + if (simd == False && where == UPD_Both && rr == False && ww == WNone) + /* simd state is in both, insn doesn't touch it --> do nothing */ + goto noaction; + + if (simd == False && where == UPD_Both && rr == False && ww == WAll) { + /* simd state is in both. insn trashes real, therefore declare + simd state only in simd. */ + narrow_Both_to_Simd: + eflags_state = UPD_Simd; + DIS_HEADER; + return; + } + + if (simd == False && where == UPD_Both && rr == False && ww == WSome) { + /* simd state is in both. insn trashes real, therefore declare + simd state only in simd. */ + goto narrow_Both_to_Simd; + } + + if (simd == False && where == UPD_Real && rr == False && ww == WAll) { + /* simd state is in real; we don't want simd state changed, but + insn writes the flags. Therefore have to copy back first. */ + put_flags_and_continue: + emit_put_eflags(); + eflags_state = UPD_Simd; + DIS_HEADER; + return; + } + + if (simd == False && where == UPD_Real && rr == False && ww == WSome) { + /* simd state is in real; we don't want simd state changed, but + insn writes the flags. Therefore have to copy back first. */ + goto put_flags_and_continue; + } + + goto unhandled; + + noaction: + DIS_HEADER; + return; + + // if (simd == False && where == UPD_Simd && FL_NONE(rrr) && FL_SOME(www)) { + // return; + //} + + unhandled: + VG_(printf)("simd %s, where %s, read %s, write %s\n", + simd ? "True " : "False", + (eflags_state == UPD_Simd ? "Simd" : (eflags_state == UPD_Real + ? "Real" : "Both")), + rr ? "True " : "False", + ww == WNone ? "None" : ww == WSome ? "Some" : "All " + ); + + VG_(core_panic)("new_emit"); +} +/* end UNUSED UNUSED UNUSED UNUSED UNUSED UNUSED UNUSED */ +#endif + + +/* Call this before emitting each instruction. + + Arguments are: + interacts_with_simd_flags: + if true, this instruction wants to interact (read and/or write) + the simulated %EFLAGS state, + otherwise it doesn't want to. + use_flags: set of (real) flags the instruction uses + set_flags: set of (real) flags the instruction sets +*/ +void VG_(new_emit) ( Bool interacts_with_simd_flags, + FlagSet use_flags, FlagSet set_flags ) +{ + Bool use, set; + + use = use_flags != FlagsEmpty + || (set_flags != FlagsEmpty && set_flags != FlagsOSZACP); + set = set_flags != FlagsEmpty; + + if (0) + VG_(printf)( + "new_emit: state=%d interacts_with_simd_flags=%d " + "use_flags=%x set_flags=%x\n", + eflags_state, interacts_with_simd_flags, use_flags, set_flags); + + if (interacts_with_simd_flags) { + if (use && eflags_state == UPD_Simd) { + /* we need the CPU flags set, but they're not already */ + eflags_state = UPD_Both; + emit_get_eflags(); + } + if (set) { + /* if we're setting the flags, then the CPU will have the + only good copy */ + eflags_state = UPD_Real; + } + } else { + /* presume that if non-simd code is using flags, it knows what + it's doing (ie, it just set up the flags). */ + if (set) { + /* This instruction is going to trash the flags, so we'd + better save them away and say that they're only in the + simulated state. */ + maybe_emit_put_eflags(); + eflags_state = UPD_Simd; + } + } + + if (dis) + VG_(printf)("\t %4d: ", emitted_code_used ); +} + + +/*----------------------------------------------------*/ +/*--- Addressing modes ---*/ +/*----------------------------------------------------*/ + +static __inline__ UChar mkModRegRM ( UChar mod, UChar reg, UChar regmem ) +{ + return ((mod & 3) << 6) | ((reg & 7) << 3) | (regmem & 7); +} + +static __inline__ UChar mkSIB ( Int scale, Int regindex, Int regbase ) +{ + Int shift; + switch (scale) { + case 1: shift = 0; break; + case 2: shift = 1; break; + case 4: shift = 2; break; + case 8: shift = 3; break; + default: VG_(core_panic)( "mkSIB" ); + } + return ((shift & 3) << 6) | ((regindex & 7) << 3) | (regbase & 7); +} + +static __inline__ void emit_amode_litmem_reg ( Addr addr, Int reg ) +{ + /* ($ADDR), reg */ + VG_(emitB) ( mkModRegRM(0, reg, 5) ); + VG_(emitL) ( addr ); +} + +static __inline__ void emit_amode_regmem_reg ( Int regmem, Int reg ) +{ + /* (regmem), reg */ + if (regmem == R_ESP) + VG_(core_panic)("emit_amode_regmem_reg"); + if (regmem == R_EBP) { + VG_(emitB) ( mkModRegRM(1, reg, 5) ); + VG_(emitB) ( 0x00 ); + } else { + VG_(emitB)( mkModRegRM(0, reg, regmem) ); + } +} + +void VG_(emit_amode_offregmem_reg) ( Int off, Int regmem, Int reg ) +{ + if (regmem == R_ESP) + VG_(core_panic)("emit_amode_offregmem_reg(ESP)"); + if (off < -128 || off > 127) { + /* Use a large offset */ + /* d32(regmem), reg */ + VG_(emitB) ( mkModRegRM(2, reg, regmem) ); + VG_(emitL) ( off ); + } else { + /* d8(regmem), reg */ + VG_(emitB) ( mkModRegRM(1, reg, regmem) ); + VG_(emitB) ( off & 0xFF ); + } +} + +static __inline__ void emit_amode_sib_reg ( Int off, Int scale, Int regbase, + Int regindex, Int reg ) +{ + if (regindex == R_ESP) + VG_(core_panic)("emit_amode_sib_reg(ESP)"); + if (off < -128 || off > 127) { + /* Use a 32-bit offset */ + VG_(emitB) ( mkModRegRM(2, reg, 4) ); /* SIB with 32-bit displacement */ + VG_(emitB) ( mkSIB( scale, regindex, regbase ) ); + VG_(emitL) ( off ); + } else { + /* Use an 8-bit offset */ + VG_(emitB) ( mkModRegRM(1, reg, 4) ); /* SIB with 8-bit displacement */ + VG_(emitB) ( mkSIB( scale, regindex, regbase ) ); + VG_(emitB) ( off & 0xFF ); + } +} + +void VG_(emit_amode_ereg_greg) ( Int e_reg, Int g_reg ) +{ + /* other_reg, reg */ + VG_(emitB) ( mkModRegRM(3, g_reg, e_reg) ); +} + +static __inline__ void emit_amode_greg_ereg ( Int g_reg, Int e_reg ) +{ + /* other_reg, reg */ + VG_(emitB) ( mkModRegRM(3, g_reg, e_reg) ); +} + + +/*----------------------------------------------------*/ +/*--- Opcode translation ---*/ +/*----------------------------------------------------*/ + +static __inline__ Int mkGrp1opcode ( Opcode opc ) +{ + switch (opc) { + case ADD: return 0; + case OR: return 1; + case ADC: return 2; + case SBB: return 3; + case AND: return 4; + case SUB: return 5; + case XOR: return 6; + default: VG_(core_panic)("mkGrp1opcode"); + } +} + +static __inline__ FlagSet nonshiftop_use(Opcode opc) +{ + switch(opc) { + case ADC: + case SBB: + return FlagC; + + case ADD: + case OR: + case AND: + case SUB: + case XOR: + return FlagsEmpty; + + default: + VG_(core_panic)("nonshiftop_use"); + } +} + +static __inline__ FlagSet nonshiftop_set(Opcode opc) +{ + switch(opc) { + case ADC: + case SBB: + case ADD: + case OR: + case AND: + case SUB: + case XOR: + return FlagsOSZACP; + + default: + VG_(core_panic)("nonshiftop_set"); + } +} + +static __inline__ Int mkGrp2opcode ( Opcode opc ) +{ + switch (opc) { + case ROL: return 0; + case ROR: return 1; + case RCL: return 2; + case RCR: return 3; + case SHL: return 4; + case SHR: return 5; + case SAR: return 7; + default: VG_(core_panic)("mkGrp2opcode"); + } +} + +static __inline__ FlagSet shiftop_use(Opcode opc) +{ + switch(opc) { + case ROR: + case ROL: + case SHL: + case SHR: + case SAR: + return FlagsEmpty; + + case RCL: + case RCR: + return FlagC; + + default: + VG_(core_panic)("shiftop_use"); + } +} + +static __inline__ FlagSet shiftop_set(Opcode opc) +{ + switch(opc) { + case ROR: + case ROL: + case RCL: + case RCR: + return FlagsOC; + + case SHL: + case SHR: + case SAR: + return FlagsOSZACP; + + default: + VG_(core_panic)("shiftop_set"); + } +} + +static __inline__ Int mkGrp3opcode ( Opcode opc ) +{ + switch (opc) { + case NOT: return 2; + case NEG: return 3; + default: VG_(core_panic)("mkGrp3opcode"); + } +} + +static __inline__ Int mkGrp4opcode ( Opcode opc ) +{ + switch (opc) { + case INC: return 0; + case DEC: return 1; + default: VG_(core_panic)("mkGrp4opcode"); + } +} + +static __inline__ Int mkGrp5opcode ( Opcode opc ) +{ + switch (opc) { + case CALLM: return 2; + case JMP: return 4; + default: VG_(core_panic)("mkGrp5opcode"); + } +} + +static __inline__ UChar mkPrimaryOpcode ( Opcode opc ) +{ + switch (opc) { + case ADD: return 0x00; + case ADC: return 0x10; + case AND: return 0x20; + case XOR: return 0x30; + case OR: return 0x08; + case SBB: return 0x18; + case SUB: return 0x28; + default: VG_(core_panic)("mkPrimaryOpcode"); + } +} + +/*----------------------------------------------------*/ +/*--- v-size (4, or 2 with OSO) insn emitters ---*/ +/*----------------------------------------------------*/ + +void VG_(emit_movv_offregmem_reg) ( Int sz, Int off, Int areg, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0x8B ); /* MOV Ev, Gv */ + VG_(emit_amode_offregmem_reg) ( off, areg, reg ); + if (dis) + VG_(printf)( "\n\t\tmov%c\t0x%x(%s), %s\n", + nameISize(sz), off, nameIReg(4,areg), nameIReg(sz,reg)); +} + +void VG_(emit_movv_reg_offregmem) ( Int sz, Int reg, Int off, Int areg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + + if (sz & DO_BOUNDSCHECK) { + boundscheck(); + sz &= ~DO_BOUNDSCHECK; + } + + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0x89 ); /* MOV Gv, Ev */ + VG_(emit_amode_offregmem_reg) ( off, areg, reg ); + if (dis) + VG_(printf)( "\n\t\tmov%c\t%s, 0x%x(%s)\n", + nameISize(sz), nameIReg(sz,reg), off, nameIReg(4,areg)); +} + +static void emit_movv_regmem_reg ( Int sz, Int reg1, Int reg2 ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + + if (sz & DO_BOUNDSCHECK) { + boundscheck(); + sz &= ~DO_BOUNDSCHECK; + } + + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0x8B ); /* MOV Ev, Gv */ + emit_amode_regmem_reg ( reg1, reg2 ); + if (dis) + VG_(printf)( "\n\t\tmov%c\t(%s), %s\n", + nameISize(sz), nameIReg(4,reg1), nameIReg(sz,reg2)); +} + +static void emit_movv_reg_regmem ( Int sz, Int reg1, Int reg2 ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + + if (sz & DO_BOUNDSCHECK) { + boundscheck(); + sz &= ~DO_BOUNDSCHECK; + } + + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0x89 ); /* MOV Gv, Ev */ + emit_amode_regmem_reg ( reg2, reg1 ); + if (dis) + VG_(printf)( "\n\t\tmov%c\t%s, (%s)\n", + nameISize(sz), nameIReg(sz,reg1), nameIReg(4,reg2)); +} + +void VG_(emit_movv_reg_reg) ( Int sz, Int reg1, Int reg2 ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0x89 ); /* MOV Gv, Ev */ + VG_(emit_amode_ereg_greg) ( reg2, reg1 ); + if (dis) + VG_(printf)( "\n\t\tmov%c\t%s, %s\n", + nameISize(sz), nameIReg(sz,reg1), nameIReg(sz,reg2)); +} + +void VG_(emit_nonshiftopv_lit_reg) ( Bool simd_flags, + Int sz, Opcode opc, + UInt lit, Int reg ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + + if (sz == 2) VG_(emitB) ( 0x66 ); + if (lit == VG_(extend_s_8to32)(lit & 0x000000FF)) { + /* short form OK */ + VG_(emitB) ( 0x83 ); /* Grp1 Ib,Ev */ + VG_(emit_amode_ereg_greg) ( reg, mkGrp1opcode(opc) ); + VG_(emitB) ( lit & 0x000000FF ); + } else { + VG_(emitB) ( 0x81 ); /* Grp1 Iv,Ev */ + VG_(emit_amode_ereg_greg) ( reg, mkGrp1opcode(opc) ); + if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit ); + } + if (dis) + VG_(printf)( "\n\t\t%s%c\t$0x%x, %s\n", + VG_(name_UOpcode)(False,opc), nameISize(sz), + lit, nameIReg(sz,reg)); +} + +void VG_(emit_nonshiftopv_lit_offregmem) ( Bool simd_flags, Int sz, + Opcode opc, UInt lit, + Int off, Int regmem ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + if (sz == 2) VG_(emitB) ( 0x66 ); + if (lit == VG_(extend_s_8to32)(lit & 0x000000FF)) { + /* short form OK */ + VG_(emitB) ( 0x83 ); /* Grp1 Ib,Ev */ + VG_(emit_amode_offregmem_reg) ( off, regmem, mkGrp1opcode(opc) ); + VG_(emitB) ( lit & 0x000000FF ); + } else { + VG_(emitB) ( 0x81 ); /* Grp1 Iv,Ev */ + VG_(emit_amode_offregmem_reg) ( off, regmem, mkGrp1opcode(opc) ); + if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit ); + } + if (dis) + VG_(printf)( "\n\t\t%s%c\t$0x%x, 0x%x(%s)\n", + VG_(name_UOpcode)(False,opc), nameISize(sz), + lit, off, nameIReg(sz,regmem)); +} + +void VG_(emit_shiftopv_lit_reg) ( Bool simd_flags, + Int sz, Opcode opc, + UInt lit, Int reg ) +{ + VG_(new_emit)(simd_flags, shiftop_use(opc), shiftop_set(opc)); + + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0xC1 ); /* Grp2 Ib,Ev */ + VG_(emit_amode_ereg_greg) ( reg, mkGrp2opcode(opc) ); + VG_(emitB) ( lit ); + if (dis) + VG_(printf)( "\n\t\t%s%c\t$%d, %s\n", + VG_(name_UOpcode)(False,opc), nameISize(sz), + lit, nameIReg(sz,reg)); +} + +static void emit_shiftopv_cl_stack0 ( Bool simd_flags, Int sz, Opcode opc ) +{ + VG_(new_emit)(simd_flags, shiftop_use(opc), shiftop_set(opc)); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0xD3 ); /* Grp2 CL,Ev */ + VG_(emitB) ( mkModRegRM ( 1, mkGrp2opcode(opc), 4 ) ); + VG_(emitB) ( 0x24 ); /* a SIB, I think `d8(%esp)' */ + VG_(emitB) ( 0x00 ); /* the d8 displacement */ + if (dis) + VG_(printf)("\n\t\t%s%c %%cl, 0(%%esp)\n", + VG_(name_UOpcode)(False,opc), nameISize(sz) ); +} + +static void emit_shiftopb_cl_stack0 ( Bool simd_flags, Opcode opc ) +{ + VG_(new_emit)(simd_flags, shiftop_use(opc), shiftop_set(opc)); + VG_(emitB) ( 0xD2 ); /* Grp2 CL,Eb */ + VG_(emitB) ( mkModRegRM ( 1, mkGrp2opcode(opc), 4 ) ); + VG_(emitB) ( 0x24 ); /* a SIB, I think `d8(%esp)' */ + VG_(emitB) ( 0x00 ); /* the d8 displacement */ + if (dis) + VG_(printf)("\n\t\t%s%c %%cl, 0(%%esp)\n", + VG_(name_UOpcode)(False,opc), nameISize(1) ); +} + +static void emit_nonshiftopv_offregmem_reg ( Bool simd_flags, Int sz, + Opcode opc, + Int off, Int areg, Int reg ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 3 + mkPrimaryOpcode(opc) ); /* op Ev, Gv */ + VG_(emit_amode_offregmem_reg) ( off, areg, reg ); + if (dis) + VG_(printf)( "\n\t\t%s%c\t0x%x(%s), %s\n", + VG_(name_UOpcode)(False,opc), nameISize(sz), + off, nameIReg(4,areg), nameIReg(sz,reg)); +} + +#if 0 +/* evidently unused */ +static void emit_nonshiftopv_reg_offregmem ( Bool simd_flags, Int sz, Opcode opc, + Int off, Int areg, Int reg ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 1 + mkPrimaryOpcode(opc) ); /* op Gv, Ev */ + VG_(emit_amode_offregmem_reg) ( off, areg, reg ); + if (dis) + VG_(printf)( "\n\t\t%s%c\t0x%s, %x(%s),\n", + VG_(name_UOpcode)(False,opc), nameISize(sz), + nameIReg(sz,reg), off, nameIReg(4,areg)); +} +#endif + +void VG_(emit_nonshiftopv_reg_reg) ( Bool simd_flags, Int sz, Opcode opc, + Int reg1, Int reg2 ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + if (sz == 2) VG_(emitB) ( 0x66 ); +# if 0 + /* Perfectly correct, but the GNU assembler uses the other form. + Therefore we too use the other form, to aid verification. */ + VG_(emitB) ( 3 + mkPrimaryOpcode(opc) ); /* op Ev, Gv */ + VG_(emit_amode_ereg_greg) ( reg1, reg2 ); +# else + VG_(emitB) ( 1 + mkPrimaryOpcode(opc) ); /* op Gv, Ev */ + emit_amode_greg_ereg ( reg1, reg2 ); +# endif + if (dis) + VG_(printf)( "\n\t\t%s%c\t%s, %s\n", + VG_(name_UOpcode)(False,opc), nameISize(sz), + nameIReg(sz,reg1), nameIReg(sz,reg2)); +} + +void VG_(emit_movv_lit_reg) ( Int sz, UInt lit, Int reg ) +{ +#if 0 + if (lit == 0 && eflags_state != UPD_Real) { + /* Only emit this for zeroing if it won't stomp flags */ + VG_(emit_nonshiftopv_reg_reg) ( False, sz, XOR, reg, reg ); + return; + } +#endif + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0xB8+reg ); /* MOV imm, Gv */ + if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit ); + if (dis) + VG_(printf)( "\n\t\tmov%c\t$0x%x, %s\n", + nameISize(sz), lit, nameIReg(sz,reg)); +} + +void VG_(emit_unaryopv_reg) ( Bool simd_flags, Int sz, Opcode opc, Int reg ) +{ + switch (opc) { + case NEG: + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsOSZACP); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0xF7 ); + VG_(emit_amode_ereg_greg) ( reg, mkGrp3opcode(NEG) ); + if (dis) + VG_(printf)( "\n\t\tneg%c\t%s\n", + nameISize(sz), nameIReg(sz,reg)); + break; + case NOT: + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsEmpty); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0xF7 ); + VG_(emit_amode_ereg_greg) ( reg, mkGrp3opcode(NOT) ); + if (dis) + VG_(printf)( "\n\t\tnot%c\t%s\n", + nameISize(sz), nameIReg(sz,reg)); + break; + case DEC: + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsOSZAP); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0x48 + reg ); + if (dis) + VG_(printf)( "\n\t\tdec%c\t%s\n", + nameISize(sz), nameIReg(sz,reg)); + break; + case INC: + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsOSZAP); + if (sz == 2) VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0x40 + reg ); + if (dis) + VG_(printf)( "\n\t\tinc%c\t%s\n", + nameISize(sz), nameIReg(sz,reg)); + break; + default: + VG_(core_panic)("VG_(emit_unaryopv_reg)"); + } +} + +void VG_(emit_pushv_reg) ( Int sz, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + if (sz == 2) { + VG_(emitB) ( 0x66 ); + } else { + vg_assert(sz == 4); + } + VG_(emitB) ( 0x50 + reg ); + if (dis) + VG_(printf)("\n\t\tpush%c %s\n", nameISize(sz), nameIReg(sz,reg)); +} + +void VG_(emit_popv_reg) ( Int sz, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + if (sz == 2) { + VG_(emitB) ( 0x66 ); + } else { + vg_assert(sz == 4); + } + VG_(emitB) ( 0x58 + reg ); + if (dis) + VG_(printf)("\n\t\tpop%c %s\n", nameISize(sz), nameIReg(sz,reg)); +} + +void VG_(emit_pushl_lit32) ( UInt int32 ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x68 ); + VG_(emitL) ( int32 ); + if (dis) + VG_(printf)("\n\t\tpushl $0x%x\n", int32 ); +} + +void VG_(emit_pushl_lit8) ( Int lit8 ) +{ + vg_assert(lit8 >= -128 && lit8 < 128); + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x6A ); + VG_(emitB) ( (UChar)((UInt)lit8) ); + if (dis) + VG_(printf)("\n\t\tpushl $%d\n", lit8 ); +} + +void VG_(emit_cmpl_zero_reg) ( Bool simd_flags, Int reg ) +{ + VG_(new_emit)(simd_flags, False, FlagsOSZACP); + VG_(emitB) ( 0x83 ); + VG_(emit_amode_ereg_greg) ( reg, 7 /* Grp 3 opcode for CMP */ ); + VG_(emitB) ( 0x00 ); + if (dis) + VG_(printf)("\n\t\tcmpl $0, %s\n", nameIReg(4,reg)); +} + +static void emit_swapl_reg_ECX ( Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x87 ); /* XCHG Gv,Ev */ + VG_(emit_amode_ereg_greg) ( reg, R_ECX ); + if (dis) + VG_(printf)("\n\t\txchgl %%ecx, %s\n", nameIReg(4,reg)); +} + +void VG_(emit_swapl_reg_EAX) ( Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x90 + reg ); /* XCHG Gv,eAX */ + if (dis) + VG_(printf)("\n\t\txchgl %%eax, %s\n", nameIReg(4,reg)); +} + +static void emit_swapl_reg_reg ( Int reg1, Int reg2 ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x87 ); /* XCHG Gv,Ev */ + VG_(emit_amode_ereg_greg) ( reg1, reg2 ); + if (dis) + VG_(printf)("\n\t\txchgl %s, %s\n", nameIReg(4,reg1), + nameIReg(4,reg2)); +} + +static void emit_bswapl_reg ( Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( 0xC8 + reg ); /* BSWAP r32 */ + if (dis) + VG_(printf)("\n\t\tbswapl %s\n", nameIReg(4,reg)); +} + +static void emit_movl_reg_reg ( Int regs, Int regd ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x89 ); /* MOV Gv,Ev */ + VG_(emit_amode_ereg_greg) ( regd, regs ); + if (dis) + VG_(printf)("\n\t\tmovl %s, %s\n", nameIReg(4,regs), nameIReg(4,regd)); +} + +void VG_(emit_movv_lit_offregmem) ( Int sz, UInt lit, Int off, Int memreg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + if (sz == 2) { + VG_(emitB) ( 0x66 ); + } else { + vg_assert(sz == 4); + } + VG_(emitB) ( 0xC7 ); /* Grp11 Ev */ + VG_(emit_amode_offregmem_reg) ( off, memreg, 0 /* Grp11 subopcode for MOV */ ); + if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit ); + if (dis) + VG_(printf)( "\n\t\tmov%c\t$0x%x, 0x%x(%s)\n", + nameISize(sz), lit, off, nameIReg(4,memreg) ); +} + + +/*----------------------------------------------------*/ +/*--- b-size (1 byte) instruction emitters ---*/ +/*----------------------------------------------------*/ + +/* There is some doubt as to whether C6 (Grp 11) is in the + 486 insn set. ToDo: investigate. */ +void VG_(emit_movb_lit_offregmem) ( UInt lit, Int off, Int memreg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0xC6 ); /* Grp11 Eb */ + VG_(emit_amode_offregmem_reg) ( off, memreg, 0 /* Grp11 subopcode for MOV */ ); + VG_(emitB) ( lit ); + if (dis) + VG_(printf)( "\n\t\tmovb\t$0x%x, 0x%x(%s)\n", + lit, off, nameIReg(4,memreg) ); +} + +static void emit_nonshiftopb_offregmem_reg ( Bool simd_flags, Opcode opc, + Int off, Int areg, Int reg ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + VG_(emitB) ( 2 + mkPrimaryOpcode(opc) ); /* op Eb, Gb */ + VG_(emit_amode_offregmem_reg) ( off, areg, reg ); + if (dis) + VG_(printf)( "\n\t\t%sb\t0x%x(%s), %s\n", + VG_(name_UOpcode)(False,opc), off, nameIReg(4,areg), + nameIReg(1,reg)); +} + +static void emit_nonshiftopb_lit_offregmem ( Bool simd_flags, Opcode opc, + UInt lit, Int off, Int areg ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + VG_(emitB) ( 0x80 ); + VG_(emit_amode_offregmem_reg) ( off, areg, mkGrp1opcode(opc) ); + VG_(emitB) ( lit ); + if (dis) + VG_(printf)( "\n\t\t%sb\t$0x%x, 0x%x(%s)\n", + VG_(name_UOpcode)(False,opc), lit, off, nameIReg(4,areg)); +} + +#if 0 +/* evidently unused */ +static void emit_nonshiftopb_reg_offregmem ( Bool simd_flags, Opcode opc, + Int off, Int areg, Int reg ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + VG_(emitB) ( 0 + mkPrimaryOpcode(opc) ); /* op Gb, Eb */ + VG_(emit_amode_offregmem_reg) ( off, areg, reg ); + if (dis) + VG_(printf)( "\n\t\t%sb\t0x%s , %x(%s)\n", + VG_(name_UOpcode)(False,opc), + nameIReg(1,reg), + off, nameIReg(4,areg)); +} +#endif + +void VG_(emit_movb_reg_offregmem) ( Int reg, Int off, Int areg ) +{ + /* Could do better when reg == %al. */ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x88 ); /* MOV G1, E1 */ + VG_(emit_amode_offregmem_reg) ( off, areg, reg ); + if (dis) + VG_(printf)( "\n\t\tmovb\t%s, 0x%x(%s)\n", + nameIReg(1,reg), off, nameIReg(4,areg)); +} + +static void emit_nonshiftopb_reg_reg ( Bool simd_flags, Opcode opc, + Int reg1, Int reg2 ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + VG_(emitB) ( 2 + mkPrimaryOpcode(opc) ); /* op Eb, Gb */ + VG_(emit_amode_ereg_greg) ( reg1, reg2 ); + if (dis) + VG_(printf)( "\n\t\t%sb\t%s, %s\n", + VG_(name_UOpcode)(False,opc), + nameIReg(1,reg1), nameIReg(1,reg2)); +} + +static void emit_movb_reg_regmem ( Bool bounds, Int reg1, Int reg2 ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + + if (bounds) + boundscheck(); + + VG_(emitB) ( 0x88 ); /* MOV G1, E1 */ + emit_amode_regmem_reg ( reg2, reg1 ); + if (dis) + VG_(printf)( "\n\t\tmovb\t%s, (%s)\n", nameIReg(1,reg1), + nameIReg(4,reg2)); +} + +static void emit_nonshiftopb_lit_reg ( Bool simd_flags, Opcode opc, + UInt lit, Int reg ) +{ + VG_(new_emit)(simd_flags, nonshiftop_use(opc), nonshiftop_set(opc)); + VG_(emitB) ( 0x80 ); /* Grp1 Ib,Eb */ + VG_(emit_amode_ereg_greg) ( reg, mkGrp1opcode(opc) ); + VG_(emitB) ( lit & 0x000000FF ); + if (dis) + VG_(printf)( "\n\t\t%sb\t$0x%x, %s\n", VG_(name_UOpcode)(False,opc), + lit, nameIReg(1,reg)); +} + +static void emit_shiftopb_lit_reg ( Bool simd_flags, Opcode opc, + UInt lit, Int reg ) +{ + VG_(new_emit)(simd_flags, shiftop_use(opc), shiftop_set(opc)); + VG_(emitB) ( 0xC0 ); /* Grp2 Ib,Eb */ + VG_(emit_amode_ereg_greg) ( reg, mkGrp2opcode(opc) ); + VG_(emitB) ( lit ); + if (dis) + VG_(printf)( "\n\t\t%sb\t$%d, %s\n", + VG_(name_UOpcode)(False,opc), + lit, nameIReg(1,reg)); +} + +void VG_(emit_unaryopb_reg) ( Bool simd_flags, Opcode opc, Int reg ) +{ + switch (opc) { + case INC: + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsOSZAP); + VG_(emitB) ( 0xFE ); + VG_(emit_amode_ereg_greg) ( reg, mkGrp4opcode(INC) ); + if (dis) + VG_(printf)( "\n\t\tincb\t%s\n", nameIReg(1,reg)); + break; + case DEC: + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsOSZAP); + VG_(emitB) ( 0xFE ); + VG_(emit_amode_ereg_greg) ( reg, mkGrp4opcode(DEC) ); + if (dis) + VG_(printf)( "\n\t\tdecb\t%s\n", nameIReg(1,reg)); + break; + case NOT: + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0xF6 ); + VG_(emit_amode_ereg_greg) ( reg, mkGrp3opcode(NOT) ); + if (dis) + VG_(printf)( "\n\t\tnotb\t%s\n", nameIReg(1,reg)); + break; + case NEG: + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsOSZACP); + VG_(emitB) ( 0xF6 ); + VG_(emit_amode_ereg_greg) ( reg, mkGrp3opcode(NEG) ); + if (dis) + VG_(printf)( "\n\t\tnegb\t%s\n", nameIReg(1,reg)); + break; + default: + VG_(core_panic)("VG_(emit_unaryopb_reg)"); + } +} + +void VG_(emit_testb_lit_reg) ( Bool simd_flags, UInt lit, Int reg ) +{ + VG_(new_emit)(simd_flags, FlagsEmpty, FlagsOSZACP); + VG_(emitB) ( 0xF6 ); /* Grp3 Eb */ + VG_(emit_amode_ereg_greg) ( reg, 0 /* Grp3 subopcode for TEST */ ); + VG_(emitB) ( lit ); + if (dis) + VG_(printf)("\n\t\ttestb $0x%x, %s\n", lit, nameIReg(1,reg)); +} + +/*----------------------------------------------------*/ +/*--- zero-extended load emitters ---*/ +/*----------------------------------------------------*/ + +void VG_(emit_movzbl_offregmem_reg) ( Bool bounds, Int off, Int regmem, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + if (bounds) + boundscheck(); + VG_(emitB) ( 0x0F ); VG_(emitB) ( 0xB6 ); /* MOVZBL */ + VG_(emit_amode_offregmem_reg) ( off, regmem, reg ); + if (dis) + VG_(printf)( "\n\t\tmovzbl\t0x%x(%s), %s\n", + off, nameIReg(4,regmem), nameIReg(4,reg)); +} + +static void emit_movzbl_regmem_reg ( Bool bounds, Int reg1, Int reg2 ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + + if (bounds) + boundscheck(); + + VG_(emitB) ( 0x0F ); VG_(emitB) ( 0xB6 ); /* MOVZBL */ + emit_amode_regmem_reg ( reg1, reg2 ); + if (dis) + VG_(printf)( "\n\t\tmovzbl\t(%s), %s\n", nameIReg(4,reg1), + nameIReg(4,reg2)); +} + +void VG_(emit_movzwl_offregmem_reg) ( Bool bounds, Int off, Int areg, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + + if (bounds) + boundscheck(); + + VG_(emitB) ( 0x0F ); VG_(emitB) ( 0xB7 ); /* MOVZWL */ + VG_(emit_amode_offregmem_reg) ( off, areg, reg ); + if (dis) + VG_(printf)( "\n\t\tmovzwl\t0x%x(%s), %s\n", + off, nameIReg(4,areg), nameIReg(4,reg)); +} + +void VG_( emit_movzwl_regmem_reg ) ( Bool bounds, Int reg1, Int reg2 ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + + if (bounds) + boundscheck(); + + VG_(emitB) ( 0x0F ); VG_(emitB) ( 0xB7 ); /* MOVZWL */ + emit_amode_regmem_reg ( reg1, reg2 ); + if (dis) + VG_(printf)( "\n\t\tmovzwl\t(%s), %s\n", nameIReg(4,reg1), + nameIReg(4,reg2)); +} + +/*----------------------------------------------------*/ +/*--- FPU instruction emitters ---*/ +/*----------------------------------------------------*/ + +static void emit_get_sse_state ( void ) +{ + Int off = 4 * VGOFF_(m_ssestate); + if (VG_(have_ssestate)) { + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( 0xAE ); VG_(emitB) ( 0x8D ); /* fxrstor d32(%ebp) */ + VG_(emitL) ( off ); + if (dis) + VG_(printf)("\n\t\tfxrstor\t%d(%%ebp)\n", off ); + } else { + /* Not a SSE-capable CPU. Just do frstor. */ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0xDD ); VG_(emitB) ( 0xA5 ); /* frstor d32(%ebp) */ + VG_(emitL) ( off ); + if (dis) + VG_(printf)("\n\t\tfrstor\t%d(%%ebp)\n", off ); + } +} + +static void emit_put_sse_state ( void ) +{ + Int off = 4 * VGOFF_(m_ssestate); + if (VG_(have_ssestate)) { + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( 0xAE ); VG_(emitB) ( 0x85 ); /* fxsave d32(%ebp) */ + VG_(emitL) ( off ); + if (dis) + VG_(printf)("\n\t\tfxsave\t%d(%%ebp)\n", off ); + } else { + /* Not a SSE-capable CPU. Just do fnsave. */ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0xDD ); VG_(emitB) ( 0xB5 ); /* fnsave d32(%ebp) */ + VG_(emitL) ( off ); + if (dis) + VG_(printf)("\n\t\tfnsave\t%d(%%ebp)\n", off ); + } +} + +static void emit_fpu_no_mem ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + if (dis) + VG_(printf)("\n\t\tfpu-0x%x:0x%x\n", + (UInt)first_byte, (UInt)second_byte ); +} + +static void emit_fpu_regmem ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte_masked, + Int reg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + + boundscheck(); /* assume all FPU ops are the client's */ + + VG_(emitB) ( first_byte ); + emit_amode_regmem_reg ( reg, second_byte_masked >> 3 ); + if (dis) + VG_(printf)("\n\t\tfpu-0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte_masked, + nameIReg(4,reg) ); +} + +static void emit_MMX2_regmem ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + + boundscheck(); + + VG_(emitB) ( 0x0F ); + VG_(emitB) ( first_byte ); + second_byte &= 0x38; /* mask out mod and rm fields */ + emit_amode_regmem_reg ( ireg, second_byte >> 3 ); + if (dis) + VG_(printf)("\n\t\tmmx2-0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + nameIReg(4,ireg) ); +} + +static void emit_MMX2a1 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + + boundscheck(); + + VG_(emitB) ( 0x0F ); + VG_(emitB) ( first_byte ); + second_byte &= 0x38; /* mask out mod and rm fields */ + emit_amode_regmem_reg ( ireg, second_byte >> 3 ); + VG_(emitB) ( third_byte ); + if (dis) + VG_(printf)("\n\t\tmmx2a1-0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, (UInt)third_byte, + nameIReg(4,ireg) ); +} + +static void emit_SSE2a ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + + boundscheck(); + + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + third_byte &= 0x38; /* mask out mod and rm fields */ + emit_amode_regmem_reg ( ireg, third_byte >> 3 ); + if (dis) + VG_(printf)("\n\t\tsse2a-0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, (UInt)third_byte, + nameIReg(4,ireg) ); +} + +static void emit_SSE2e1 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + third_byte &= 0x38; /* mask out mod and rm fields */ + third_byte |= 0xC0; /* set top two bits: mod = 11b */ + third_byte |= (ireg & 7); /* patch in our ireg */ + VG_(emitB) ( third_byte ); + VG_(emitB) ( fourth_byte ); + if (dis) + VG_(printf)( + "\n\t\tsse2e1--0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, + nameIReg(4,ireg) + ); +} + +static void emit_SSE2g1 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + third_byte &= 0xC7; /* mask out reg field */ + third_byte |= 0xC0; /* set top two bits: mod = 11b */ + third_byte |= ((ireg & 7) << 3); /* patch in our ireg */ + VG_(emitB) ( third_byte ); + VG_(emitB) ( fourth_byte ); + if (dis) + VG_(printf)( + "\n\t\tsse2g1_reg_wr--0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, + nameIReg(4,ireg) + ); +} + +static void emit_SSE2g ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + third_byte &= 0xC7; /* mask out reg field */ + third_byte |= 0xC0; /* set top two bits: mod = 11b */ + third_byte |= ((ireg & 7) << 3); /* patch in our ireg */ + VG_(emitB) ( third_byte ); + if (dis) + VG_(printf)( + "\n\t\tsse2g--0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, (UInt)third_byte, + nameIReg(4,ireg) + ); +} + +static void emit_SSE2a1 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + + boundscheck(); + + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + third_byte &= 0x38; /* mask out mod and rm fields */ + emit_amode_regmem_reg ( ireg, third_byte >> 3 ); + VG_(emitB) ( fourth_byte ); + if (dis) + VG_(printf)("\n\t\tsse2a1-0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, + nameIReg(4,ireg) ); +} + +static void emit_SSE3a ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + + boundscheck(); + + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + fourth_byte &= 0x38; /* mask out mod and rm fields */ + emit_amode_regmem_reg ( ireg, fourth_byte >> 3 ); + if (dis) + VG_(printf)("\n\t\tsse3a-0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, + nameIReg(4,ireg) ); +} + +static void emit_SSE3e ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + fourth_byte &= 0x38; /* mask out mod and rm fields */ + fourth_byte |= 0xC0; /* set top two bits: mod = 11b */ + fourth_byte |= (ireg & 7); /* patch in our ireg */ + VG_(emitB) ( fourth_byte ); + if (dis) + VG_(printf)( + "\n\t\tsse3e--0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, + nameIReg(4,ireg) + ); +} + +static void emit_SSE3e1 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + UChar fifth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + fourth_byte &= 0x38; /* mask out mod and rm fields */ + fourth_byte |= 0xC0; /* set top two bits: mod = 11b */ + fourth_byte |= (ireg & 7); /* patch in our ireg */ + VG_(emitB) ( fourth_byte ); + VG_(emitB) ( fifth_byte ); + if (dis) + VG_(printf)( + "\n\t\tsse3e1--0x%x:0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, (UInt)fifth_byte, + nameIReg(4,ireg) + ); +} + +static void emit_SSE3g1 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + UChar fifth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + fourth_byte &= 0xC7; /* mask out reg field */ + fourth_byte |= 0xC0; /* set top two bits: mod = 11b */ + fourth_byte |= ((ireg & 7) << 3); /* patch in our ireg */ + VG_(emitB) ( fourth_byte ); + VG_(emitB) ( fifth_byte ); + if (dis) + VG_(printf)( + "\n\t\tsse3g1_reg_wr--0x%x:0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, (UInt)fifth_byte, + nameIReg(4,ireg) + ); +} + +static void emit_SSE3g ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + fourth_byte &= 0xC7; /* mask out reg field */ + fourth_byte |= 0xC0; /* set top two bits: mod = 11b */ + fourth_byte |= ((ireg & 7) << 3); /* patch in our ireg */ + VG_(emitB) ( fourth_byte ); + if (dis) + VG_(printf)( + "\n\t\tsse3g--0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, + nameIReg(4,ireg) + ); +} + +static void emit_SSE3a1 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + UChar fifth_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + + boundscheck(); + + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + fourth_byte &= 0x38; /* mask out mod and rm fields */ + emit_amode_regmem_reg ( ireg, fourth_byte >> 3 ); + VG_(emitB) ( fifth_byte ); + if (dis) + VG_(printf)("\n\t\tsse3a1-0x%x:0x%x:0x%x:0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, + (UInt)fifth_byte, + nameIReg(4,ireg) ); +} + +static void emit_SSE4 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + VG_(emitB) ( fourth_byte ); + if (dis) + VG_(printf)("\n\t\tsse4-0x%x:0x%x:0x%x:0x%x\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte ); +} + +static void emit_SSE5 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + UChar fourth_byte, + UChar fifth_byte ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + VG_(emitB) ( fourth_byte ); + VG_(emitB) ( fifth_byte ); + if (dis) + VG_(printf)("\n\t\tsse5-0x%x:0x%x:0x%x:0x%x:0x%x\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, (UInt)fourth_byte, + (UInt)fifth_byte ); +} + +static void emit_SSE3 ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + if (dis) + VG_(printf)("\n\t\tsse3-0x%x:0x%x:0x%x\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte ); +} + +static void emit_SSE3ag_MemRd_RegWr ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + Int addr_reg, + Int dest_reg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + /* 4th byte can be completely synthesised from addr_reg and + dest_reg. */ + emit_amode_regmem_reg ( addr_reg, dest_reg ); + if (dis) + VG_(printf)("\n\t\tsse3ag_mem_rd_reg_wr-0x%x:0x%x:0x%x(addr=%s, dest=%s)\n", + (UInt)first_byte, (UInt)second_byte, + (UInt)third_byte, nameIReg(4, addr_reg), + nameIReg(4, dest_reg)); +} + +static void emit_MMX2_reg_to_mmxreg ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( first_byte ); + second_byte &= 0x38; /* mask out mod and rm fields */ + second_byte |= 0xC0; /* set top two bits: mod = 11b */ + second_byte |= (ireg & 7); /* patch in our ireg */ + VG_(emitB) ( second_byte ); + if (dis) + VG_(printf)("\n\t\tmmx2:reg-to-mmxreg--0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + nameIReg(4,ireg) ); +} + +static void emit_MMX2_mmxreg_to_reg ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + Int ireg ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( first_byte ); + second_byte &= 0x38; /* mask out mod and rm fields */ + second_byte |= 0xC0; /* set top two bits: mod = 11b */ + second_byte |= (ireg & 7); /* patch in our ireg */ + VG_(emitB) ( second_byte ); + if (dis) + VG_(printf)("\n\t\tmmx2:mmxreg-to-reg--0x%x:0x%x-(%s)\n", + (UInt)first_byte, (UInt)second_byte, + nameIReg(4,ireg) ); +} + +static void emit_MMX3_no_mem ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte, + UChar third_byte ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + VG_(emitB) ( third_byte ); + if (dis) + VG_(printf)("\n\t\tmmx3-0x%x:0x%x:0x%x\n", + (UInt)first_byte, (UInt)second_byte, (UInt)third_byte ); +} + +static void emit_MMX2_no_mem ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte, + UChar second_byte ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( first_byte ); + VG_(emitB) ( second_byte ); + if (dis) + VG_(printf)("\n\t\tmmx2-0x%x:0x%x\n", + (UInt)first_byte, (UInt)second_byte ); +} + +static void emit_MMX1_no_mem ( FlagSet uses_sflags, + FlagSet sets_sflags, + UChar first_byte ) +{ + VG_(new_emit)(True, uses_sflags, sets_sflags); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( first_byte ); + if (dis) + VG_(printf)("\n\t\tmmx1-0x%x\n", + (UInt)first_byte ); +} + + +/*----------------------------------------------------*/ +/*--- misc instruction emitters ---*/ +/*----------------------------------------------------*/ + +void VG_(emit_call_reg) ( Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsOSZACP); /* XXX */ + VG_(emitB) ( 0xFF ); /* Grp5 */ + VG_(emit_amode_ereg_greg) ( reg, mkGrp5opcode(CALLM) ); + if (dis) + VG_(printf)( "\n\t\tcall\t*%s\n", nameIReg(4,reg) ); +} + +static +void emit_call_star_EBP_off ( Bool simd_flags, Int byte_off, + FlagSet use_flag, FlagSet set_flag ) +{ + /* Used for helpers which expect to see Simd flags in Real flags */ + VG_(new_emit)(simd_flags, use_flag, set_flag); + + if (byte_off < -128 || byte_off > 127) { + VG_(emitB) ( 0xFF ); + VG_(emitB) ( 0x95 ); + VG_(emitL) ( byte_off ); + } else { + VG_(emitB) ( 0xFF ); + VG_(emitB) ( 0x55 ); + VG_(emitB) ( byte_off ); + } + if (dis) + VG_(printf)( "\n\t\tcall * %d(%%ebp)\n", byte_off ); +} + +#if 0 +/* evidently unused */ +static void emit_addlit8_offregmem ( Int lit8, Int regmem, Int off ) +{ + vg_assert(lit8 >= -128 && lit8 < 128); + VG_(new_emit)(True, FlagsEmpty, FlagsOSZACP); + VG_(emitB) ( 0x83 ); /* Grp1 Ib,Ev */ + VG_(emit_amode_offregmem_reg) ( off, regmem, + 0 /* Grp1 subopcode for ADD */ ); + VG_(emitB) ( lit8 & 0xFF ); + if (dis) + VG_(printf)( "\n\t\taddl $%d, %d(%s)\n", lit8, off, + nameIReg(4,regmem)); +} +#endif + +void VG_(emit_add_lit_to_esp) ( Int lit ) +{ + if (lit < -128 || lit > 127) VG_(core_panic)("VG_(emit_add_lit_to_esp)"); + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x8D ); + VG_(emitB) ( 0x64 ); + VG_(emitB) ( 0x24 ); + VG_(emitB) ( lit & 0xFF ); + if (dis) + VG_(printf)( "\n\t\tlea\t%d(%%esp), %%esp\n", lit ); +} + + +static void emit_movb_AL_zeroESPmem ( void ) +{ + /* movb %al, 0(%esp) */ + /* 88442400 movb %al, 0(%esp) */ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x88 ); + VG_(emitB) ( 0x44 ); + VG_(emitB) ( 0x24 ); + VG_(emitB) ( 0x00 ); + if (dis) + VG_(printf)( "\n\t\tmovb %%al, 0(%%esp)\n" ); +} + +static void emit_movb_zeroESPmem_AL ( void ) +{ + /* movb 0(%esp), %al */ + /* 8A442400 movb 0(%esp), %al */ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x8A ); + VG_(emitB) ( 0x44 ); + VG_(emitB) ( 0x24 ); + VG_(emitB) ( 0x00 ); + if (dis) + VG_(printf)( "\n\t\tmovb 0(%%esp), %%al\n" ); +} + +/* Jump target states */ +#define TGT_UNDEF (1 << 16) +#define TGT_FORWARD (2 << 16) +#define TGT_BACKWARD (3 << 16) + +static inline Int tgt_state(Int tgt) +{ + return tgt & 0xffff0000; +} + +static inline Int tgt_addr(Int tgt) +{ + return tgt & 0x0000ffff; +} + +static inline Int mk_tgt(Int state, Int addr) +{ + vg_assert(state == TGT_UNDEF || + state == TGT_FORWARD || + state == TGT_BACKWARD); + vg_assert((addr & 0xffff0000) == 0); + + return state | addr; +} + +void VG_(init_target) ( Int *tgt ) +{ + *tgt = TGT_UNDEF; +} + +void VG_(target_back) ( Int *tgt ) +{ + vg_assert(tgt_state(*tgt) == TGT_UNDEF); + + *tgt = mk_tgt(TGT_BACKWARD, emitted_code_used); +} + +void VG_(target_forward) ( Int *tgt ) +{ + Int delta; + + vg_assert(tgt_state(*tgt) == TGT_FORWARD || + tgt_state(*tgt) == TGT_UNDEF); + + if (tgt_state(*tgt) == TGT_UNDEF) + return; /* target not used */ + + delta = emitted_code_used - (tgt_addr(*tgt) + 1); + vg_assert(delta >= -128 && delta <= 127); + vg_assert(tgt_addr(*tgt) >= 0); + vg_assert(tgt_addr(*tgt) < emitted_code_used); + emitted_code[tgt_addr(*tgt)] = delta; + if (dis) + VG_(printf)("(target to jump site %d; delta: %d)\n", + tgt_addr(*tgt), delta); +} + +void VG_(emit_target_delta) ( Int *tgt ) +{ + vg_assert(tgt_state(*tgt) == TGT_UNDEF || + tgt_state(*tgt) == TGT_BACKWARD); + + if (tgt_state(*tgt) == TGT_UNDEF) { + /* forward jump */ + *tgt = mk_tgt(TGT_FORWARD, emitted_code_used); + VG_(emitB) (0x00); + } else { + /* backward jump */ + Int delta = emitted_code_used - (tgt_addr(*tgt) + 1); + vg_assert(delta >= -128 && delta <= 127); + VG_(emitB) (delta); + } +} + + +/* Emit a jump short with an 8-bit signed offset. Note that the + offset is that which should be added to %eip once %eip has been + advanced over this insn. */ +void VG_(emit_jcondshort_delta) ( Bool simd_flags, Condcode cond, Int delta, JumpPred pred ) +{ + vg_assert(delta >= -128 && delta <= 127); + VG_(new_emit)(simd_flags, FlagsOSZCP, FlagsEmpty); + + if (VG_(clo_branchpred) && + pred != JP_NONE && + pred != static_pred(cond, delta > 0)) + VG_(emitB)(pred == JP_TAKEN ? 0x3e : 0x2e); + + VG_(emitB) ( 0x70 + (UInt)cond ); + VG_(emitB) ( (UChar)delta ); + if (dis) + VG_(printf)( "\n\t\tj%s-8%s\t%%eip+%d\n", + VG_(name_UCondcode)(cond), predstr(pred), delta ); +} + +/* Same as above, but defers emitting the delta */ +void VG_(emit_jcondshort_target) ( Bool simd, Condcode cond, Int *tgt, JumpPred pred ) +{ + VG_(new_emit)(simd, FlagsOSZCP, FlagsEmpty); + + if (VG_(clo_branchpred) && + pred != JP_NONE && + pred != static_pred(cond, tgt_state(*tgt) != TGT_BACKWARD)) + VG_(emitB)(pred == JP_TAKEN ? 0x3e : 0x2e); + + VG_(emitB) ( 0x70 + (UInt)cond ); + VG_(emit_target_delta) (tgt); + if (dis) + VG_(printf)( "\n\t\tj%s-8%s\t%%eip+(%d)\n", + VG_(name_UCondcode)(cond), predstr(pred), tgt_addr(*tgt) ); +} + + + +static void emit_setb_reg ( Bool simd, Int reg, Condcode cond ) +{ + VG_(new_emit)(simd, FlagsOSZCP, FlagsEmpty); + VG_(emitB) ( 0x0F ); VG_(emitB) ( 0x90 + (UChar)cond ); + VG_(emit_amode_ereg_greg) ( reg, 0 ); + if (dis) + VG_(printf)("\n\t\tset%s %s\n", + VG_(name_UCondcode)(cond), nameIReg(1,reg)); +} + +static void emit_ret ( void ) +{ + maybe_emit_put_eflags(); /* make sure flags are stored */ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0xC3 ); /* RET */ + if (dis) + VG_(printf)("\n\t\tret\n"); +} + +/* Predicate used in sanity checks elsewhere - returns true if any + jump-site is an actual chained jump */ +__attribute__((unused)) +static Bool is_chained_jumpsite(Addr a) +{ + UChar *cp = (UChar *)a; + + return (*cp == 0xE9); /* 0xE9 -- jmp */ +} + +static +Bool is_fresh_jumpsite(UChar *cp) +{ + return + cp[0] == 0x0F && /* UD2 */ + cp[1] == 0x0B && + cp[2] == 0x0F && /* UD2 */ + cp[3] == 0x0B && + cp[4] == 0x90; /* NOP */ +} + +/* Predicate used in sanity checks elsewhere - returns true if all + jump-sites are calls to VG_(patch_me) */ +static Bool is_unchained_jumpsite(Addr a) +{ + UChar *cp = (UChar *)a; + Int delta = ((Addr)&VG_(patch_me)) - (a + VG_PATCHME_CALLSZ); + Int idelta; + + if (*cp++ != 0xE8) /* 0xE8 == call */ + return False; + + idelta = (*cp++) << 0; + idelta |= (*cp++) << 8; + idelta |= (*cp++) << 16; + idelta |= (*cp++) << 24; + + return idelta == delta; +} + +/* Return target address for a direct jmp */ +Addr VG_(get_jmp_dest)(Addr a) +{ + Int delta; + UChar *cp = (UChar *)a; + + if (*cp++ != 0xE9) /* 0xE9 == jmp */ + return 0; + + delta = (*cp++) << 0; + delta |= (*cp++) << 8; + delta |= (*cp++) << 16; + delta |= (*cp++) << 24; + + return a + VG_PATCHME_JMPSZ + delta; +} + +/* unchain a BB by generating a call to VG_(patch_me) */ +void VG_(unchain_jumpsite)(Addr a) +{ + Int delta = ((Addr)&VG_(patch_me)) - (a + VG_PATCHME_CALLSZ); + UChar *cp = (UChar *)a; + + if (is_unchained_jumpsite(a)) + return; /* don't write unnecessarily */ + + if (!is_fresh_jumpsite(cp)) + VG_(bb_dechain_count)++; /* update stats */ + + *cp++ = 0xE8; /* call */ + *cp++ = (delta >> 0) & 0xff; + *cp++ = (delta >> 8) & 0xff; + *cp++ = (delta >> 16) & 0xff; + *cp++ = (delta >> 24) & 0xff; +} + +/* This doesn't actually generate a call to VG_(patch_me), but + reserves enough space in the instruction stream for it to happen + and records the offset into the jump table. This is because call + is a relative jump, and so will be affected when this code gets + moved about. The translation table will "unchain" this basic block + on insertion (with VG_(unchain_BB)()), and thereby generate a + proper call instruction. */ +static void emit_call_patchme( void ) +{ + vg_assert(VG_PATCHME_CALLSZ == 5); + + maybe_emit_put_eflags(); /* save flags before end of BB */ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + + if (jumpidx >= VG_MAX_JUMPS) { + /* If there too many jumps in this basic block, fall back to + dispatch loop. We still need to keep it the same size as the + call sequence. */ + VG_(emitB) ( 0xC3 ); /* ret */ + VG_(emitB) ( 0x8d ); /* 4 byte nop (lea 0x0(%esi,1),%esi) */ + VG_(emitB) ( 0x74 ); + VG_(emitB) ( 0x26 ); + VG_(emitB) ( 0x00 ); + + if (dis) + VG_(printf)("\n\t\tret; nop4\n"); + + if (0 && VG_(clo_verbosity)) + VG_(message)(Vg_DebugMsg, "too many chained jumps in basic-block"); + } else { + jumps[jumpidx++] = emitted_code_used; + + VG_(emitB) ( 0x0F ); /* UD2 - undefined instruction */ + VG_(emitB) ( 0x0B ); + VG_(emitB) ( 0x0F ); /* UD2 - undefined instruction */ + VG_(emitB) ( 0x0B ); + VG_(emitB) ( 0x90 ); /* NOP */ + + if (dis) + VG_(printf)("\n\t\tud2; ud2; nop /* call VG_(patch_me) */\n"); + } +} + +void VG_(emit_pushal) ( void ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x60 ); /* PUSHAL */ + if (dis) + VG_(printf)("\n\t\tpushal\n"); +} + +void VG_(emit_popal) ( void ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x61 ); /* POPAL */ + if (dis) + VG_(printf)("\n\t\tpopal\n"); +} + +static void emit_lea_litreg_reg ( UInt lit, Int regmem, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x8D ); /* LEA M,Gv */ + VG_(emit_amode_offregmem_reg) ( (Int)lit, regmem, reg ); + if (dis) + VG_(printf)("\n\t\tleal 0x%x(%s), %s\n", + lit, nameIReg(4,regmem), nameIReg(4,reg) ); +} + +static void emit_lea_sib_reg ( UInt lit, Int scale, + Int regbase, Int regindex, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x8D ); /* LEA M,Gv */ + emit_amode_sib_reg ( (Int)lit, scale, regbase, regindex, reg ); + if (dis) + VG_(printf)("\n\t\tleal 0x%x(%s,%s,%d), %s\n", + lit, nameIReg(4,regbase), + nameIReg(4,regindex), scale, + nameIReg(4,reg) ); +} + +void VG_(emit_AMD_prefetch_reg) ( Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsEmpty); + VG_(emitB) ( 0x0F ); + VG_(emitB) ( 0x0D ); + emit_amode_regmem_reg ( reg, 1 /* 0 is prefetch; 1 is prefetchw */ ); + if (dis) + VG_(printf)("\n\t\tamd-prefetch (%s)\n", nameIReg(4,reg) ); +} + +/*----------------------------------------------------*/ +/*--- Instruction synthesisers ---*/ +/*----------------------------------------------------*/ + +static Condcode invertCondition ( Condcode cond ) +{ + return (Condcode)(1 ^ (UInt)cond); +} + + +/* Synthesise a call to *baseBlock[offset], ie, + call * (4 x offset)(%ebp). +*/ +void VG_(synth_call) ( Bool ensure_shortform, Int word_offset, + Bool simd_flags, FlagSet use_flags, FlagSet set_flags ) +{ + vg_assert(word_offset >= 0); + vg_assert(word_offset < VG_BASEBLOCK_WORDS); + if (ensure_shortform) { + vg_assert(word_offset < 32); + } + emit_call_star_EBP_off ( simd_flags, 4 * word_offset, use_flags, set_flags ); +} + +static void maybe_emit_movl_reg_reg ( UInt src, UInt dst ) +{ + if (src != dst) { + VG_(emit_movv_reg_reg) ( 4, src, dst ); + ccall_arg_setup_instrs++; + } +} + +/* 'maybe' because it is sometimes skipped eg. for "movl %eax,%eax" */ +static void maybe_emit_movl_litOrReg_reg ( UInt litOrReg, Tag tag, UInt reg ) +{ + if (RealReg == tag) { + maybe_emit_movl_reg_reg ( litOrReg, reg ); + } else if (Literal == tag) { + VG_(emit_movv_lit_reg) ( 4, litOrReg, reg ); + ccall_arg_setup_instrs++; + } + else + VG_(core_panic)("emit_movl_litOrReg_reg: unexpected tag"); +} + +static +void emit_swapl_arg_regs ( UInt reg1, UInt reg2 ) +{ + if (R_EAX == reg1) { + VG_(emit_swapl_reg_EAX) ( reg2 ); + } else if (R_EAX == reg2) { + VG_(emit_swapl_reg_EAX) ( reg1 ); + } else { + emit_swapl_reg_reg ( reg1, reg2 ); + } + ccall_arg_setup_instrs++; +} + +static +void emit_two_regs_args_setup ( UInt src1, UInt src2, UInt dst1, UInt dst2) +{ + if (dst1 != src2) { + maybe_emit_movl_reg_reg ( src1, dst1 ); + maybe_emit_movl_reg_reg ( src2, dst2 ); + + } else if (dst2 != src1) { + maybe_emit_movl_reg_reg ( src2, dst2 ); + maybe_emit_movl_reg_reg ( src1, dst1 ); + + } else { + /* swap to break cycle */ + emit_swapl_arg_regs ( dst1, dst2 ); + } +} + +static +void emit_three_regs_args_setup ( UInt src1, UInt src2, UInt src3, + UInt dst1, UInt dst2, UInt dst3) +{ + if (dst1 != src2 && dst1 != src3) { + maybe_emit_movl_reg_reg ( src1, dst1 ); + emit_two_regs_args_setup ( src2, src3, dst2, dst3 ); + + } else if (dst2 != src1 && dst2 != src3) { + maybe_emit_movl_reg_reg ( src2, dst2 ); + emit_two_regs_args_setup ( src1, src3, dst1, dst3 ); + + } else if (dst3 != src1 && dst3 != src2) { + maybe_emit_movl_reg_reg ( src3, dst3 ); + emit_two_regs_args_setup ( src1, src2, dst1, dst2 ); + + } else { + /* break cycle */ + if (dst1 == src2 && dst2 == src3 && dst3 == src1) { + emit_swapl_arg_regs ( dst1, dst2 ); + emit_swapl_arg_regs ( dst1, dst3 ); + + } else if (dst1 == src3 && dst2 == src1 && dst3 == src2) { + emit_swapl_arg_regs ( dst1, dst3 ); + emit_swapl_arg_regs ( dst1, dst2 ); + + } else { + VG_(core_panic)("impossible 3-cycle"); + } + } +} + +static +void emit_two_regs_or_lits_args_setup ( UInt argv[], Tag tagv[], + UInt src1, UInt src2, + UInt dst1, UInt dst2) +{ + /* If either are lits, order doesn't matter */ + if (Literal == tagv[src1] || Literal == tagv[src2]) { + maybe_emit_movl_litOrReg_reg ( argv[src1], tagv[src1], dst1 ); + maybe_emit_movl_litOrReg_reg ( argv[src2], tagv[src2], dst2 ); + + } else { + emit_two_regs_args_setup ( argv[src1], argv[src2], dst1, dst2 ); + } +} + +static +void emit_three_regs_or_lits_args_setup ( UInt argv[], Tag tagv[], + UInt src1, UInt src2, UInt src3, + UInt dst1, UInt dst2, UInt dst3) +{ + // SSS: fix this eventually -- make STOREV use two RealRegs? + /* Not supporting literals for 3-arg C functions -- they're only used + by STOREV which has 2 args */ + vg_assert(RealReg == tagv[src1] && + RealReg == tagv[src2] && + RealReg == tagv[src3]); + emit_three_regs_args_setup ( argv[src1], argv[src2], argv[src3], + dst1, dst2, dst3 ); +} + +/* Synthesise a call to a C function `fn' (which must be registered in + baseBlock) doing all the reg saving and arg handling work. + + WARNING: a UInstr should *not* be translated with synth_ccall followed + by some other x86 assembly code; vg_liveness_analysis() doesn't expect + such behaviour and everything will fall over. + */ +void VG_(synth_ccall) ( Addr fn, Int argc, Int regparms_n, UInt argv[], + Tag tagv[], Int ret_reg, + RRegSet regs_live_before, RRegSet regs_live_after ) +{ + Int i; + Int stack_used = 0; + Bool preserve_eax, preserve_ecx, preserve_edx; + + vg_assert(0 <= regparms_n && regparms_n <= 3); + + ccalls++; + + /* If %e[acd]x is live before and after the C call, save/restore it. + Unless the return values clobbers the reg; in this case we must not + save/restore the reg, because the restore would clobber the return + value. (Before and after the UInstr really constitute separate live + ranges, but you miss this if you don't consider what happens during + the UInstr.) */ +# define PRESERVE_REG(realReg) \ + (IS_RREG_LIVE(VG_(realreg_to_rank)(realReg), regs_live_before) && \ + IS_RREG_LIVE(VG_(realreg_to_rank)(realReg), regs_live_after) && \ + ret_reg != realReg) + + preserve_eax = PRESERVE_REG(R_EAX); + preserve_ecx = PRESERVE_REG(R_ECX); + preserve_edx = PRESERVE_REG(R_EDX); + +# undef PRESERVE_REG + + /* Save caller-save regs as required */ + if (preserve_eax) { VG_(emit_pushv_reg) ( 4, R_EAX ); ccall_reg_saves++; } + if (preserve_ecx) { VG_(emit_pushv_reg) ( 4, R_ECX ); ccall_reg_saves++; } + if (preserve_edx) { VG_(emit_pushv_reg) ( 4, R_EDX ); ccall_reg_saves++; } + + /* Args are passed in two groups: (a) via stack (b) via regs. regparms_n + is the number of args passed in regs (maximum 3 for GCC on x86). */ + + ccall_args += argc; + + /* First push stack args (RealRegs or Literals) in reverse order. */ + for (i = argc-1; i >= regparms_n; i--) { + switch (tagv[i]) { + case RealReg: + VG_(emit_pushv_reg) ( 4, argv[i] ); + break; + case Literal: + /* Use short form of pushl if possible. */ + if (argv[i] == VG_(extend_s_8to32) ( argv[i] )) + VG_(emit_pushl_lit8) ( VG_(extend_s_8to32)(argv[i]) ); + else + VG_(emit_pushl_lit32)( argv[i] ); + break; + default: + VG_(printf)("tag=%d\n", tagv[i]); + VG_(core_panic)("VG_(synth_ccall): bad tag"); + } + stack_used += 4; + ccall_arg_setup_instrs++; + } + + /* Then setup args in registers (arg[123] --> %e[adc]x; note order!). + If moving values between registers, be careful not to clobber any on + the way. Happily we can use xchgl to swap registers. + */ + switch (regparms_n) { + + /* Trickiest. Args passed in %eax, %edx, and %ecx. */ + case 3: + emit_three_regs_or_lits_args_setup ( argv, tagv, 0, 1, 2, + R_EAX, R_EDX, R_ECX ); + break; + + /* Less-tricky. Args passed in %eax and %edx. */ + case 2: + emit_two_regs_or_lits_args_setup ( argv, tagv, 0, 1, R_EAX, R_EDX ); + break; + + /* Easy. Just move arg1 into %eax (if not already in there). */ + case 1: + maybe_emit_movl_litOrReg_reg ( argv[0], tagv[0], R_EAX ); + break; + + case 0: + break; + + default: + VG_(core_panic)("VG_(synth_call): regparms_n value not in range 0..3"); + } + + /* Call the function - may trash all flags */ + VG_(synth_call) ( False, VG_(helper_offset) ( fn ), False, + FlagsEmpty, FlagsOSZACP ); + + /* Clear any args from stack */ + if (0 != stack_used) { + VG_(emit_add_lit_to_esp) ( stack_used ); + ccall_stack_clears++; + } + + /* Move return value into ret_reg if necessary and not already there */ + if (INVALID_REALREG != ret_reg) { + ccall_retvals++; + if (R_EAX != ret_reg) { + VG_(emit_movv_reg_reg) ( 4, R_EAX, ret_reg ); + ccall_retval_movs++; + } + } + + /* Restore live caller-save regs as required */ + if (preserve_edx) VG_(emit_popv_reg) ( 4, R_EDX ); + if (preserve_ecx) VG_(emit_popv_reg) ( 4, R_ECX ); + if (preserve_eax) VG_(emit_popv_reg) ( 4, R_EAX ); +} + +static void load_ebp_from_JmpKind ( JmpKind jmpkind ) +{ + switch (jmpkind) { + case JmpBoring: + break; + case JmpRet: + break; + case JmpCall: + break; + case JmpSyscall: + VG_(emit_movv_lit_reg) ( 4, VG_TRC_EBP_JMP_SYSCALL, R_EBP ); + break; + case JmpClientReq: + VG_(emit_movv_lit_reg) ( 4, VG_TRC_EBP_JMP_CLIENTREQ, R_EBP ); + break; + case JmpYield: + VG_(emit_movv_lit_reg) ( 4, VG_TRC_EBP_JMP_YIELD, R_EBP ); + break; + default: + VG_(core_panic)("load_ebp_from_JmpKind"); + } +} + +/* Jump to the next translation, by loading its original addr into + %eax and returning to the scheduler. Signal special requirements + by loading a special value into %ebp first. +*/ +static void synth_jmp_reg ( Int reg, JmpKind jmpkind ) +{ + maybe_emit_put_eflags(); /* save flags here */ + load_ebp_from_JmpKind ( jmpkind ); + if (reg != R_EAX) + VG_(emit_movv_reg_reg) ( 4, reg, R_EAX ); + emit_ret(); +} + +static void synth_mov_reg_offregmem ( Int size, Int reg, Int off, Int areg ); + +/* Same deal as synth_jmp_reg. */ +static void synth_jmp_lit ( Addr addr, JmpKind jmpkind ) +{ + maybe_emit_put_eflags(); /* save flags here */ + + VG_(emit_movv_lit_reg) ( 4, addr, R_EAX ); + + if (VG_(clo_chain_bb) && (jmpkind == JmpBoring || jmpkind == JmpCall)) { + synth_mov_reg_offregmem(4, R_EAX, 4*VGOFF_(m_eip), R_EBP); /* update EIP */ + emit_call_patchme(); + } else { + load_ebp_from_JmpKind ( jmpkind ); + emit_ret(); + } +} + + +static void synth_mov_offregmem_reg ( Int size, Int off, Int areg, Int reg ); +static void synth_nonshiftop_lit_reg ( Bool simd_flags, + Opcode opcode, Int size, + UInt lit, Int reg ); + +static void synth_jcond_lit ( Condcode cond, + Addr addr, + Bool eax_trashable ) +{ + UInt mask; + Bool simd; + Int tgt, tgt2, tgt_jump; + + VG_(init_target)(&tgt); + VG_(init_target)(&tgt2); + VG_(init_target)(&tgt_jump); + + /* Ensure simulated %EFLAGS are up-to-date, by copying back %eflags + if need be */ + maybe_emit_put_eflags(); + vg_assert(eflags_state == UPD_Both || eflags_state == UPD_Simd); + + if (eflags_state == UPD_Both) { + /* The flags are already set up, so we just use them as is. */ + simd = True; + cond = invertCondition(cond); + } else { + Bool parity = False; /* test Z or P */ + + /* The simd state contains the most recent version, so we emit a + sequence to calculate the relevant condition directly out of + the simd flags. This is much cheaper (on P3/P4/Athlon) than + copying them back to the real flags via popf. Notice that + some of these sequences trash %eax, but that should be free + now since this is the end of a bb and therefore all regs are + dead. */ + simd = False; + + switch (cond) { + + case CondLE: /* Z || S != O -> S || !P */ + case CondNLE: /* !Z && S == O -> !S && P */ + vg_assert(eax_trashable); + + VG_(emit_movv_offregmem_reg) + ( 4, VGOFF_(m_eflags) * 4, R_EBP, R_EAX ); + /* eax == %EFLAGS */ + + VG_(emit_nonshiftopv_lit_reg) + ( False, 4, AND, EFlagO|EFlagS|EFlagZ, R_EAX ); + /* eax just contains OF, SF and ZF */ + + VG_(emit_shiftopv_lit_reg)( False, 4, ROR, 7, R_EAX ); + /* eax has OF and SF in lower 8 bits, and ZF in MSB */ + + /* actually set the real cpu flags, since ROR changes + neither P nor Z */ + VG_(emit_nonshiftopv_reg_reg)( False, 4, OR, R_EAX, R_EAX ); + + if (cond == CondLE) { + /* test Z */ + VG_(emit_jcondshort_target)(False, CondS, &tgt_jump, JP_NONE); + /* test OF != SF */ + cond = CondP; + } else { + /* test Z */ + VG_(emit_jcondshort_target)(False, CondS, &tgt2, JP_NONE); + /* test OF == SF */ + cond = CondNP; + } + break; + + case CondL: + case CondNL: + vg_assert(eax_trashable); + + VG_(emit_movv_offregmem_reg) + ( 4, VGOFF_(m_eflags) * 4, R_EBP, R_EAX ); + /* eax == %EFLAGS */ + + VG_(emit_shiftopv_lit_reg)( False, 4, SHR, 7, R_EAX ); + /* eax has OF and SF in lower byte */ + + VG_(emit_testb_lit_reg) ( False, 0x11, R_EAX); + /* PF = OF == SF */ + + /* Testing P now is OK since SHR sets it */ + if (cond == CondL) cond = CondP; else cond = CondNP; + break; + + case CondB: + case CondNB: + mask = EFlagC; goto simple; /* C=1 */ + + case CondZ: + case CondNZ: + mask = EFlagZ; goto simple; /* Z=1 */ + + case CondBE: + case CondNBE: + mask = EFlagC | EFlagZ; goto simple; /* C=1 || Z=1 */ + + case CondS: + case CondNS: + mask = EFlagS; goto simple; /* S=1 */ + + case CondP: + case CondNP: + mask = EFlagP; goto simple; /* P=1 */ + + case CondO: + case CondNO: + mask = EFlagO; goto simple; /* O=1 */ + + default: + VG_(printf)("synth_jcond_lit: unhandled simd case %d (%s)\n", + (Int)cond, VG_(name_UCondcode)(cond) ); + VG_(core_panic)("synth_jcond_lit: unhandled simd case"); + + simple: + VG_(new_emit)(False, False, FlagsOSZACP); + if ((mask & 0xff) == mask) { + VG_(emitB) ( 0xF6 ); /* Grp3 */ + VG_(emit_amode_offregmem_reg)( + VGOFF_(m_eflags) * 4, R_EBP, 0 /* subcode for TEST */); + VG_(emitB) (mask); + if (dis) + VG_(printf)("\n\t\ttestb $0x%x, %d(%%ebp)\n", + mask, VGOFF_(m_eflags) * 4); + } else { + /* all cond codes are in lower 16 bits */ + vg_assert((mask & 0xffff) == mask); + + VG_(emitB) ( 0x66 ); + VG_(emitB) ( 0xF7 ); + VG_(emit_amode_offregmem_reg)( + VGOFF_(m_eflags) * 4, R_EBP, 0 /* subcode for TEST */); + VG_(emitW) (mask); + if (dis) + VG_(printf)("\n\t\ttestl $0x%x, %d(%%ebp)\n", + mask, VGOFF_(m_eflags) * 4); + } + + cond = (parity ? CondP : CondZ) | (cond & 1); + break; + } + } + + VG_(emit_jcondshort_target) ( simd, cond, &tgt, JP_NONE ); + + VG_(target_forward)(&tgt_jump); + synth_jmp_lit ( addr, JmpBoring ); + + VG_(target_forward)(&tgt); + VG_(target_forward)(&tgt2); +} + + + +static void synth_jmp_ifzero_reg_lit ( Int reg, Addr addr ) +{ + Int tgt; + + VG_(init_target)(&tgt); + + VG_(emit_cmpl_zero_reg) ( False, reg ); + + VG_(emit_jcondshort_target) ( False, CondNZ, &tgt, JP_NONE ); + synth_jmp_lit ( addr, JmpBoring ); + + VG_(target_forward)(&tgt); +} + + +static void synth_mov_lit_reg ( Int size, UInt lit, Int reg ) +{ + /* Load the zero-extended literal into reg, at size l, + regardless of the request size. */ + VG_(emit_movv_lit_reg) ( 4, lit, reg ); +} + + +static void synth_mov_regmem_reg ( Int size, Int reg1, Int reg2 ) +{ + switch (size & ~DO_BOUNDSCHECK) { + case 4: emit_movv_regmem_reg ( size, reg1, reg2 ); break; + case 2: VG_(emit_movzwl_regmem_reg) ( size & DO_BOUNDSCHECK, reg1, reg2 ); break; + case 1: emit_movzbl_regmem_reg ( size & DO_BOUNDSCHECK, reg1, reg2 ); break; + default: VG_(core_panic)("synth_mov_regmem_reg"); + } +} + + +static void synth_mov_offregmem_reg ( Int size, Int off, Int areg, Int reg ) +{ + switch (size & ~DO_BOUNDSCHECK) { + case 4: VG_(emit_movv_offregmem_reg) ( 4, off, areg, reg ); break; + case 2: VG_(emit_movzwl_offregmem_reg) ( size & DO_BOUNDSCHECK, off, areg, reg ); break; + case 1: VG_(emit_movzbl_offregmem_reg) ( size & DO_BOUNDSCHECK, off, areg, reg ); break; + default: VG_(core_panic)("synth_mov_offregmem_reg"); + } +} + + +static void synth_mov_reg_offregmem ( Int size, Int reg, + Int off, Int areg ) +{ + switch (size) { + case 4: VG_(emit_movv_reg_offregmem) ( 4, reg, off, areg ); break; + case 2: VG_(emit_movv_reg_offregmem) ( 2, reg, off, areg ); break; + case 1: if (reg < 4) { + VG_(emit_movb_reg_offregmem) ( reg, off, areg ); + } + else { + VG_(emit_swapl_reg_EAX) ( reg ); + VG_(emit_movb_reg_offregmem) ( R_AL, off, areg ); + VG_(emit_swapl_reg_EAX) ( reg ); + } + break; + default: VG_(core_panic)("synth_mov_reg_offregmem"); + } +} + + +static void synth_mov_reg_memreg ( Int size, Int reg1, Int reg2 ) +{ + Int s1; + switch (size & ~DO_BOUNDSCHECK) { + case 4: + case 2: emit_movv_reg_regmem ( size, reg1, reg2 ); break; + case 1: if (reg1 < 4) { + emit_movb_reg_regmem ( size & DO_BOUNDSCHECK, reg1, reg2 ); + } + else { + /* Choose a swap reg which is < 4 and not reg1 or reg2. */ + for (s1 = 0; s1 == reg1 || s1 == reg2; s1++) ; + emit_swapl_reg_reg ( s1, reg1 ); + emit_movb_reg_regmem ( size & DO_BOUNDSCHECK, s1, reg2 ); + emit_swapl_reg_reg ( s1, reg1 ); + } + break; + default: VG_(core_panic)("synth_mov_reg_litmem"); + } +} + + +static void synth_unaryop_reg ( Bool simd_flags, + Opcode opcode, Int size, + Int reg ) +{ + /* NB! opcode is a uinstr opcode, not an x86 one! */ + switch (size) { + case 4: VG_(emit_unaryopv_reg) ( simd_flags, 4, opcode, reg ); + break; + case 2: VG_(emit_unaryopv_reg) ( simd_flags, 2, opcode, reg ); + break; + case 1: if (reg < 4) { + VG_(emit_unaryopb_reg) ( simd_flags, opcode, reg ); + } else { + VG_(emit_swapl_reg_EAX) ( reg ); + VG_(emit_unaryopb_reg) ( simd_flags, opcode, R_AL ); + VG_(emit_swapl_reg_EAX) ( reg ); + } + break; + default: VG_(core_panic)("synth_unaryop_reg"); + } +} + + + +static void synth_nonshiftop_reg_reg ( Bool simd_flags, + Opcode opcode, Int size, + Int reg1, Int reg2 ) +{ + /* NB! opcode is a uinstr opcode, not an x86 one! */ + switch (size) { + case 4: VG_(emit_nonshiftopv_reg_reg) ( simd_flags, 4, opcode, reg1, reg2 ); + break; + case 2: VG_(emit_nonshiftopv_reg_reg) ( simd_flags, 2, opcode, reg1, reg2 ); + break; + case 1: { /* Horrible ... */ + Int s1, s2; + /* Choose s1 and s2 to be x86 regs which we can talk about the + lowest 8 bits, ie either %eax, %ebx, %ecx or %edx. Make + sure s1 != s2 and that neither of them equal either reg1 or + reg2. Then use them as temporaries to make things work. */ + if (reg1 < 4 && reg2 < 4) { + emit_nonshiftopb_reg_reg(simd_flags, opcode, reg1, reg2); + break; + } + for (s1 = 0; s1 == reg1 || s1 == reg2; s1++) ; + if (reg1 >= 4 && reg2 < 4) { + emit_swapl_reg_reg ( reg1, s1 ); + emit_nonshiftopb_reg_reg(simd_flags, opcode, s1, reg2); + emit_swapl_reg_reg ( reg1, s1 ); + break; + } + for (s2 = 0; s2 == reg1 || s2 == reg2 || s2 == s1; s2++) ; + if (reg1 < 4 && reg2 >= 4) { + emit_swapl_reg_reg ( reg2, s2 ); + emit_nonshiftopb_reg_reg(simd_flags, opcode, reg1, s2); + emit_swapl_reg_reg ( reg2, s2 ); + break; + } + if (reg1 >= 4 && reg2 >= 4 && reg1 != reg2) { + emit_swapl_reg_reg ( reg1, s1 ); + emit_swapl_reg_reg ( reg2, s2 ); + emit_nonshiftopb_reg_reg(simd_flags, opcode, s1, s2); + emit_swapl_reg_reg ( reg1, s1 ); + emit_swapl_reg_reg ( reg2, s2 ); + break; + } + if (reg1 >= 4 && reg2 >= 4 && reg1 == reg2) { + emit_swapl_reg_reg ( reg1, s1 ); + emit_nonshiftopb_reg_reg(simd_flags, opcode, s1, s1); + emit_swapl_reg_reg ( reg1, s1 ); + break; + } + VG_(core_panic)("synth_nonshiftopb_reg_reg"); + } + default: VG_(core_panic)("synth_nonshiftop_reg_reg"); + } +} + +#if 0 +/* evidently unused */ +static void synth_nonshiftop_reg_offregmem ( + Bool simd_flags, + Opcode opcode, Int size, + Int off, Int areg, Int reg ) +{ + switch (size) { + case 4: + emit_nonshiftopv_reg_offregmem ( simd_flags, 4, opcode, off, areg, reg ); + break; + case 2: + emit_nonshiftopv_reg_offregmem ( simd_flags, 2, opcode, off, areg, reg ); + break; + case 1: + if (reg < 4) { + emit_nonshiftopb_reg_offregmem ( simd_flags, opcode, off, areg, reg ); + } else { + VG_(emit_swapl_reg_EAX) ( reg ); + emit_nonshiftopb_reg_offregmem ( simd_flags, opcode, off, areg, R_AL ); + VG_(emit_swapl_reg_EAX) ( reg ); + } + break; + default: + VG_(core_panic)("synth_nonshiftop_reg_offregmem"); + } +} +#endif + +static void synth_nonshiftop_offregmem_reg ( + Bool simd_flags, + Opcode opcode, Int size, + Int off, Int areg, Int reg ) +{ + switch (size) { + case 4: + emit_nonshiftopv_offregmem_reg ( simd_flags, 4, opcode, off, areg, reg ); + break; + case 2: + emit_nonshiftopv_offregmem_reg ( simd_flags, 2, opcode, off, areg, reg ); + break; + case 1: + if (reg < 4) { + emit_nonshiftopb_offregmem_reg ( simd_flags, opcode, off, areg, reg ); + } else { + VG_(emit_swapl_reg_EAX) ( reg ); + emit_nonshiftopb_offregmem_reg ( simd_flags, opcode, off, areg, R_AL ); + VG_(emit_swapl_reg_EAX) ( reg ); + } + break; + default: + VG_(core_panic)("synth_nonshiftop_offregmem_reg"); + } +} + + +static void synth_nonshiftop_lit_reg ( Bool simd_flags, + Opcode opcode, Int size, + UInt lit, Int reg ) +{ + switch (size) { + case 4: VG_(emit_nonshiftopv_lit_reg) ( simd_flags, 4, opcode, lit, reg ); + break; + case 2: VG_(emit_nonshiftopv_lit_reg) ( simd_flags, 2, opcode, lit, reg ); + break; + case 1: if (reg < 4) { + emit_nonshiftopb_lit_reg ( simd_flags, opcode, lit, reg ); + } else { + VG_(emit_swapl_reg_EAX) ( reg ); + emit_nonshiftopb_lit_reg ( simd_flags, opcode, lit, R_AL ); + VG_(emit_swapl_reg_EAX) ( reg ); + } + break; + default: VG_(core_panic)("synth_nonshiftop_lit_reg"); + } +} + +static void synth_nonshiftop_lit_offregmem ( Bool simd_flags, + Opcode opcode, Int size, + UInt lit, Int off, Int regmem ) +{ + switch (size) { + case 4: VG_(emit_nonshiftopv_lit_offregmem) ( simd_flags, 4, opcode, lit, off, regmem ); + break; + case 2: VG_(emit_nonshiftopv_lit_offregmem) ( simd_flags, 2, opcode, lit, off, regmem ); + break; + case 1: emit_nonshiftopb_lit_offregmem ( simd_flags, opcode, lit, off, regmem ); + break; + default: VG_(core_panic)("synth_nonshiftop_lit_offregmem"); + } +} + + +static void synth_mul_reg_reg ( Bool upd_cc, + Opcode opcode, Int size, + Int reg1, Int reg2 ) +{ + VG_(new_emit)(upd_cc, FlagsEmpty, FlagsOSZACP); + + switch (size) { + case 2: + VG_(emitB)(0x66); + /* FALLTHROUGH */ + case 4: + VG_(emitB)(0x0F); + VG_(emitB)(0xAF); + VG_(emit_amode_ereg_greg)(reg1, reg2); + break; + + case 1: + VG_(core_panic)("can't do byte mul"); + break; + } + if (dis) + VG_(printf)("\n\t\timul%c\t%s, %s\n", + nameISize(size), + nameIReg(size, reg1), + nameIReg(size, reg2)); +} + +static void synth_mul_lit_reg ( Bool upd_cc, + Opcode opcode, Int size, + UInt lit, Int reg ) +{ + VG_(new_emit)(upd_cc, FlagsEmpty, FlagsOSZACP); + + switch (size) { + case 2: + VG_(emitB)(0x66); + /* FALLTHROUGH */ + case 4: + VG_(emitB)(0x69); + VG_(emit_amode_ereg_greg)(reg, 0); + if (size == 2) + VG_(emitW)(lit); + else + VG_(emitL)(lit); + break; + + case 1: + VG_(core_panic)("can't do byte mul"); + break; + } + if (dis) + VG_(printf)("\n\t\timul%c\t%d, %s\n", + nameISize(size), + lit, + nameIReg(size, reg)); +} + +static void synth_mul_offregmem_reg ( + Bool upd_cc, + Opcode opcode, Int size, + Int off, Int areg, Int reg ) +{ + VG_(new_emit)(upd_cc, FlagsEmpty, FlagsOSZACP); + + switch(size) { + case 2: + VG_(emitB)(0x66); + /* FALLTHROUGH */ + case 4: + VG_(emitB)(0x0F); + VG_(emitB)(0xAF); + VG_(emit_amode_offregmem_reg)(off, areg, reg); + break; + + case 1: + VG_(core_panic)("can't do byte mul"); + } + + if (dis) + VG_(printf)("\n\t\timul%c\t0x%x(%s), %s\n", + nameISize(size), off, nameIReg(4,areg),nameIReg(size,reg)); + +} + + +static void synth_push_reg ( Int size, Int reg ) +{ + switch (size) { + case 4: + VG_(emit_pushv_reg) ( 4, reg ); + break; + case 2: + VG_(emit_pushv_reg) ( 2, reg ); + break; + /* Pray that we don't have to generate this really cruddy bit of + code very often. Could do better, but can I be bothered? */ + case 1: + vg_assert(reg != R_ESP); /* duh */ + VG_(emit_add_lit_to_esp)(-1); + if (reg != R_EAX) VG_(emit_swapl_reg_EAX) ( reg ); + emit_movb_AL_zeroESPmem(); + if (reg != R_EAX) VG_(emit_swapl_reg_EAX) ( reg ); + break; + default: + VG_(core_panic)("synth_push_reg"); + } +} + + +static void synth_pop_reg ( Int size, Int reg ) +{ + switch (size) { + case 4: + VG_(emit_popv_reg) ( 4, reg ); + break; + case 2: + VG_(emit_popv_reg) ( 2, reg ); + break; + case 1: + /* Same comment as above applies. */ + vg_assert(reg != R_ESP); /* duh */ + if (reg != R_EAX) VG_(emit_swapl_reg_EAX) ( reg ); + emit_movb_zeroESPmem_AL(); + if (reg != R_EAX) VG_(emit_swapl_reg_EAX) ( reg ); + VG_(emit_add_lit_to_esp)(1); + break; + default: VG_(core_panic)("synth_pop_reg"); + } +} + + +static void synth_shiftop_reg_reg ( Bool simd_flags, + Opcode opcode, Int size, + Int regs, Int regd ) +{ + synth_push_reg ( size, regd ); + if (regs != R_ECX) emit_swapl_reg_ECX ( regs ); + switch (size) { + case 4: emit_shiftopv_cl_stack0 ( simd_flags, 4, opcode ); break; + case 2: emit_shiftopv_cl_stack0 ( simd_flags, 2, opcode ); break; + case 1: emit_shiftopb_cl_stack0 ( simd_flags, opcode ); break; + default: VG_(core_panic)("synth_shiftop_reg_reg"); + } + if (regs != R_ECX) emit_swapl_reg_ECX ( regs ); + synth_pop_reg ( size, regd ); +} + + +static void synth_shiftop_lit_reg ( Bool simd_flags, + Opcode opcode, Int size, + UInt lit, Int reg ) +{ + switch (size) { + case 4: VG_(emit_shiftopv_lit_reg) ( simd_flags, 4, opcode, lit, reg ); + break; + case 2: VG_(emit_shiftopv_lit_reg) ( simd_flags, 2, opcode, lit, reg ); + break; + case 1: if (reg < 4) { + emit_shiftopb_lit_reg ( simd_flags, opcode, lit, reg ); + } else { + VG_(emit_swapl_reg_EAX) ( reg ); + emit_shiftopb_lit_reg ( simd_flags, opcode, lit, R_AL ); + VG_(emit_swapl_reg_EAX) ( reg ); + } + break; + default: VG_(core_panic)("synth_shiftop_lit_reg"); + } +} + + +static void synth_setb_reg ( Bool simd, Int reg, Condcode cond ) +{ + if (reg < 4) { + emit_setb_reg ( simd, reg, cond ); + } else { + VG_(emit_swapl_reg_EAX) ( reg ); + emit_setb_reg ( simd, R_AL, cond ); + VG_(emit_swapl_reg_EAX) ( reg ); + } +} + + +static void synth_MMX2_regmem ( Bool uses_flags, Bool sets_flags, + UChar first_byte, + UChar second_byte, + Int ireg ) +{ + emit_MMX2_regmem ( uses_flags, sets_flags, + first_byte, second_byte, ireg ); +} + + +static void synth_MMX2a1 ( Bool uses_flags, Bool sets_flags, + UChar first_byte, + UChar second_byte, + UChar third_byte, + Int ireg ) +{ + emit_MMX2a1 ( uses_flags, sets_flags, + first_byte, second_byte, third_byte, ireg ); +} + + +static void synth_MMX2_reg_to_mmxreg ( Bool uses_flags, Bool sets_flags, + UChar first_byte, + UChar second_byte, + Int ireg ) +{ + emit_MMX2_reg_to_mmxreg ( uses_flags, sets_flags, + first_byte, second_byte, ireg ); +} + +static void synth_MMX2_mmxreg_to_reg ( Bool uses_flags, Bool sets_flags, + UChar first_byte, + UChar second_byte, + Int ireg ) +{ + emit_MMX2_mmxreg_to_reg ( uses_flags, sets_flags, + first_byte, second_byte, ireg ); +} + +static void synth_MMX2_no_mem ( Bool uses_flags, Bool sets_flags, + UChar first_byte, + UChar second_byte ) +{ + emit_MMX2_no_mem ( uses_flags, sets_flags, first_byte, second_byte ); +} + + +static void synth_MMX3_no_mem ( Bool uses_flags, Bool sets_flags, + UChar first_byte, + UChar second_byte, + UChar third_byte ) +{ + emit_MMX3_no_mem ( uses_flags, sets_flags, + first_byte, second_byte, third_byte ); +} + + +static void synth_MMX1_no_mem ( Bool uses_flags, Bool sets_flags, + UChar first_byte ) +{ + emit_MMX1_no_mem ( uses_flags, sets_flags, first_byte ); +} + + +static void synth_fpu_regmem ( Bool uses_flags, Bool sets_flags, + UChar first_byte, + UChar second_byte_masked, + Int reg ) +{ + emit_fpu_regmem ( uses_flags, sets_flags, + first_byte, second_byte_masked, reg ); +} + + +static void synth_fpu_no_mem ( Bool uses_flags, Bool sets_flags, + UChar first_byte, + UChar second_byte ) +{ + emit_fpu_no_mem ( uses_flags, sets_flags, first_byte, second_byte ); +} + + +static void synth_movl_reg_reg ( Int src, Int dst ) +{ + emit_movl_reg_reg ( src, dst ); +} + +static void synth_cmovl_reg_reg ( Condcode cond, Int src, Int dst ) +{ + Int tgt; + + VG_(init_target)(&tgt); + + VG_(emit_jcondshort_target) ( True, invertCondition(cond), &tgt, JP_NONE); + emit_movl_reg_reg ( src, dst ); + + VG_(target_forward)(&tgt); +} + + +/*----------------------------------------------------*/ +/*--- Top level of the uinstr -> x86 translation. ---*/ +/*----------------------------------------------------*/ + +/* Return the byte offset from %ebp (ie, into baseBlock) + for the specified ArchReg or SpillNo. */ +static Int spillOrArchOffset ( Int size, Tag tag, UInt value ) +{ + if (tag == SpillNo) { + vg_assert(size == 4); + vg_assert(value >= 0 && value < VG_MAX_SPILLSLOTS); + return 4 * (value + VGOFF_(spillslots)); + } + if (tag == ArchReg) { + switch (value) { + case R_EAX: return 4 * VGOFF_(m_eax); + case R_ECX: return 4 * VGOFF_(m_ecx); + case R_EDX: return 4 * VGOFF_(m_edx); + case R_EBX: return 4 * VGOFF_(m_ebx); + case R_ESP: + if (size == 1) return 4 * VGOFF_(m_eax) + 1; + else return 4 * VGOFF_(m_esp); + case R_EBP: + if (size == 1) return 4 * VGOFF_(m_ecx) + 1; + else return 4 * VGOFF_(m_ebp); + case R_ESI: + if (size == 1) return 4 * VGOFF_(m_edx) + 1; + else return 4 * VGOFF_(m_esi); + case R_EDI: + if (size == 1) return 4 * VGOFF_(m_ebx) + 1; + else return 4 * VGOFF_(m_edi); + } + } + VG_(core_panic)("spillOrArchOffset"); +} + +static Int eflagsOffset ( void ) +{ + return 4 * VGOFF_(m_eflags); +} + +static Int segRegOffset ( UInt archregs ) +{ + switch (archregs) { + case R_CS: return 4 * VGOFF_(m_cs); + case R_SS: return 4 * VGOFF_(m_ss); + case R_DS: return 4 * VGOFF_(m_ds); + case R_ES: return 4 * VGOFF_(m_es); + case R_FS: return 4 * VGOFF_(m_fs); + case R_GS: return 4 * VGOFF_(m_gs); + default: VG_(core_panic)("segRegOffset"); + } +} + +UInt VG_(get_archreg) ( UInt arch ) +{ + switch (arch) { + case R_EAX: return VG_(baseBlock)[ VGOFF_(m_eax) ]; + case R_ECX: return VG_(baseBlock)[ VGOFF_(m_ecx) ]; + case R_EDX: return VG_(baseBlock)[ VGOFF_(m_edx) ]; + case R_EBX: return VG_(baseBlock)[ VGOFF_(m_ebx) ]; + case R_ESP: return VG_(baseBlock)[ VGOFF_(m_esp) ]; + case R_EBP: return VG_(baseBlock)[ VGOFF_(m_ebp) ]; + case R_ESI: return VG_(baseBlock)[ VGOFF_(m_esi) ]; + case R_EDI: return VG_(baseBlock)[ VGOFF_(m_edi) ]; + default: VG_(core_panic)( "get_archreg"); + } +} + +UInt VG_(get_thread_archreg) ( ThreadId tid, UInt arch ) +{ + ThreadState* tst; + + vg_assert(VG_(is_valid_tid)(tid)); + tst = & VG_(threads)[tid]; + + switch (arch) { + case R_EAX: return tst->arch.m_eax; + case R_ECX: return tst->arch.m_ecx; + case R_EDX: return tst->arch.m_edx; + case R_EBX: return tst->arch.m_ebx; + case R_ESP: return tst->arch.m_esp; + case R_EBP: return tst->arch.m_ebp; + case R_ESI: return tst->arch.m_esi; + case R_EDI: return tst->arch.m_edi; + default: VG_(core_panic)( "get_thread_archreg"); + } +} + +/* Return the baseBlock index for the specified shadow register */ +static Int shadow_reg_index ( Int arch ) +{ + switch (arch) { + case R_EAX: return VGOFF_(sh_eax); + case R_ECX: return VGOFF_(sh_ecx); + case R_EDX: return VGOFF_(sh_edx); + case R_EBX: return VGOFF_(sh_ebx); + case R_ESP: return VGOFF_(sh_esp); + case R_EBP: return VGOFF_(sh_ebp); + case R_ESI: return VGOFF_(sh_esi); + case R_EDI: return VGOFF_(sh_edi); + default: VG_(core_panic)( "shadow_reg_index"); + } +} + +/* Return the byte offset from %ebp (ie, into baseBlock) + for the specified shadow register */ +Int VG_(shadow_reg_offset) ( Int arch ) +{ + return 4 * shadow_reg_index ( arch ); +} + +Int VG_(shadow_flags_offset) ( void ) +{ + return 4 * VGOFF_(sh_eflags); +} + +/* Accessing shadow arch. registers */ +UInt VG_(get_shadow_archreg) ( UInt archreg ) +{ + return VG_(baseBlock)[ shadow_reg_index(archreg) ]; +} + +void VG_(set_shadow_archreg) ( UInt archreg, UInt val ) +{ + VG_(baseBlock)[ shadow_reg_index(archreg) ] = val; +} + +void VG_(set_shadow_eflags) ( UInt val ) +{ + VG_(baseBlock)[ VGOFF_(sh_eflags) ] = val; +} + +UInt VG_(get_thread_shadow_archreg) ( ThreadId tid, UInt archreg ) +{ + ThreadState* tst; + + vg_assert(VG_(is_valid_tid)(tid)); + tst = & VG_(threads)[tid]; + + switch (archreg) { + case R_EAX: return tst->arch.sh_eax; + case R_ECX: return tst->arch.sh_ecx; + case R_EDX: return tst->arch.sh_edx; + case R_EBX: return tst->arch.sh_ebx; + case R_ESP: return tst->arch.sh_esp; + case R_EBP: return tst->arch.sh_ebp; + case R_ESI: return tst->arch.sh_esi; + case R_EDI: return tst->arch.sh_edi; + default: VG_(core_panic)( "get_thread_shadow_archreg"); + } +} + +void VG_(set_thread_shadow_archreg) ( ThreadId tid, UInt archreg, UInt val ) +{ + ThreadState* tst; + + vg_assert(VG_(is_valid_tid)(tid)); + tst = & VG_(threads)[tid]; + + switch (archreg) { + case R_EAX: tst->arch.sh_eax = val; break; + case R_ECX: tst->arch.sh_ecx = val; break; + case R_EDX: tst->arch.sh_edx = val; break; + case R_EBX: tst->arch.sh_ebx = val; break; + case R_ESP: tst->arch.sh_esp = val; break; + case R_EBP: tst->arch.sh_ebp = val; break; + case R_ESI: tst->arch.sh_esi = val; break; + case R_EDI: tst->arch.sh_edi = val; break; + default: VG_(core_panic)( "set_thread_shadow_archreg"); + } +} + +Addr VG_(shadow_archreg_address) ( UInt archreg ) +{ + return (Addr) & VG_(baseBlock)[ shadow_reg_index(archreg) ]; +} + +static void synth_WIDEN_signed ( Int sz_src, Int sz_dst, Int reg ) +{ + if (sz_src == 1 && sz_dst == 4) { + VG_(emit_shiftopv_lit_reg) ( False, 4, SHL, 24, reg ); + VG_(emit_shiftopv_lit_reg) ( False, 4, SAR, 24, reg ); + } + else if (sz_src == 2 && sz_dst == 4) { + VG_(emit_shiftopv_lit_reg) ( False, 4, SHL, 16, reg ); + VG_(emit_shiftopv_lit_reg) ( False, 4, SAR, 16, reg ); + } + else if (sz_src == 1 && sz_dst == 2) { + VG_(emit_shiftopv_lit_reg) ( False, 2, SHL, 8, reg ); + VG_(emit_shiftopv_lit_reg) ( False, 2, SAR, 8, reg ); + } + else + VG_(core_panic)("synth_WIDEN"); +} + + +/*----------------------------------------------------*/ +/*--- Generate code for a single UInstr. ---*/ +/*----------------------------------------------------*/ + +static __inline__ +Bool writeFlagUse ( UInstr* u ) +{ + return (u->flags_w != FlagsEmpty); +} + +static __inline__ +Bool readFlagUse ( UInstr* u ) +{ + /* If the UInstr writes some flags but not all, then we still need + to consider it as reading flags so that the unchanged values are + passed through properly. (D is special) */ + return + (u->flags_r != FlagsEmpty) || + (u->flags_w != FlagsEmpty && u->flags_w != FlagsOSZACP) ; +} + +static __inline__ +Bool anyFlagUse ( UInstr* u ) +{ + return readFlagUse(u) || writeFlagUse(u); +} + + +/* *fplive==True indicates that the simulated machine's FPU/SSE state is in + the real machine's cpu. If so we need to be very careful not to trash it. + If FPU/SSE state is live and we deem it necessary to copy it back to + the simulated machine's FPU/SSE state, we do so. The final state of + fpliveness is returned. In short we _must_ do put_sse_state if + there is any chance at all that the code generated for a UInstr + will change the real FPU/MMX/SSE/SSE2 state. +*/ +static void emitUInstr ( UCodeBlock* cb, Int i, + RRegSet regs_live_before, + /* Running state, which we update. */ + Bool* sselive, /* True<==>FPU/SSE + state in real FPU */ + Addr* orig_eip, /* previous curr_eip, or zero */ + Addr* curr_eip ) /* current eip */ +{ + Int old_emitted_code_used; + UInstr* u = &cb->instrs[i]; + + if (dis) + VG_(pp_UInstr_regs)(i, u); + + old_emitted_code_used = emitted_code_used; + + switch (u->opcode) { + case NOP: case LOCK: case CALLM_S: case CALLM_E: break; + + case INCEIP: + /* Advance %EIP some small amount. */ + *curr_eip += (UInt)(u->val1); + + if (*orig_eip == 0 /* we don't know what the old value was */ + || ((*orig_eip & ~0xFF) != (*curr_eip & ~0xFF))) { + /* We have to update all 32 bits of the value. */ + VG_(emit_movv_lit_offregmem)( + 4, *curr_eip, 4*VGOFF_(m_eip), R_EBP); + } else { + /* Cool! we only need to update lowest 8 bits */ + VG_(emit_movb_lit_offregmem)( + *curr_eip & 0xFF, 4*VGOFF_(m_eip)+0, R_EBP); + } + + *orig_eip = *curr_eip; + break; + + case LEA1: { + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == RealReg); + emit_lea_litreg_reg ( u->lit32, u->val1, u->val2 ); + break; + } + + case LEA2: { + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == RealReg); + vg_assert(u->tag3 == RealReg); + emit_lea_sib_reg ( u->lit32, u->extra4b, + u->val1, u->val2, u->val3 ); + break; + } + + case WIDEN: { + vg_assert(u->tag1 == RealReg); + if (u->signed_widen) { + synth_WIDEN_signed ( u->extra4b, u->size, u->val1 ); + } else { + /* no need to generate any code. */ + } + break; + } + + case STORE: { + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == RealReg); + synth_mov_reg_memreg ( u->size | DO_BOUNDSCHECK, u->val1, u->val2 ); + break; + } + + case LOAD: { + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == RealReg); + synth_mov_regmem_reg ( u->size | DO_BOUNDSCHECK, u->val1, u->val2 ); + break; + } + + case GET: { + vg_assert(u->tag1 == ArchReg || u->tag1 == SpillNo); + vg_assert(u->tag2 == RealReg); + synth_mov_offregmem_reg ( + u->size, + spillOrArchOffset( u->size, u->tag1, u->val1 ), + R_EBP, + u->val2 + ); + break; + } + + case PUT: { + vg_assert(u->tag2 == ArchReg || u->tag2 == SpillNo); + vg_assert(u->tag1 == RealReg); + synth_mov_reg_offregmem ( + u->size, + u->val1, + spillOrArchOffset( u->size, u->tag2, u->val2 ), + R_EBP + ); + break; + } + + case GETSEG: { + vg_assert(u->tag1 == ArchRegS); + vg_assert(u->tag2 == RealReg); + vg_assert(u->size == 2); + synth_mov_offregmem_reg ( + 4, + segRegOffset( u->val1 ), + R_EBP, + u->val2 + ); + break; + } + + case PUTSEG: { + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == ArchRegS); + vg_assert(u->size == 2); + synth_mov_reg_offregmem ( + 4, + u->val1, + segRegOffset( u->val2 ), + R_EBP + ); + break; + } + + case GETF: { + vg_assert(u->size == 2 || u->size == 4); + vg_assert(u->tag1 == RealReg); + + /* This complexity is because the D(irection) flag is stored + separately from the rest of EFLAGS. */ + + /* We're only fetching from the Simd state, so make sure it's + up to date. */ + maybe_emit_put_eflags(); + + /* get D in u->val1 (== 1 or -1) */ + synth_mov_offregmem_reg (u->size, 4*VGOFF_(m_dflag), R_EBP, u->val1); + + /* u->val1 &= EFlagD (== 0 or EFlagD) */ + synth_nonshiftop_lit_reg(False, AND, u->size, EFlagD, u->val1); + + /* EFLAGS &= ~EFlagD (make sure there's no surprises) */ + synth_nonshiftop_lit_offregmem(False, AND, u->size, ~EFlagD, + eflagsOffset(), R_EBP); + + /* EFLAGS &= ~EFlagD (make sure there's no surprises) */ + synth_nonshiftop_lit_offregmem(False, AND, u->size, ~EFlagD, + eflagsOffset(), R_EBP); + + /* u->val1 |= EFLAGS (EFLAGS & EflagD == 0) */ + synth_nonshiftop_offregmem_reg(False, OR, u->size, + eflagsOffset(), R_EBP, u->val1); + break; + } + + case PUTF: { + vg_assert(u->size == 2 || u->size == 4); + vg_assert(u->tag1 == RealReg); + + /* When putting a value into EFLAGS, this generates the + correct value for m_dflag (-1 or 1), and clears the D bit + in EFLAGS. */ + + /* We're updating the whole flag state, so the old state + doesn't matter; make sure that the new simulated state + will be fetched when needed. */ + eflags_state = UPD_Simd; + + /* store EFLAGS (with D) */ + synth_mov_reg_offregmem (u->size, u->val1, eflagsOffset(), R_EBP); + + /* u->val1 &= EFlagD */ + synth_nonshiftop_lit_reg(False, AND, u->size, EFlagD, u->val1); + + /* computes: u->val1 = (u->val1 == 0) ? 1 : -1 */ + synth_unaryop_reg(False, NEG, u->size, u->val1); + synth_nonshiftop_reg_reg(False, SBB, u->size, u->val1, u->val1); + synth_nonshiftop_lit_reg(False, SBB, u->size, -1, u->val1); + + /* save D */ + synth_mov_reg_offregmem(u->size, u->val1, 4*VGOFF_(m_dflag), R_EBP); + + /* EFLAGS &= ~EFlagD */ + synth_nonshiftop_lit_offregmem(False, AND, u->size, ~EFlagD, + eflagsOffset(), R_EBP); + break; + } + + case MOV: { + vg_assert(u->tag1 == RealReg || u->tag1 == Literal); + vg_assert(u->tag2 == RealReg); + switch (u->tag1) { + case RealReg: vg_assert(u->size == 4); + if (u->val1 != u->val2) + synth_movl_reg_reg ( u->val1, u->val2 ); + break; + case Literal: synth_mov_lit_reg ( u->size, u->lit32, u->val2 ); + break; + default: VG_(core_panic)("emitUInstr:mov"); + } + break; + } + + case USESEG: { + /* Lazy: copy all three vals; synth_ccall ignores any unnecessary + ones. */ + UInt argv[] = { u->val1, u->val2 }; + UInt tagv[] = { RealReg, RealReg }; + UInt ret_reg = u->val2; + + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == RealReg); + vg_assert(u->size == 0); + + if (*sselive) { + emit_put_sse_state(); + *sselive = False; + } + + VG_(synth_ccall) ( (Addr) & VG_(do_useseg), + 2, /* args */ + 0, /* regparms_n */ + argv, tagv, + ret_reg, regs_live_before, u->regs_live_after ); + break; + } + + case MUL: { + vg_assert(u->tag2 == RealReg); + + switch(u->tag1) { + case Literal: + synth_mul_lit_reg(anyFlagUse(u), + u->opcode, u->size, u->lit32, u->val2); + break; + case RealReg: + synth_mul_reg_reg(anyFlagUse(u), + u->opcode, u->size, u->val1, u->val2); + break; + case ArchReg: + synth_mul_offregmem_reg(anyFlagUse(u), + u->opcode, u->size, + spillOrArchOffset(u->size, u->tag1, u->val1), + R_EBP, u->val2); + break; + + default: VG_(core_panic)("emitUInstr:MUL"); + } + break; + } + + case SBB: + case ADC: + case XOR: + case OR: + case AND: + case SUB: + case ADD: { + vg_assert(u->tag2 == RealReg); + switch (u->tag1) { + case Literal: synth_nonshiftop_lit_reg ( + anyFlagUse(u), + u->opcode, u->size, u->lit32, u->val2 ); + break; + case RealReg: synth_nonshiftop_reg_reg ( + anyFlagUse(u), + u->opcode, u->size, u->val1, u->val2 ); + break; + case ArchReg: synth_nonshiftop_offregmem_reg ( + anyFlagUse(u), + u->opcode, u->size, + spillOrArchOffset( u->size, u->tag1, u->val1 ), + R_EBP, + u->val2 ); + break; + default: VG_(core_panic)("emitUInstr:non-shift-op"); + } + break; + } + + case RCR: + case RCL: + case ROR: + case ROL: + case SAR: + case SHR: + case SHL: { + vg_assert(u->tag2 == RealReg); + switch (u->tag1) { + case Literal: synth_shiftop_lit_reg ( + anyFlagUse(u), + u->opcode, u->size, u->lit32, u->val2 ); + break; + case RealReg: synth_shiftop_reg_reg ( + anyFlagUse(u), + u->opcode, u->size, u->val1, u->val2 ); + break; + default: VG_(core_panic)("emitUInstr:shift-op"); + } + break; + } + + case INC: + case DEC: + case NEG: + case NOT: + vg_assert(u->tag1 == RealReg); + synth_unaryop_reg ( + anyFlagUse(u), u->opcode, u->size, u->val1 ); + break; + + case BSWAP: + vg_assert(u->tag1 == RealReg); + vg_assert(u->size == 4); + vg_assert(!VG_(any_flag_use)(u)); + emit_bswapl_reg ( u->val1 ); + break; + + case CMOV: + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == RealReg); + vg_assert(u->cond != CondAlways); + vg_assert(u->size == 4); + synth_cmovl_reg_reg ( u->cond, u->val1, u->val2 ); + break; + + case JMP: { + vg_assert(u->tag2 == NoValue); + vg_assert(u->tag1 == RealReg || u->tag1 == Literal); + if (*sselive) { + emit_put_sse_state(); + *sselive = False; + } + if (u->cond == CondAlways) { + switch (u->tag1) { + case RealReg: + synth_jmp_reg ( u->val1, u->jmpkind ); + break; + case Literal: + synth_jmp_lit ( u->lit32, u->jmpkind ); + break; + default: + VG_(core_panic)("emitUInstr(JMP, unconditional, default)"); + break; + } + } else { + switch (u->tag1) { + case RealReg: + VG_(core_panic)("emitUInstr(JMP, conditional, RealReg)"); + break; + case Literal: + vg_assert(u->jmpkind == JmpBoring); + /* %eax had better not be live since synth_jcond_lit + trashes it in some circumstances. If that turns + out to be a problem we can get synth_jcond_lit to + push/pop it when it is live. */ + vg_assert(! IS_RREG_LIVE(VG_(realreg_to_rank)(R_EAX), + u->regs_live_after)); + synth_jcond_lit ( u->cond, u->lit32, True ); + break; + default: + VG_(core_panic)("emitUInstr(JMP, conditional, default)"); + break; + } + } + break; + } + + case JIFZ: + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == Literal); + vg_assert(u->size == 4); + if (*sselive) { + emit_put_sse_state(); + *sselive = False; + } + synth_jmp_ifzero_reg_lit ( u->val1, u->lit32 ); + break; + + case PUSH: + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == NoValue); + VG_(emit_pushv_reg) ( 4, u->val1 ); + break; + + case POP: + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == NoValue); + VG_(emit_popv_reg) ( 4, u->val1 ); + break; + + case CALLM: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == NoValue); + vg_assert(u->size == 0); + if (*sselive) { + emit_put_sse_state(); + *sselive = False; + } + /* Call to a helper which is pretending to be a real CPU + instruction (and therefore operates on Real flags and + registers) */ + VG_(synth_call) ( False, u->val1, + True, u->flags_r, u->flags_w ); + break; + + case CCALL: { + /* If you change this, remember to change USESEG above, since + that's just a copy of this, slightly simplified. */ + /* Lazy: copy all three vals; synth_ccall ignores any unnecessary + ones. */ + UInt argv[] = { u->val1, u->val2, u->val3 }; + UInt tagv[] = { RealReg, RealReg, RealReg }; + UInt ret_reg = ( u->has_ret_val ? u->val3 : INVALID_REALREG ); + + if (u->argc >= 1) vg_assert(u->tag1 == RealReg); + else vg_assert(u->tag1 == NoValue); + if (u->argc >= 2) vg_assert(u->tag2 == RealReg); + else vg_assert(u->tag2 == NoValue); + if (u->argc == 3 || u->has_ret_val) vg_assert(u->tag3 == RealReg); + else vg_assert(u->tag3 == NoValue); + vg_assert(u->size == 0); + + if (*sselive) { + emit_put_sse_state(); + *sselive = False; + } + VG_(synth_ccall) ( u->lit32, u->argc, u->regparms_n, argv, tagv, + ret_reg, regs_live_before, u->regs_live_after ); + break; + } + + case CLEAR: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == NoValue); + VG_(emit_add_lit_to_esp) ( u->val1 ); + break; + + case CC2VAL: + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == NoValue); + vg_assert(VG_(any_flag_use)(u)); + synth_setb_reg ( True, u->val1, u->cond ); + break; + + case FPU_R: + case FPU_W: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == RealReg); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_fpu_regmem ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 ); + break; + + case FPU: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == NoValue); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_fpu_no_mem ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF ); + break; + + case MMX2_MemWr: + case MMX2_MemRd: + vg_assert(u->size == 4 || u->size == 8); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == RealReg); + vg_assert(u->tag3 == NoValue); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_MMX2_regmem ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 ); + break; + + case MMX2a1_MemRd: + vg_assert(u->size == 8); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_MMX2a1 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 & 0xFF, + u->val3 ); + break; + + case MMX2_ERegRd: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == RealReg); + vg_assert(u->tag3 == NoValue); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_MMX2_reg_to_mmxreg ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 ); + break; + + case MMX2_ERegWr: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == RealReg); + vg_assert(u->tag3 == NoValue); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_MMX2_mmxreg_to_reg ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 ); + break; + + case MMX1: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == NoValue); + vg_assert(u->tag3 == NoValue); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_MMX1_no_mem ( u->flags_r, u->flags_w, + u->val1 & 0xFF ); + break; + + case MMX2: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == NoValue); + vg_assert(u->tag3 == NoValue); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_MMX2_no_mem ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF ); + break; + + case MMX3: + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == NoValue); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + synth_MMX3_no_mem ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 & 0xFF ); + break; + + case SSE2a_MemWr: + case SSE2a_MemRd: + vg_assert(u->size == 4 || u->size == 8 + || u->size == 16 || u->size == 512); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE2a ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 & 0xFF, + u->val3 ); + break; + + case SSE2g_RegWr: + vg_assert(u->size == 4); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE2g ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 & 0xFF, + u->val3 ); + break; + + case SSE2g1_RegWr: + vg_assert(u->size == 4); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE2g1 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 & 0xFF, + u->lit32 & 0xFF, + u->val3 ); + break; + + case SSE2e1_RegRd: + vg_assert(u->size == 2); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE2e1 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 & 0xFF, + u->lit32 & 0xFF, + u->val3 ); + break; + + case SSE2a1_MemRd: + vg_assert(u->size == 4 || u->size == 8 || u->size == 16); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE2a1 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF, + u->val3 ); + break; + + case SSE3a_MemWr: + case SSE3a_MemRd: + vg_assert(u->size == 4 || u->size == 8 || u->size == 16); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE3a ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF, + u->val3 ); + break; + + case SSE3e_RegWr: + case SSE3e_RegRd: + case SSE3g_RegWr: + vg_assert(u->size == 4); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + if (u->opcode==SSE3e_RegRd || u->opcode==SSE3e_RegWr) { + emit_SSE3e ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF, + u->val3 ); + } else { + emit_SSE3g ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF, + u->val3 ); + } + break; + + case SSE3g1_RegWr: + vg_assert(u->size == 4); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE3g1 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF, + u->lit32 & 0xFF, + u->val3 ); + break; + + case SSE3e1_RegRd: + vg_assert(u->size == 2); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE3e1 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF, + u->lit32 & 0xFF, + u->val3 ); + break; + + case SSE3a1_MemRd: + vg_assert(u->size == 8 || u->size == 16); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == RealReg); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE3a1 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF, + u->lit32 & 0xFF, + u->val3 ); + break; + + case SSE5: + vg_assert(u->size == 0); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == Lit16); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE5 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF, + u->val3 & 0xFF ); + break; + + case SSE4: + vg_assert(u->size == 0); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == NoValue); + vg_assert(u->flags_r == FlagsEmpty); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE4 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + (u->val2 >> 8) & 0xFF, + u->val2 & 0xFF ); + break; + + case SSE3: + vg_assert(u->size == 0); + vg_assert(u->tag1 == Lit16); + vg_assert(u->tag2 == Lit16); + vg_assert(u->tag3 == NoValue); + vg_assert(!readFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE3 ( u->flags_r, u->flags_w, + (u->val1 >> 8) & 0xFF, + u->val1 & 0xFF, + u->val2 & 0xFF ); + break; + + case SSE3ag_MemRd_RegWr: + vg_assert(u->size == 4 || u->size == 8); + vg_assert(u->tag1 == RealReg); + vg_assert(u->tag2 == RealReg); + vg_assert(u->tag3 == NoValue); + vg_assert(!anyFlagUse(u)); + if (!(*sselive)) { + emit_get_sse_state(); + *sselive = True; + } + emit_SSE3ag_MemRd_RegWr ( u->flags_r, u->flags_w, + (u->lit32 >> 24) & 0xFF, + (u->lit32 >> 16) & 0xFF, + (u->lit32 >> 8) & 0xFF, + u->val1, u->val2 ); + break; + + default: + if (VG_(needs).extended_UCode) { + if (*sselive) { + emit_put_sse_state(); + *sselive = False; + } + SK_(emit_XUInstr)(u, regs_live_before); + } else { + VG_(printf)("\nError:\n" + " unhandled opcode: %u. Perhaps " + " VG_(needs).extended_UCode should be set?\n", + u->opcode); + VG_(pp_UInstr)(0,u); + VG_(core_panic)("emitUInstr: unimplemented opcode"); + } + } + + if (0 && (*sselive)) { + emit_put_sse_state(); + *sselive = False; + } + + /* Update UInstr histogram */ + vg_assert(u->opcode < 100); + histogram[u->opcode].counts++; + histogram[u->opcode].size += (emitted_code_used - old_emitted_code_used); +} + + +/* Emit x86 for the ucode in cb, returning the address of the + generated code and setting *nbytes to its size. */ +UChar* VG_(emit_code) ( UCodeBlock* cb, + Int* nbytes, + UShort j[VG_MAX_JUMPS] ) +{ + Int i; + UChar regs_live_before = 0; /* No regs live at BB start */ + Bool sselive; + Addr orig_eip, curr_eip; + Int tgt; + + reset_state(); + + if (dis) VG_(printf)("Generated x86 code:\n"); + + /* Generate subl $1, VG_(dispatch_ctr) and drop into dispatch if we hit + zero. We have to do this regardless of whether we're t-chaining + or not. (The ia32 optimisation guide recommends sub over dec.) */ + VG_(init_target)(&tgt); + VG_(new_emit)(False, FlagsEmpty, FlagsOSZAP); + VG_(emitB) (0x83); /* subl */ + emit_amode_litmem_reg((Addr)&VG_(dispatch_ctr), 5); + VG_(emitB) (0x01); + + if (dis) + VG_(printf)("\n\t\tsubl $1, (%p)\n", &VG_(dispatch_ctr)); + + VG_(emit_jcondshort_target)(False, CondNZ, &tgt, JP_TAKEN); + VG_(emit_movv_lit_reg) ( 4, VG_TRC_INNER_COUNTERZERO, R_EBP ); + emit_ret(); + VG_(target_forward)(&tgt); + + /* Set up running state. */ + sselive = False; + orig_eip = cb->orig_eip; /* we know EIP is up to date on BB entry */ + curr_eip = cb->orig_eip; + vg_assert(curr_eip != 0); /* otherwise the incremental updating + algorithm gets messed up. */ + /* for each uinstr ... */ + for (i = 0; i < cb->used; i++) { + UInstr* u = &cb->instrs[i]; + VG_(sanity_check_UInstr)( i, u ); + if (cb->instrs[i].opcode != NOP) { + emitUInstr( cb, i, regs_live_before, + &sselive, &orig_eip, &curr_eip ); + } + regs_live_before = u->regs_live_after; + } + if (dis) VG_(printf)("\n"); + vg_assert(!sselive); /* SSE state must be saved by end of BB */ + vg_assert(eflags_state != UPD_Real); /* flags can't just be in CPU */ + + vg_assert(NULL != j); + vg_assert(jumpidx <= VG_MAX_JUMPS); + for(i = 0; i < jumpidx; i++) + j[i] = jumps[i]; + + /* Returns a pointer to the emitted code. This will have to be + copied by the caller into the translation cache, and then freed */ + *nbytes = emitted_code_used; + return emitted_code; +} + +#undef dis + +/*--------------------------------------------------------------------*/ +/*--- end vg_from_ucode.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_hashtable.c b/VEX/head20041019/coregrind/vg_hashtable.c new file mode 100644 index 0000000000..8b19cf73a8 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_hashtable.c @@ -0,0 +1,180 @@ + +/*--------------------------------------------------------------------*/ +/*--- A separately chained hash table. vg_hashtable.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core.h" + +/*--------------------------------------------------------------------*/ +/*--- Declarations ---*/ +/*--------------------------------------------------------------------*/ + +/* Holds malloc'd but not freed blocks. Static, so zero-inited by default. */ + +#define VG_N_CHAINS 4999 /* a prime number */ + +#define VG_CHAIN_NO(aa) (((UInt)(aa)) % VG_N_CHAINS) + +/*--------------------------------------------------------------------*/ +/*--- Functions ---*/ +/*--------------------------------------------------------------------*/ + +VgHashTable VG_(HT_construct)(void) +{ + /* Initialises to zero, ie. all entries NULL */ + return VG_(calloc)(VG_N_CHAINS, sizeof(VgHashNode*)); +} + +Int VG_(HT_count_nodes) ( VgHashTable table ) +{ + VgHashNode* node; + UInt chain; + Int n = 0; + + for (chain = 0; chain < VG_N_CHAINS; chain++) + for (node = table[chain]; node != NULL; node = node->next) + n++; + return n; +} + +/* Puts a new, heap allocated VgHashNode, into the malloclist. */ +void VG_(HT_add_node) ( VgHashTable table, VgHashNode* node ) +{ + UInt chain = VG_CHAIN_NO(node->key); + node->next = table[chain]; + table[chain] = node; +} + +/* Looks up a VgHashNode in the table. Also returns the address of + the previous node's `next' pointer which allows it to be removed from the + list later without having to look it up again. */ +VgHashNode* VG_(HT_get_node) ( VgHashTable table, UInt key, + /*OUT*/VgHashNode*** next_ptr ) +{ + VgHashNode *prev, *curr; + Int chain; + + chain = VG_CHAIN_NO(key); + + prev = NULL; + curr = table[chain]; + while (True) { + if (curr == NULL) + break; + if (key == curr->key) + break; + prev = curr; + curr = curr->next; + } + + if (NULL == prev) + *next_ptr = & table[chain]; + else + *next_ptr = & prev->next; + + return curr; +} + +/* Allocates a suitably-sized array, copies all the malloc'd block + shadows into it, then returns both the array and the size of it. This is + used by the memory-leak detector. +*/ +VgHashNode** VG_(HT_to_array) ( VgHashTable table, /*OUT*/ UInt* n_shadows ) +{ + UInt i, j; + VgHashNode** arr; + VgHashNode* node; + + *n_shadows = 0; + for (i = 0; i < VG_N_CHAINS; i++) { + for (node = table[i]; node != NULL; node = node->next) { + (*n_shadows)++; + } + } + if (*n_shadows == 0) + return NULL; + + arr = VG_(malloc)( *n_shadows * sizeof(VgHashNode*) ); + + j = 0; + for (i = 0; i < VG_N_CHAINS; i++) { + for (node = table[i]; node != NULL; node = node->next) { + arr[j++] = node; + } + } + vg_assert(j == *n_shadows); + + return arr; +} + +/* Return the first VgHashNode satisfying the predicate p. */ +VgHashNode* VG_(HT_first_match) ( VgHashTable table, + Bool (*p) ( VgHashNode*, void* ), + void* d ) +{ + UInt i; + VgHashNode* node; + + for (i = 0; i < VG_N_CHAINS; i++) + for (node = table[i]; node != NULL; node = node->next) + if ( p(node, d) ) + return node; + + return NULL; +} + +void VG_(HT_apply_to_all_nodes)( VgHashTable table, + void (*f)(VgHashNode*, void*), + void* d ) +{ + UInt i; + VgHashNode* node; + + for (i = 0; i < VG_N_CHAINS; i++) { + for (node = table[i]; node != NULL; node = node->next) { + f(node, d); + } + } +} + +void VG_(HT_destruct)(VgHashTable table) +{ + UInt i; + VgHashNode* node; + + for (i = 0; i < VG_N_CHAINS; i++) { + for (node = table[i]; node != NULL; node = node->next) { + VG_(free)(node); + } + } + VG_(free)(table); +} + +/*--------------------------------------------------------------------*/ +/*--- end vg_hashtable.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_helpers.S b/VEX/head20041019/coregrind/vg_helpers.S new file mode 100644 index 0000000000..c4e73cd746 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_helpers.S @@ -0,0 +1,732 @@ +##--------------------------------------------------------------------## +##--- Support routines for the JITter output. ---## +##--- vg_helpers.S ---## +##--------------------------------------------------------------------## + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "core_asm.h" + +/* ------------------ SIMULATED CPU HELPERS ------------------ */ +/* A stubs for a return which we want to catch: a signal return. + returns and pthread returns. In the latter case, the thread's + return value is in %EAX, so we pass this as the first argument + to the request. In both cases we use the user request mechanism. + You need to to read the definition of VALGRIND_MAGIC_SEQUENCE + in valgrind.h to make sense of this. + + This isn't used in-place. It is copied into the client address space + at an arbitary address. Therefore, this code must be completely + position-independent. +*/ +.global VG_(trampoline_code_start) +.global VG_(trampoline_code_length) +.global VG_(tramp_sigreturn_offset) +.global VG_(tramp_syscall_offset) + +VG_(trampoline_code_start): +sigreturn_start: + subl $20, %esp # allocate arg block + movl %esp, %edx # %edx == &_zzq_args[0] + movl $VG_USERREQ__SIGNAL_RETURNS, 0(%edx) # request + movl $0, 4(%edx) # arg1 + movl $0, 8(%edx) # arg2 + movl $0, 12(%edx) # arg3 + movl $0, 16(%edx) # arg4 + movl %edx, %eax + # and now the magic sequence itself: + roll $29, %eax + roll $3, %eax + rorl $27, %eax + rorl $5, %eax + roll $13, %eax + roll $19, %eax + # should never get here + ud2 + + # We can point our sysinfo stuff here + .align 16 +syscall_start: + int $0x80 + ret +tramp_code_end: + +.data +VG_(trampoline_code_length): + .long tramp_code_end - VG_(trampoline_code_start) +VG_(tramp_sigreturn_offset): + .long sigreturn_start - VG_(trampoline_code_start) +VG_(tramp_syscall_offset): + .long syscall_start - VG_(trampoline_code_start) +.text + + +/* ------------------ REAL CPU HELPERS ------------------ */ +/* The rest of this lot run on the real CPU. */ + +/* Various helper routines, for instructions which are just too + darn tedious for the JITter to output code in-line: + + * integer division + * integer multiplication + * setting and getting obscure eflags + * double-length shifts + * eight byte compare and exchange + + All routines use a standard calling convention designed for + calling from translations, in which the incoming args are + underneath the return address, the callee saves _all_ registers, + and the incoming parameters can be modified, to return results. +*/ + +/* Fetch the time-stamp-ctr reg. + On entry: + dummy, replaced by %EAX value + dummy, replaced by %EDX value + RA <- %esp +*/ +.global VG_(helper_RDTSC) +VG_(helper_RDTSC): + pushl %eax + pushl %edx + rdtsc + movl %edx, 12(%esp) + movl %eax, 16(%esp) + popl %edx + popl %eax + ret + +/* + Fetch a byte/word/dword from given port + On entry: + size 1, 2 or 4 + port, replaced by result + RA +*/ +.global VG_(helper_IN) +VG_(helper_IN): + pushl %eax + pushl %edx + movl 16(%esp), %eax + movl 12(%esp), %edx + + pushfl + cmpl $4, %eax + je in_dword + cmpl $2, %eax + je in_word +in_byte: + inb (%dx), %al + jmp in_done +in_word: + in (%dx), %ax + jmp in_done +in_dword: + inl (%dx),%eax +in_done: + popfl + movl %eax,12(%esp) + popl %edx + popl %eax + ret + +/* + Write a byte/word/dword to given port + On entry: + size 1, 2 or 4 + port + value + RA +*/ +.global VG_(helper_OUT) +VG_(helper_OUT): + pushl %eax + pushl %edx + movl 16(%esp), %edx + movl 12(%esp), %eax + + pushfl + cmpl $4, 20(%esp) + je out_dword + cmpl $2, 20(%esp) + je out_word +out_byte: + outb %al,(%dx) + jmp out_done +out_word: + out %ax,(%dx) + jmp out_done +out_dword: + outl %eax,(%dx) +out_done: + popfl + popl %edx + popl %eax + ret + + +/* Do the CPUID instruction. + On entry: + dummy, replaced by %EAX value + dummy, replaced by %EBX value + dummy, replaced by %ECX value + dummy, replaced by %EDX value + RA <- %esp + + We save registers and package up the args so we can call a C helper + for all this. +*/ +.global VG_(helper_CPUID) +VG_(helper_CPUID): + pushl %ebp + movl %esp,%ebp + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %esi + pushl %edi + pushf + + lea 2*4(%ebp),%eax /* &edx */ + pushl %eax + addl $4,%eax /* &ecx */ + pushl %eax + addl $4,%eax /* &ebx */ + pushl %eax + addl $4,%eax /* &eax */ + pushl %eax + pushl (%eax) /* eax */ + + call VG_(helperc_CPUID) + addl $20,%esp + + popf + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + popl %eax + popl %ebp + ret + +/* Fetch the FPU status register. + On entry: + dummy, replaced by result + RA <- %esp +*/ +.global VG_(helper_fstsw_AX) +VG_(helper_fstsw_AX): + pushl %eax + pushl %esi + movl VGOFF_(m_ssestate), %esi + + pushfl + cmpb $0, VG_(have_ssestate) + jz aa1nosse + fxrstor (%ebp, %esi, 4) + jmp aa1merge +aa1nosse: + frstor (%ebp, %esi, 4) +aa1merge: + popfl + + fstsw %ax + popl %esi + movw %ax, 8(%esp) + popl %eax + ret + + +/* Copy %ah into %eflags. + On entry: + value of %eax + RA <- %esp +*/ +.global VG_(helper_SAHF) +VG_(helper_SAHF): + pushl %eax + movl 8(%esp), %eax + sahf + popl %eax + ret + +/* Copy %eflags into %ah. + On entry: + value of %eax + RA <- %esp +*/ +.global VG_(helper_LAHF) +VG_(helper_LAHF): + pushl %eax + movl 8(%esp), %eax + lahf + movl %eax, 8(%esp) + popl %eax + ret + + +/* Do %al = DAS(%al). Note that the passed param has %AL as the least + significant 8 bits, since it was generated with GETB %AL, + some-temp. Fortunately %al is the least significant 8 bits of + %eax anyway, which is why it's safe to work with %eax as a + whole. + + On entry: + value of %eax + RA <- %esp +*/ +.global VG_(helper_DAS) +VG_(helper_DAS): + pushl %eax + movl 8(%esp), %eax + das + movl %eax, 8(%esp) + popl %eax + ret + + +/* Similarly, do %al = DAA(%al). */ +.global VG_(helper_DAA) +VG_(helper_DAA): + pushl %eax + movl 8(%esp), %eax + daa + movl %eax, 8(%esp) + popl %eax + ret + + +/* Similarly, do %ax = AAS(%ax). */ +.global VG_(helper_AAS) +VG_(helper_AAS): + pushl %eax + movl 8(%esp), %eax + aas + movl %eax, 8(%esp) + popl %eax + ret + + +/* Similarly, do %ax = AAA(%ax). */ +.global VG_(helper_AAA) +VG_(helper_AAA): + pushl %eax + movl 8(%esp), %eax + aaa + movl %eax, 8(%esp) + popl %eax + ret + + +/* Similarly, do %ax = AAD(%ax). */ +.global VG_(helper_AAD) +VG_(helper_AAD): + pushl %eax + movl 8(%esp), %eax + aad + movl %eax, 8(%esp) + popl %eax + ret + + +/* Similarly, do %ax = AAM(%ax). */ +.global VG_(helper_AAM) +VG_(helper_AAM): + pushl %eax + movl 8(%esp), %eax + aam + movl %eax, 8(%esp) + popl %eax + ret + + +/* Bit scan forwards/reverse. Sets flags (??). + On entry: + value, replaced by result + RA <- %esp +*/ +.global VG_(helper_bsrw) +VG_(helper_bsrw): + pushl %eax + movw 12(%esp), %ax + bsrw 8(%esp), %ax + movw %ax, 12(%esp) + popl %eax + ret + +.global VG_(helper_bsrl) +VG_(helper_bsrl): + pushl %eax + movl 12(%esp), %eax + bsrl 8(%esp), %eax + movl %eax, 12(%esp) + popl %eax + ret + +.global VG_(helper_bsfw) +VG_(helper_bsfw): + pushl %eax + movw 12(%esp), %ax + bsfw 8(%esp), %ax + movw %ax, 12(%esp) + popl %eax + ret + +.global VG_(helper_bsfl) +VG_(helper_bsfl): + pushl %eax + movl 12(%esp), %eax + bsfl 8(%esp), %eax + movl %eax, 12(%esp) + popl %eax + ret + + +/* 32-bit double-length shift left/right. + On entry: + amount + src + dst + RA <- %esp +*/ +.global VG_(helper_shldl) +VG_(helper_shldl): + pushl %eax + pushl %ebx + pushl %ecx + + movb 24(%esp), %cl + movl 20(%esp), %ebx + movl 16(%esp), %eax + shldl %cl, %ebx, %eax + movl %eax, 16(%esp) + + popl %ecx + popl %ebx + popl %eax + ret + +.global VG_(helper_shldw) +VG_(helper_shldw): + pushl %eax + pushl %ebx + pushl %ecx + + movb 24(%esp), %cl + movw 20(%esp), %bx + movw 16(%esp), %ax + shldw %cl, %bx, %ax + movw %ax, 16(%esp) + + popl %ecx + popl %ebx + popl %eax + ret + +.global VG_(helper_shrdl) +VG_(helper_shrdl): + pushl %eax + pushl %ebx + pushl %ecx + + movb 24(%esp), %cl + movl 20(%esp), %ebx + movl 16(%esp), %eax + shrdl %cl, %ebx, %eax + movl %eax, 16(%esp) + + popl %ecx + popl %ebx + popl %eax + ret + +.global VG_(helper_shrdw) +VG_(helper_shrdw): + pushl %eax + pushl %ebx + pushl %ecx + + movb 24(%esp), %cl + movw 20(%esp), %bx + movw 16(%esp), %ax + shrdw %cl, %bx, %ax + movw %ax, 16(%esp) + + popl %ecx + popl %ebx + popl %eax + ret + + +/* Get the direction flag, and return either 1 or -1. */ +.global VG_(helper_get_dirflag) +VG_(helper_get_dirflag): + pushl %eax + + movl VGOFF_(m_dflag), %eax + movl (%ebp, %eax, 4), %eax + movl %eax, 8(%esp) + + popl %eax + ret + +/* Clear/set the direction flag. */ +.global VG_(helper_CLD) +VG_(helper_CLD): + pushl %eax + + movl VGOFF_(m_dflag), %eax + movl $1, (%ebp, %eax, 4) + + popl %eax + ret + +.global VG_(helper_STD) +VG_(helper_STD): + pushl %eax + + movl VGOFF_(m_dflag), %eax + movl $-1, (%ebp, %eax, 4) + + popl %eax + ret + +/* Clear/set/complement the carry flag. */ +.global VG_(helper_CLC) +VG_(helper_CLC): + clc + ret + +.global VG_(helper_STC) +VG_(helper_STC): + stc + ret + +.global VG_(helper_CMC) +VG_(helper_CMC): + cmc + ret + +/* Signed 32-to-64 multiply. */ +.globl VG_(helper_imul_32_64) +VG_(helper_imul_32_64): + pushl %eax + pushl %edx + movl 16(%esp), %eax + imull 12(%esp) + movl %eax, 16(%esp) + movl %edx, 12(%esp) + popl %edx + popl %eax + ret + +/* Signed 16-to-32 multiply. */ +.globl VG_(helper_imul_16_32) +VG_(helper_imul_16_32): + pushl %eax + pushl %edx + movw 16(%esp), %ax + imulw 12(%esp) + movw %ax, 16(%esp) + movw %dx, 12(%esp) + popl %edx + popl %eax + ret + +/* Signed 8-to-16 multiply. */ +.globl VG_(helper_imul_8_16) +VG_(helper_imul_8_16): + pushl %eax + pushl %edx + movb 16(%esp), %al + imulb 12(%esp) + movw %ax, 16(%esp) + popl %edx + popl %eax + ret + + + + + + +/* Unsigned 32-to-64 multiply. */ +.globl VG_(helper_mul_32_64) +VG_(helper_mul_32_64): + pushl %eax + pushl %edx + movl 16(%esp), %eax + mull 12(%esp) + movl %eax, 16(%esp) + movl %edx, 12(%esp) + popl %edx + popl %eax + ret + +/* Unsigned 16-to-32 multiply. */ +.globl VG_(helper_mul_16_32) +VG_(helper_mul_16_32): + pushl %eax + pushl %edx + movw 16(%esp), %ax + mulw 12(%esp) + movw %ax, 16(%esp) + movw %dx, 12(%esp) + popl %edx + popl %eax + ret + +/* Unsigned 8-to-16 multiply. */ +.globl VG_(helper_mul_8_16) +VG_(helper_mul_8_16): + pushl %eax + pushl %edx + movb 16(%esp), %al + mulb 12(%esp) + movw %ax, 16(%esp) + popl %edx + popl %eax + ret + + + + +/* Unsigned 64-into-32 divide. */ +.globl VG_(helper_div_64_32) +VG_(helper_div_64_32): + pushl %eax + pushl %edx + movl 16(%esp),%eax + movl 12(%esp),%edx + divl 20(%esp) + movl %eax,16(%esp) + movl %edx,12(%esp) + popl %edx + popl %eax + ret + +/* Signed 64-into-32 divide. */ +.globl VG_(helper_idiv_64_32) +VG_(helper_idiv_64_32): + pushl %eax + pushl %edx + movl 16(%esp),%eax + movl 12(%esp),%edx + idivl 20(%esp) + movl %eax,16(%esp) + movl %edx,12(%esp) + popl %edx + popl %eax + ret + +/* Unsigned 32-into-16 divide. */ +.globl VG_(helper_div_32_16) +VG_(helper_div_32_16): + pushl %eax + pushl %edx + movw 16(%esp),%ax + movw 12(%esp),%dx + divw 20(%esp) + movw %ax,16(%esp) + movw %dx,12(%esp) + popl %edx + popl %eax + ret + +/* Signed 32-into-16 divide. */ +.globl VG_(helper_idiv_32_16) +VG_(helper_idiv_32_16): + pushl %eax + pushl %edx + movw 16(%esp),%ax + movw 12(%esp),%dx + idivw 20(%esp) + movw %ax,16(%esp) + movw %dx,12(%esp) + popl %edx + popl %eax + ret + +/* Unsigned 16-into-8 divide. */ +.globl VG_(helper_div_16_8) +VG_(helper_div_16_8): + pushl %eax + movw 12(%esp),%ax + divb 16(%esp) + movb %ah,12(%esp) + movb %al,8(%esp) + popl %eax + ret + +/* Signed 16-into-8 divide. */ +.globl VG_(helper_idiv_16_8) +VG_(helper_idiv_16_8): + pushl %eax + movw 12(%esp),%ax + idivb 16(%esp) + movb %ah,12(%esp) + movb %al,8(%esp) + popl %eax + ret + + +/* Eight byte compare and exchange. */ +.globl VG_(helper_cmpxchg8b) +VG_(helper_cmpxchg8b): + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + movl 20(%esp), %eax + movl 24(%esp), %edx + movl 28(%esp), %ebx + movl 32(%esp), %ecx + cmpxchg8b 36(%esp) + movl %eax, 20(%esp) + movl %edx, 24(%esp) + movl %ebx, 28(%esp) + movl %ecx, 32(%esp) + popl %edx + popl %ecx + popl %ebx + popl %eax + ret + + +/* Undefined instruction (generates SIGILL) */ +.globl VG_(helper_undefined_instruction) +VG_(helper_undefined_instruction): +1: ud2 + jmp 1b + +/* Let the linker know we don't need an executable stack */ +.section .note.GNU-stack,"",@progbits + +##--------------------------------------------------------------------## +##--- end vg_helpers.S ---## +##--------------------------------------------------------------------## diff --git a/VEX/head20041019/coregrind/vg_instrument.c b/VEX/head20041019/coregrind/vg_instrument.c new file mode 100644 index 0000000000..8df8c78914 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_instrument.c @@ -0,0 +1,258 @@ + +/*--------------------------------------------------------------------*/ +/*--- Higher-level UCode sequence builders ---*/ +/*--- vg_instrument.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* We only import tool.h here, because this file only provides functions + for doing things that could be done directly by the tool -- it's just to + make tools' lives easier, rather than let them do something they + couldn't otherwise do. */ +#include "tool.h" + + +void VG_(lit_to_reg)(UCodeBlock* cb, UInt lit, UInt t) +{ + uInstr2 (cb, MOV, 4, Literal, 0, TempReg, t); + uLiteral(cb, lit); +} + +UInt VG_(lit_to_newreg)(UCodeBlock* cb, UInt lit) +{ + UInt t = newTemp(cb); + uInstr2 (cb, MOV, 4, Literal, 0, TempReg, t); + uLiteral(cb, lit); + return t; +} + +// f() +void VG_(ccall_0_0)(UCodeBlock* cb, Addr f) +{ + uInstr0(cb, CCALL, 0); + uCCall(cb, f, 0, 0, /*retval*/False); +} + +// f(reg) +void VG_(ccall_R_0)(UCodeBlock* cb, Addr f, UInt t1, UInt regparms_n) +{ + sk_assert(regparms_n <= 1); + uInstr1(cb, CCALL, 0, TempReg, t1); + uCCall(cb, f, 1, regparms_n, /*retval*/False); +} + +// f(lit) +void VG_(ccall_L_0)(UCodeBlock* cb, Addr f, UInt lit1, UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit1); + VG_(ccall_R_0)(cb, f, t1, regparms_n); +} + +// reg = f(reg) +void VG_(ccall_R_R)(UCodeBlock* cb, Addr f, UInt t1, UInt t_ret, + UInt regparms_n) +{ + sk_assert(regparms_n <= 1); + sk_assert(t1 < VG_(get_num_temps)(cb)); // help catch lits accidentally passed in + uInstr3(cb, CCALL, 0, TempReg, t1, NoValue, 0, TempReg, t_ret); + uCCall(cb, f, 1, regparms_n, /*retval*/True); +} + +// reg = f(lit) +void VG_(ccall_L_R)(UCodeBlock* cb, Addr f, UInt lit1, UInt t_ret, + UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit1); + VG_(ccall_R_R)(cb, f, t1, t_ret, regparms_n); +} + +// f(reg, reg) +void VG_(ccall_RR_0)(UCodeBlock* cb, Addr f, UInt t1, UInt t2, UInt regparms_n) +{ + sk_assert(regparms_n <= 2); + sk_assert(t1 < VG_(get_num_temps)(cb)); + sk_assert(t2 < VG_(get_num_temps)(cb)); + uInstr2(cb, CCALL, 0, TempReg, t1, TempReg, t2); + uCCall(cb, f, 2, regparms_n, /*retval*/False); +} + +// f(reg, lit) +void VG_(ccall_RL_0)(UCodeBlock* cb, Addr f, UInt t1, UInt lit2, + UInt regparms_n) +{ + UInt t2 = VG_(lit_to_newreg)(cb, lit2); + VG_(ccall_RR_0)(cb, f, t1, t2, regparms_n); +} + +// f(lit, reg) +void VG_(ccall_LR_0)(UCodeBlock* cb, Addr f, UInt lit1, UInt t2, + UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit1); + VG_(ccall_RR_0)(cb, f, t1, t2, regparms_n); +} + +// f(lit, lit) +void VG_(ccall_LL_0)(UCodeBlock* cb, Addr f, UInt lit1, UInt lit2, + UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit1); + UInt t2 = VG_(lit_to_newreg)(cb, lit2); + VG_(ccall_RR_0)(cb, f, t1, t2, regparms_n); +} + +// reg = f(reg, reg) +void VG_(ccall_RR_R)(UCodeBlock* cb, Addr f, UInt t1, UInt t2, UInt t_ret, + UInt regparms_n) +{ + sk_assert(regparms_n <= 2); + sk_assert(t1 < VG_(get_num_temps)(cb)); + sk_assert(t2 < VG_(get_num_temps)(cb)); + uInstr3(cb, CCALL, 0, TempReg, t1, TempReg, t2, TempReg, t_ret); + uCCall(cb, f, 2, regparms_n, /*retval*/True); +} + +// reg = f(reg, lit) +void VG_(ccall_RL_R)(UCodeBlock* cb, Addr f, UInt t1, UInt lit2, UInt t_ret, + UInt regparms_n) +{ + UInt t2 = VG_(lit_to_newreg)(cb, lit2); + VG_(ccall_RR_R)(cb, f, t1, t2, t_ret, regparms_n); +} + +// reg = f(lit, reg) +void VG_(ccall_LR_R)(UCodeBlock* cb, Addr f, UInt lit1, UInt t2, UInt t_ret, + UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit1); + VG_(ccall_RR_R)(cb, f, t1, t2, t_ret, regparms_n); +} + +// reg = f(lit, lit) +void VG_(ccall_LL_R)(UCodeBlock* cb, Addr f, UInt lit1, UInt lit2, UInt t_ret, + UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit2); + UInt t2 = VG_(lit_to_newreg)(cb, lit2); + VG_(ccall_RR_R)(cb, f, t1, t2, t_ret, regparms_n); +} + +// f(reg, reg, reg) +void VG_(ccall_RRR_0)(UCodeBlock* cb, Addr f, UInt t1, UInt t2, + UInt t3, UInt regparms_n) +{ + sk_assert(regparms_n <= 3); + sk_assert(t1 < VG_(get_num_temps)(cb)); + sk_assert(t2 < VG_(get_num_temps)(cb)); + sk_assert(t3 < VG_(get_num_temps)(cb)); + uInstr3(cb, CCALL, 0, TempReg, t1, TempReg, t2, TempReg, t3); + uCCall(cb, f, 3, regparms_n, /*retval*/False); +} + +// f(reg, lit, lit) +void VG_(ccall_RLL_0)(UCodeBlock* cb, Addr f, UInt t1, UInt lit2, + UInt lit3, UInt regparms_n) +{ + UInt t2 = VG_(lit_to_newreg)(cb, lit2); + UInt t3 = VG_(lit_to_newreg)(cb, lit3); + VG_(ccall_RRR_0)(cb, f, t1, t2, t3, regparms_n); +} + +// f(lit, reg, reg) +void VG_(ccall_LRR_0)(UCodeBlock* cb, Addr f, UInt lit1, UInt t2, + UInt t3, UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit1); + VG_(ccall_RRR_0)(cb, f, t1, t2, t3, regparms_n); +} + +// f(lit, lit, reg) +void VG_(ccall_LLR_0)(UCodeBlock* cb, Addr f, UInt lit1, UInt lit2, + UInt t3, UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit1); + UInt t2 = VG_(lit_to_newreg)(cb, lit2); + VG_(ccall_RRR_0)(cb, f, t1, t2, t3, regparms_n); +} + +// f(lit, lit, lit) +void VG_(ccall_LLL_0)(UCodeBlock* cb, Addr f, UInt lit1, UInt lit2, + UInt lit3, UInt regparms_n) +{ + UInt t1 = VG_(lit_to_newreg)(cb, lit1); + UInt t2 = VG_(lit_to_newreg)(cb, lit2); + UInt t3 = VG_(lit_to_newreg)(cb, lit3); + VG_(ccall_RRR_0)(cb, f, t1, t2, t3, regparms_n); +} + +void VG_(reg_to_globvar)(UCodeBlock* cb, UInt t, UInt* globvar_ptr) +{ + Int t_gv = VG_(lit_to_newreg)(cb, (UInt)globvar_ptr); + uInstr2(cb, STORE, 4, TempReg, t, TempReg, t_gv); +} + +void VG_(lit_to_globvar)(UCodeBlock* cb, UInt lit, UInt* globvar_ptr) +{ + Int t_lit = VG_(lit_to_newreg)(cb, lit); + VG_(reg_to_globvar)(cb, t_lit, globvar_ptr); +} + +/*-------------------------------------------------------------------- + Old versions of these functions, for backwards compatibility + --------------------------------------------------------------------*/ + +void VG_(call_helper_0_0)(UCodeBlock* cb, Addr f) +{ + VG_(ccall_0_0)(cb, f); +} + +void VG_(call_helper_1_0)(UCodeBlock* cb, Addr f, UInt arg1, UInt regparms_n) +{ + VG_(ccall_L_0)(cb, f, arg1, regparms_n); +} + +void VG_(call_helper_2_0)(UCodeBlock* cb, Addr f, UInt arg1, UInt arg2, + UInt regparms_n) +{ + VG_(ccall_LL_0)(cb, f, arg1, arg2, regparms_n); +} + +void VG_(set_global_var)(UCodeBlock* cb, Addr globvar_ptr, UInt val) +{ + VG_(lit_to_globvar)(cb, val, (UInt*)globvar_ptr); +} + +void VG_(set_global_var_tempreg)(UCodeBlock* cb, Addr globvar_ptr, UInt t_val) +{ + VG_(reg_to_globvar)(cb, t_val, (UInt*)globvar_ptr); +} + +/*--------------------------------------------------------------------*/ +/*--- end vg_instrument.c ---*/ +/*--------------------------------------------------------------------*/ + + diff --git a/VEX/head20041019/coregrind/vg_intercept.c.base b/VEX/head20041019/coregrind/vg_intercept.c.base new file mode 100644 index 0000000000..b76d3b9a08 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_intercept.c.base @@ -0,0 +1,128 @@ + +/*--------------------------------------------------------------------*/ +/*--- Intercepts for various libc functions we want to capture ---*/ +/*--- (mostly for threading purposes). vg_intercept.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + + +/* --------------------------------------------------------------------- + ALL THE CODE IN THIS FILE RUNS ON THE SIMULATED CPU. It is + intended for various reasons as drop-in replacements for libc + functions. These functions are not called directly - they're the + targets of code redirection. They're named weirdly so that the + intercept code can find them when the shared object is initially + loaded. + ------------------------------------------------------------------ */ + +/* + The gory details: Symbols in this file are mangled by a preprocessor + to produce a special symbol name. All symbols that need this handling + should be passed to a special VG_INTERCEPT macro. This macro takes + two arguments: a library name and a function name. These specify the + function and the library that contains it that we need to intercept. + For example: + + int VG_INTERCEPT(soname:libc.so.6, raise)(int sig) { ... } + + This example says that the "raise" function in the shared object + libc.so.6 should be intercepted and redirected to the following piece + of code. + + Internally, what's happening here is that this intercept gets turned + into a special magic symbol name, with the ':' and '.' parts replaced + by escapes, and a special prefix stuck on front. When we slurp in + an object file, we scan the symbol table for the magic prefixes, + demangle any symbols found and set up the intercepts that way. + + This is the safest way to do this, because we're not relying on + intercepts being set up by code that may be called after other code + that need the intercepts has had a chance to run. + */ + +#include "valgrind.h" +#include "core.h" +#include +#include + +int VG_INTERCEPT(soname:libc.so.6, raise)(int sig) +{ + return kill(getpid(), sig); +} + +int VG_INTERCEPT(soname:libc.so.6, __libc_raise)(int) + __attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, raise)), + visibility("protected"))); + +int VG_INTERCEPT(soname:libc.so.6, __GI_raise)(int) + __attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, raise)), + visibility("protected"))); + +int VG_INTERCEPT(soname:libc.so.6, __raise)(int) + __attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, raise)), + visibility("protected"))); + +/* Don't alias, so there's no chance that "gsignal" will appear in a + message instead of "raise" */ +int VG_INTERCEPT(soname:libc.so.6, gsignal)(int sig) +{ + return VG_INTERCEPT(soname:libc.so.6, raise)(sig); +} + +int VG_INTERCEPT(soname:libc.so.6, __libc_gsignal)(int) + __attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, gsignal)), + visibility("protected"))); + +int VG_INTERCEPT(soname:libc.so.6, __GI_gsignal)(int) + __attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, gsignal)), + visibility("protected"))); + +int VG_INTERCEPT(soname:libc.so.6, __gsignal)(int) + __attribute__((alias(VG_INTERCEPT_ALIAS(soname:libc.so.6, gsignal)), + visibility("protected"))); + +/* --------------------------------------------------------------------- + Hook for running __libc_freeres once the program exits. + ------------------------------------------------------------------ */ + +void VG_WRAPPER(freeres)( void ) +{ + int res; +#ifndef __UCLIBC__ + extern void __libc_freeres(void); + __libc_freeres(); +#endif + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__LIBC_FREERES_DONE, 0, 0, 0, 0); + /*NOTREACHED*/ + *(int *)0 = 'x'; +} + +/*--------------------------------------------------------------------*/ +/*--- end vg_intercept.c ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/VEX/head20041019/coregrind/vg_libpthread.c b/VEX/head20041019/coregrind/vg_libpthread.c new file mode 100644 index 0000000000..cb01723164 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_libpthread.c @@ -0,0 +1,3506 @@ + +/*--------------------------------------------------------------------*/ +/*--- A replacement for the standard libpthread.so. ---*/ +/*--- vg_libpthread.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* ALL THIS CODE RUNS ON THE SIMULATED CPU. + + This is a replacement for the standard libpthread.so. It is loaded + as part of the client's image (if required) and directs pthread + calls through to Valgrind's request mechanism. + + A couple of caveats. + + 1. Since it's a binary-compatible replacement for an existing library, + we must take care to used exactly the same data layouts, etc, as + the standard pthread.so does. + + 2. Since this runs as part of the client, there are no specific + restrictions on what headers etc we can include, so long as + this libpthread.so does not end up having dependencies on .so's + which the real one doesn't. + + Later ... it appears we cannot call file-related stuff in libc here, + perhaps fair enough. Be careful what you call from here. Even exit() + doesn't work (gives infinite recursion and then stack overflow); hence + myexit(). Also fprintf doesn't seem safe. +*/ + +#include "valgrind.h" /* For the request-passing mechanism */ +#include "core.h" /* For the VG_USERREQ__* constants */ + +#define __USE_UNIX98 +#include +#include +#undef __USE_UNIX98 + +#define __USE_GNU +#include +#undef __USE_GNU + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +# define strong_alias(name, aliasname) \ + extern __typeof (name) aliasname __attribute__ ((alias (#name))); + +# define weak_alias(name, aliasname) \ + extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))); + + +/* --------------------------------------------------------------------- + Our own definition of types that vary between LinuxThreads and NPTL. + ------------------------------------------------------------------ */ + +/* Moving from LinuxThreads to NPTL, several crucial types (eg. + pthread_mutex_t, pthread_mutexattr_t, etc) in pthreadtypes.h were changed + in binary-compatible, but source-incompatible, ways. We can similarly + use any layout we want, so long as it's binary-compatible. However, we + can no longer use the LinuxThreads types, because they won't work on NPTL + systems. Thus, we have to introduce a layer of indirection, and define + our own versions of these types (vg_pthread_mutex_t, etc). NPTL does + pretty much the same thing, and it keeps many of its internal types + secret. + + We can layout our types however we want, as long as we put the small + number of fields in the right place for binary compatibility (eg. + mutex->kind). To make life easy, our versions have the exact same layout + as the LinuxThreads ones; only the type names and field names are + different (they differ only by include "vg" at the start). + + In our implementation of the pthread operations (pthread_mutex_lock(), + pthread_mutexattr_settype(), etc) we always cast the standard pthread + types to our own types, (eg. pthread_mutex_t --> vg_pthread_mutex_t), + before working with them. + + Note that we have various mutexes (and condvars) in this file that have the + type pthread_mutex_t (and pthread_cond_t). That is fine, because they + are always only handled by calling the standard pthread functions (eg. + pthread_mutex_lock()) on them. Phew. + + WARNING: as a result of all this, we should *never* access these standard + pthread types as is; they *must* be converted to the vg_pthread_foo_t + equivalent. It would be nice if this was enforced... (but compilation + on NPTL-only systems should fail if this rule isn't followed...?) +*/ + +#include // for 'struct __sched_param' + +typedef struct __vg_pthread_attr_s +{ + int __vg_detachstate; + int __vg_schedpolicy; + struct __sched_param __vg_schedparam; + int __vg_inheritsched; + int __vg_scope; + size_t __vg_guardsize; + int __vg_stackaddr_set; + void *__vg_stackaddr; + size_t __vg_stacksize; +} vg_pthread_attr_t; + +typedef struct +{ + int __vg_mutexkind; +} vg_pthread_mutexattr_t; + +typedef struct _vg_pthread_rwlock_t +{ + struct _vg_pthread_fastlock __vg_rw_lock; /* Lock to guarantee mutual exclusion */ + int __vg_rw_readers; /* Number of readers */ + /*_pthread_descr*/ void* __vg_rw_writer; /* Identity of writer, or NULL if none */ + /*_pthread_descr*/ void* __vg_rw_read_waiting; /* Threads waiting for reading */ + /*_pthread_descr*/ void* __vg_rw_write_waiting; /* Threads waiting for writing */ + int __vg_rw_kind; /* Reader/Writer preference selection */ + int __vg_rw_pshared; /* Shared between processes or not */ +} vg_pthread_rwlock_t; + +typedef struct +{ + int __vg_lockkind; + int __vg_pshared; +} vg_pthread_rwlockattr_t; + +/* Converting pthread types to vg_pthread types. We always check that the + passed-in type is as big as ours, for safety. We also zero the pointer + to the original struct, to ensure we don't accidentally use it again. */ + +#define CONVERT(foo, x, vg_x) \ + my_assert(sizeof(*x) >= sizeof(vg_pthread_##foo##_t)); \ + vg_x = (vg_pthread_##foo##_t*)x; \ + x = 0; // ensure we don't accidentally use x again! + + +/* --------------------------------------------------------------------- + Our own definition of types that only exist in NPTL. + ------------------------------------------------------------------ */ + +#ifndef HAVE___PTHREAD_UNWIND_BUF_T + +typedef struct +{ + struct + { + jmp_buf __cancel_jmp_buf; + int __mask_was_saved; + } __cancel_jmp_buf[1]; + void *__pad[4]; +} __pthread_unwind_buf_t __attribute__ ((__aligned__)); + +#endif + +/* --------------------------------------------------------------------- + Forwardses. + ------------------------------------------------------------------ */ + +#define WEAK __attribute__((weak)) + +static +__inline__ +int is_kerror ( int res ) +{ + if (res >= -4095 && res <= -1) + return 1; + else + return 0; +} + + +#ifdef GLIBC_2_3 + /* kludge by JRS (not from glibc) ... */ + typedef void* __locale_t; + + /* Copied from locale/locale.h in glibc-2.2.93 sources */ + /* This value can be passed to `uselocale' and may be returned by + it. Passing this value to any other function has undefined + behavior. */ +# define LC_GLOBAL_LOCALE ((__locale_t) -1L) + extern __locale_t __uselocale ( __locale_t ); +#endif + +static void +init_global_thread_specific_state ( void ); + +static void +init_thread_specific_state ( void ); + +static void +set_ret_val ( void* ); +static void * +get_ret_val ( void ); + +/* --------------------------------------------------------------------- + Helpers. We have to be pretty self-sufficient. + ------------------------------------------------------------------ */ + +/* Number of times any given error message is printed. */ +#define N_MOANS 3 + +/* Extract from Valgrind the value of VG_(clo_trace_pthread_level). + Returns 0 (none) if not running on Valgrind. */ +static +int get_pt_trace_level ( void ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__GET_PTHREAD_TRACE_LEVEL, + 0, 0, 0, 0); + return res; +} + +/* Don't do anything if we're not under Valgrind */ +static __inline__ +void ensure_valgrind ( char* caller ) +{ + if (!RUNNING_ON_VALGRIND) { + const char msg[] = "Warning: this libpthread.so should only be run with Valgrind\n"; + VG_(do_syscall)(__NR_write, 2, msg, sizeof(msg)-1); + VG_(do_syscall)(__NR_exit, 1); + } +} + +/* While we're at it ... hook our own startup function into this + game. */ + +static +__attribute__((noreturn)) +void barf ( const char* str ) +{ + char buf[1000]; + strcpy(buf, "\nvalgrind's libpthread.so: "); + strcat(buf, str); + strcat(buf, "\nPlease report this bug at: "); + strcat(buf, VG_BUGS_TO); + strcat(buf, "\n\n"); + VALGRIND_INTERNAL_PRINTF(buf); + _exit(1); + /* We have to persuade gcc into believing this doesn't return. */ + while (1) { }; +} + + +static void cat_n_send ( char* s1, char* s2, char* s3 ) +{ + char buf[1000]; + if (get_pt_trace_level() >= 0) { + snprintf(buf, sizeof(buf), "%s%s%s", s1, s2, s3); + buf[sizeof(buf)-1] = '\0'; + VALGRIND_INTERNAL_PRINTF(buf); + } +} + +static void oh_dear ( char* fn, char* aux, char* s ) +{ + cat_n_send ( "warning: Valgrind's ", fn, s ); + if (NULL != aux) + cat_n_send ( " ", aux, "" ); + cat_n_send ( " your program may misbehave as a result", "", "" ); +} + +static void ignored ( char* fn, char* aux ) +{ + oh_dear ( fn, aux, " does nothing" ); +} + +static void kludged ( char* fn, char* aux ) +{ + oh_dear ( fn, aux, " is incomplete" ); +} + + +__attribute__((noreturn)) +void vgPlain_unimp ( char* fn ) +{ + cat_n_send ( "valgrind's libpthread.so: UNIMPLEMENTED FUNCTION: ", fn, "" ); + barf("unimplemented function"); +} + + +static +void my_assert_fail ( const Char* expr, const Char* file, Int line, const Char* fn ) +{ + char buf[1000]; + static Bool entered = False; + if (entered) + _exit(2); + entered = True; + sprintf(buf, "\n%s: %s:%d (%s): Assertion `%s' failed.\n", + "valgrind", file, line, fn, expr ); + cat_n_send ( "", buf, "" ); + sprintf(buf, "Please report this bug at: %s\n\n", VG_BUGS_TO); + cat_n_send ( "", buf, "" ); + _exit(1); +} + +#define MY__STRING(__str) #__str + +#define my_assert(expr) \ + ((void) ((expr) ? 0 : \ + (my_assert_fail (MY__STRING(expr), \ + __FILE__, __LINE__, \ + __PRETTY_FUNCTION__), 0))) + +static +void my_free ( void* ptr ) +{ +#if 0 + int res; + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__FREE, ptr, 0, 0, 0); + my_assert(res == 0); +#else + free(ptr); +#endif +} + + +static +void* my_malloc ( int nbytes ) +{ + void* res; +#if 0 + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__MALLOC, nbytes, 0, 0, 0); +#else + res = malloc(nbytes); +#endif + my_assert(res != (void*)0); + return res; +} + + + +/* --------------------------------------------------------------------- + Pass pthread_ calls to Valgrind's request mechanism. + ------------------------------------------------------------------ */ + + +/* --------------------------------------------------- + Ummm .. + ------------------------------------------------ */ + +static +void pthread_error ( const char* msg ) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 0, + VG_USERREQ__PTHREAD_ERROR, + msg, 0, 0, 0); +} + + +/* --------------------------------------------------- + Here so it can be inlined without complaint. + ------------------------------------------------ */ + +__inline__ +pthread_t pthread_self(void) +{ + int tid; + ensure_valgrind("pthread_self"); + VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + if (tid < 1 || tid >= VG_N_THREADS) + barf("pthread_self: invalid ThreadId"); + return tid; +} + + +/* --------------------------------------------------- + THREAD ATTRIBUTES + ------------------------------------------------ */ + +int pthread_attr_init(pthread_attr_t *attr) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + + /* Just initialise the fields which we might look at. */ + vg_attr->__vg_detachstate = PTHREAD_CREATE_JOINABLE; + /* Linuxthreads sets this field to the value __getpagesize(), so I + guess the following is OK. */ + vg_attr->__vg_guardsize = VKI_BYTES_PER_PAGE; + /* No special stack yet. */ + vg_attr->__vg_stackaddr = 0; + vg_attr->__vg_stacksize = VG_PTHREAD_STACK_SIZE; + return 0; +} + +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + + if (detachstate != PTHREAD_CREATE_JOINABLE + && detachstate != PTHREAD_CREATE_DETACHED) { + pthread_error("pthread_attr_setdetachstate: " + "detachstate is invalid"); + return EINVAL; + } + vg_attr->__vg_detachstate = detachstate; + return 0; +} + +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + *detachstate = vg_attr->__vg_detachstate; + return 0; +} + +int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit) +{ + static int moans = N_MOANS; + if (moans-- > 0) + kludged("pthread_attr_getinheritsched", NULL); + *inherit = PTHREAD_EXPLICIT_SCHED; + return 0; +} + +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit) +{ + static int moans = N_MOANS; + if (moans-- > 0) + ignored("pthread_attr_setinheritsched", NULL); + return 0; +} + +WEAK +int pthread_attr_setstacksize (pthread_attr_t *attr, + size_t stacksize) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + vg_attr->__vg_stacksize = stacksize; + return 0; +} + + +/* This is completely bogus. */ +int pthread_attr_getschedparam(const pthread_attr_t *attr, + struct sched_param *param) +{ + static int moans = N_MOANS; + if (moans-- > 0) + kludged("pthread_attr_getschedparam", NULL); +# ifdef HAVE_SCHED_PRIORITY + if (param) param->sched_priority = 0; /* who knows */ +# else + if (param) param->__sched_priority = 0; /* who knows */ +# endif + return 0; +} + +int pthread_attr_setschedparam(pthread_attr_t *attr, + const struct sched_param *param) +{ + static int moans = N_MOANS; + if (moans-- > 0) + ignored("pthread_attr_setschedparam", "(scheduling not changeable)"); + return 0; +} + +int pthread_attr_destroy(pthread_attr_t *attr) +{ + static int moans = N_MOANS; + if (moans-- > 0) + ignored("pthread_attr_destroy", NULL); + return 0; +} + +/* These are no-ops, as with LinuxThreads. */ +int pthread_attr_setscope ( pthread_attr_t *attr, int scope ) +{ + ensure_valgrind("pthread_attr_setscope"); + if (scope == PTHREAD_SCOPE_SYSTEM) + return 0; + pthread_error("pthread_attr_setscope: " + "invalid or unsupported scope"); + if (scope == PTHREAD_SCOPE_PROCESS) + return ENOTSUP; + return EINVAL; +} + +int pthread_attr_getscope ( const pthread_attr_t *attr, int *scope ) +{ + ensure_valgrind("pthread_attr_setscope"); + if (scope) + *scope = PTHREAD_SCOPE_SYSTEM; + return 0; +} + + +/* Pretty bogus. Avoid if possible. */ +int pthread_getattr_np (pthread_t thread, pthread_attr_t *attr) +{ + StackInfo si; + int res; + int detached; + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + + ensure_valgrind("pthread_getattr_np"); + kludged("pthread_getattr_np", NULL); + vg_attr->__vg_detachstate = PTHREAD_CREATE_JOINABLE; + vg_attr->__vg_schedpolicy = SCHED_OTHER; + vg_attr->__vg_schedparam.sched_priority = 0; + vg_attr->__vg_inheritsched = PTHREAD_EXPLICIT_SCHED; + vg_attr->__vg_scope = PTHREAD_SCOPE_SYSTEM; + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__GET_STACK_INFO, + thread, &si, 0, 0 ); + vg_attr->__vg_guardsize = si.guardsize; + vg_attr->__vg_stackaddr = (void *)si.base; + vg_attr->__vg_stackaddr_set = 0; + vg_attr->__vg_stacksize = si.size; + VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */, + VG_USERREQ__SET_OR_GET_DETACH, + 2 /* get */, thread, 0, 0); + my_assert(detached == 0 || detached == 1); + if (detached) + vg_attr->__vg_detachstate = PTHREAD_CREATE_DETACHED; + return 0; +} + + +WEAK +int pthread_attr_getstack ( const pthread_attr_t * attr, + void ** stackaddr, + size_t *stacksize ) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + ensure_valgrind("pthread_attr_getstack"); + if (stackaddr) + *stackaddr = vg_attr->__vg_stackaddr; + if (stacksize) + *stacksize = vg_attr->__vg_stacksize; + return 0; +} + +WEAK +int pthread_attr_getstackaddr ( const pthread_attr_t * attr, + void ** stackaddr ) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + ensure_valgrind("pthread_attr_getstackaddr"); + if (stackaddr) + *stackaddr = vg_attr->__vg_stackaddr; + return 0; +} + +WEAK +int pthread_attr_getstacksize ( const pthread_attr_t * attr, + size_t * stacksize ) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + ensure_valgrind("pthread_attr_getstacksize"); + if (stacksize) + *stacksize = vg_attr->__vg_stacksize; + return 0; +} + +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + if (policy != SCHED_OTHER && policy != SCHED_FIFO && policy != SCHED_RR) + return EINVAL; + vg_attr->__vg_schedpolicy = policy; + return 0; +} + +int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + *policy = vg_attr->__vg_schedpolicy; + return 0; +} + + +WEAK +int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + vg_attr->__vg_guardsize = guardsize; + return 0; +} + +WEAK +int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) +{ + vg_pthread_attr_t* vg_attr; + CONVERT(attr, attr, vg_attr); + *guardsize = vg_attr->__vg_guardsize; + return 0; +} + +/* Again, like LinuxThreads. */ + +static int concurrency_current_level = 0; + +WEAK +int pthread_setconcurrency(int new_level) +{ + if (new_level < 0) + return EINVAL; + else { + concurrency_current_level = new_level; + return 0; + } +} + +WEAK +int pthread_getconcurrency(void) +{ + return concurrency_current_level; +} + + + +/* --------------------------------------------------- + Helper functions for running a thread + and for clearing up afterwards. + ------------------------------------------------ */ + +typedef void *(*__attribute__ ((stdcall)) REGPARM(3) allocate_tls_t) (void *result); +typedef void (*__attribute__ ((stdcall)) REGPARM(3) deallocate_tls_t) (void *tcb, int dealloc_tcb); + +static allocate_tls_t allocate_tls = NULL; +static deallocate_tls_t deallocate_tls = NULL; + +static +int get_gs() +{ + int gs; + + asm volatile ("movw %%gs, %w0" : "=q" (gs)); + + return gs & 0xffff; +} + +static +void set_gs( int gs ) +{ + asm volatile ("movw %w0, %%gs" :: "q" (gs)); +} + +static +void *get_tcb() +{ + void *tcb; + + asm volatile ("movl %%gs:0, %0" : "=r" (tcb)); + + return tcb; +} + +/* All exiting threads eventually pass through here, bearing the + return value, or PTHREAD_CANCELED, in ret_val. */ +static +__attribute__((noreturn)) +void thread_exit_wrapper ( void* ret_val ) +{ + int detached, res; + CleanupEntry cu; + pthread_key_t key; + void** specifics_ptr; + + /* Run this thread's key finalizers. Really this should be run + PTHREAD_DESTRUCTOR_ITERATIONS times. */ + for (key = 0; key < VG_N_THREAD_KEYS; key++) { + VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, + VG_USERREQ__GET_KEY_D_AND_S, + key, &cu, 0, 0 ); + if (res == 0) { + /* valid key */ + my_assert(cu.type == VgCt_Function); + if (cu.data.function.fn && cu.data.function.arg) + cu.data.function.fn /* destructor for key */ + ( cu.data.function.arg /* specific for key for this thread */ ); + continue; + } + my_assert(res == -1); + } + + /* Free up my specifics space, if any. */ + VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, + VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, + pthread_self(), 0, 0, 0); + my_assert(specifics_ptr != (void**)3); + my_assert(specifics_ptr != (void**)1); /* 1 means invalid thread */ + if (specifics_ptr != NULL) + my_free(specifics_ptr); + + /* Free up any TLS data */ + if ((get_gs() & 7) == 3 && pthread_self() > 1) { + my_assert(deallocate_tls != NULL); + deallocate_tls(get_tcb(), 1); + } + + /* Decide on my final disposition. */ + VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */, + VG_USERREQ__SET_OR_GET_DETACH, + 2 /* get */, pthread_self(), 0, 0); + my_assert(detached == 0 || detached == 1); + + if (detached) { + /* Detached; I just quit right now. */ + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__QUIT, 0, 0, 0, 0); + } else { + /* Not detached; so I wait for a joiner. */ + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__WAIT_JOINER, ret_val, 0, 0, 0); + } + /* NOTREACHED */ + barf("thread_exit_wrapper: still alive?!"); +} + + +/* This function is a wrapper function for running a thread. It runs + the root function specified in pthread_create, and then, should the + root function return a value, it arranges to run the thread's + cleanup handlers and exit correctly. */ + +/* Struct used to convey info from pthread_create to thread_wrapper. + Must be careful not to pass to the child thread any pointers to + objects which might be on the parent's stack. */ +typedef + struct { + int attr__detachstate; + void* tls_data; + int tls_segment; + unsigned long sysinfo; + void* (*root_fn) ( void* ); + void* arg; + sigset_t sigmask; + } + NewThreadInfo; + +/* Struct used to describe a TDB header, copied from glibc. */ +typedef + struct { + void *tcb; + void *dtv; + void *self; + int multiple_threads; + unsigned long sysinfo; + } + tcbhead_t; + +/* This is passed to the VG_USERREQ__APPLY_IN_NEW_THREAD and so must + not return. Note that this runs in the new thread, not the + parent. */ +static +__attribute__((noreturn)) +void thread_wrapper ( NewThreadInfo* info ) +{ + int attr__detachstate; + void* tls_data; + int tls_segment; + unsigned long sysinfo; + void* (*root_fn) ( void* ); + void* arg; + void* ret_val; + __pthread_unwind_buf_t ub; + + attr__detachstate = info->attr__detachstate; + tls_data = info->tls_data; + tls_segment = info->tls_segment; + sysinfo = info->sysinfo; + root_fn = info->root_fn; + arg = info->arg; + + if (tls_data) { + tcbhead_t *tcb = tls_data; + struct vki_modify_ldt_ldt_s ldt_info; + + /* Fill in the TCB header */ + tcb->tcb = tcb; + tcb->self = tcb; + tcb->multiple_threads = 1; + tcb->sysinfo = sysinfo; + + /* Fill in an LDT descriptor */ + ldt_info.entry_number = tls_segment; + ldt_info.base_addr = (unsigned long)tls_data; + ldt_info.limit = 0xfffff; + ldt_info.seg_32bit = 1; + ldt_info.contents = 0; + ldt_info.read_exec_only = 0; + ldt_info.limit_in_pages = 1; + ldt_info.seg_not_present = 0; + ldt_info.useable = 1; + ldt_info.reserved = 0; + + /* Install the thread area */ + VG_(do_syscall)(__NR_set_thread_area, &ldt_info); + + /* Setup the GS segment register */ + set_gs(ldt_info.entry_number * 8 + 3); + } + + /* Minimally observe the attributes supplied. */ + if (attr__detachstate != PTHREAD_CREATE_DETACHED + && attr__detachstate != PTHREAD_CREATE_JOINABLE) + pthread_error("thread_wrapper: invalid attr->__detachstate"); + if (attr__detachstate == PTHREAD_CREATE_DETACHED) + pthread_detach(pthread_self()); + + /* Initialise thread specific state */ + init_thread_specific_state(); + + /* Now that everything is set up, restore our signal mask (we're + ready to accept signals) */ + sigprocmask(SIG_SETMASK, &info->sigmask, NULL); + + /* Free up the arg block that pthread_create malloced. */ + my_free(info); + + + if (setjmp(ub.__cancel_jmp_buf[0].__cancel_jmp_buf) == 0) { + CleanupEntry cu; + int res; + + cu.type = VgCt_Longjmp; + cu.data.longjmp.ub = &ub; + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__CLEANUP_PUSH, + &cu, 0, 0, 0); + + /* The root function might not return. But if it does we simply + move along to thread_exit_wrapper. All other ways out for the + thread (cancellation, or calling pthread_exit) lead there + too. */ + ret_val = root_fn(arg); + } + else { + ret_val = get_ret_val(); + } + + thread_exit_wrapper(ret_val); + /* NOTREACHED */ +} + + +/* --------------------------------------------------- + CLEANUP STACKS + ------------------------------------------------ */ + +void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer, + void (*__routine) (void *), + void *__arg) +{ + int res; + CleanupEntry cu; + ensure_valgrind("_pthread_cleanup_push"); + cu.type = VgCt_Function; + cu.data.function.fn = __routine; + cu.data.function.arg = __arg; + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__CLEANUP_PUSH, + &cu, 0, 0, 0); + my_assert(res == 0); +} + + +void _pthread_cleanup_push_defer (struct _pthread_cleanup_buffer *__buffer, + void (*__routine) (void *), + void *__arg) +{ + /* As _pthread_cleanup_push, but first save the thread's original + cancellation type in __buffer and set it to Deferred. */ + int orig_ctype; + ensure_valgrind("_pthread_cleanup_push_defer"); + /* Set to Deferred, and put the old cancellation type in res. */ + my_assert(-1 != PTHREAD_CANCEL_DEFERRED); + my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); + my_assert(sizeof(struct _pthread_cleanup_buffer) >= sizeof(int)); + VALGRIND_MAGIC_SEQUENCE(orig_ctype, (-1) /* default */, + VG_USERREQ__SET_CANCELTYPE, + PTHREAD_CANCEL_DEFERRED, 0, 0, 0); + my_assert(orig_ctype != -1); + *((int*)(__buffer)) = orig_ctype; + /* Now push the cleanup. */ + _pthread_cleanup_push(NULL, __routine, __arg); +} + + +void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, + int __execute) +{ + int res; + CleanupEntry cu; + ensure_valgrind("_pthread_cleanup_push"); + cu.type = VgCt_None; /* paranoia */ + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__CLEANUP_POP, + &cu, 0, 0, 0); + my_assert(cu.type == VgCt_Function); + if (res == 0) { + /* pop succeeded */ + if (__execute) { + cu.data.function.fn ( cu.data.function.arg ); + } + return; + } + if (res == -1) { + /* stack underflow */ + return; + } + barf("_pthread_cleanup_pop"); +} + + +void _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *__buffer, + int __execute) +{ + int orig_ctype, fake_ctype; + /* As _pthread_cleanup_pop, but after popping/running the handler, + restore the thread's original cancellation type from the first + word of __buffer. */ + _pthread_cleanup_pop(NULL, __execute); + orig_ctype = *((int*)(__buffer)); + my_assert(orig_ctype == PTHREAD_CANCEL_DEFERRED + || orig_ctype == PTHREAD_CANCEL_ASYNCHRONOUS); + my_assert(-1 != PTHREAD_CANCEL_DEFERRED); + my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); + my_assert(sizeof(struct _pthread_cleanup_buffer) >= sizeof(int)); + VALGRIND_MAGIC_SEQUENCE(fake_ctype, (-1) /* default */, + VG_USERREQ__SET_CANCELTYPE, + orig_ctype, 0, 0, 0); + my_assert(fake_ctype == PTHREAD_CANCEL_DEFERRED); +} + + +REGPARM(1) +void __pthread_register_cancel (__pthread_unwind_buf_t *__buf) +{ + int res; + CleanupEntry cu; + ensure_valgrind("__pthread_register_cancel"); + cu.type = VgCt_Longjmp; + cu.data.longjmp.ub = __buf; + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__CLEANUP_PUSH, + &cu, 0, 0, 0); + my_assert(res == 0); +} + + +REGPARM(1) +void __pthread_register_cancel_defer (__pthread_unwind_buf_t *__buf) +{ + /* As __pthread_register cancel, but save the thread's original + cancellation type and set it to Deferred. */ + int res; + CleanupEntry cu; + ensure_valgrind("__pthread_register_cancel_defer"); + cu.type = VgCt_Longjmp; + cu.data.longjmp.ub = __buf; + /* Set to Deferred, and save the old cancellation type. */ + my_assert(-1 != PTHREAD_CANCEL_DEFERRED); + my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); + my_assert(sizeof(struct _pthread_cleanup_buffer) >= sizeof(int)); + VALGRIND_MAGIC_SEQUENCE(cu.data.longjmp.ctype, (-1) /* default */, + VG_USERREQ__SET_CANCELTYPE, + PTHREAD_CANCEL_DEFERRED, 0, 0, 0); + my_assert(cu.data.longjmp.ctype != -1); + /* Now push the cleanup. */ + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__CLEANUP_PUSH, + &cu, 0, 0, 0); + my_assert(res == 0); +} + + +REGPARM(1) +void __pthread_unregister_cancel (__pthread_unwind_buf_t *__buf) +{ + int res; + CleanupEntry cu; + ensure_valgrind("__pthread_unregister_cancel"); + cu.type = VgCt_None; /* paranoia */ + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__CLEANUP_POP, + &cu, 0, 0, 0); + my_assert(cu.type == VgCt_Longjmp); + my_assert(cu.data.longjmp.ub == __buf); + return; +} + + +REGPARM(1) +void __pthread_unregister_restore (__pthread_unwind_buf_t *__buf) +{ + int res; + CleanupEntry cu; + int fake_ctype; + /* As __pthread_unregister_cancel, but after popping/running the + handler, restore the thread's original cancellation type. */ + ensure_valgrind("__pthread_unregister_cancel_restore"); + cu.type = VgCt_None; /* paranoia */ + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__CLEANUP_POP, + &cu, 0, 0, 0); + my_assert(cu.type == VgCt_Longjmp); + my_assert(cu.data.longjmp.ub == __buf); + /* Restore the original cancellation type. */ + my_assert(cu.data.longjmp.ctype == PTHREAD_CANCEL_DEFERRED + || cu.data.longjmp.ctype == PTHREAD_CANCEL_ASYNCHRONOUS); + my_assert(-1 != PTHREAD_CANCEL_DEFERRED); + my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); + VALGRIND_MAGIC_SEQUENCE(fake_ctype, (-1) /* default */, + VG_USERREQ__SET_CANCELTYPE, + cu.data.longjmp.ctype, 0, 0, 0); + my_assert(fake_ctype == PTHREAD_CANCEL_DEFERRED); + return; +} + +REGPARM(1) +__attribute ((__noreturn__)) +void __pthread_unwind (__pthread_unwind_buf_t *__buf) +{ + int res; + CleanupEntry cu; + while (1) { + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__CLEANUP_POP, + &cu, 0, 0, 0); + my_assert(res == 0); + if (cu.type == VgCt_Longjmp) break; + if (0) printf("running cleanup handler"); + my_assert(cu.type == VgCt_Function); + cu.data.function.fn ( cu.data.function.arg ); + } + my_assert(cu.type == VgCt_Longjmp); + my_assert(__buf == NULL || __buf == cu.data.longjmp.ub); + __buf = cu.data.longjmp.ub; + longjmp(__buf->__cancel_jmp_buf[0].__cancel_jmp_buf, 1); + /* NOTREACHED */ +} + + +REGPARM(1) +__attribute ((__noreturn__)) +void __pthread_unwind_next (__pthread_unwind_buf_t *__buf) +{ + __pthread_unwind(NULL); + /* NOTREACHED */ +} + + +/* --------------------------------------------------- + THREADs + ------------------------------------------------ */ + +static void __valgrind_pthread_yield ( void ) +{ + int res; + ensure_valgrind("pthread_yield"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_YIELD, 0, 0, 0, 0); +} + +WEAK +int pthread_yield ( void ) +{ + __valgrind_pthread_yield(); + return 0; +} + + +int pthread_equal(pthread_t thread1, pthread_t thread2) +{ + return thread1 == thread2 ? 1 : 0; +} + + +/* Bundle up the args into a malloc'd block and create a new thread + consisting of thread_wrapper() applied to said malloc'd block. */ +int +pthread_create (pthread_t *__restrict __thredd, + __const pthread_attr_t *__restrict __attr, + void *(*__start_routine) (void *), + void *__restrict __arg) +{ + int tid_child; + NewThreadInfo* info; + int gs; + StackInfo si; + vg_pthread_attr_t* __vg_attr; + CONVERT(attr, __attr, __vg_attr); + + ensure_valgrind("pthread_create"); + + /* make sure the tsd keys, and hence locale info, for the root + thread are initialised before we get into complications making + new threads. */ + init_global_thread_specific_state(); + + /* Allocate space for the arg block. thread_wrapper will free + it. */ + info = my_malloc(sizeof(NewThreadInfo)); + my_assert(info != NULL); + + if (__vg_attr) + info->attr__detachstate = __vg_attr->__vg_detachstate; + else + info->attr__detachstate = PTHREAD_CREATE_JOINABLE; + + gs = get_gs(); + + if ((gs & 7) == 3) { + tcbhead_t *tcb = get_tcb(); + + if (allocate_tls == NULL || deallocate_tls == NULL) { + allocate_tls = (allocate_tls_t)dlsym(RTLD_DEFAULT, "_dl_allocate_tls"); + deallocate_tls = (deallocate_tls_t)dlsym(RTLD_DEFAULT, "_dl_deallocate_tls"); + } + + my_assert(allocate_tls != NULL); + + info->tls_data = allocate_tls(NULL); + info->tls_segment = gs >> 3; + info->sysinfo = tcb->sysinfo; + + tcb->multiple_threads = 1; + } else { + info->tls_data = NULL; + info->tls_segment = -1; + info->sysinfo = 0; + } + + info->root_fn = __start_routine; + info->arg = __arg; + sigprocmask(SIG_SETMASK, NULL, &info->sigmask); + + if (__attr) { + si.base = (Addr)__vg_attr->__vg_stackaddr; + si.size = __vg_attr->__vg_stacksize; + si.guardsize = __vg_attr->__vg_guardsize; + } else { + si.base = (Addr)NULL; + si.size = VG_PTHREAD_STACK_SIZE; + si.guardsize = VKI_BYTES_PER_PAGE; + } + + VALGRIND_MAGIC_SEQUENCE(tid_child, VG_INVALID_THREADID /* default */, + VG_USERREQ__APPLY_IN_NEW_THREAD, + &thread_wrapper, info, &si, 0); + my_assert(tid_child != VG_INVALID_THREADID); + + if (__thredd) + *__thredd = tid_child; + + return 0; /* success */ +} + + +int +pthread_join (pthread_t __th, void **__thread_return) +{ + int res; + ensure_valgrind("pthread_join"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_JOIN, + __th, __thread_return, 0, 0); + return res; +} + + +void pthread_exit(void *retval) +{ + ensure_valgrind("pthread_exit"); + set_ret_val(retval); + __pthread_unwind(NULL); +} + + +int pthread_detach(pthread_t th) +{ + int res; + ensure_valgrind("pthread_detach"); + /* First we enquire as to the current detach state. */ + VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, + VG_USERREQ__SET_OR_GET_DETACH, + 2 /* get */, th, 0, 0); + if (res == -1) { + /* not found */ + pthread_error("pthread_detach: " + "invalid target thread"); + return ESRCH; + } + if (res == 1) { + /* already detached */ + pthread_error("pthread_detach: " + "target thread is already detached"); + return EINVAL; + } + if (res == 0) { + VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, + VG_USERREQ__SET_OR_GET_DETACH, + 1 /* set */, th, 0, 0); + my_assert(res == 0); + return 0; + } + barf("pthread_detach"); +} + + +/* --------------------------------------------------- + MUTEX ATTRIBUTES + ------------------------------------------------ */ + +int __pthread_mutexattr_init(pthread_mutexattr_t *attr) +{ + vg_pthread_mutexattr_t* vg_attr; + CONVERT(mutexattr, attr, vg_attr); + vg_attr->__vg_mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP; + return 0; +} + +int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) +{ + vg_pthread_mutexattr_t* vg_attr; + CONVERT(mutexattr, attr, vg_attr); + + switch (type) { +# ifndef GLIBC_2_1 + case PTHREAD_MUTEX_TIMED_NP: + case PTHREAD_MUTEX_ADAPTIVE_NP: +# endif +# ifdef GLIBC_2_1 + case PTHREAD_MUTEX_FAST_NP: +# endif + case PTHREAD_MUTEX_RECURSIVE_NP: + case PTHREAD_MUTEX_ERRORCHECK_NP: + vg_attr->__vg_mutexkind = type; + return 0; + default: + pthread_error("pthread_mutexattr_settype: " + "invalid type"); + return EINVAL; + } +} + +int __pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) +{ + vg_pthread_mutexattr_t* vg_attr; + CONVERT(mutexattr, attr, vg_attr); + + *type = vg_attr->__vg_mutexkind; + + return 0; +} + +int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +{ + return 0; +} + +int __pthread_mutexattr_setpshared ( pthread_mutexattr_t* attr, int pshared) +{ + if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) + return EINVAL; + + /* For now it is not possible to shared a conditional variable. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return ENOSYS; + + return 0; +} + + +/* --------------------------------------------------- + MUTEXes + ------------------------------------------------ */ + +int __pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutexattr) +{ + vg_pthread_mutex_t* vg_mutex; + vg_pthread_mutexattr_t* vg_mutexattr; + CONVERT(mutex, mutex, vg_mutex); + CONVERT(mutexattr, mutexattr, vg_mutexattr); + + vg_mutex->__vg_m_count = 0; + vg_mutex->__vg_m_owner = (/*_pthread_descr*/void*)VG_INVALID_THREADID; + vg_mutex->__vg_m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; + if (vg_mutexattr) + vg_mutex->__vg_m_kind = vg_mutexattr->__vg_mutexkind; + return 0; +} + + +int __pthread_mutex_lock(pthread_mutex_t *mutex) +{ + int res; + vg_pthread_mutex_t* vg_mutex; + CONVERT(mutex, mutex, vg_mutex); + + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_MUTEX_LOCK, + vg_mutex, 0, 0, 0); + return res; +} + + +int __pthread_mutex_timedlock(pthread_mutex_t *mutex, + const struct timespec *abstime ) +{ + int res; + unsigned int ms_now, ms_end; + struct timeval timeval_now; + unsigned long long int ull_ms_now_after_1970; + unsigned long long int ull_ms_end_after_1970; + unsigned long long int ull_ms_now; + unsigned long long int ull_ms_end; + vg_pthread_mutex_t* vg_mutex; + CONVERT(mutex, mutex, vg_mutex); + + VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, + VG_USERREQ__READ_MILLISECOND_TIMER, + 0, 0, 0, 0); + my_assert(ms_now != 0xFFFFFFFF); + res = gettimeofday(&timeval_now, NULL); + my_assert(res == 0); + + ull_ms_now_after_1970 + = 1000ULL * ((unsigned long long int)(timeval_now.tv_sec)) + + ((unsigned long long int)(timeval_now.tv_usec / 1000)); + ull_ms_end_after_1970 + = 1000ULL * ((unsigned long long int)(abstime->tv_sec)) + + ((unsigned long long int)(abstime->tv_nsec / 1000000)); + if (ull_ms_end_after_1970 < ull_ms_now_after_1970) + ull_ms_end_after_1970 = ull_ms_now_after_1970; + ull_ms_now = ((unsigned long long int)(ms_now)); + ull_ms_end = ull_ms_now + (ull_ms_end_after_1970 - ull_ms_now_after_1970); + if (ull_ms_end >= (unsigned long long int)(0xFFFFFFFFUL)) { + /* use 0xFFFFFFFEUL because 0xFFFFFFFFUL is reserved for no timeout + (the fine difference between a long wait and a possible abort + due to a detected deadlock). + */ + ms_end = 0xFFFFFFFEUL; + } else { + ms_end = (unsigned int)(ull_ms_end); + } + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_MUTEX_TIMEDLOCK, + vg_mutex, ms_end, 0, 0); + return res; +} + + +int __pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + int res; + vg_pthread_mutex_t* vg_mutex; + CONVERT(mutex, mutex, vg_mutex); + + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_MUTEX_TRYLOCK, + vg_mutex, 0, 0, 0); + return res; +} + + +int __pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + int res; + vg_pthread_mutex_t* vg_mutex; + CONVERT(mutex, mutex, vg_mutex); + + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_MUTEX_UNLOCK, + vg_mutex, 0, 0, 0); + return res; +} + + +int __pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + vg_pthread_mutex_t* vg_mutex; + CONVERT(mutex, mutex, vg_mutex); + + /* Valgrind doesn't hold any resources on behalf of the mutex, so no + need to involve it. */ + if (vg_mutex->__vg_m_count > 0) { + /* Oh, the horror. glibc's internal use of pthreads "knows" + that destroying a lock does an implicit unlock. Make it + explicit. */ + __pthread_mutex_unlock( (pthread_mutex_t*)vg_mutex ); + pthread_error("pthread_mutex_destroy: mutex is still in use"); + return EBUSY; + } + vg_mutex->__vg_m_count = 0; + vg_mutex->__vg_m_owner = (/*_pthread_descr*/void*)VG_INVALID_THREADID; + vg_mutex->__vg_m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; + return 0; +} + + +/* --------------------------------------------------- + CONDITION VARIABLES + ------------------------------------------------ */ + +/* LinuxThreads supports no attributes for conditions. Hence ... */ + +int pthread_condattr_init(pthread_condattr_t *attr) +{ + return 0; +} + +int pthread_condattr_destroy(pthread_condattr_t *attr) +{ + return 0; +} + +int pthread_cond_init( pthread_cond_t *cond, + const pthread_condattr_t *cond_attr) +{ + vg_pthread_cond_t* vg_cond; + CONVERT(cond, cond, vg_cond); + vg_cond->__vg_c_waiting = (/*_pthread_descr*/void*)VG_INVALID_THREADID; + return 0; +} + +int pthread_cond_destroy(pthread_cond_t *cond) +{ + /* should check that no threads are waiting on this CV */ + static int moans = N_MOANS; + if (moans-- > 0) + kludged("pthread_cond_destroy", + "(it doesn't check if the cond is waited on)" ); + return 0; +} + +/* --------------------------------------------------- + SCHEDULING + ------------------------------------------------ */ + +/* This is completely bogus. */ +int pthread_getschedparam(pthread_t target_thread, + int *policy, + struct sched_param *param) +{ + static int moans = N_MOANS; + if (moans-- > 0) + kludged("pthread_getschedparam", NULL); + if (policy) *policy = SCHED_OTHER; +# ifdef HAVE_SCHED_PRIORITY + if (param) param->sched_priority = 0; /* who knows */ +# else + if (param) param->__sched_priority = 0; /* who knows */ +# endif + return 0; +} + +int pthread_setschedparam(pthread_t target_thread, + int policy, + const struct sched_param *param) +{ + static int moans = N_MOANS; + if (moans-- > 0) + ignored("pthread_setschedparam", "(scheduling not changeable)"); + return 0; +} + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + int res; + vg_pthread_mutex_t* vg_mutex; + CONVERT(mutex, mutex, vg_mutex); + + ensure_valgrind("pthread_cond_wait"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_COND_WAIT, + cond, vg_mutex, 0, 0); + return res; +} + +int pthread_cond_timedwait ( pthread_cond_t *cond, + pthread_mutex_t *mutex, + const struct timespec *abstime ) +{ + int res; + unsigned int ms_now, ms_end; + struct timeval timeval_now; + unsigned long long int ull_ms_now_after_1970; + unsigned long long int ull_ms_end_after_1970; + unsigned long long int ull_ms_now; + unsigned long long int ull_ms_end; + vg_pthread_mutex_t* vg_mutex; + CONVERT(mutex, mutex, vg_mutex); + + ensure_valgrind("pthread_cond_timedwait"); + VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, + VG_USERREQ__READ_MILLISECOND_TIMER, + 0, 0, 0, 0); + my_assert(ms_now != 0xFFFFFFFF); + res = gettimeofday(&timeval_now, NULL); + my_assert(res == 0); + + ull_ms_now_after_1970 + = 1000ULL * ((unsigned long long int)(timeval_now.tv_sec)) + + ((unsigned long long int)(timeval_now.tv_usec / 1000)); + ull_ms_end_after_1970 + = 1000ULL * ((unsigned long long int)(abstime->tv_sec)) + + ((unsigned long long int)(abstime->tv_nsec / 1000000)); + if (ull_ms_end_after_1970 < ull_ms_now_after_1970) + ull_ms_end_after_1970 = ull_ms_now_after_1970; + ull_ms_now = ((unsigned long long int)(ms_now)); + ull_ms_end = ull_ms_now + (ull_ms_end_after_1970 - ull_ms_now_after_1970); + if (ull_ms_end >= (unsigned long long int)(0xFFFFFFFFUL)) { + /* use 0xFFFFFFFEUL because 0xFFFFFFFFUL is reserved for no timeout + (the fine difference between a long wait and a possible abort + due to a detected deadlock). + */ + ms_end = 0xFFFFFFFEUL; + } else { + ms_end = (unsigned int)(ull_ms_end); + } + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_COND_TIMEDWAIT, + cond, vg_mutex, ms_end, 0); + return res; +} + + +int pthread_cond_signal(pthread_cond_t *cond) +{ + int res; + ensure_valgrind("pthread_cond_signal"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_COND_SIGNAL, + cond, 0, 0, 0); + return res; +} + +int pthread_cond_broadcast(pthread_cond_t *cond) +{ + int res; + ensure_valgrind("pthread_cond_broadcast"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_COND_BROADCAST, + cond, 0, 0, 0); + return res; +} + + +/* --------------------------------------------------- + CANCELLATION + ------------------------------------------------ */ + +int pthread_setcancelstate(int state, int *oldstate) +{ + int res; + ensure_valgrind("pthread_setcancelstate"); + if (state != PTHREAD_CANCEL_ENABLE + && state != PTHREAD_CANCEL_DISABLE) { + pthread_error("pthread_setcancelstate: " + "invalid state"); + return EINVAL; + } + my_assert(-1 != PTHREAD_CANCEL_ENABLE); + my_assert(-1 != PTHREAD_CANCEL_DISABLE); + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__SET_CANCELSTATE, + state, 0, 0, 0); + my_assert(res != -1); + if (oldstate) + *oldstate = res; + return 0; +} + +int pthread_setcanceltype(int type, int *oldtype) +{ + int res; + ensure_valgrind("pthread_setcanceltype"); + if (type != PTHREAD_CANCEL_DEFERRED + && type != PTHREAD_CANCEL_ASYNCHRONOUS) { + pthread_error("pthread_setcanceltype: " + "invalid type"); + return EINVAL; + } + my_assert(-1 != PTHREAD_CANCEL_DEFERRED); + my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__SET_CANCELTYPE, + type, 0, 0, 0); + my_assert(res != -1); + if (oldtype) + *oldtype = res; + return 0; +} + +int pthread_cancel(pthread_t thread) +{ + int res; + ensure_valgrind("pthread_cancel"); + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__SET_CANCELPEND, + thread, &pthread_exit, 0, 0); + my_assert(res != -1); + return res; +} + +static +void __my_pthread_testcancel(void) +{ + int res; + ensure_valgrind("__my_pthread_testcancel"); + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__TESTCANCEL, + 0, 0, 0, 0); + my_assert(res == 0); +} + +void pthread_testcancel ( void ) +{ + __my_pthread_testcancel(); +} + + +/* Not really sure what this is for. I suspect for doing the POSIX + requirements for fork() and exec(). We do this internally anyway + whenever those syscalls are observed, so this could be superfluous, + but hey ... +*/ +void __pthread_kill_other_threads_np ( void ) +{ + int res; + ensure_valgrind("__pthread_kill_other_threads_np"); + VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, + VG_USERREQ__NUKE_OTHER_THREADS, + 0, 0, 0, 0); + my_assert(res == 0); +} + + +/* --------------------------------------------------- + SIGNALS + ------------------------------------------------ */ + +#include + +int pthread_sigmask(int how, const sigset_t *newmask, + sigset_t *oldmask) +{ + int res; + + /* A bit subtle, because the scheduler expects newmask and oldmask + to be vki_sigset_t* rather than sigset_t*, and the two are + different. Fortunately the first 64 bits of a sigset_t are + exactly a vki_sigset_t, so we just pass the pointers through + unmodified. Haaaack! + + Also mash the how value so that the SIG_ constants from glibc + constants to VKI_ constants, so that the former do not have to + be included into vg_scheduler.c. */ + + ensure_valgrind("pthread_sigmask"); + + switch (how) { + case SIG_SETMASK: how = VKI_SIG_SETMASK; break; + case SIG_BLOCK: how = VKI_SIG_BLOCK; break; + case SIG_UNBLOCK: how = VKI_SIG_UNBLOCK; break; + default: pthread_error("pthread_sigmask: invalid how"); + return EINVAL; + } + + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_SIGMASK, + how, newmask, oldmask, 0); + + /* The scheduler tells us of any memory violations. */ + return res == 0 ? 0 : EFAULT; +} + +int sigwait ( const sigset_t* set, int* sig ) +{ + int res; + siginfo_t si; + + __my_pthread_testcancel(); + + si.si_signo = 0; + res = sigtimedwait(set, &si, NULL); + *sig = si.si_signo; + + return 0; /* always returns 0 */ +} + + +int pthread_kill(pthread_t thread, int signo) +{ + int res; + ensure_valgrind("pthread_kill"); + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_KILL, + thread, signo, 0, 0); + return res; +} + + +/* Copied verbatim from Linuxthreads */ +/* Redefine raise() to send signal to calling thread only, + as per POSIX 1003.1c */ +int raise (int sig) +{ + int retcode = pthread_kill(pthread_self(), sig); + if (retcode == 0) { + return 0; + } else { + *(__errno_location()) = retcode; + return -1; + } +} + + + +/* --------------------------------------------------- + THREAD-SPECIFICs + ------------------------------------------------ */ + +static +int key_is_valid (pthread_key_t key) +{ + int res; + VALGRIND_MAGIC_SEQUENCE(res, 2 /* default */, + VG_USERREQ__PTHREAD_KEY_VALIDATE, + key, 0, 0, 0); + my_assert(res != 2); + return res; +} + + +/* Returns NULL if thread is invalid. Otherwise, if the thread + already has a specifics area, return that. Otherwise allocate it + one. */ +static +void** get_or_allocate_specifics_ptr ( pthread_t thread ) +{ + int res, i; + void** specifics_ptr; + ensure_valgrind("get_or_allocate_specifics_ptr"); + + /* Returns zero if the thread has no specific_ptr. One if thread + is invalid. Otherwise, the specific_ptr value. This is + allocated with my_malloc and so is aligned and cannot be + confused with 1 or 3. */ + VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, + VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, + thread, 0, 0, 0); + my_assert(specifics_ptr != (void**)3); + + if (specifics_ptr == (void**)1) + return NULL; /* invalid thread */ + + if (specifics_ptr != NULL) + return specifics_ptr; /* already has a specifics ptr. */ + + /* None yet ... allocate a new one. Should never fail. */ + specifics_ptr = my_malloc( VG_N_THREAD_KEYS * sizeof(void*) ); + my_assert(specifics_ptr != NULL); + + VALGRIND_MAGIC_SEQUENCE(res, -1 /* default */, + VG_USERREQ__PTHREAD_SETSPECIFIC_PTR, + specifics_ptr, 0, 0, 0); + my_assert(res == 0); + + /* POSIX sez: "Upon thread creation, the value NULL shall be + associated with all defined keys in the new thread." This + allocation is in effect a delayed allocation of the specific + data for a thread, at its first-use. Hence we initialise it + here. */ + for (i = 0; i < VG_N_THREAD_KEYS; i++) { + specifics_ptr[i] = NULL; + } + + return specifics_ptr; +} + + +int __pthread_key_create(pthread_key_t *key, + void (*destr_function) (void *)) +{ + void** specifics_ptr; + int res, i; + ensure_valgrind("pthread_key_create"); + + /* This writes *key if successful. It should never fail. */ + VALGRIND_MAGIC_SEQUENCE(res, 1 /* default */, + VG_USERREQ__PTHREAD_KEY_CREATE, + key, destr_function, 0, 0); + + if (res == 0) { + /* POSIX sez: "Upon key creation, the value NULL shall be + associated with the new key in all active threads." */ + for (i = 0; i < VG_N_THREADS; i++) { + specifics_ptr = get_or_allocate_specifics_ptr(i); + /* we get NULL if i is an invalid thread. */ + if (specifics_ptr != NULL) + specifics_ptr[*key] = NULL; + } + } + + return res; +} + +int pthread_key_delete(pthread_key_t key) +{ + int res; + ensure_valgrind("pthread_key_delete"); + if (!key_is_valid(key)) + return EINVAL; + VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, + VG_USERREQ__PTHREAD_KEY_DELETE, + key, 0, 0, 0); + my_assert(res == 0); + return 0; +} + +int __pthread_setspecific(pthread_key_t key, const void *pointer) +{ + void** specifics_ptr; + ensure_valgrind("pthread_setspecific"); + + if (!key_is_valid(key)) + return EINVAL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + specifics_ptr[key] = (void*)pointer; + return 0; +} + +void * __pthread_getspecific(pthread_key_t key) +{ + void** specifics_ptr; + ensure_valgrind("pthread_getspecific"); + + if (!key_is_valid(key)) + return NULL; + + specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); + return specifics_ptr[key]; +} + + +/* --------------------------------------------------- + ONCEry + ------------------------------------------------ */ + +/* This protects reads and writes of the once_control variable + supplied. It is never held whilst any particular initialiser is + running. */ +static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER; + +/* Initialiser needs to be run. */ +#define P_ONCE_NOT_DONE ((PTHREAD_ONCE_INIT) + 0) + +/* Initialiser currently running. */ +#define P_ONCE_RUNNING ((PTHREAD_ONCE_INIT) + 1) + +/* Initialiser has completed. */ +#define P_ONCE_COMPLETED ((PTHREAD_ONCE_INIT) + 2) + +int __pthread_once ( pthread_once_t *once_control, + void (*init_routine) (void) ) +{ + int res; + int done; + +# define TAKE_LOCK \ + res = __pthread_mutex_lock(&once_masterlock); \ + my_assert(res == 0); + +# define RELEASE_LOCK \ + res = __pthread_mutex_unlock(&once_masterlock); \ + my_assert(res == 0); + + void cleanup(void *v) { + TAKE_LOCK; + *once_control = P_ONCE_NOT_DONE; + RELEASE_LOCK; + } + + ensure_valgrind("pthread_once"); + + /* Grab the lock transiently, so we can safely see what state this + once_control is in. */ + + TAKE_LOCK; + + switch (*once_control) { + + case P_ONCE_NOT_DONE: + /* Not started. Change state to indicate running, drop the + lock and run. */ + *once_control = P_ONCE_RUNNING; + _pthread_cleanup_push(NULL, cleanup, NULL); + RELEASE_LOCK; + init_routine(); + /* re-take the lock, and set state to indicate done. */ + TAKE_LOCK; + _pthread_cleanup_pop(NULL, False); + *once_control = P_ONCE_COMPLETED; + RELEASE_LOCK; + break; + + case P_ONCE_RUNNING: + /* This is the tricky case. The initialiser is running in + some other thread, but we have to delay this thread till + the other one completes. So we sort-of busy wait. In + fact it makes sense to yield now, because what we want to + happen is for the thread running the initialiser to + complete ASAP. */ + RELEASE_LOCK; + done = 0; + while (1) { + /* Let others run for a while. */ + __valgrind_pthread_yield(); + /* Grab the lock and see if we're done waiting. */ + TAKE_LOCK; + if (*once_control == P_ONCE_COMPLETED) + done = 1; + RELEASE_LOCK; + if (done) + break; + } + break; + + case P_ONCE_COMPLETED: + default: + /* Easy. It's already done. Just drop the lock. */ + RELEASE_LOCK; + break; + } + + return 0; + +# undef TAKE_LOCK +# undef RELEASE_LOCK +} + +#undef P_ONCE_NOT_DONE +#undef P_ONCE_RUNNING +#undef P_ONCE_COMPLETED + + +/* --------------------------------------------------- + MISC + ------------------------------------------------ */ + +static pthread_mutex_t pthread_atfork_lock + = PTHREAD_MUTEX_INITIALIZER; + +int __pthread_atfork ( void (*prepare)(void), + void (*parent)(void), + void (*child)(void) ) +{ + int n, res; + ForkHandlerEntry entry; + + ensure_valgrind("pthread_atfork"); + __pthread_mutex_lock(&pthread_atfork_lock); + + /* Fetch old counter */ + VALGRIND_MAGIC_SEQUENCE(n, -2 /* default */, + VG_USERREQ__GET_FHSTACK_USED, + 0, 0, 0, 0); + my_assert(n >= 0 && n < VG_N_FORKHANDLERSTACK); + if (n == VG_N_FORKHANDLERSTACK-1) + barf("pthread_atfork: VG_N_FORKHANDLERSTACK is too low; " + "increase and recompile"); + + /* Add entry */ + entry.prepare = *prepare; + entry.parent = *parent; + entry.child = *child; + VALGRIND_MAGIC_SEQUENCE(res, -2 /* default */, + VG_USERREQ__SET_FHSTACK_ENTRY, + n, &entry, 0, 0); + my_assert(res == 0); + + /* Bump counter */ + VALGRIND_MAGIC_SEQUENCE(res, -2 /* default */, + VG_USERREQ__SET_FHSTACK_USED, + n+1, 0, 0, 0); + my_assert(res == 0); + + __pthread_mutex_unlock(&pthread_atfork_lock); + return 0; +} + + +#ifdef GLIBC_2_3 +/* This seems to be a hook which appeared in glibc-2.3.2. */ +int __register_atfork ( void (*prepare)(void), + void (*parent)(void), + void (*child)(void) ) +{ + return __pthread_atfork(prepare,parent,child); +} +#endif + +WEAK +void __pthread_initialize ( void ) +{ + ensure_valgrind("__pthread_initialize"); +} + + +/* --------------------------------------------------- + LIBRARY-PRIVATE THREAD SPECIFIC STATE + ------------------------------------------------ */ + +#include + +/* The allowable libc TSD keys (indices) from glibc source. */ +enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, + _LIBC_TSD_KEY_DL_ERROR, + _LIBC_TSD_KEY_RPC_VARS, + _LIBC_TSD_KEY_LOCALE, + _LIBC_TSD_KEY_CTYPE_B, + _LIBC_TSD_KEY_CTYPE_TOLOWER, + _LIBC_TSD_KEY_CTYPE_TOUPPER, + _LIBC_TSD_KEY_N }; + +typedef + struct { + void *ret_val; + int *errno_ptr; + int *h_errno_ptr; + struct __res_state *res_state_ptr; + int errno_data; + int h_errno_data; + struct __res_state res_state_data; + void *libc_specifics[_LIBC_TSD_KEY_N]; + } + ThreadSpecificState; + +static ThreadSpecificState thread_specific_state[VG_N_THREADS]; + +/* Auto-initialising subsystem. global_init_done is set + after initialisation. global_init_done_mx guards it. */ +static int global_init_done = 0; +static pthread_mutex_t global_init_done_mx = PTHREAD_MUTEX_INITIALIZER; + +static +void cleanup_root(void *arg) +{ + thread_exit_wrapper(get_ret_val()); + /* NOTREACHED */ +} + +static void __attribute__((constructor)) +init_global_thread_specific_state ( void ) +{ + int res; + + /* Don't fall into deadlock if we get called again whilst we still + hold the lock, via the __uselocale() call herein. */ + if (global_init_done != 0) + return; + + /* Take the lock. */ + res = __pthread_mutex_lock(&global_init_done_mx); + if (res != 0) barf("init_global_thread_specific_state: lock"); + + /* Now test again, to be sure there is no mistake. */ + if (global_init_done != 0) { + res = __pthread_mutex_unlock(&global_init_done_mx); + if (res != 0) barf("init_global_thread_specific_state: unlock(1)"); + return; + } + + /* assert that we are the root thread. */ + my_assert(pthread_self() == 1); + + /* Signify init done - we shouldn't really do this until after + the call to init_thread_specific_state() but that routine makes + a call to __uselocale() that may bring us back here as that + routine will call __libc_tsd_set() which will call us. + + We can get away with marking the init as done now because + the important bits of init_thread_specific_state() are done + before the call to __uselocale() is made. */ + global_init_done = 1; + + /* Initialise thread specific data for the root thread. */ + init_thread_specific_state(); + + /* Install a cleanup routine to handle the root thread exiting */ + _pthread_cleanup_push(NULL, cleanup_root, NULL); + + /* Unlock and return. */ + res = __pthread_mutex_unlock(&global_init_done_mx); + if (res != 0) barf("init_global_thread_specific_state: unlock"); +} + +static void +init_thread_specific_state ( void ) +{ + int tid = pthread_self(); + int i; + + /* No return value yet */ + thread_specific_state[tid].ret_val = NULL; + + /* Initialise the errno and resolver state pointers. */ + thread_specific_state[tid].errno_ptr = NULL; + thread_specific_state[tid].h_errno_ptr = NULL; + thread_specific_state[tid].res_state_ptr = NULL; + + /* Initialise the per-thread libc data. */ + for (i = 0; i < _LIBC_TSD_KEY_N; i++) { + thread_specific_state[tid].libc_specifics[i] = NULL; + } + +# ifdef GLIBC_2_3 + /* Set this thread's locale to the global (default) locale. A hack + in support of glibc-2.3. + */ + __uselocale(LC_GLOBAL_LOCALE); +# endif +} + +static void +set_ret_val ( void* ret_val ) +{ + int tid; + VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + thread_specific_state[tid].ret_val = ret_val; +} + +static void * +get_ret_val ( void ) +{ + int tid; + VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + return thread_specific_state[tid].ret_val; +} + +int* __errno_location ( void ) +{ + int tid; + + ensure_valgrind("__errno_location"); + VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + /* 'cos I'm paranoid ... */ + if (tid < 1 || tid >= VG_N_THREADS) + barf("__errno_location: invalid ThreadId"); + if (thread_specific_state[tid].errno_ptr == NULL) { + if ((get_gs() & 7) == 3) + thread_specific_state[tid].errno_ptr = dlsym(RTLD_DEFAULT, "errno"); + else if (tid == 1) + thread_specific_state[tid].errno_ptr = dlvsym(RTLD_DEFAULT, "errno", "GLIBC_2.0"); + else + thread_specific_state[tid].errno_ptr = &thread_specific_state[tid].errno_data; + } + return thread_specific_state[tid].errno_ptr; +} + +int* __h_errno_location ( void ) +{ + int tid; + /* ensure_valgrind("__h_errno_location"); */ + VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + /* 'cos I'm paranoid ... */ + if (tid < 1 || tid >= VG_N_THREADS) + barf("__h_errno_location: invalid ThreadId"); + if (thread_specific_state[tid].h_errno_ptr == NULL) { + if ((get_gs() & 7) == 3) + thread_specific_state[tid].h_errno_ptr = dlsym(RTLD_DEFAULT, "h_errno"); + else if (tid == 1) + thread_specific_state[tid].h_errno_ptr = dlvsym(RTLD_DEFAULT, "h_errno", "GLIBC_2.0"); + else + thread_specific_state[tid].h_errno_ptr = &thread_specific_state[tid].h_errno_data; + } + return thread_specific_state[tid].h_errno_ptr; +} + +struct __res_state* __res_state ( void ) +{ + int tid; + /* ensure_valgrind("__res_state"); */ + VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, + VG_USERREQ__PTHREAD_GET_THREADID, + 0, 0, 0, 0); + /* 'cos I'm paranoid ... */ + if (tid < 1 || tid >= VG_N_THREADS) + barf("__res_state: invalid ThreadId"); + if (thread_specific_state[tid].res_state_ptr == NULL) { + if ((get_gs() & 7) == 3) { + struct __res_state **resp = dlsym(RTLD_DEFAULT, "__resp"); + + thread_specific_state[tid].res_state_ptr = *resp; + } else if (tid == 1) { + thread_specific_state[tid].res_state_ptr = dlvsym(RTLD_DEFAULT, "_res", "GLIBC_2.0"); + } else { + thread_specific_state[tid].res_state_ptr = &thread_specific_state[tid].res_state_data; + } + } + return thread_specific_state[tid].res_state_ptr; +} + + +/* --------------------------------------------------- + LIBC-PRIVATE SPECIFIC DATA + ------------------------------------------------ */ + +static int +libc_internal_tsd_set ( enum __libc_tsd_key_t key, + const void * pointer ) +{ + int tid = pthread_self(); + /* printf("SET SET SET key %d ptr %p\n", key, pointer); */ + if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) + barf("libc_internal_tsd_set: invalid key"); + init_global_thread_specific_state(); + thread_specific_state[tid].libc_specifics[key] = (void *)pointer; + return 0; +} + +static void * +libc_internal_tsd_get ( enum __libc_tsd_key_t key ) +{ + int tid = pthread_self(); + /* printf("GET GET GET key %d\n", key); */ + if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) + barf("libc_internal_tsd_get: invalid key"); + init_global_thread_specific_state(); + return thread_specific_state[tid].libc_specifics[key]; +} + + +int (*__libc_internal_tsd_set) + (enum __libc_tsd_key_t key, const void * pointer) + = libc_internal_tsd_set; + +void* (*__libc_internal_tsd_get) + (enum __libc_tsd_key_t key) + = libc_internal_tsd_get; + + +#ifdef GLIBC_2_3 +/* This one was first spotted be me in the glibc-2.2.93 sources. */ +static void** +libc_internal_tsd_address ( enum __libc_tsd_key_t key ) +{ + int tid = pthread_self(); + /* printf("ADDR ADDR ADDR key %d\n", key); */ + if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) + barf("libc_internal_tsd_address: invalid key"); + init_global_thread_specific_state(); + return &thread_specific_state[tid].libc_specifics[key]; +} + +void ** (*__libc_internal_tsd_address) + (enum __libc_tsd_key_t key) + = libc_internal_tsd_address; +#endif + + +/* --------------------------------------------------------------------- + These are here (I think) because they are deemed cancellation + points by POSIX. For the moment we'll simply pass the call along + to the corresponding thread-unaware (?) libc routine. + ------------------------------------------------------------------ */ + +static void *libpthread_handle; + +#define FORWARD(name, altname, args...) \ + ({ \ + static name##_t name##_ptr = NULL; \ + if (libpthread_handle == NULL) { \ + libpthread_handle = dlopen("libpthread.so.0", RTLD_LAZY); \ + my_assert(libpthread_handle != NULL); \ + } \ + if (name##_ptr == NULL) { \ + if ((name##_ptr = (name##_t)dlsym(RTLD_NEXT, #name)) == NULL) \ + name##_ptr = (name##_t)dlsym(RTLD_DEFAULT, #altname); \ + my_assert(name##_ptr != NULL && name##_ptr != dlsym(libpthread_handle, #name)); \ + } \ + name##_ptr(args); \ + }) + +typedef +int (*sigaction_t) + (int signum, + const struct sigaction *act, + struct sigaction *oldact); +int sigaction(int signum, + const struct sigaction *act, + struct sigaction *oldact) +{ + __my_pthread_testcancel(); +#ifdef GLIBC_2_1 + return FORWARD(sigaction, __sigaction, signum, act, oldact); +#else + return FORWARD(sigaction, __libc_sigaction, signum, act, oldact); +#endif +} + +typedef +int (*accept_t)(int fd, struct sockaddr *addr, socklen_t *len); + +WEAK +int accept(int fd, struct sockaddr *addr, socklen_t *len) +{ + __my_pthread_testcancel(); + return FORWARD(accept, __libc_accept, fd, addr, len); +} + +typedef +int (*connect_t)(int sockfd, + const struct sockaddr *serv_addr, + socklen_t addrlen); +WEAK +int connect(int sockfd, + const struct sockaddr *serv_addr, + socklen_t addrlen) +{ + __my_pthread_testcancel(); + return FORWARD(connect, __libc_connect, sockfd, serv_addr, addrlen); +} + + +typedef +int (*fcntl_t)(int fd, int cmd, long arg); +WEAK +int fcntl(int fd, int cmd, long arg) +{ + __my_pthread_testcancel(); + return FORWARD(fcntl, __libc_fcntl, fd, cmd, arg); +} + + +typedef +ssize_t (*write_t)(int fd, const void *buf, size_t count); +WEAK +ssize_t write(int fd, const void *buf, size_t count) +{ + __my_pthread_testcancel(); + return FORWARD(write, __libc_write, fd, buf, count); +} + + +typedef +ssize_t (*read_t)(int fd, void *buf, size_t count); +WEAK +ssize_t read(int fd, void *buf, size_t count) +{ + __my_pthread_testcancel(); + return FORWARD(read, __libc_read, fd, buf, count); +} + +typedef +int (*open64_t)(const char *pathname, int flags, mode_t mode); +/* WEAK */ +int open64(const char *pathname, int flags, mode_t mode) +{ + return FORWARD(open64, __libc_open64, pathname, flags, mode); +} + +typedef +int (*open_t)(const char *pathname, int flags, mode_t mode); +/* WEAK */ +int open(const char *pathname, int flags, mode_t mode) +{ + return FORWARD(open, __libc_open, pathname, flags, mode); +} + +typedef +int (*close_t)(int fd); +WEAK +int close(int fd) +{ + __my_pthread_testcancel(); + return FORWARD(close, __libc_close, fd); +} + + +typedef +pid_t (*waitpid_t)(pid_t pid, int *status, int options); +WEAK +pid_t waitpid(pid_t pid, int *status, int options) +{ + __my_pthread_testcancel(); + return FORWARD(waitpid, __libc_waitpid, pid, status, options); +} + + +typedef +int (*__nanosleep_t)(const struct timespec *req, struct timespec *rem); +WEAK +int __nanosleep(const struct timespec *req, struct timespec *rem) +{ + __my_pthread_testcancel(); + return FORWARD(__nanosleep, __libc_nanosleep, req, rem); +} + +typedef +int (*pause_t)(void); +WEAK +int pause(void) +{ + __my_pthread_testcancel(); + return FORWARD(pause, __libc_pause); +} + + +typedef +int (*__tcdrain_t)(int fd); +WEAK +int __tcdrain(int fd) +{ + __my_pthread_testcancel(); + return FORWARD(__tcdrain, __libc_tcdrain, fd); +} + + +typedef +int (*fsync_t)(int fd); +WEAK +int fsync(int fd) +{ + __my_pthread_testcancel(); + return FORWARD(fsync, __libc_fsync, fd); +} + + +typedef +off_t (*lseek_t)(int fildes, off_t offset, int whence); +WEAK +off_t lseek(int fildes, off_t offset, int whence) +{ + __my_pthread_testcancel(); + return FORWARD(lseek, __libc_lseek, fildes, offset, whence); +} + + +typedef +__off64_t (*lseek64_t)(int fildes, __off64_t offset, int whence); +WEAK +__off64_t lseek64(int fildes, __off64_t offset, int whence) +{ + __my_pthread_testcancel(); + return FORWARD(lseek64, __libc_lseek64, fildes, offset, whence); +} + + +typedef +ssize_t (*__pread64_t) (int __fd, void *__buf, size_t __nbytes, + __off64_t __offset); +ssize_t __pread64 (int __fd, void *__buf, size_t __nbytes, + __off64_t __offset) +{ + __my_pthread_testcancel(); + return FORWARD(__pread64, __libc_pread64, __fd, __buf, __nbytes, __offset); +} + + +typedef +ssize_t (*__pwrite64_t) (int __fd, const void *__buf, size_t __nbytes, + __off64_t __offset); +ssize_t __pwrite64 (int __fd, const void *__buf, size_t __nbytes, + __off64_t __offset) +{ + __my_pthread_testcancel(); + return FORWARD(__pwrite64, __libc_pwrite64, __fd, __buf, __nbytes, __offset); +} + + +typedef +ssize_t (*pwrite_t)(int fd, const void *buf, size_t count, off_t offset); +WEAK +ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + __my_pthread_testcancel(); + return FORWARD(pwrite, __libc_pwrite, fd, buf, count, offset); +} + + +typedef +ssize_t (*pread_t)(int fd, void *buf, size_t count, off_t offset); +WEAK +ssize_t pread(int fd, void *buf, size_t count, off_t offset) +{ + __my_pthread_testcancel(); + return FORWARD(pread, __libc_pread, fd, buf, count, offset); +} + +typedef +int (*recv_t)(int s, void *msg, size_t len, int flags); +WEAK +int recv(int s, void *msg, size_t len, int flags) +{ + __my_pthread_testcancel(); + return FORWARD(recv, __libc_recv, s, msg, len, flags); +} + +typedef +int (*send_t)(int s, const void *msg, size_t len, int flags); +WEAK +int send(int s, const void *msg, size_t len, int flags) +{ + __my_pthread_testcancel(); + return FORWARD(send, __libc_send, s, msg, len, flags); +} + + +typedef +int (*sendmsg_t)(int s, const struct msghdr *msg, int flags); +WEAK +int sendmsg(int s, const struct msghdr *msg, int flags) +{ + __my_pthread_testcancel(); + return FORWARD(sendmsg, __libc_sendmsg, s, msg, flags); +} + + +typedef +int (*recvmsg_t)(int s, struct msghdr *msg, int flags); +WEAK +int recvmsg(int s, struct msghdr *msg, int flags) +{ + __my_pthread_testcancel(); + return FORWARD(recvmsg, __libc_recvmsg, s, msg, flags); +} + + +typedef +int (*recvfrom_t)(int s, void *buf, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen); +WEAK +int recvfrom(int s, void *buf, size_t len, int flags, + struct sockaddr *from, socklen_t *fromlen) +{ + __my_pthread_testcancel(); + return FORWARD(recvfrom, __libc_recfrom, s, buf, len, flags, from, fromlen); +} + + +typedef +int (*sendto_t)(int s, const void *msg, size_t len, int flags, + const struct sockaddr *to, socklen_t tolen); +WEAK +int sendto(int s, const void *msg, size_t len, int flags, + const struct sockaddr *to, socklen_t tolen) +{ + __my_pthread_testcancel(); + return FORWARD(sendto, __libc_sendto, s, msg, len, flags, to, tolen); +} + + +typedef +int (*system_t)(const char* str); +WEAK +int system(const char* str) +{ + __my_pthread_testcancel(); + return FORWARD(system, __libc_system, str); +} + + +typedef +pid_t (*wait_t)(int *status); +WEAK +pid_t wait(int *status) +{ + __my_pthread_testcancel(); + return FORWARD(wait, __libc_wait, status); +} + + +typedef +int (*msync_t)(const void *start, size_t length, int flags); +WEAK +int msync(const void *start, size_t length, int flags) +{ + __my_pthread_testcancel(); + return FORWARD(msync, __libc_msync, start, length, flags); +} + +strong_alias(close, __close) +strong_alias(fcntl, __fcntl) +strong_alias(lseek, __lseek) +strong_alias(open, __open) +strong_alias(open64, __open64) +strong_alias(read, __read) +strong_alias(wait, __wait) +strong_alias(write, __write) +strong_alias(connect, __connect) +strong_alias(send, __send) +strong_alias(pause, __pause) + +weak_alias (__pread64, pread64) +weak_alias (__pwrite64, pwrite64) +weak_alias(__nanosleep, nanosleep) +weak_alias(__tcdrain, tcdrain) + + +typedef +void (*longjmp_t)(jmp_buf env, int val) __attribute((noreturn)); +/* not weak: WEAK */ +void longjmp(jmp_buf env, int val) +{ + FORWARD(longjmp, __libc_longjmp, env, val); +} + + +typedef void (*siglongjmp_t) (sigjmp_buf env, int val) + __attribute__ ((noreturn)); +void siglongjmp(sigjmp_buf env, int val) +{ + kludged("siglongjmp", "(it ignores cleanup handlers)"); + FORWARD(siglongjmp, __libc_siglongjmp, env, val); +} + + +/*--- fork and its helper ---*/ + +static +void run_fork_handlers ( int what ) +{ + ForkHandlerEntry entry; + int n_h, n_handlers, i, res; + + my_assert(what == 0 || what == 1 || what == 2); + + /* Fetch old counter */ + VALGRIND_MAGIC_SEQUENCE(n_handlers, -2 /* default */, + VG_USERREQ__GET_FHSTACK_USED, + 0, 0, 0, 0); + my_assert(n_handlers >= 0 && n_handlers < VG_N_FORKHANDLERSTACK); + + /* Prepare handlers (what == 0) are called in opposite order of + calls to pthread_atfork. Parent and child handlers are called + in the same order as calls to pthread_atfork. */ + if (what == 0) + n_h = n_handlers - 1; + else + n_h = 0; + + for (i = 0; i < n_handlers; i++) { + VALGRIND_MAGIC_SEQUENCE(res, -2 /* default */, + VG_USERREQ__GET_FHSTACK_ENTRY, + n_h, &entry, 0, 0); + my_assert(res == 0); + switch (what) { + case 0: if (entry.prepare) entry.prepare(); + n_h--; break; + case 1: if (entry.parent) entry.parent(); + n_h++; break; + case 2: if (entry.child) entry.child(); + n_h++; break; + default: barf("run_fork_handlers: invalid what"); + } + } + + if (what != 0 /* prepare */) { + /* Empty out the stack. */ + VALGRIND_MAGIC_SEQUENCE(res, -2 /* default */, + VG_USERREQ__SET_FHSTACK_USED, + 0, 0, 0, 0); + my_assert(res == 0); + } +} + +typedef +pid_t (*__fork_t)(void); +pid_t __fork(void) +{ + pid_t pid; + __my_pthread_testcancel(); + __pthread_mutex_lock(&pthread_atfork_lock); + + run_fork_handlers(0 /* prepare */); + pid = FORWARD(__fork, __libc_fork); + if (pid == 0) { + /* I am the child */ + run_fork_handlers(2 /* child */); + __pthread_mutex_unlock(&pthread_atfork_lock); + __pthread_mutex_init(&pthread_atfork_lock, NULL); + } else { + /* I am the parent */ + run_fork_handlers(1 /* parent */); + __pthread_mutex_unlock(&pthread_atfork_lock); + } + return pid; +} + + +pid_t __vfork(void) +{ + return __fork(); +} + + + +/* --------------------------------------------------------------------- + Hacky implementation of semaphores. + ------------------------------------------------------------------ */ + +#include + +typedef + struct { + pthread_mutex_t se_mx; + pthread_cond_t se_cv; + int count; + int waiters; + } + vg_sem_t; + +#define SEM_CHECK_MAGIC 0x5b1d0772 + +typedef + struct { + union { + vg_sem_t* p; + int i; + } shadow; + int err_check; + } + user_sem_t; + + +static vg_sem_t* se_new ( sem_t* orig ) +{ + user_sem_t* u_sem = (user_sem_t*)orig; + vg_sem_t* vg_sem; + + vg_sem = my_malloc(sizeof(vg_sem_t)); + + u_sem->shadow.p = vg_sem; + u_sem->err_check = u_sem->shadow.i ^ SEM_CHECK_MAGIC; + + return vg_sem; +} + +static vg_sem_t* se_lookup ( sem_t* orig ) +{ + user_sem_t* u_sem = (user_sem_t*) orig; + + if(!u_sem->shadow.p || ((u_sem->shadow.i ^ SEM_CHECK_MAGIC) != u_sem->err_check)) + return NULL; + + return u_sem->shadow.p; +} + +static void se_free( sem_t* orig ) +{ + user_sem_t* u_sem = (user_sem_t*) orig; + + my_free(u_sem->shadow.p); + + u_sem->shadow.p = NULL; + u_sem->err_check = 0; + + return; +} + +int sem_init(sem_t *sem, int pshared, unsigned int value) +{ + int res; + vg_sem_t* vg_sem; + ensure_valgrind("sem_init"); + if (pshared != 0) { + pthread_error("sem_init: unsupported pshared value"); + *(__errno_location()) = ENOSYS; + return -1; + } + vg_sem = se_new(sem); + + res = pthread_mutex_init(&vg_sem->se_mx, NULL); + my_assert(res == 0); + res = pthread_cond_init(&vg_sem->se_cv, NULL); + my_assert(res == 0); + vg_sem->count = value; + vg_sem->waiters = 0; + return 0; +} + +int sem_wait ( sem_t* sem ) +{ + int res; + vg_sem_t* vg_sem; + ensure_valgrind("sem_wait"); + vg_sem = se_lookup(sem); + if(!vg_sem) { + pthread_error("sem_wait: semaphore overwritten or not initialized"); + *(__errno_location()) = EINVAL; + return -1; + } + res = __pthread_mutex_lock(&vg_sem->se_mx); + my_assert(res == 0); + while (vg_sem->count == 0) { + ++vg_sem->waiters; + res = pthread_cond_wait(&vg_sem->se_cv, &vg_sem->se_mx); + --vg_sem->waiters; + my_assert(res == 0); + } + vg_sem->count--; + res = __pthread_mutex_unlock(&vg_sem->se_mx); + my_assert(res == 0); + return 0; +} + +int sem_post ( sem_t* sem ) +{ + int res; + vg_sem_t* vg_sem; + ensure_valgrind("sem_post"); + vg_sem = se_lookup(sem); + if(!vg_sem) { + pthread_error("sem_post: semaphore overwritten or not initialized"); + *(__errno_location()) = EINVAL; + return -1; + } + res = __pthread_mutex_lock(&vg_sem->se_mx); + my_assert(res == 0); + if (vg_sem->count == 0) { + vg_sem->count++; + res = pthread_cond_broadcast(&vg_sem->se_cv); + my_assert(res == 0); + } else { + vg_sem->count++; + } + res = __pthread_mutex_unlock(&vg_sem->se_mx); + my_assert(res == 0); + return 0; +} + + +int sem_trywait ( sem_t* sem ) +{ + int ret, res; + vg_sem_t* vg_sem; + ensure_valgrind("sem_trywait"); + vg_sem = se_lookup(sem); + if(!vg_sem) { + pthread_error("sem_trywait: semaphore overwritten or not initialized"); + *(__errno_location()) = EINVAL; + return -1; + } + res = __pthread_mutex_lock(&vg_sem->se_mx); + my_assert(res == 0); + if (vg_sem->count > 0) { + vg_sem->count--; + ret = 0; + } else { + ret = -1; + *(__errno_location()) = EAGAIN; + } + res = __pthread_mutex_unlock(&vg_sem->se_mx); + my_assert(res == 0); + return ret; +} + + +int sem_getvalue(sem_t* sem, int * sval) +{ + int res; + vg_sem_t* vg_sem; + ensure_valgrind("sem_getvalue"); + vg_sem = se_lookup(sem); + if(!vg_sem) { + pthread_error("sem_getvalue: semaphore overwritten or not initialized"); + *(__errno_location()) = EINVAL; + return -1; + } + res = __pthread_mutex_lock(&vg_sem->se_mx); + my_assert(res == 0); + *sval = vg_sem->count; + res = __pthread_mutex_unlock(&vg_sem->se_mx); + my_assert(res == 0); + return 0; +} + + +int sem_destroy(sem_t * sem) +{ + /* if someone waiting on this semaphore, errno = EBUSY, return -1 */ + vg_sem_t* vg_sem; + int res; + ensure_valgrind("sem_destroy"); + vg_sem = se_lookup(sem); + if(!vg_sem) { + pthread_error("sem_destroy: semaphore overwritten or not initialized"); + *(__errno_location()) = EINVAL; + return -1; + } + res = __pthread_mutex_lock(&vg_sem->se_mx); + my_assert(res == 0); + if (vg_sem->waiters > 0) + { + *(__errno_location()) = EBUSY; + res = __pthread_mutex_unlock(&vg_sem->se_mx); + my_assert(res == 0); + return -1; + } + res = pthread_cond_destroy(&vg_sem->se_cv); + my_assert(res == 0); + res = __pthread_mutex_unlock(&vg_sem->se_mx); + my_assert(res == 0); + res = pthread_mutex_destroy(&vg_sem->se_mx); + my_assert(res == 0); + se_free(sem); + return 0; +} + + +int sem_timedwait(sem_t* sem, const struct timespec *abstime) +{ + int res; + vg_sem_t* vg_sem; + ensure_valgrind("sem_timedwait"); + vg_sem = se_lookup(sem); + if(!vg_sem) { + pthread_error("sem_timedwait: semaphore overwritten or not initialized"); + *(__errno_location()) = EINVAL; + return -1; + } + res = __pthread_mutex_lock(&vg_sem->se_mx); + my_assert(res == 0); + while ( vg_sem->count == 0 && res != ETIMEDOUT ) { + ++vg_sem->waiters; + res = pthread_cond_timedwait(&vg_sem->se_cv, &vg_sem->se_mx, abstime); + --vg_sem->waiters; + } + if ( vg_sem->count > 0 ) { + vg_sem->count--; + res = __pthread_mutex_unlock(&vg_sem->se_mx); + my_assert(res == 0 ); + return 0; + } else { + res = __pthread_mutex_unlock(&vg_sem->se_mx); + my_assert(res == 0 ); + *(__errno_location()) = ETIMEDOUT; + return -1; + } +} + + +/* --------------------------------------------------------------------- + Reader-writer locks. + ------------------------------------------------------------------ */ + +typedef + struct { + int prefer_w; /* != 0 --> prefer writer */ + int nwait_r; /* # of waiting readers */ + int nwait_w; /* # of waiting writers */ + pthread_cond_t cv_r; /* for signalling readers */ + pthread_cond_t cv_w; /* for signalling writers */ + pthread_mutex_t mx; + int status; + /* allowed range for status: >= -1. -1 means 1 writer currently + active, >= 0 means N readers currently active. */ + } + vg_rwlock_t; + + +static pthread_mutex_t rw_new_mx = PTHREAD_MUTEX_INITIALIZER; + +#define RWLOCK_CHECK_MAGIC 0xb5d17027 + + +static void init_vg_rwlock ( vg_rwlock_t* vg_rwl ) +{ + int res = 0; + vg_rwl->prefer_w = 1; + vg_rwl->nwait_r = 0; + vg_rwl->nwait_w = 0; + vg_rwl->status = 0; + res = pthread_mutex_init(&vg_rwl->mx, NULL); + res |= pthread_cond_init(&vg_rwl->cv_r, NULL); + res |= pthread_cond_init(&vg_rwl->cv_w, NULL); + my_assert(res == 0); +} + +static vg_rwlock_t* rw_new ( pthread_rwlock_t* orig ) +{ + int res; + vg_rwlock_t* rwl; + vg_pthread_rwlock_t* vg_orig; + CONVERT(rwlock, orig, vg_orig); + + res = __pthread_mutex_lock(&rw_new_mx); + my_assert(res == 0); + + rwl = my_malloc(sizeof(vg_rwlock_t)); + + vg_orig->__vg_rw_writer = rwl; + vg_orig->__vg_rw_read_waiting = (void *)((Addr)rwl ^ RWLOCK_CHECK_MAGIC); + + init_vg_rwlock(rwl); + if (vg_orig->__vg_rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP) + rwl->prefer_w = 0; + + res = __pthread_mutex_unlock(&rw_new_mx); + my_assert(res == 0); + + return rwl; +} + +static vg_rwlock_t* rw_lookup ( pthread_rwlock_t* orig ) +{ + vg_rwlock_t* rwl; + vg_pthread_rwlock_t* vg_orig; + CONVERT(rwlock, orig, vg_orig); + + if (vg_orig->__vg_rw_writer == NULL) + rwl = rw_new ((pthread_rwlock_t*)vg_orig); + else if (((Addr)vg_orig->__vg_rw_writer ^ RWLOCK_CHECK_MAGIC) == (Addr)vg_orig->__vg_rw_read_waiting) + rwl = vg_orig->__vg_rw_writer; + else + rwl = NULL; + + return rwl; +} + +static void rw_free ( pthread_rwlock_t* orig ) +{ + int res; + vg_rwlock_t* rwl; + vg_pthread_rwlock_t* vg_orig; + CONVERT(rwlock, orig, vg_orig); + + rwl = vg_orig->__vg_rw_writer; + + vg_orig->__vg_rw_writer = NULL; + vg_orig->__vg_rw_read_waiting = NULL; + + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + + res = pthread_cond_destroy(&rwl->cv_w); + res |= pthread_cond_destroy(&rwl->cv_r); + res |= pthread_mutex_destroy(&rwl->mx); + my_assert(res == 0); + + my_free(rwl); + + return; +} + +int pthread_rwlock_init ( pthread_rwlock_t* orig, + const pthread_rwlockattr_t* attr ) +{ + vg_rwlock_t* rwl; + vg_pthread_rwlock_t* vg_orig; + vg_pthread_rwlockattr_t* vg_attr; + CONVERT(rwlock, orig, vg_orig); + CONVERT(rwlockattr, attr, vg_attr); + + if (0) printf ("pthread_rwlock_init\n"); + /* Install the lock preference; the remapper needs to know it. */ + if (vg_attr) + vg_orig->__vg_rw_kind = vg_attr->__vg_lockkind; + else + vg_orig->__vg_rw_kind = PTHREAD_RWLOCK_DEFAULT_NP; + /* Allocate the shadow */ + rwl = rw_new ((pthread_rwlock_t *)vg_orig); + return 0; +} + + +static +void pthread_rwlock_rdlock_CANCEL_HDLR ( void* rwl_v ) +{ + vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v; + rwl->nwait_r--; + pthread_mutex_unlock (&rwl->mx); +} + + +int pthread_rwlock_rdlock ( pthread_rwlock_t* orig ) +{ + int res; + vg_rwlock_t* rwl; + + if (0) printf ("pthread_rwlock_rdlock\n"); + rwl = rw_lookup (orig); + if(!rwl) { + pthread_error("pthread_rwlock_rdlock: lock overwritten or not initialized"); + return EINVAL; + } + res = __pthread_mutex_lock(&rwl->mx); + my_assert(res == 0); + if (rwl->status < 0) { + my_assert(rwl->status == -1); + rwl->nwait_r++; + pthread_cleanup_push( pthread_rwlock_rdlock_CANCEL_HDLR, rwl ); + while (1) { + if (rwl->status == 0) break; + res = pthread_cond_wait(&rwl->cv_r, &rwl->mx); + my_assert(res == 0); + } + pthread_cleanup_pop(0); + rwl->nwait_r--; + } + my_assert(rwl->status >= 0); + rwl->status++; + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return 0; +} + + +int pthread_rwlock_tryrdlock ( pthread_rwlock_t* orig ) +{ + int res; + vg_rwlock_t* rwl; + + if (0) printf ("pthread_rwlock_tryrdlock\n"); + rwl = rw_lookup (orig); + if(!rwl) { + pthread_error("pthread_rwlock_tryrdlock: lock overwritten or not initialized"); + return EINVAL; + } + res = __pthread_mutex_lock(&rwl->mx); + my_assert(res == 0); + if (rwl->status == -1) { + /* Writer active; we have to give up. */ + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return EBUSY; + } + /* Success */ + my_assert(rwl->status >= 0); + rwl->status++; + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return 0; +} + + +static +void pthread_rwlock_wrlock_CANCEL_HDLR ( void* rwl_v ) +{ + vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v; + rwl->nwait_w--; + pthread_mutex_unlock (&rwl->mx); +} + + +int pthread_rwlock_wrlock ( pthread_rwlock_t* orig ) +{ + int res; + vg_rwlock_t* rwl; + + if (0) printf ("pthread_rwlock_wrlock\n"); + rwl = rw_lookup (orig); + if(!rwl) { + pthread_error("pthread_rwlock_wrlock: lock overwritten or not initialized"); + return EINVAL; + } + res = __pthread_mutex_lock(&rwl->mx); + my_assert(res == 0); + if (rwl->status != 0) { + rwl->nwait_w++; + pthread_cleanup_push( pthread_rwlock_wrlock_CANCEL_HDLR, rwl ); + while (1) { + if (rwl->status == 0) break; + res = pthread_cond_wait(&rwl->cv_w, &rwl->mx); + my_assert(res == 0); + } + pthread_cleanup_pop(0); + rwl->nwait_w--; + } + my_assert(rwl->status == 0); + rwl->status = -1; + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return 0; +} + + +int pthread_rwlock_trywrlock ( pthread_rwlock_t* orig ) +{ + int res; + vg_rwlock_t* rwl; + if (0) printf ("pthread_wrlock_trywrlock\n"); + rwl = rw_lookup (orig); + if(!rwl) { + pthread_error("pthread_rwlock_trywrlock: lock overwritten or not initialized"); + return EINVAL; + } + res = __pthread_mutex_lock(&rwl->mx); + my_assert(res == 0); + if (rwl->status != 0) { + /* Reader(s) or a writer active; we have to give up. */ + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return EBUSY; + } + /* Success */ + my_assert(rwl->status == 0); + rwl->status = -1; + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return 0; +} + + +int pthread_rwlock_unlock ( pthread_rwlock_t* orig ) +{ + int res; + vg_rwlock_t* rwl; + if (0) printf ("pthread_rwlock_unlock\n"); + rwl = rw_lookup (orig); + if(!rwl) { + pthread_error("pthread_rwlock_unlock: lock overwritten or not initialized"); + return EINVAL; + } + res = __pthread_mutex_lock(&rwl->mx); + my_assert(res == 0); + if (rwl->status == 0) { + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return EPERM; + } + my_assert(rwl->status != 0); + if (rwl->status == -1) { + rwl->status = 0; + } else { + my_assert(rwl->status > 0); + rwl->status--; + } + + my_assert(rwl->status >= 0); + + if (rwl->prefer_w) { + + /* Favour waiting writers, if any. */ + if (rwl->nwait_w > 0) { + /* Writer(s) are waiting. */ + if (rwl->status == 0) { + /* We can let a writer in. */ + res = pthread_cond_signal(&rwl->cv_w); + my_assert(res == 0); + } else { + /* There are still readers active. Do nothing; eventually + they will disappear, at which point a writer will be + admitted. */ + } + } + else + /* No waiting writers. */ + if (rwl->nwait_r > 0) { + /* Let in a waiting reader. */ + res = pthread_cond_signal(&rwl->cv_r); + my_assert(res == 0); + } + + } else { + + /* Favour waiting readers, if any. */ + if (rwl->nwait_r > 0) { + /* Reader(s) are waiting; let one in. */ + res = pthread_cond_signal(&rwl->cv_r); + my_assert(res == 0); + } + else + /* No waiting readers. */ + if (rwl->nwait_w > 0 && rwl->status == 0) { + /* We have waiting writers and no active readers; let a + writer in. */ + res = pthread_cond_signal(&rwl->cv_w); + my_assert(res == 0); + } + } + + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return 0; +} + + +int pthread_rwlock_destroy ( pthread_rwlock_t *orig ) +{ + int res; + vg_rwlock_t* rwl; + if (0) printf ("pthread_rwlock_destroy\n"); + rwl = rw_lookup (orig); + if(!rwl) { + pthread_error("pthread_rwlock_destroy: lock overwritten or not initialized"); + return EINVAL; + } + res = __pthread_mutex_lock(&rwl->mx); + my_assert(res == 0); + if (rwl->status != 0 || rwl->nwait_r > 0 || rwl->nwait_w > 0) { + res = __pthread_mutex_unlock(&rwl->mx); + my_assert(res == 0); + return EBUSY; + } + rw_free (orig); + return 0; +} + + +/* Copied directly from LinuxThreads. */ +int +pthread_rwlockattr_init (pthread_rwlockattr_t *attr) +{ + vg_pthread_rwlockattr_t* vg_attr; + CONVERT(rwlockattr, attr, vg_attr); + vg_attr->__vg_lockkind = 0; + vg_attr->__vg_pshared = PTHREAD_PROCESS_PRIVATE; + return 0; +} + +/* Copied directly from LinuxThreads. */ +int +pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr) +{ + return 0; +} + +/* Copied directly from LinuxThreads. */ +int +pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) +{ + vg_pthread_rwlockattr_t* vg_attr; + CONVERT(rwlockattr, attr, vg_attr); + + if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) + return EINVAL; + + /* For now it is not possible to shared a conditional variable. */ + if (pshared != PTHREAD_PROCESS_PRIVATE) + return ENOSYS; + + vg_attr->__vg_pshared = pshared; + + return 0; +} + + + +/* --------------------------------------------------------------------- + Manage the allocation and use of RT signals. The Valgrind core + uses one. glibc needs us to implement this to make RT signals + work; things just seem to crash if we don't. + ------------------------------------------------------------------ */ +int __libc_current_sigrtmin (void) +{ + int res; + + VALGRIND_MAGIC_SEQUENCE(res, 0, + VG_USERREQ__GET_SIGRT_MIN, + 0, 0, 0, 0); + + return res; +} + +int __libc_current_sigrtmax (void) +{ + int res; + + VALGRIND_MAGIC_SEQUENCE(res, 0, + VG_USERREQ__GET_SIGRT_MAX, + 0, 0, 0, 0); + + return res; +} + +int __libc_allocate_rtsig (int high) +{ + int res; + + VALGRIND_MAGIC_SEQUENCE(res, 0, + VG_USERREQ__ALLOC_RTSIG, + high, 0, 0, 0); + + return res; +} + +/* --------------------------------------------------------------------- + B'stard. + ------------------------------------------------------------------ */ +strong_alias(__pthread_mutex_lock, pthread_mutex_lock) +strong_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock) +strong_alias(__pthread_mutex_trylock, pthread_mutex_trylock) +strong_alias(__pthread_mutex_unlock, pthread_mutex_unlock) +strong_alias(__pthread_mutexattr_init, pthread_mutexattr_init) + weak_alias(__pthread_mutexattr_settype, pthread_mutexattr_settype) + weak_alias(__pthread_mutexattr_gettype, pthread_mutexattr_gettype) + weak_alias(__pthread_mutexattr_setpshared, pthread_mutexattr_setpshared) +strong_alias(__pthread_mutex_init, pthread_mutex_init) +strong_alias(__pthread_mutexattr_destroy, pthread_mutexattr_destroy) +strong_alias(__pthread_mutex_destroy, pthread_mutex_destroy) +strong_alias(__pthread_once, pthread_once) +strong_alias(__pthread_atfork, pthread_atfork) +strong_alias(__pthread_key_create, pthread_key_create) +strong_alias(__pthread_getspecific, pthread_getspecific) +strong_alias(__pthread_setspecific, pthread_setspecific) + +#ifndef GLIBC_2_1 +strong_alias(sigaction, __sigaction) +#endif + +weak_alias(__fork, fork) +weak_alias(__vfork, vfork) +weak_alias (__pthread_kill_other_threads_np, pthread_kill_other_threads_np) + +/*--------------------------------------------------*/ + +weak_alias(pthread_rwlock_rdlock, __pthread_rwlock_rdlock) +weak_alias(pthread_rwlock_unlock, __pthread_rwlock_unlock) +weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock) + +weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy) +weak_alias(pthread_rwlock_init, __pthread_rwlock_init) +weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock) +weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock) + + +#ifndef __UCLIBC__ +/* These are called as part of stdio to lock the FILE structure for MT + programs. Unfortunately, the lock is not always a pthreads lock - + the NPTL version uses a lighter-weight lock which uses futex + directly (and uses a structure which is smaller than + pthread_mutex). So basically, this is completely broken on recent + glibcs. */ + +#undef _IO_flockfile +void _IO_flockfile ( _IO_FILE * file ) +{ + pthread_mutex_lock(file->_lock); +} +strong_alias(_IO_flockfile, __flockfile); +weak_alias(_IO_flockfile, flockfile); + +#undef _IO_funlockfile +void _IO_funlockfile ( _IO_FILE * file ) +{ + pthread_mutex_unlock(file->_lock); +} +strong_alias(_IO_funlockfile, __funlockfile); +weak_alias(_IO_funlockfile, funlockfile); +#endif + + +/* This doesn't seem to be needed to simulate libpthread.so's external + interface, but many people complain about its absence. */ + +strong_alias(__pthread_mutexattr_settype, __pthread_mutexattr_setkind_np) +weak_alias(__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np) + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_libpthread.vs b/VEX/head20041019/coregrind/vg_libpthread.vs new file mode 100644 index 0000000000..afc76ed3fe --- /dev/null +++ b/VEX/head20041019/coregrind/vg_libpthread.vs @@ -0,0 +1,201 @@ + GLIBC_2.0 { + pthread_join; pthread_self; pthread_equal; + pthread_exit; pthread_detach; + + pthread_getschedparam; pthread_setschedparam; + + pthread_attr_destroy; + pthread_attr_getdetachstate; pthread_attr_setdetachstate; + pthread_attr_getschedparam; pthread_attr_setschedparam; + pthread_attr_getschedpolicy; pthread_attr_setschedpolicy; + pthread_attr_getinheritsched; pthread_attr_setinheritsched; + pthread_attr_getscope; pthread_attr_setscope; + + pthread_mutex_init; pthread_mutex_destroy; + pthread_mutex_lock; pthread_mutex_trylock; pthread_mutex_unlock; + + pthread_mutexattr_init; pthread_mutexattr_destroy; + + # Don't version these, because it doesn't matter for Valgrind's libpthread + #pthread_cond_init; pthread_cond_destroy; + #pthread_cond_wait; pthread_cond_timedwait; + #pthread_cond_signal; pthread_cond_broadcast; + + pthread_condattr_destroy; pthread_condattr_init; + + pthread_cancel; pthread_testcancel; + pthread_setcancelstate; pthread_setcanceltype; + + pthread_sigmask; pthread_kill; + + pthread_key_create; pthread_key_delete; + pthread_getspecific; pthread_setspecific; + + pthread_once; + + pthread_atfork; + + flockfile; funlockfile; ftrylockfile; + + # Non-standard POSIX1.x functions. + pthread_mutexattr_getkind_np; pthread_mutexattr_setkind_np; + + # Protected names for functions used in other shared objects. + __pthread_mutex_init; __pthread_mutex_destroy; + __pthread_mutex_lock; __pthread_mutex_trylock; __pthread_mutex_unlock; + __pthread_mutexattr_init; __pthread_mutexattr_destroy; + __pthread_mutexattr_settype; + __pthread_key_create; __pthread_getspecific; __pthread_setspecific; + __pthread_once; __pthread_atfork; + _IO_flockfile; _IO_ftrylockfile; _IO_funlockfile; + + # Hidden entry point (through macros). + #_pthread_cleanup_pop; _pthread_cleanup_pop_restore; _pthread_cleanup_push; + #_pthread_cleanup_push_defer; + + # Semaphores. + #sem_destroy; sem_getvalue; sem_init; sem_post; sem_trywait; sem_wait; + + # Special fork handling. + fork; __fork; vfork; + + # Cancellation points. + close; __close; fcntl; __fcntl; read; __read; write; __write; accept; + connect; __connect; recv; recvfrom; recvmsg; send; __send; sendmsg; sendto; + fsync; lseek; __lseek; msync; nanosleep; open; __open; pause; tcdrain; + system; wait; __wait; waitpid; + + # Hidden entry point (through macros). + _pthread_cleanup_push; _pthread_cleanup_pop; + _pthread_cleanup_push_defer; _pthread_cleanup_pop_restore; + + pthread_kill_other_threads_np; + + # The error functions. + __errno_location; __h_errno_location; + + # Functions which previously have been overwritten. + sigwait; sigaction; __sigaction; _exit; _Exit; longjmp; siglongjmp; + raise; + }; + + GLIBC_2.1 { + pthread_create; + pthread_attr_init; + + pthread_attr_getguardsize; pthread_attr_setguardsize; + pthread_attr_getstackaddr; pthread_attr_setstackaddr; + pthread_attr_getstacksize; pthread_attr_setstacksize; + + pthread_mutexattr_gettype; pthread_mutexattr_settype; + + pthread_rwlock_init; pthread_rwlock_destroy; + pthread_rwlock_rdlock; pthread_rwlock_wrlock; pthread_rwlock_unlock; + pthread_rwlock_tryrdlock; pthread_rwlock_trywrlock; + + pthread_rwlockattr_init; pthread_rwlockattr_destroy; + pthread_rwlockattr_getpshared; pthread_rwlockattr_setpshared; + pthread_rwlockattr_getkind_np; pthread_rwlockattr_setkind_np; + + pthread_getconcurrency; pthread_setconcurrency; + + # Semaphores. + #sem_destroy; sem_getvalue; sem_init; sem_post; sem_trywait; sem_wait; + + __libc_current_sigrtmin; __libc_current_sigrtmax; + __libc_allocate_rtsig; + } GLIBC_2.0; + + GLIBC_2.1.1 { + sem_close; sem_open; sem_unlink; + } GLIBC_2.1; + + GLIBC_2.1.2 { + __vfork; + } GLIBC_2.1.1; + + GLIBC_2.2 { + pthread_mutexattr_getpshared; pthread_mutexattr_setpshared; + + pthread_condattr_getpshared; pthread_condattr_setpshared; + + # New functions from IEEE Std. 1003.1-2001. + pthread_mutex_timedlock; + + pthread_rwlock_timedrdlock; pthread_rwlock_timedwrlock; + + pthread_attr_getstack; pthread_attr_setstack; + + pthread_spin_destroy; pthread_spin_init; pthread_spin_lock; + pthread_spin_trylock; pthread_spin_unlock; + + pthread_barrier_init; pthread_barrier_destroy; pthread_barrier_wait; + pthread_barrierattr_destroy; pthread_barrierattr_init; + pthread_barrierattr_setpshared; + + sem_timedwait; + + pthread_yield; + + pthread_getcpuclockid; + + # Cancellation points. + lseek64; open64; __open64; pread; pread64; __pread64; pwrite; pwrite64; + __pwrite64; + + # Names used internally. + __pthread_rwlock_init; __pthread_rwlock_destroy; + __pthread_rwlock_rdlock; __pthread_rwlock_tryrdlock; + __pthread_rwlock_wrlock; __pthread_rwlock_trywrlock; + __pthread_rwlock_unlock; + + __res_state; + } GLIBC_2.1.2; + + GLIBC_2.2.3 { + # Extensions. + pthread_getattr_np; + } GLIBC_2.2; + + GLIBC_2.2.6 { + # Cancellation wrapper + __nanosleep; + } GLIBC_2.2.3; + + GLIBC_2.3.2 { + # Changed pthread_cond_t. + # Don't version these, because it doesn't matter for Valgrind's libpthread + #pthread_cond_init; pthread_cond_destroy; + #pthread_cond_wait; pthread_cond_timedwait; + #pthread_cond_signal; pthread_cond_broadcast; + } GLIBC_2.2.6; + + GLIBC_2.3.3 { + # 1003.1-2001 function accidentally left out in 2.2. + pthread_barrierattr_getpshared; + + # Unix CS option. + pthread_condattr_getclock; pthread_condattr_setclock; + + # Proposed API extensions. + pthread_tryjoin_np; pthread_timedjoin_np; + + # New cancellation cleanup handling. + __pthread_register_cancel; __pthread_unregister_cancel; + __pthread_register_cancel_defer; __pthread_unregister_cancel_restore; + __pthread_unwind_next; + __pthread_cleanup_routine; + + # New affinity interfaces. + pthread_getaffinity_np; pthread_setaffinity_np; + pthread_attr_getaffinity_np; pthread_attr_setaffinity_np; + } GLIBC_2.3.2; + + GLIBC_PRIVATE { + global: + __pthread_initialize_minimal; __pthread_cleanup_upto; + __pthread_clock_gettime; __pthread_clock_settime; + __pthread_unwind; + local: + __pthread_clock_gettime_*; __pthread_clock_settime_*; + }; diff --git a/VEX/head20041019/coregrind/vg_libpthread_unimp.c b/VEX/head20041019/coregrind/vg_libpthread_unimp.c new file mode 100644 index 0000000000..f40c57d9cf --- /dev/null +++ b/VEX/head20041019/coregrind/vg_libpthread_unimp.c @@ -0,0 +1,250 @@ + +/*--------------------------------------------------------------------*/ +/*--- Give dummy bindings for everything the real libpthread.so ---*/ +/*--- binds. vg_libpthread_unimp.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* --------------------------------------------------------------------- + ALL THIS CODE RUNS ON THE SIMULATED CPU. + Give a binding for everything the real libpthread.so binds. + ------------------------------------------------------------------ */ + +# define strong_alias(name, aliasname) \ + extern __typeof (name) aliasname __attribute__ ((alias (#name))); + +# define weak_alias(name, aliasname) \ + extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))); + +# define symbol_version(real, name, version) \ + __asm__(".symver " #real "," #name "@" #version) + +# define default_symbol_version(real, name, version) \ + __asm__(".symver " #real "," #name "@@" #version) + +extern void vgPlain_unimp ( char* ); +#define unimp(str) vgPlain_unimp(str) + +//void _IO_flockfile ( void ) { unimp("_IO_flockfile"); } +void _IO_ftrylockfile ( void ) { unimp("_IO_ftrylockfile"); } +//void _IO_funlockfile ( void ) { unimp("_IO_funlockfile"); } +//void __close ( void ) { unimp("__close"); } +//void __connect ( void ) { unimp("__connect"); } +//void __errno_location ( void ) { unimp("__errno_location"); } +//void __fcntl ( void ) { unimp("__fcntl"); } +//void __fork ( void ) { unimp("__fork"); } +//void __h_errno_location ( void ) { unimp("__h_errno_location"); } +//void __libc_allocate_rtsig ( void ) { unimp("__libc_allocate_rtsig"); } +//void __libc_current_sigrtmax ( void ) { unimp("__libc_current_sigrtmax"); } +//void __libc_current_sigrtmin ( void ) { unimp("__libc_current_sigrtmin"); } +//void __lseek ( void ) { unimp("__lseek"); } +//void __open ( void ) { unimp("__open"); } +//void __open64 ( void ) { unimp("__open64"); } +//void __pread64 ( void ) { unimp("__pread64"); } +//void __pthread_atfork ( void ) { unimp("__pthread_atfork"); } +//void __pthread_getspecific ( void ) { unimp("__pthread_getspecific"); } +//void __pthread_key_create ( void ) { unimp("__pthread_key_create"); } +//void __pthread_kill_other_threads_np ( void ) { unimp("__pthread_kill_other_threads_np"); } +//void __pthread_mutex_destroy ( void ) { unimp("__pthread_mutex_destroy"); } +//void __pthread_mutex_init ( void ) { unimp("__pthread_mutex_init"); } +//void __pthread_mutex_lock ( void ) { unimp("__pthread_mutex_lock"); } +//void __pthread_mutex_trylock ( void ) { unimp("__pthread_mutex_trylock"); } +//void __pthread_mutex_unlock ( void ) { unimp("__pthread_mutex_unlock"); } +//void __pthread_mutexattr_destroy ( void ) { unimp("__pthread_mutexattr_destroy"); } +//void __pthread_mutexattr_init ( void ) { unimp("__pthread_mutexattr_init"); } +//void __pthread_mutexattr_settype ( void ) { unimp("__pthread_mutexattr_settype"); } +//void __pthread_once ( void ) { unimp("__pthread_once"); } +//void __pthread_setspecific ( void ) { unimp("__pthread_setspecific"); } +//void __pwrite64 ( void ) { unimp("__pwrite64"); } +//void __read ( void ) { unimp("__read"); } +//void __res_state ( void ) { unimp("__res_state"); } +//void __send ( void ) { unimp("__send"); } +//void __sigaction ( void ) { unimp("__sigaction"); } +//--//void __vfork ( void ) { unimp("__vfork"); } +//void __wait ( void ) { unimp("__wait"); } +//void __write ( void ) { unimp("__write"); } +//void _pthread_cleanup_pop ( void ) { unimp("_pthread_cleanup_pop"); } +//void _pthread_cleanup_pop_restore ( void ) { unimp("_pthread_cleanup_pop_restore"); } +//void _pthread_cleanup_push ( void ) { unimp("_pthread_cleanup_push"); } +//void _pthread_cleanup_push_defer ( void ) { unimp("_pthread_cleanup_push_defer"); } +//void longjmp ( void ) { unimp("longjmp"); } +//void pthread_atfork ( void ) { unimp("pthread_atfork"); } +//void pthread_attr_destroy ( void ) { unimp("pthread_attr_destroy"); } +//void pthread_attr_getdetachstate ( void ) { unimp("pthread_attr_getdetachstate"); } +//void pthread_attr_getinheritsched ( void ) { unimp("pthread_attr_getinheritsched"); } +//void pthread_attr_getschedparam ( void ) { unimp("pthread_attr_getschedparam"); } +//void pthread_attr_getschedpolicy ( void ) { unimp("pthread_attr_getschedpolicy"); } +//void pthread_attr_getscope ( void ) { unimp("pthread_attr_getscope"); } + +//void pthread_attr_setdetachstate ( void ) { unimp("pthread_attr_setdetachstate"); } +//void pthread_attr_setinheritsched ( void ) { unimp("pthread_attr_setinheritsched"); } +//void pthread_attr_setschedparam ( void ) { unimp("pthread_attr_setschedparam"); } +//void pthread_attr_setschedpolicy ( void ) { unimp("pthread_attr_setschedpolicy"); } +//void pthread_attr_setscope ( void ) { unimp("pthread_attr_setscope"); } +void pthread_barrier_destroy ( void ) { unimp("pthread_barrier_destroy"); } +void pthread_barrier_init ( void ) { unimp("pthread_barrier_init"); } +void pthread_barrier_wait ( void ) { unimp("pthread_barrier_wait"); } +void pthread_barrierattr_destroy ( void ) { unimp("pthread_barrierattr_destroy"); } +void pthread_barrierattr_init ( void ) { unimp("pthread_barrierattr_init"); } +void pthread_barrierattr_setpshared ( void ) { unimp("pthread_barrierattr_setpshared"); } +//void pthread_cancel ( void ) { unimp("pthread_cancel"); } +//void pthread_cond_broadcast ( void ) { unimp("pthread_cond_broadcast"); } +//void pthread_cond_destroy ( void ) { unimp("pthread_cond_destroy"); } +//void pthread_cond_init ( void ) { unimp("pthread_cond_init"); } +//void pthread_cond_signal ( void ) { unimp("pthread_cond_signal"); } +//void pthread_cond_timedwait ( void ) { unimp("pthread_cond_timedwait"); } +//void pthread_cond_wait ( void ) { unimp("pthread_cond_wait"); } +//void pthread_condattr_destroy ( void ) { unimp("pthread_condattr_destroy"); } +void pthread_condattr_getpshared ( void ) { unimp("pthread_condattr_getpshared"); } +//void pthread_condattr_init ( void ) { unimp("pthread_condattr_init"); } +void pthread_condattr_setpshared ( void ) { unimp("pthread_condattr_setpshared"); } +//void pthread_detach ( void ) { unimp("pthread_detach"); } +//void pthread_equal ( void ) { unimp("pthread_equal"); } +//void pthread_exit ( void ) { unimp("pthread_exit"); } +//void pthread_getattr_np ( void ) { unimp("pthread_getattr_np"); } +void pthread_getcpuclockid ( void ) { unimp("pthread_getcpuclockid"); } +//void pthread_getschedparam ( void ) { unimp("pthread_getschedparam"); } +//void pthread_getspecific ( void ) { unimp("pthread_getspecific"); } +//void pthread_join ( void ) { unimp("pthread_join"); } +//void pthread_key_create ( void ) { unimp("pthread_key_create"); } +//void pthread_key_delete ( void ) { unimp("pthread_key_delete"); } +//void pthread_kill ( void ) { unimp("pthread_kill"); } +//void pthread_mutex_destroy ( void ) { unimp("pthread_mutex_destroy"); } +//void pthread_mutex_init ( void ) { unimp("pthread_mutex_init"); } +//void pthread_mutex_lock ( void ) { unimp("pthread_mutex_lock"); } +//void pthread_mutex_timedlock ( void ) { unimp("pthread_mutex_timedlock"); } +//void pthread_mutex_trylock ( void ) { unimp("pthread_mutex_trylock"); } +//void pthread_mutex_unlock ( void ) { unimp("pthread_mutex_unlock"); } +//void pthread_mutexattr_destroy ( void ) { unimp("pthread_mutexattr_destroy"); } +//void pthread_mutexattr_init ( void ) { unimp("pthread_mutexattr_init"); } +//void pthread_once ( void ) { unimp("pthread_once"); } +//void pthread_rwlock_destroy ( void ) { unimp("pthread_rwlock_destroy"); } +//void pthread_rwlock_init ( void ) { unimp("pthread_rwlock_init"); } +//void pthread_rwlock_rdlock ( void ) { unimp("pthread_rwlock_rdlock"); } +void pthread_rwlock_timedrdlock ( void ) { unimp("pthread_rwlock_timedrdlock"); } +void pthread_rwlock_timedwrlock ( void ) { unimp("pthread_rwlock_timedwrlock"); } +//void pthread_rwlock_tryrdlock ( void ) { unimp("pthread_rwlock_tryrdlock"); } +//void pthread_rwlock_trywrlock ( void ) { unimp("pthread_rwlock_trywrlock"); } +//void pthread_rwlock_unlock ( void ) { unimp("pthread_rwlock_unlock"); } +//void pthread_rwlock_wrlock ( void ) { unimp("pthread_rwlock_wrlock"); } +//void pthread_rwlockattr_destroy ( void ) { unimp("pthread_rwlockattr_destroy"); } +void pthread_rwlockattr_getkind_np ( void ) { unimp("pthread_rwlockattr_getkind_np"); } +void pthread_rwlockattr_getpshared ( void ) { unimp("pthread_rwlockattr_getpshared"); } +//void pthread_rwlockattr_init ( void ) { unimp("pthread_rwlockattr_init"); } +void pthread_rwlockattr_setkind_np ( void ) { unimp("pthread_rwlockattr_setkind_np"); } +//void pthread_rwlockattr_setpshared ( void ) { unimp("pthread_rwlockattr_setpshared"); } +//void pthread_self ( void ) { unimp("pthread_self"); } +//void pthread_setcancelstate ( void ) { unimp("pthread_setcancelstate"); } +//void pthread_setcanceltype ( void ) { unimp("pthread_setcanceltype"); } +//void pthread_setschedparam ( void ) { unimp("pthread_setschedparam"); } +//void pthread_setspecific ( void ) { unimp("pthread_setspecific"); } +//void pthread_sigmask ( void ) { unimp("pthread_sigmask"); } +//void pthread_testcancel ( void ) { unimp("pthread_testcancel"); } +//void raise ( void ) { unimp("raise"); } +void sem_close ( void ) { unimp("sem_close"); } +void sem_open ( void ) { unimp("sem_open"); } +//void sem_timedwait ( void ) { unimp("sem_timedwait"); } +void sem_unlink ( void ) { unimp("sem_unlink"); } +//void sigaction ( void ) { unimp("sigaction"); } +//void siglongjmp ( void ) { unimp("siglongjmp"); } +//void sigwait ( void ) { unimp("sigwait"); } + +void __pthread_clock_gettime_private ( void ) { unimp("__pthread_clock_gettime"); } +void __pthread_clock_settime_private ( void ) { unimp("__pthread_clock_settime"); } +strong_alias(__pthread_clock_gettime_private, __pthread_clock_gettime_223); +strong_alias(__pthread_clock_settime_private, __pthread_clock_settime_223); +symbol_version(__pthread_clock_gettime_223, __pthread_clock_gettime, GLIBC_2.2.3); +symbol_version(__pthread_clock_settime_223, __pthread_clock_settime, GLIBC_2.2.3); +default_symbol_version(__pthread_clock_gettime_private, __pthread_clock_gettime, GLIBC_PRIVATE); +default_symbol_version(__pthread_clock_settime_private, __pthread_clock_settime, GLIBC_PRIVATE); + + + +//weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy) +//weak_alias(pthread_rwlock_init, __pthread_rwlock_init) +//weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock) +//weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock) +//weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock) +weak_alias(_IO_ftrylockfile, ftrylockfile) + +//__attribute__((weak)) void pread ( void ) { vgPlain_unimp("pread"); } +//__attribute__((weak)) void pwrite ( void ) { vgPlain_unimp("pwrite"); } +//__attribute__((weak)) void msync ( void ) { vgPlain_unimp("msync"); } +//__attribute__((weak)) void pause ( void ) { vgPlain_unimp("pause"); } +//__attribute__((weak)) void recvfrom ( void ) { vgPlain_unimp("recvfrom"); } +//__attribute__((weak)) void recvmsg ( void ) { vgPlain_unimp("recvmsg"); } +//__attribute__((weak)) void sendmsg ( void ) { vgPlain_unimp("sendmsg"); } +//__attribute__((weak)) void tcdrain ( void ) { vgPlain_unimp("tcdrain"); } +//--//__attribute__((weak)) void vfork ( void ) { vgPlain_unimp("vfork"); } + +//__attribute__((weak)) void pthread_attr_getguardsize ( void ) +// { vgPlain_unimp("pthread_attr_getguardsize"); } +//__attribute__((weak)) void pthread_attr_getstack ( void ) +// { vgPlain_unimp("pthread_attr_getstack"); } +__attribute__((weak)) void pthread_attr_getstackaddr ( void ) + { vgPlain_unimp("pthread_attr_getstackaddr"); } +__attribute__((weak)) void pthread_attr_getstacksize ( void ) + { vgPlain_unimp("pthread_attr_getstacksize"); } +//__attribute__((weak)) void pthread_attr_setguardsize ( void ) +// { vgPlain_unimp("pthread_attr_setguardsize"); } +__attribute__((weak)) void pthread_attr_setstack ( void ) + { vgPlain_unimp("pthread_attr_setstack"); } +__attribute__((weak)) void pthread_attr_setstackaddr ( void ) + { vgPlain_unimp("pthread_attr_setstackaddr"); } +//__attribute__((weak)) void pthread_attr_setstacksize ( void ) +// { vgPlain_unimp("pthread_attr_setstacksize"); } +//__attribute__((weak)) void pthread_getconcurrency ( void ) +// { vgPlain_unimp("pthread_getconcurrency"); } +//__attribute__((weak)) void pthread_kill_other_threads_np ( void ) +// { vgPlain_unimp("pthread_kill_other_threads_np"); } +__attribute__((weak)) void pthread_mutexattr_getkind_np ( void ) + { vgPlain_unimp("pthread_mutexattr_getkind_np"); } +__attribute__((weak)) void pthread_mutexattr_getpshared ( void ) + { vgPlain_unimp("pthread_mutexattr_getpshared"); } +//__attribute__((weak)) void pthread_mutexattr_gettype ( void ) +// { vgPlain_unimp("pthread_mutexattr_gettype"); } +__attribute__((weak)) void pthread_mutexattr_setkind_np ( void ) + { vgPlain_unimp("pthread_mutexattr_setkind_np"); } +//__attribute__((weak)) void pthread_mutexattr_setpshared ( void ) +// { vgPlain_unimp("pthread_mutexattr_setpshared"); } +//__attribute__((weak)) void pthread_setconcurrency ( void ) +// { vgPlain_unimp("pthread_setconcurrency"); } +//__attribute__((weak)) void pthread_spin_destroy ( void ) +// { vgPlain_unimp("pthread_spin_destroy"); } +//__attribute__((weak)) void pthread_spin_init ( void ) +// { vgPlain_unimp("pthread_spin_init"); } +//__attribute__((weak)) void pthread_spin_lock ( void ) +// { vgPlain_unimp("pthread_spin_lock"); } +//__attribute__((weak)) void pthread_spin_trylock ( void ) +// { vgPlain_unimp("pthread_spin_trylock"); } +//__attribute__((weak)) void pthread_spin_unlock ( void ) +// { vgPlain_unimp("pthread_spin_unlock"); } + + +/*--------------------------------------------------------------------*/ +/*--- end vg_libpthread_unimp.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/coregrind/vg_main.c b/VEX/head20041019/coregrind/vg_main.c new file mode 100644 index 0000000000..cf61287d68 --- /dev/null +++ b/VEX/head20041019/coregrind/vg_main.c @@ -0,0 +1,2933 @@ + +/*--------------------------------------------------------------------*/ +/*--- Startup: the real stuff vg_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#define _FILE_OFFSET_BITS 64 + +#include "core.h" +#include "ume.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef AT_DCACHEBSIZE +#define AT_DCACHEBSIZE 19 +#endif /* AT_DCACHEBSIZE */ + +#ifndef AT_ICACHEBSIZE +#define AT_ICACHEBSIZE 20 +#endif /* AT_ICACHEBSIZE */ + +#ifndef AT_UCACHEBSIZE +#define AT_UCACHEBSIZE 21 +#endif /* AT_UCACHEBSIZE */ + +#ifndef AT_SYSINFO +#define AT_SYSINFO 32 +#endif /* AT_SYSINFO */ + +#ifndef AT_SYSINFO_EHDR +#define AT_SYSINFO_EHDR 33 +#endif /* AT_SYSINFO_EHDR */ + +#ifndef AT_SECURE +#define AT_SECURE 23 /* secure mode boolean */ +#endif /* AT_SECURE */ + +/* redzone gap between client address space and shadow */ +#define REDZONE_SIZE (1 * 1024*1024) + +/* size multiple for client address space */ +#define CLIENT_SIZE_MULTIPLE (1 * 1024*1024) + +/* Proportion of client space for its heap (rest is for mmaps + stack) */ +#define CLIENT_HEAP_PROPORTION 0.333 + +/*====================================================================*/ +/*=== Global entities not referenced from generated code ===*/ +/*====================================================================*/ + +/* --------------------------------------------------------------------- + Startup stuff + ------------------------------------------------------------------ */ +/* linker-defined base address */ +extern char kickstart_base; + +/* Client address space, lowest to highest (see top of ume.c) */ +Addr VG_(client_base); /* client address space limits */ +Addr VG_(client_end); +Addr VG_(client_mapbase); +Addr VG_(client_trampoline_code); +Addr VG_(clstk_base); +Addr VG_(clstk_end); + +Addr VG_(brk_base); /* start of brk */ +Addr VG_(brk_limit); /* current brk */ + +Addr VG_(shadow_base); /* tool's shadow memory */ +Addr VG_(shadow_end); + +Addr VG_(valgrind_base); /* valgrind's address range */ + +// Note that VG_(valgrind_last) names the last byte of the section, whereas +// the VG_(*_end) vars name the byte one past the end of the section. +Addr VG_(valgrind_last); + +vki_rlimit VG_(client_rlimit_data); +vki_rlimit VG_(client_rlimit_stack); + +/* This is set early to indicate whether this CPU has the + SSE/fxsave/fxrestor features. */ +Bool VG_(have_ssestate); + +/* stage1 (main) executable */ +static Int vgexecfd = -1; + +/* client executable */ +Int VG_(clexecfd) = -1; + +/* Path to library directory */ +const Char *VG_(libdir) = VG_LIBDIR; + +/* our argc/argv */ +static Int vg_argc; +static Char **vg_argv; + +/* PID of the main thread */ +Int VG_(main_pid); + +/* PGRP of process */ +Int VG_(main_pgrp); + +/* Application-visible file descriptor limits */ +Int VG_(fd_soft_limit) = -1; +Int VG_(fd_hard_limit) = -1; + +/* As deduced from esp_at_startup, the client's argc, argv[] and + envp[] as extracted from the client's stack at startup-time. */ +Int VG_(client_argc); +Char** VG_(client_argv); +Char** VG_(client_envp); + +/* --------------------------------------------------------------------- + Running stuff + ------------------------------------------------------------------ */ + +/* Counts downwards in VG_(run_innerloop). */ +UInt VG_(dispatch_ctr); + +/* 64-bit counter for the number of basic blocks done. */ +ULong VG_(bbs_done); + +/* Tell the logging mechanism whether we are logging to a file + descriptor or a socket descriptor. */ +Bool VG_(logging_to_filedes) = True; + + +/*====================================================================*/ +/*=== Counters, for profiling purposes only ===*/ +/*====================================================================*/ + +// These ones maintained by vg_dispatch.S +UInt VG_(bb_enchain_count) = 0; // Number of chain operations done +UInt VG_(bb_dechain_count) = 0; // Number of unchain operations done +UInt VG_(unchained_jumps_done) = 0; // Number of unchained jumps done + +/* Counts pertaining to internal sanity checking. */ +static UInt sanity_fast_count = 0; +static UInt sanity_slow_count = 0; + +static void print_all_stats ( void ) +{ + // Translation stats + VG_(print_tt_tc_stats)(); + VG_(message)(Vg_DebugMsg, + "chainings: %d chainings, %d unchainings.", + VG_(bb_enchain_count), VG_(bb_dechain_count) ); + VG_(message)(Vg_DebugMsg, + " dispatch: %llu jumps (bb entries); of them %u (%lu%%) unchained.", + VG_(bbs_done), + VG_(unchained_jumps_done), + ((ULong)(100) * (ULong)(VG_(unchained_jumps_done))) + / ( VG_(bbs_done)==0 ? 1 : VG_(bbs_done) ) + ); + + // Scheduler stats + VG_(print_scheduler_stats)(); + + // Reg-alloc stats + VG_(print_reg_alloc_stats)(); + VG_(message)(Vg_DebugMsg, + " sanity: %d cheap, %d expensive checks.", + sanity_fast_count, sanity_slow_count ); + + // C call stats + VG_(print_ccall_stats)(); + + // UInstr histogram + if (VG_(clo_verbosity) > 3) + VG_(print_UInstr_histogram)(); + + // Memory stats + if (VG_(clo_verbosity) > 2) { + VG_(message)(Vg_DebugMsg, ""); + VG_(message)(Vg_DebugMsg, + "------ Valgrind's internal memory use stats follow ------" ); + VG_(sanity_check_malloc_all)(); + VG_(print_all_arena_stats)(); + VG_(message)(Vg_DebugMsg, ""); + VG_(message)(Vg_DebugMsg, + "------ Valgrind's ExeContext management stats follow ------" ); + VG_(print_ExeContext_stats)(); + } +} + + +/*====================================================================*/ +/*=== Miscellaneous global functions ===*/ +/*====================================================================*/ + +static Int ptrace_setregs(Int pid, ThreadId tid) +{ + if (VG_(is_running_thread)( tid )) + return VGA_(ptrace_setregs_from_BB)(pid); + else + return VGA_(ptrace_setregs_from_tst)(pid, &VG_(threads)[tid].arch); +} + +/* Start debugger and get it to attach to this process. Called if the + user requests this service after an error has been shown, so she can + poke around and look at parameters, memory, etc. You can't + meaningfully get the debugger to continue the program, though; to + continue, quit the debugger. */ +void VG_(start_debugger) ( Int tid ) +{ + Int pid; + + if ((pid = fork()) == 0) { + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + VG_(kkill)(VG_(getpid)(), VKI_SIGSTOP); + + } else if (pid > 0) { + Int status; + Int res; + + if ((res = VG_(waitpid)(pid, &status, 0)) == pid && + WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP && + ptrace_setregs(pid, tid) == 0 && + kill(pid, SIGSTOP) == 0 && + ptrace(PTRACE_DETACH, pid, NULL, 0) == 0) { + Char pidbuf[15]; + Char file[30]; + Char buf[100]; + Char *bufptr; + Char *cmdptr; + + VG_(sprintf)(pidbuf, "%d", pid); + VG_(sprintf)(file, "/proc/%d/fd/%d", pid, VG_(clexecfd)); + + bufptr = buf; + cmdptr = VG_(clo_db_command); + + while (*cmdptr) { + switch (*cmdptr) { + case '%': + switch (*++cmdptr) { + case 'f': + VG_(memcpy)(bufptr, file, VG_(strlen)(file)); + bufptr += VG_(strlen)(file); + cmdptr++; + break; + case 'p': + VG_(memcpy)(bufptr, pidbuf, VG_(strlen)(pidbuf)); + bufptr += VG_(strlen)(pidbuf); + cmdptr++; + break; + default: + *bufptr++ = *cmdptr++; + break; + } + break; + default: + *bufptr++ = *cmdptr++; + break; + } + } + + *bufptr++ = '\0'; + + VG_(message)(Vg_UserMsg, "starting debugger with cmd: %s", buf); + res = VG_(system)(buf); + if (res == 0) { + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, + "Debugger has detached. Valgrind regains control. We continue."); + } else { + VG_(message)(Vg_UserMsg, "Apparently failed!"); + VG_(message)(Vg_UserMsg, ""); + } + } + + VG_(kkill)(pid, VKI_SIGKILL); + VG_(waitpid)(pid, &status, 0); + } +} + + +/* Print some helpful-ish text about unimplemented things, and give + up. */ +void VG_(unimplemented) ( Char* msg ) +{ + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, + "Valgrind detected that your program requires"); + VG_(message)(Vg_UserMsg, + "the following unimplemented functionality:"); + VG_(message)(Vg_UserMsg, " %s", msg); + VG_(message)(Vg_UserMsg, + "This may be because the functionality is hard to implement,"); + VG_(message)(Vg_UserMsg, + "or because no reasonable program would behave this way,"); + VG_(message)(Vg_UserMsg, + "or because nobody has yet needed it. In any case, let us know at"); + VG_(message)(Vg_UserMsg, + "%s and/or try to work around the problem, if you can.", VG_BUGS_TO); + VG_(message)(Vg_UserMsg, + ""); + VG_(message)(Vg_UserMsg, + "Valgrind has to exit now. Sorry. Bye!"); + VG_(message)(Vg_UserMsg, + ""); + VG_(pp_sched_status)(); + VG_(exit)(1); +} + +Addr VG_(get_stack_pointer) ( void ) +{ + return VG_(baseBlock)[VGOFF_(m_esp)]; +} + +/* Debugging thing .. can be called from assembly with OYNK macro. */ +void VG_(oynk) ( Int n ) +{ + OINK(n); +} + +/* Initialize the PID and PGRP of scheduler LWP; this is also called + in any new children after fork. */ +static void newpid(ThreadId unused) +{ + /* PID of scheduler LWP */ + VG_(main_pid) = VG_(getpid)(); + VG_(main_pgrp) = VG_(getpgrp)(); +} + +/*====================================================================*/ +/*=== Check we were launched by stage 1 ===*/ +/*====================================================================*/ + +/* Look for our AUXV table */ +int scan_auxv(void* init_sp) +{ + const struct ume_auxv *auxv = find_auxv((int *)init_sp); + int padfile = -1, found = 0; + + for (; auxv->a_type != AT_NULL; auxv++) + switch(auxv->a_type) { + case AT_UME_PADFD: + padfile = auxv->u.a_val; + found |= 1; + break; + + case AT_UME_EXECFD: + vgexecfd = auxv->u.a_val; + found |= 2; + break; + } + + if ( found != (1|2) ) { + fprintf(stderr, "valgrind: stage2 must be launched by stage1\n"); + exit(127); + } + vg_assert(padfile >= 0); + return padfile; +} + + +/*====================================================================*/ +/*=== Address space determination ===*/ +/*====================================================================*/ + +static void layout_remaining_space(Addr argc_addr, float ratio) +{ + Int ires; + void* vres; + addr_t client_size, shadow_size; + + VG_(valgrind_base) = (addr_t)&kickstart_base; + VG_(valgrind_last) = ROUNDUP(argc_addr, 0x10000) - 1; // stack + + // This gives the client the largest possible address space while + // taking into account the tool's shadow needs. + client_size = ROUNDDN((VG_(valgrind_base)-REDZONE_SIZE) / (1.+ratio), + CLIENT_SIZE_MULTIPLE); + VG_(client_base) = CLIENT_BASE; + VG_(client_end) = VG_(client_base) + client_size; + /* where !FIXED mmap goes */ + VG_(client_mapbase) = VG_(client_base) + + PGROUNDDN((addr_t)(client_size * CLIENT_HEAP_PROPORTION)); + + VG_(shadow_base) = VG_(client_end) + REDZONE_SIZE; + VG_(shadow_end) = VG_(valgrind_base); + shadow_size = VG_(shadow_end) - VG_(shadow_base); + +#define SEGSIZE(a,b) ((VG_(b) - VG_(a))/(1024*1024)) + + if (0) + VG_(printf)( + "client_base %8x (%dMB)\n" + "client_mapbase %8x (%dMB)\n" + "client_end %8x (%dMB)\n" + "shadow_base %8x (%dMB)\n" + "shadow_end %8x\n" + "valgrind_base %8x (%dMB)\n" + "valgrind_last %8x\n", + VG_(client_base), SEGSIZE(client_base, client_mapbase), + VG_(client_mapbase), SEGSIZE(client_mapbase, client_end), + VG_(client_end), SEGSIZE(client_end, shadow_base), + VG_(shadow_base), SEGSIZE(shadow_base, shadow_end), + VG_(shadow_end), + VG_(valgrind_base), SEGSIZE(valgrind_base, valgrind_last), + VG_(valgrind_last) + ); + +#undef SEGSIZE + + // Ban redzone + vres = mmap((void *)VG_(client_end), REDZONE_SIZE, PROT_NONE, + MAP_FIXED|MAP_ANON|MAP_PRIVATE|MAP_NORESERVE, -1, 0); + vg_assert((void*)-1 != vres); + + // Make client hole + ires = munmap((void*)VG_(client_base), client_size); + vg_assert(0 == ires); + + // Map shadow memory. + // Initially all inaccessible, incrementally initialized as it is used + if (shadow_size != 0) { + vres = mmap((char *)VG_(shadow_base), shadow_size, PROT_NONE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED|MAP_NORESERVE, -1, 0); + if ((void*)-1 == vres) { + fprintf(stderr, + "valgrind: Could not allocate address space (%p bytes)\n" + "valgrind: for shadow memory\n" + "valgrind: Possible causes:\n" + "valgrind: - For some systems (especially under RedHat 8), Valgrind\n" + "valgrind: needs at least 1.5GB swap space.\n" + "valgrind: - Or, your virtual memory size may be limited (check\n" + "valgrind: with 'ulimit -v').\n" + "valgrind: - Or, your system may use a kernel that provides only a\n" + "valgrind: too-small (eg. 2GB) user address space.\n" + , (void*)shadow_size + ); + exit(1); + } + } +} + +/*====================================================================*/ +/*=== Command line setup ===*/ +/*====================================================================*/ + +/* Nb: malloc'd memory never freed -- kept throughout like argv, envp */ +static char* get_file_clo(char* dir) +{ +# define FLEN 512 + Int fd, n; + struct stat s1; + char* f_clo = NULL; + char filename[FLEN]; + + snprintf(filename, FLEN, "%s/.valgrindrc", ( NULL == dir ? "" : dir ) ); + fd = VG_(open)(filename, 0, VKI_S_IRUSR); + if ( fd > 0 ) { + if ( 0 == fstat(fd, &s1) ) { + f_clo = malloc(s1.st_size+1); + vg_assert(f_clo); + n = read(fd, f_clo, s1.st_size); + if (n == -1) n = 0; + f_clo[n] = '\0'; + } + close(fd); + } + return f_clo; +# undef FLEN +} + +#define ISSPACE(cc) ((cc) == ' ' || (cc) == '\t' || (cc) == '\n') + +static Int count_args(char* s) +{ + Int n = 0; + if (s) { + char* cp = s; + while (True) { + // We have alternating sequences: blanks, non-blanks, blanks... + // count the non-blanks sequences. + while ( ISSPACE(*cp) ) cp++; + if ( !*cp ) break; + n++; + while ( !ISSPACE(*cp) && *cp ) cp++; + } + } + return n; +} + +/* add args out of environment, skipping multiple spaces and -- args */ +static char** copy_args( char* s, char** to ) +{ + if (s) { + char* cp = s; + while (True) { + // We have alternating sequences: blanks, non-blanks, blanks... + // copy the non-blanks sequences, and add terminating '\0' + while ( ISSPACE(*cp) ) cp++; + if ( !*cp ) break; + *to++ = cp; + while ( !ISSPACE(*cp) && *cp ) cp++; + if ( *cp ) *cp++ = '\0'; // terminate if necessary + if (VG_STREQ(to[-1], "--")) to--; // undo any '--' arg + } + } + return to; +} + +#undef ISSPACE + +// Augment command line with arguments from environment and .valgrindrc +// files. +static void augment_command_line(Int* vg_argc_inout, char*** vg_argv_inout) +{ + int vg_argc0 = *vg_argc_inout; + char** vg_argv0 = *vg_argv_inout; + + char* env_clo = getenv(VALGRINDOPTS); + char* f1_clo = get_file_clo( getenv("HOME") ); + char* f2_clo = get_file_clo("."); + + /* copy any extra args from file or environment, if present */ + if ( (env_clo && *env_clo) || (f1_clo && *f1_clo) || (f2_clo && *f2_clo) ) { + /* ' ' separated extra options */ + char **from; + char **to; + int orig_arg_count, env_arg_count, f1_arg_count, f2_arg_count; + + for ( orig_arg_count = 0; vg_argv0[orig_arg_count]; orig_arg_count++ ); + + env_arg_count = count_args(env_clo); + f1_arg_count = count_args(f1_clo); + f2_arg_count = count_args(f2_clo); + + if (0) + printf("extra-argc=%d %d %d\n", + env_arg_count, f1_arg_count, f2_arg_count); + + /* +2: +1 for null-termination, +1 for added '--' */ + from = vg_argv0; + vg_argv0 = malloc( (orig_arg_count + env_arg_count + f1_arg_count + + f2_arg_count + 2) * sizeof(char **)); + vg_assert(vg_argv0); + to = vg_argv0; + + /* copy argv[0] */ + *to++ = *from++; + + /* Copy extra args from env var and file, in the order: ~/.valgrindrc, + * $VALGRIND_OPTS, ./.valgrindrc -- more local options are put later + * to override less local ones. */ + to = copy_args(f1_clo, to); + to = copy_args(env_clo, to); + to = copy_args(f2_clo, to); + + /* copy original arguments, stopping at command or -- */ + while (*from) { + if (**from != '-') + break; + if (VG_STREQ(*from, "--")) { + from++; /* skip -- */ + break; + } + *to++ = *from++; + } + + /* add -- */ + *to++ = "--"; + + vg_argc0 = to - vg_argv0; + + /* copy rest of original command line, then NULL */ + while (*from) *to++ = *from++; + *to = NULL; + } + + *vg_argc_inout = vg_argc0; + *vg_argv_inout = vg_argv0; +} + +#define VG_CLO_SEP '\01' + +static void get_command_line( int argc, char** argv, + Int* vg_argc_out, Char*** vg_argv_out, + char*** cl_argv_out ) +{ + int vg_argc0; + char** vg_argv0; + char** cl_argv; + char* env_clo = getenv(VALGRINDCLO); + + if (env_clo != NULL && *env_clo != '\0') { + char *cp; + char **cpp; + + /* OK, VALGRINDCLO is set, which means we must be a child of another + Valgrind process using --trace-children, so we're getting all our + arguments from VALGRINDCLO, and the entire command line belongs to + the client (including argv[0]) */ + vg_argc0 = 1; /* argv[0] */ + for (cp = env_clo; *cp; cp++) + if (*cp == VG_CLO_SEP) + vg_argc0++; + + vg_argv0 = malloc(sizeof(char **) * (vg_argc0 + 1)); + vg_assert(vg_argv0); + + cpp = vg_argv0; + + *cpp++ = "valgrind"; /* nominal argv[0] */ + *cpp++ = env_clo; + + // Replace the VG_CLO_SEP args separator with '\0' + for (cp = env_clo; *cp; cp++) { + if (*cp == VG_CLO_SEP) { + *cp++ = '\0'; /* chop it up in place */ + *cpp++ = cp; + } + } + *cpp = NULL; + cl_argv = argv; + + } else { + /* Count the arguments on the command line. */ + vg_argv0 = argv; + + for (vg_argc0 = 1; vg_argc0 < argc; vg_argc0++) { + if (argv[vg_argc0][0] != '-') /* exe name */ + break; + if (VG_STREQ(argv[vg_argc0], "--")) { /* dummy arg */ + vg_argc0++; + break; + } + } + cl_argv = &argv[vg_argc0]; + + /* Get extra args from VALGRIND_OPTS and .valgrindrc files. + Note we don't do this if getting args from VALGRINDCLO, as + those extra args will already be present in VALGRINDCLO. */ + augment_command_line(&vg_argc0, &vg_argv0); + } + + if (0) { + Int i; + for (i = 0; i < vg_argc0; i++) + printf("vg_argv0[%d]=\"%s\"\n", i, vg_argv0[i]); + } + + *vg_argc_out = vg_argc0; + *vg_argv_out = (Char**)vg_argv0; + *cl_argv_out = cl_argv; +} + + +/*====================================================================*/ +/*=== Environment and stack setup ===*/ +/*====================================================================*/ + +/* Scan a colon-separated list, and call a function on each element. + The string must be mutable, because we insert a temporary '\0', but + the string will end up unmodified. (*func) should return True if it + doesn't need to see any more. + + This routine will return True if (*func) returns True and False if + it reaches the end of the list without that happening. +*/ +static Bool scan_colsep(char *colsep, Bool (*func)(const char *)) +{ + char *cp, *entry; + int end; + + if (colsep == NULL || + *colsep == '\0') + return False; + + entry = cp = colsep; + + do { + end = (*cp == '\0'); + + if (*cp == ':' || *cp == '\0') { + char save = *cp; + + *cp = '\0'; + if ((*func)(entry)) { + *cp = save; + return True; + } + *cp = save; + entry = cp+1; + } + cp++; + } while(!end); + + return False; +} + +static Bool contains(const char *p) { + if (VG_STREQ(p, VG_(libdir))) { + return True; + } + return False; +} + +/* Prepare the client's environment. This is basically a copy of our + environment, except: + 1. LD_LIBRARY_PATH=$VALGRINDLIB:$LD_LIBRARY_PATH + 2. LD_PRELOAD=$VALGRINDLIB/vg_inject.so:($VALGRINDLIB/vgpreload_TOOL.so:)?$LD_PRELOAD + + If any of these is missing, then it is added. + + Yummy. String hacking in C. + + If this needs to handle any more variables it should be hacked + into something table driven. + */ +static char **fix_environment(char **origenv, const char *preload) +{ + static const char inject_so[] = "vg_inject.so"; + static const char ld_library_path[] = "LD_LIBRARY_PATH="; + static const char ld_preload[] = "LD_PRELOAD="; + static const char valgrind_clo[] = VALGRINDCLO "="; + static const int ld_library_path_len = sizeof(ld_library_path)-1; + static const int ld_preload_len = sizeof(ld_preload)-1; + static const int valgrind_clo_len = sizeof(valgrind_clo)-1; + int ld_preload_done = 0; + int ld_library_path_done = 0; + char *inject_path; + int inject_path_len; + int vgliblen = strlen(VG_(libdir)); + char **cpp; + char **ret; + int envc; + const int preloadlen = (preload == NULL) ? 0 : strlen(preload); + + /* Find the vg_inject.so; also make room for the tool preload + library */ + inject_path_len = sizeof(inject_so) + vgliblen + preloadlen + 16; + inject_path = malloc(inject_path_len); + vg_assert(inject_path); + + if (preload) + snprintf(inject_path, inject_path_len, "%s/%s:%s", + VG_(libdir), inject_so, preload); + else + snprintf(inject_path, inject_path_len, "%s/%s", + VG_(libdir), inject_so); + + /* Count the original size of the env */ + envc = 0; /* trailing NULL */ + for (cpp = origenv; cpp && *cpp; cpp++) + envc++; + + /* Allocate a new space */ + ret = malloc(sizeof(char *) * (envc+3+1)); /* 3 new entries + NULL */ + vg_assert(ret); + + /* copy it over */ + for (cpp = ret; *origenv; ) + *cpp++ = *origenv++; + *cpp = NULL; + + vg_assert(envc == (cpp - ret)); + + /* Walk over the new environment, mashing as we go */ + for (cpp = ret; cpp && *cpp; cpp++) { + if (memcmp(*cpp, ld_library_path, ld_library_path_len) == 0) { + /* If the LD_LIBRARY_PATH already contains libdir, then don't + bother adding it again, even if it isn't the first (it + seems that the Java runtime will keep reexecing itself + unless its paths are at the front of LD_LIBRARY_PATH) */ + if (!scan_colsep(*cpp + ld_library_path_len, contains)) { + int len = strlen(*cpp) + vgliblen*2 + 16; + char *cp = malloc(len); + vg_assert(cp); + + snprintf(cp, len, "%s%s:%s", + ld_library_path, VG_(libdir), + (*cpp)+ld_library_path_len); + + *cpp = cp; + } + + ld_library_path_done = 1; + } else if (memcmp(*cpp, ld_preload, ld_preload_len) == 0) { + int len = strlen(*cpp) + inject_path_len; + char *cp = malloc(len); + vg_assert(cp); + + snprintf(cp, len, "%s%s:%s", + ld_preload, inject_path, (*cpp)+ld_preload_len); + + *cpp = cp; + + ld_preload_done = 1; + } else if (memcmp(*cpp, valgrind_clo, valgrind_clo_len) == 0) { + *cpp = ""; + } + } + + /* Add the missing bits */ + + if (!ld_library_path_done) { + int len = ld_library_path_len + vgliblen*2 + 16; + char *cp = malloc(len); + vg_assert(cp); + + snprintf(cp, len, "%s%s", ld_library_path, VG_(libdir)); + + ret[envc++] = cp; + } + + if (!ld_preload_done) { + int len = ld_preload_len + inject_path_len; + char *cp = malloc(len); + vg_assert(cp); + + snprintf(cp, len, "%s%s", + ld_preload, inject_path); + + ret[envc++] = cp; + } + + ret[envc] = NULL; + + return ret; +} + +extern char **environ; /* our environment */ +//#include + +/* Add a string onto the string table, and return its address */ +static char *copy_str(char **tab, const char *str) +{ + char *cp = *tab; + char *orig = cp; + + while(*str) + *cp++ = *str++; + *cp++ = '\0'; + + if (0) + printf("copied %p \"%s\" len %d\n", orig, orig, cp-orig); + + *tab = cp; + + return orig; +} + +/* + This sets up the client's initial stack, containing the args, + environment and aux vector. + + The format of the stack is: + + higher address +-----------------+ + | Trampoline code | + +-----------------+ + | | + : string table : + | | + +-----------------+ + | AT_NULL | + - - + | auxv | + +-----------------+ + | NULL | + - - + | envp | + +-----------------+ + | NULL | + - - + | argv | + +-----------------+ + | argc | + lower address +-----------------+ <- esp + | undefined | + : : + */ +static Addr setup_client_stack(void* init_sp, + char **orig_argv, char **orig_envp, + const struct exeinfo *info, + UInt** client_auxv) +{ + void* res; + char **cpp; + char *strtab; /* string table */ + char *stringbase; + addr_t *ptr; + struct ume_auxv *auxv; + const struct ume_auxv *orig_auxv; + const struct ume_auxv *cauxv; + unsigned stringsize; /* total size of strings in bytes */ + unsigned auxsize; /* total size of auxv in bytes */ + int argc; /* total argc */ + int envc; /* total number of env vars */ + unsigned stacksize; /* total client stack size */ + addr_t cl_esp; /* client stack base (initial esp) */ + + /* use our own auxv as a prototype */ + orig_auxv = find_auxv(init_sp); + + /* ==================== compute sizes ==================== */ + + /* first of all, work out how big the client stack will be */ + stringsize = 0; + + /* paste on the extra args if the loader needs them (ie, the #! + interpreter and its argument) */ + argc = 0; + if (info->interp_name != NULL) { + argc++; + stringsize += strlen(info->interp_name) + 1; + } + if (info->interp_args != NULL) { + argc++; + stringsize += strlen(info->interp_args) + 1; + } + + /* now scan the args we're given... */ + for (cpp = orig_argv; *cpp; cpp++) { + argc++; + stringsize += strlen(*cpp) + 1; + } + + /* ...and the environment */ + envc = 0; + for (cpp = orig_envp; cpp && *cpp; cpp++) { + envc++; + stringsize += strlen(*cpp) + 1; + } + + /* now, how big is the auxv? */ + auxsize = sizeof(*auxv); /* there's always at least one entry: AT_NULL */ + for (cauxv = orig_auxv; cauxv->a_type != AT_NULL; cauxv++) { + if (cauxv->a_type == AT_PLATFORM) + stringsize += strlen(cauxv->u.a_ptr) + 1; + auxsize += sizeof(*cauxv); + } + + /* OK, now we know how big the client stack is */ + stacksize = + sizeof(int) + /* argc */ + sizeof(char **)*argc + /* argv */ + sizeof(char **) + /* terminal NULL */ + sizeof(char **)*envc + /* envp */ + sizeof(char **) + /* terminal NULL */ + auxsize + /* auxv */ + ROUNDUP(stringsize, sizeof(int)) +/* strings (aligned) */ + VKI_BYTES_PER_PAGE; /* page for trampoline code */ + + // decide where stack goes! + VG_(clstk_end) = VG_(client_end); + + VG_(client_trampoline_code) = VG_(clstk_end) - VKI_BYTES_PER_PAGE; + + /* cl_esp is the client's stack pointer */ + cl_esp = VG_(clstk_end) - stacksize; + cl_esp = ROUNDDN(cl_esp, 16); /* make stack 16 byte aligned */ + + /* base of the string table (aligned) */ + stringbase = strtab = (char *)(VG_(client_trampoline_code) - ROUNDUP(stringsize, sizeof(int))); + + VG_(clstk_base) = PGROUNDDN(cl_esp); + + if (0) + printf("stringsize=%d auxsize=%d stacksize=%d\n" + "clstk_base %p\n" + "clstk_end %p\n", + stringsize, auxsize, stacksize, + (void*)VG_(clstk_base), (void*)VG_(clstk_end)); + + + /* ==================== allocate space ==================== */ + + /* allocate a stack - mmap enough space for the stack */ + res = mmap((void *)PGROUNDDN(cl_esp), VG_(clstk_end) - PGROUNDDN(cl_esp), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); + vg_assert((void*)-1 != res); + + /* ==================== copy client stack ==================== */ + + ptr = (addr_t *)cl_esp; + + /* --- argc --- */ + *ptr++ = argc; /* client argc */ + + /* --- argv --- */ + if (info->interp_name) { + *ptr++ = (addr_t)copy_str(&strtab, info->interp_name); + free(info->interp_name); + } + if (info->interp_args) { + *ptr++ = (addr_t)copy_str(&strtab, info->interp_args); + free(info->interp_args); + } + for (cpp = orig_argv; *cpp; ptr++, cpp++) { + *ptr = (addr_t)copy_str(&strtab, *cpp); + } + *ptr++ = 0; + + /* --- envp --- */ + VG_(client_envp) = (Char **)ptr; + for (cpp = orig_envp; cpp && *cpp; ptr++, cpp++) + *ptr = (addr_t)copy_str(&strtab, *cpp); + *ptr++ = 0; + + /* --- auxv --- */ + auxv = (struct ume_auxv *)ptr; + *client_auxv = (UInt *)auxv; + + for (; orig_auxv->a_type != AT_NULL; auxv++, orig_auxv++) { + /* copy the entry... */ + *auxv = *orig_auxv; + + /* ...and fix up the copy */ + switch(auxv->a_type) { + case AT_PHDR: + if (info->phdr == 0) + auxv->a_type = AT_IGNORE; + else + auxv->u.a_val = info->phdr; + break; + + case AT_PHNUM: + if (info->phdr == 0) + auxv->a_type = AT_IGNORE; + else + auxv->u.a_val = info->phnum; + break; + + case AT_BASE: + if (info->interp_base == 0) + auxv->a_type = AT_IGNORE; + else + auxv->u.a_val = info->interp_base; + break; + + case AT_PLATFORM: /* points to a platform description string */ + auxv->u.a_ptr = copy_str(&strtab, orig_auxv->u.a_ptr); + break; + + case AT_ENTRY: + auxv->u.a_val = info->entry; + break; + + case AT_IGNORE: + case AT_EXECFD: + case AT_PHENT: + case AT_PAGESZ: + case AT_FLAGS: + case AT_NOTELF: + case AT_UID: + case AT_EUID: + case AT_GID: + case AT_EGID: + case AT_CLKTCK: + case AT_HWCAP: + case AT_FPUCW: + case AT_DCACHEBSIZE: + case AT_ICACHEBSIZE: + case AT_UCACHEBSIZE: + /* All these are pointerless, so we don't need to do anything + about them. */ + break; + + case AT_SECURE: + /* If this is 1, then it means that this program is running + suid, and therefore the dynamic linker should be careful + about LD_PRELOAD, etc. However, since stage1 (the thing + the kernel actually execve's) should never be SUID, and we + need LD_PRELOAD/LD_LIBRARY_PATH to work for the client, we + set AT_SECURE to 0. */ + auxv->u.a_val = 0; + break; + + case AT_SYSINFO: + /* Leave this unmolested for now, but we'll update it later + when we set up the client trampoline code page */ + break; + + case AT_SYSINFO_EHDR: + /* Trash this, because we don't reproduce it */ + auxv->a_type = AT_IGNORE; + break; + + default: + /* stomp out anything we don't know about */ + if (0) + printf("stomping auxv entry %d\n", auxv->a_type); + auxv->a_type = AT_IGNORE; + break; + + } + } + *auxv = *orig_auxv; + vg_assert(auxv->a_type == AT_NULL); + + /* --- trampoline page --- */ + VG_(memcpy)( (void *)VG_(client_trampoline_code), + &VG_(trampoline_code_start), VG_(trampoline_code_length) ); + + vg_assert((strtab-stringbase) == stringsize); + + /* We know the initial ESP is pointing at argc/argv */ + VG_(client_argc) = *(Int*)cl_esp; + VG_(client_argv) = (Char**)(cl_esp + sizeof(Int)); + + return cl_esp; +} + +/*====================================================================*/ +/*=== Find executable ===*/ +/*====================================================================*/ + +static const char* executable_name; + +static Bool match_executable(const char *entry) { + char buf[strlen(entry) + strlen(executable_name) + 2]; + + /* empty PATH element means . */ + if (*entry == '\0') + entry = "."; + + snprintf(buf, sizeof(buf), "%s/%s", entry, executable_name); + + if (access(buf, R_OK|X_OK) == 0) { + executable_name = strdup(buf); + vg_assert(NULL != executable_name); + return True; + } + return False; +} + +static const char* find_executable(const char* exec) +{ + vg_assert(NULL != exec); + executable_name = exec; + if (strchr(executable_name, '/') == NULL) { + /* no '/' - we need to search the path */ + char *path = getenv("PATH"); + scan_colsep(path, match_executable); + } + return executable_name; +} + + +/*====================================================================*/ +/*=== Loading tools ===*/ +/*====================================================================*/ + +static void list_tools(void) +{ + DIR *dir = opendir(VG_(libdir)); + struct dirent *de; + int first = 1; + + if (dir == NULL) { + fprintf(stderr, "Can't open %s: %s (installation problem?)\n", + VG_(libdir), strerror(errno)); + return; + } + + while ((de = readdir(dir)) != NULL) { + int len = strlen(de->d_name); + + /* look for vgskin_TOOL.so names */ + if (len > (7+1+3) && /* "vgskin_" + at least 1-char toolname + ".so" */ + strncmp(de->d_name, "vgskin_", 7) == 0 && + VG_STREQ(de->d_name + len - 3, ".so")) { + if (first) { + fprintf(stderr, "Available tools:\n"); + first = 0; + } + de->d_name[len-3] = '\0'; + fprintf(stderr, "\t%s\n", de->d_name+7); + } + } + + closedir(dir); + + if (first) + fprintf(stderr, "No tools available in \"%s\" (installation problem?)\n", + VG_(libdir)); +} + + +/* Find and load a tool, and check it looks ok. Also looks to see if there's + * a matching vgpreload_*.so file, and returns its name in *preloadpath. */ +static void load_tool( const char *toolname, void** handle_out, + ToolInfo** toolinfo_out, char **preloadpath_out ) +{ + Bool ok; + int len = strlen(VG_(libdir)) + strlen(toolname)*2 + 16; + char buf[len]; + void* handle; + ToolInfo* toolinfo; + char* preloadpath = NULL; + Int* vg_malloc_redzonep; + + // XXX: allowing full paths for --tool option -- does it make sense? + // Doesn't allow for vgpreload_.so. + + if (strchr(toolname, '/') != 0) { + /* toolname contains '/', and so must be a pathname */ + handle = dlopen(toolname, RTLD_NOW); + } else { + /* just try in the libdir */ + snprintf(buf, len, "%s/vgskin_%s.so", VG_(libdir), toolname); + handle = dlopen(buf, RTLD_NOW); + + if (handle != NULL) { + snprintf(buf, len, "%s/vgpreload_%s.so", VG_(libdir), toolname); + if (access(buf, R_OK) == 0) { + preloadpath = strdup(buf); + vg_assert(NULL != preloadpath); + } + } + } + + ok = (NULL != handle); + if (!ok) { + fprintf(stderr, "Can't open tool \"%s\": %s\n", toolname, dlerror()); + goto bad_load; + } + + toolinfo = dlsym(handle, "vgSkin_tool_info"); + ok = (NULL != toolinfo); + if (!ok) { + fprintf(stderr, "Tool \"%s\" doesn't define SK_(tool_info) - " + "add VG_DETERMINE_INTERFACE_VERSION?\n", toolname); + goto bad_load; + } + + ok = (toolinfo->sizeof_ToolInfo == sizeof(*toolinfo) && + toolinfo->interface_major_version == VG_CORE_INTERFACE_MAJOR_VERSION && + toolinfo->sk_pre_clo_init != NULL); + if (!ok) { + fprintf(stderr, "Error:\n" + " Tool and core interface versions do not match.\n" + " Interface version used by core is: %d.%d (size %d)\n" + " Interface version used by tool is: %d.%d (size %d)\n" + " The major version numbers must match.\n", + VG_CORE_INTERFACE_MAJOR_VERSION, + VG_CORE_INTERFACE_MINOR_VERSION, + sizeof(*toolinfo), + toolinfo->interface_major_version, + toolinfo->interface_minor_version, + toolinfo->sizeof_ToolInfo); + fprintf(stderr, " You need to at least recompile, and possibly update,\n"); + if (VG_CORE_INTERFACE_MAJOR_VERSION > toolinfo->interface_major_version) + fprintf(stderr, " your tool to work with this version of Valgrind.\n"); + else + fprintf(stderr, " your version of Valgrind to work with this tool.\n"); + goto bad_load; + } + + // Set redzone size for V's allocator + vg_malloc_redzonep = dlsym(handle, STR(VG_(vg_malloc_redzone_szB))); + if ( NULL != vg_malloc_redzonep ) { + VG_(vg_malloc_redzone_szB) = *vg_malloc_redzonep; + } + + vg_assert(NULL != handle && NULL != toolinfo); + *handle_out = handle; + *toolinfo_out = toolinfo; + *preloadpath_out = preloadpath; + return; + + + bad_load: + if (handle != NULL) + dlclose(handle); + + fprintf(stderr, "valgrind: couldn't load tool\n"); + list_tools(); + exit(127); +} + + +/*====================================================================*/ +/*=== Command line errors ===*/ +/*====================================================================*/ + +static void abort_msg ( void ) +{ + VG_(clo_log_to) = VgLogTo_Fd; + VG_(clo_log_fd) = 2; /* stderr */ +} + +void VG_(bad_option) ( Char* opt ) +{ + abort_msg(); + VG_(printf)("valgrind: Bad option `%s'; aborting.\n", opt); + VG_(printf)("valgrind: Use --help for more information.\n"); + VG_(exit)(1); +} + +static void missing_tool_option ( void ) +{ + abort_msg(); + VG_(printf)("valgrind: Missing --tool option\n"); + list_tools(); + VG_(printf)("valgrind: Use --help for more information.\n"); + VG_(exit)(1); +} + +static void missing_prog ( void ) +{ + abort_msg(); + VG_(printf)("valgrind: no program specified\n"); + VG_(printf)("valgrind: Use --help for more information.\n"); + VG_(exit)(1); +} + +static void config_error ( Char* msg ) +{ + abort_msg(); + VG_(printf)("valgrind: Startup or configuration error:\n %s\n", msg); + VG_(printf)("valgrind: Unable to start up properly. Giving up.\n"); + VG_(exit)(1); +} + + +/*====================================================================*/ +/*=== Loading the client ===*/ +/*====================================================================*/ + +static void load_client(char* cl_argv[], const char* exec, Int need_help, + /*out*/struct exeinfo* info, /*out*/Addr* client_eip) +{ + // If they didn't specify an executable with --exec, and didn't specify + // --help, then use client argv[0] (searching $PATH if necessary). + if (NULL == exec && !need_help) { + if (cl_argv[0] == NULL || + ( NULL == (exec = find_executable(cl_argv[0])) ) ) + { + missing_prog(); + } + } + + info->map_base = VG_(client_mapbase); + info->exe_base = VG_(client_base); + info->exe_end = VG_(client_end); + info->argv = cl_argv; + + if (need_help) { + VG_(clexecfd) = -1; + // Set the minimal number of entries in 'info' to continue. + info->interp_name = NULL; + info->interp_args = NULL; + } else { + Int ret; + VG_(clexecfd) = VG_(open)(exec, VKI_O_RDONLY, VKI_S_IRUSR); + ret = do_exec(exec, info); + if (ret != 0) { + fprintf(stderr, "valgrind: do_exec(%s) failed: %s\n", + exec, strerror(ret)); + exit(127); + } + } + + /* Copy necessary bits of 'info' that were filled in */ + *client_eip = info->init_eip; + VG_(brk_base) = VG_(brk_limit) = info->brkbase; +} + +/*====================================================================*/ +/*=== Address space unpadding ===*/ +/*====================================================================*/ + +typedef struct { + char* killpad_start; + char* killpad_end; + struct stat* killpad_padstat; +} killpad_extra; + +static int killpad(char *segstart, char *segend, const char *perm, off_t off, + int maj, int min, int ino, void* ex) +{ + killpad_extra* extra = ex; + void *b, *e; + int res; + + vg_assert(NULL != extra->killpad_padstat); + + if (extra->killpad_padstat->st_dev != makedev(maj, min) || + extra->killpad_padstat->st_ino != ino) + return 1; + + if (segend <= extra->killpad_start || segstart >= extra->killpad_end) + return 1; + + if (segstart <= extra->killpad_start) + b = extra->killpad_start; + else + b = segstart; + + if (segend >= extra->killpad_end) + e = extra->killpad_end; + else + e = segend; + + res = munmap(b, (char *)e-(char *)b); + vg_assert(0 == res); + + return 1; +} + +// Remove padding of 'padfile' from a range of address space. +void as_unpad(void *start, void *end, int padfile) +{ + static struct stat padstat; + killpad_extra extra; + int res; + + vg_assert(padfile > 0); + + res = fstat(padfile, &padstat); + vg_assert(0 == res); + extra.killpad_padstat = &padstat; + extra.killpad_start = start; + extra.killpad_end = end; + foreach_map(killpad, &extra); +} + +void as_closepadfile(int padfile) +{ + int res = close(padfile); + vg_assert(0 == res); +} + + +/*====================================================================*/ +/*=== Command-line: variables, processing, etc ===*/ +/*====================================================================*/ + +/* Define, and set defaults. */ +Bool VG_(clo_error_limit) = True; +Bool VG_(clo_db_attach) = False; +Char* VG_(clo_db_command) = VG_CLO_DEFAULT_DBCOMMAND; +Bool VG_(clo_gen_suppressions) = False; +Int VG_(clo_sanity_level) = 1; +Int VG_(clo_verbosity) = 1; +Bool VG_(clo_demangle) = True; +Bool VG_(clo_trace_children) = False; + +/* See big comment in core.h for meaning of these three. + fd is initially stdout, for --help, but gets moved to stderr by default + immediately afterwards. */ +VgLogTo VG_(clo_log_to) = VgLogTo_Fd; +Int VG_(clo_log_fd) = 1; +Char* VG_(clo_log_name) = NULL; + +Bool VG_(clo_time_stamp) = False; + +Int VG_(clo_input_fd) = 0; /* stdin */ +Int VG_(clo_n_suppressions) = 0; +Char* VG_(clo_suppressions)[VG_CLO_MAX_SFILES]; +Bool VG_(clo_profile) = False; +Bool VG_(clo_single_step) = False; +Bool VG_(clo_optimise) = True; +UChar VG_(clo_trace_codegen) = 0; // 00000000b +Bool VG_(clo_trace_syscalls) = False; +Bool VG_(clo_trace_signals) = False; +Bool VG_(clo_trace_symtab) = False; +Bool VG_(clo_trace_sched) = False; +Int VG_(clo_trace_pthread_level) = 0; +Int VG_(clo_dump_error) = 0; +Int VG_(clo_backtrace_size) = 4; +Char* VG_(clo_weird_hacks) = NULL; +Bool VG_(clo_run_libc_freeres) = True; +Bool VG_(clo_track_fds) = False; +Bool VG_(clo_chain_bb) = True; +Bool VG_(clo_show_below_main) = False; +Bool VG_(clo_pointercheck) = True; +Bool VG_(clo_branchpred) = False; + +static Bool VG_(clo_wait_for_gdb) = False; + +/* If we're doing signal routing, poll for signals every 50mS by + default. */ +Int VG_(clo_signal_polltime) = 50; + +/* These flags reduce thread wakeup latency on syscall completion and + signal delivery, respectively. The downside is possible unfairness. */ +Bool VG_(clo_lowlat_syscalls) = False; /* low-latency syscalls */ +Bool VG_(clo_lowlat_signals) = False; /* low-latency signals */ + + +void usage ( Bool debug_help ) +{ + Char* usage1 = +"usage: valgrind --tool= [options] prog-and-args\n" +"\n" +" common user options for all Valgrind tools, with defaults in [ ]:\n" +" --tool= use the Valgrind tool named \n" +" -h --help show this message\n" +" --help-debug show this message, plus debugging options\n" +" --version show version\n" +" -q --quiet run silently; only print error msgs\n" +" -v --verbose be more verbose, incl counts of errors\n" +" --trace-children=no|yes Valgrind-ise child processes? [no]\n" +" --track-fds=no|yes track open file descriptors? [no]\n" +" --time-stamp=no|yes add timestamps to log messages? [no]\n" +"\n" +" uncommon user options for all Valgrind tools:\n" +" --run-libc-freeres=no|yes free up glibc memory at exit? [yes]\n" +" --weird-hacks=hack1,hack2,... recognised hacks: lax-ioctls [none]\n" +" --signal-polltime=

Valgrind, version 2.2.0

+
This manual was last updated on 31 August 2004
+

+ +

+jseward@acm.org, + njn25@cam.ac.uk
+Copyright © 2000-2004 Julian Seward, Nick Nethercote +

+ +Valgrind is licensed under the GNU General Public License, version +2
+ +An open-source tool for debugging and profiling Linux-x86 executables. +

+ +

+ +


+ +

Contents of this manual

+ +

Introduction

+ 1.1  + An overview of Valgrind
+ 1.2  + How to navigate this manual + +

+ Using and understanding the Valgrind core

+ 2.1  + What it does with your program
+ 2.2  + Getting started
+ 2.3  + The commentary
+ 2.4  + Reporting of errors
+ 2.5  + Suppressing errors
+ 2.6  + Command-line flags for the Valgrind core
+ 2.7  + The Client Request mechanism
+ 2.8  + Support for POSIX pthreads
+ 2.9  + Handling of signals
+ 2.10  + Building and installing
+ 2.11  + If you have problems
+ 2.12  + Limitations
+ 2.13  + How it works -- a rough overview
+ 2.14  + An example run
+ 2.15  + Warning messages you might see
+ +

+ Memcheck: a heavyweight memory checker

+ +

+ Cachegrind: a cache-miss profiler

+ +

+ Addrcheck: a lightweight memory checker

+ +

+ Helgrind: a data-race detector

+ +

+ Massif: a heap profiler

+ +

+The following is not part of the user manual. It describes how you can +write tools for Valgrind, in order to make new program supervision +tools. + +

+ Valgrind Tools

+ +

+The following are not part of the user manual. They describe internal +details of how Valgrind works. Reading them may rot your brain. You +have been warned. + +

+ The design and implementation of Valgrind

+ +

10  + How Cachegrind works

+ +
+ + diff --git a/VEX/head20041019/docs/proxylwp.sxw b/VEX/head20041019/docs/proxylwp.sxw new file mode 100644 index 0000000000..38ceeaf788 Binary files /dev/null and b/VEX/head20041019/docs/proxylwp.sxw differ diff --git a/VEX/head20041019/glibc-2.1.supp b/VEX/head20041019/glibc-2.1.supp new file mode 100644 index 0000000000..de93976b80 --- /dev/null +++ b/VEX/head20041019/glibc-2.1.supp @@ -0,0 +1,184 @@ +##----------------------------------------------------------------------## + +# Errors to suppress by default for glibc 2.1.3 + +# Format of this file is: +# { +# name_of_suppression +# tool_name:supp_kind +# (optional extra info for some suppression types) +# caller0 name, or /name/of/so/file.so +# caller1 name, or ditto +# (optionally: caller2 name) +# (optionally: caller3 name) +# } +# +# For Memcheck, the supp_kinds are: +# +# Param Value1 Value2 Value4 Value8 Value16 +# Free Addr1 Addr2 Addr4 Addr8 Addr16 +# Cond (previously known as Value0) +# +# and the optional extra info is: +# if Param: name of system call param +# if Free: name of free-ing fn) + +#-------- Suppress errors appearing as a result of calling +#-------- __libc_freeres() + +{ + __libc_freeres/free_mem(Addr4) + Addrcheck,Memcheck:Addr4 + fun:free_mem + fun:__libc_freeres +} + + +##----------------------------------------------------------------------## + +{ + pthread_mutex_unlock/__register_frame_info_bases + core:PThread + fun:pthread_mutex_unlock + fun:__register_frame_info_bases +} + +{ + socketcall.connect(serv_addr)/connect/*(Param) + Addrcheck,Memcheck:Param + socketcall.connect(serv_addr) + fun:connect + fun:* +} + +{ + strrchr/_dl_map_object_from_fd/_dl_map_object(Addr4) + Addrcheck,Memcheck:Addr4 + fun:strrchr + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + strrchr/_dl_map_object_from_fd/_dl_map_object(Value1) + Memcheck:Value1 + fun:strrchr + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + llseek(result)/lseek64/_IO_file_seek(Param) + Addrcheck,Memcheck:Param + llseek(result) + fun:lseek64 + fun:_IO_file_seek +} + +{ + __rawmemchr/_nl_*/*locale(Addr4) + Addrcheck,Memcheck:Addr4 + fun:__rawmemchr + fun:_nl_* + fun:*locale +} + +# new ones for RH62 ls -l +{ + __strchrnul/__nss_database_lookup(Cond) + Memcheck:Cond + fun:__strchrnul + fun:__nss_database_lookup +} +{ + __strchrnul/__gethostbyname_r(Cond) + Memcheck:Cond + fun:__strchrnul + fun:__gethostbyname_r +} + +{ + strrchr/_dl_map*/_dl_map*(Cond) + Memcheck:Cond + fun:strrchr + fun:_dl_map* + fun:_dl_map* +} + +{ + strchr/dl_open_worker/_dl_catch_error(Cond) + Memcheck:Cond + fun:strchr + fun:dl_open_worker + fun:_dl_catch_error +} + +{ + __rawmemchr/???/__getgrgid_r(Cond) + Memcheck:Cond + fun:__rawmemchr + fun:* + fun:__getgrgid_r +} + +{ + __rawmemchr/_nl_*/*locale*(Cond) + Memcheck:Cond + fun:__rawmemchr + fun:_nl_* + fun:*locale* +} + +{ + _dl_relocate_object/dl_open_worker(Value0) + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_open_worker +} + +##----------------------------------------------------------------------## +## from a Debian machine running kernel 2.2.19 I believe +## I guess most of these are the same as above really, but +## Debian stripped their libc-2.1.3 + +{ + libc-2.1.3.so/libc-2.1.3.so/libc-2.1.3.so(Cond) + Memcheck:Cond + obj:*libc-2.1.3.so + obj:*libc-2.1.3.so + obj:*libc-2.1.3.so +} + +{ + strchr/libc-2.1.3.so(Cond) + Memcheck:Cond + fun:*strchr* + obj:*libc-2.1.3.so +} + +{ + libc-2.1.3.so/libXt.so(Cond) + Memcheck:Cond + obj:*libc-2.1.3.so + obj:*libXt.so* +} + +{ + socketcall.connect(serv_addr)/connect/*(Param) + Addrcheck,Memcheck:Param + socketcall.connect(serv_addr) + obj:*libc-2.1.3.so + obj:*libX11.so* +} + +##----------------------------------------------------------------------## +## For a leak in Valgrind's own libpthread.so :( +{ + my_malloc/get_or_allocate_specifics_ptr/pthread_key_create(Leak) + Memcheck:Leak + fun:malloc + fun:my_malloc + fun:get_or_allocate_specifics_ptr + fun:pthread_key_create +} + diff --git a/VEX/head20041019/glibc-2.2.supp b/VEX/head20041019/glibc-2.2.supp new file mode 100644 index 0000000000..e50ac438c6 --- /dev/null +++ b/VEX/head20041019/glibc-2.2.supp @@ -0,0 +1,459 @@ + +##----------------------------------------------------------------------## + +# Errors to suppress by default with glibc 2.2.4 / 2.2.5 + +# Format of this file is: +# { +# name_of_suppression +# tool_name:supp_kind +# (optional extra info for some suppression types) +# caller0 name, or /name/of/so/file.so +# caller1 name, or ditto +# (optionally: caller2 name) +# (optionally: caller3 name) +# } +# +# For Memcheck, the supp_kinds are: +# +# Param Value1 Value2 Value4 Value8 Value16 +# Free Addr1 Addr2 Addr4 Addr8 Addr16 +# Cond (previously known as Value0) +# +# and the optional extra info is: +# if Param: name of system call param +# if Free: name of free-ing fn) + +#-------- For SuSE 8.2 (gcc 3.3, glibc 2.3.2) +{ + __GI___stpcpy/* + Memcheck:Cond + fun:__GI___stpcpy + fun:* +} +{ + strlen/__GI__dl_open/dlopen_doit + Memcheck:Cond + fun:strlen + fun:__GI__dl_open + fun:dlopen_doit +} +{ + strlen/_dl_signal_cerror/_dl_lookup_symbol_internal/do_dlsym + Memcheck:Cond + fun:_dl_signal_cerror + fun:_dl_lookup_symbol_internal + fun:do_dlsym +} + + +#-------- For R H 8.0 +{ + elf_dynamic_do_rel.7/_dl_relocate_object_internal/dl_open_worker(Cond) + Memcheck:Cond + fun:elf_dynamic_do_rel.7 + fun:_dl_relocate_object_internal + fun:dl_open_worker +} + + +#-------- For R H 7.3 on i686 +{ + _dl_relocate_object*/*libc-2.2.?.so/_dl_catch_error*(Cond) + Memcheck:Cond + fun:_dl_relocate_object* + obj:*libc-2.2.?.so + fun:_dl_catch_error* +} + + +#-------- SuSE 8.1 stuff (gcc-3.2, glibc-2.2.5 + SuSE's hacks) +{ + __stpcpy/* + Memcheck:Cond + fun:__stpcpy + fun:* +} +{ + strlen/decompose_rpath/_dl_map_object + Memcheck:Cond + fun:strlen + fun:decompose_rpath + fun:_dl_map_object +} +{ + strlen/_dl_sym/dlsym_doit + Memcheck:Cond + fun:strlen + fun:_dl_sym + fun:dlsym_doit +} +{ + trecurse/__gconv_release_shlib(Addr4) + Addrcheck,Memcheck:Addr4 + fun:trecurse + fun:__gconv_release_shlib +} +{ + do_release_shlib/trecurse(Addr4) + Addrcheck,Memcheck:Addr4 + fun:do_release_shlib + fun:trecurse +} + +#-------- R H Limbo (glibc-2.2.90) stuff +{ + elf_dynamic_do_rela.8/_dl_relocate_object_internal + Memcheck:Cond + fun:elf_dynamic_do_rela.8 + fun:_dl_relocate_object_internal +} + +{ + __strnlen/_nl_make_l10nflist(Cond) + Memcheck:Cond + fun:__strnlen + fun:_nl_make_l10nflist +} + +{ + __strnlen/*vfprintf* + Memcheck:Cond + fun:__strnlen + fun:*vfprintf* +} + +{ + libXaw.so.7.0(Cond) + Memcheck:Cond + obj:*libXaw.so.7.0 +} + +#-------- Suppress errors appearing as a result of calling +#-------- __libc_freeres() + +{ + __twalk/*(Addr4) + Addrcheck,Memcheck:Addr4 + fun:__twalk +} + +{ + do_release_shlib/__twalk(Addr4) + Addrcheck,Memcheck:Addr4 + fun:do_release_shlib + fun:__twalk +} + +{ + __libc_freeres/free_mem/free(Free) + Addrcheck,Memcheck:Free + fun:free + fun:free_mem + fun:__libc_freeres +} + +#-------- Data races +{ + _dl_lookup_symbol/fixup/_dl_runtime_resolve + Helgrind:Eraser + fun:_dl_lookup_symbol + fun:fixup + fun:_dl_runtime_resolve +} + +{ + _dl_lookup_versioned_symbol/fixup/_dl_runtime_resolve + Helgrind:Eraser + fun:_dl_lookup_versioned_symbol + fun:fixup + fun:_dl_runtime_resolve +} + +{ + _dl_lookup_versioned_symbol_internal/fixup/_dl_runtime_resolve + Helgrind:Eraser + fun:_dl_lookup_versioned_symbol_internal + fun:fixup + fun:_dl_runtime_resolve +} + +{ + _dl_fini/exit/__libc_start_main + Helgrind:Eraser + fun:_dl_fini + fun:exit + fun:__libc_start_main +} + +#-------- Threading bugs? +# glibc 'knows' that destroying a locked mutex will unlock it +{ + pthread_error/pthread_mutex_destroy/__closedir + core:PThread + fun:pthread_error + fun:pthread_mutex_destroy + fun:__closedir +} + +{ + pthread_error/pthread_mutex_destroy/_IO_default_finish + core:PThread + fun:pthread_error + fun:pthread_mutex_destroy + fun:_IO_default_finish* +} + +{ + pthread_mutex_unlock/_IO_funlockfile + core:PThread + fun:pthread_mutex_unlock + fun:_IO_funlockfile +} + +# even more glibc suppressions ? +{ + libc-2.2.4.so/libc-2.2.4.so/libc-2.2.4.so(Cond) + Memcheck:Cond + obj:*libc-2.2.?.so + obj:*libc-2.2.?.so + obj:*libc-2.2.?.so +} +{ + libc-2.2.4.so/libc-2.2.4.so/libc-2.2.4.so(Value4) + Memcheck:Value4 + obj:*libc-2.2.?.so + obj:*libc-2.2.?.so + obj:*libc-2.2.?.so +} + +##### glibc 2.2.5 stuff perhaps? +##### suppressions for coolo +{ + strchr/dl_open_worker(Cond) + Memcheck:Cond + fun:strchr + fun:dl_open_worker +} +{ + __rawmemchr(Cond) + Memcheck:Cond + fun:__rawmemchr +} +{ + _IO_vfprintf/__strnlen(Cond) + Memcheck:Cond + fun:__strnlen + fun:_IO_vfprintf +} +{ + __strchrnul/gethostbyname*(Cond) + Memcheck:Cond + fun:__strchrnul + fun:gethostbyname* +} + + +##---- +{ + strlen/*dl_map_object*(Cond) + Memcheck:Cond + fun:strlen + fun:*dl_map_object* +} + +{ + strlen/*dl_open_worker*(Cond) + Memcheck:Cond + fun:strlen + fun:*dl_open_worker* +} + +{ + *rawmemchr*/*nss*(Cond) + Memcheck:Cond + fun:*rawmemchr* + fun:*nss* +} + +{ + *strchrnul*/*nss*(Cond) + Memcheck:Cond + fun:*strchrnul* + fun:*nss* +} + +# gcc version 2.96 20000731 (Red Hat Linux 7.1 2.96-98) +# on Red Hat 7.2 (x86) miscompiles __mpn_construct_double in +# __mpn_construct_double (../sysdeps/ieee754/dbl-64/mpn2dbl.c:45) +# (glibc-2.2.4) to read and write below %esp. Hence the following +# two: +{ + __mpn_construct_double/*(Addr4) + Addrcheck,Memcheck:Addr4 + fun:__mpn_construct_double + fun:* +} +{ + __mpn_construct_double/*(Addr8) + Addrcheck,Memcheck:Addr8 + fun:__mpn_construct_double + fun:* +} + +# More of the same (gcc bug, I'm pretty sure) +{ + __fabs/*(Addr4) + Addrcheck,Memcheck:Addr4 + fun:__fabs + fun:* +} +{ + __fabs/*(Addr8) + Addrcheck,Memcheck:Addr8 + fun:__fabs + fun:* +} + + +# Not sure what this is about ... but anyway +{ + pthread_sighandler/*(Addr4) + Addrcheck,Memcheck:Addr4 + fun:pthread_sighandler + fun:* +} + + +# More glibc stuff, AFAICS + +{ + __strnlen/__argz_stringify/_nl_make_l10nflist(Cond) + Memcheck:Cond + fun:__strnlen + fun:__argz_stringify + fun:_nl_make_l10nflist +} + +#-------------- +{ + _dl_relocate_object*/dl_open_worker/_dl_catch_error*(Cond) + Memcheck:Cond + fun:_dl_relocate_object* + fun:dl_open_worker + fun:_dl_catch_error* +} +{ + _dl_relocate_object/libc-2.2.4.so/_dl_catch_error(Cond) + Memcheck:Cond + fun:_dl_relocate_object + obj:*libc-2.2.?.so + fun:_dl_catch_error +} +{ + _dl_relocate_object/dl_main(Cond) + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_main +} +{ + _dl_relocate_object_internal/dl_main(Cond) + Memcheck:Cond + fun:_dl_relocate_object_internal + fun:dl_main +} + +{ + strrchr/_dl_map_object_from_fd/_dl_map_object(Cond) + Memcheck:Cond + fun:strrchr + fun:_dl_map_object_from_fd + fun:_dl_map_object +} + +{ + _dl_init/ld-2.2.4.so(Cond) + Memcheck:Cond + fun:_dl_start + obj:/lib/ld-2.2.4.so +} + +#------------------- +{ + socketcall.connect(serv_addr)/connect/* + Addrcheck,Memcheck:Param + socketcall.connect(serv_addr) + fun:connect + fun:* +} +{ + socketcall.connect(serv_addr)/libc-2.2.4.so/libc-2.2.4.so + Addrcheck,Memcheck:Param + socketcall.connect(serv_addr) + obj:*libc-2.2.?.so + obj:*libc-2.2.?.so +} + +#---------------------- +{ + write(buf)/write/libX11.so.6.2/libX11.so.6.2(Param) + Addrcheck,Memcheck:Param + write(buf) + fun:write + obj:/usr/X11R6/lib/libX11.so.6.2 + obj:/usr/X11R6/lib/libX11.so.6.2 +} +{ + write(buf)/libc-2.2.4.so/libX11.so.6.2/libX11.so.6.2(Param) + Addrcheck,Memcheck:Param + write(buf) + obj:*libc-2.2.?.so + obj:/usr/X11R6/lib/libX11.so.6.2 + obj:/usr/X11R6/lib/libX11.so.6.2 +} + +#{ +# llseek(result)/lseek64/_IO_file_seek(Param) +# Param +# llseek(result) +# fun:lseek64 +# fun:_IO_file_seek +#} + +{ + writev(vector[...])/__writev/libX11.so.6.2/libX11.so.6.2 + Addrcheck,Memcheck:Param + writev(vector[...]) + fun:__writev + obj:/usr/X11R6/lib/libX11.so.6.2 + obj:/usr/X11R6/lib/libX11.so.6.2 +} + +#---------------- +{ + __rawmemchr/libXt.so.6.0/libXt.so.6.0 + Memcheck:Cond + fun:__rawmemchr + obj:/usr/X11R6/lib/libXt.so.6.0 + obj:/usr/X11R6/lib/libXt.so.6.0 +} +{ + libc-2.2.4.so/libXt.so.6.0/libXt.so.6.0 + Memcheck:Cond + obj:*libc-2.2.?.so + obj:/usr/X11R6/lib/libXt.so.6.0 + obj:/usr/X11R6/lib/libXt.so.6.0 +} + +##----------------------------------------------------------------------## +## For a leak in Valgrind's own libpthread.so :( +{ + my_malloc/get_or_allocate_specifics_ptr/pthread_key_create(Leak) + Memcheck:Leak + fun:malloc + fun:my_malloc + fun:get_or_allocate_specifics_ptr + fun:pthread_key_create +} + + diff --git a/VEX/head20041019/glibc-2.3.supp b/VEX/head20041019/glibc-2.3.supp new file mode 100644 index 0000000000..4d0161cd78 --- /dev/null +++ b/VEX/head20041019/glibc-2.3.supp @@ -0,0 +1,235 @@ + +##----------------------------------------------------------------------## + +# Errors to suppress by default with glibc 2.3.x + +# Format of this file is: +# { +# name_of_suppression +# tool_name:supp_kind +# (optional extra info for some suppression types) +# caller0 name, or /name/of/so/file.so +# caller1 name, or ditto +# (optionally: caller2 name) +# (optionally: caller3 name) +# } +# +# For Memcheck, the supp_kinds are: +# +# Param Value1 Value2 Value4 Value8 Value16 +# Free Addr1 Addr2 Addr4 Addr8 Addr16 +# Cond (previously known as Value0) +# +# and the optional extra info is: +# if Param: name of system call param +# if Free: name of free-ing fn) + +{ + __GI___stpcpy/* + Memcheck:Cond + fun:__GI___stpcpy + fun:* +} +{ + strlen/__GI__dl_open/dlopen_doit + Memcheck:Cond + fun:strlen + fun:__GI__dl_open + fun:dlopen_doit +} +{ + strlen/_dl_signal_cerror/_dl_lookup_symbol_internal/do_dlsym + Memcheck:Cond + fun:_dl_signal_cerror + fun:_dl_lookup_symbol_internal + fun:do_dlsym +} +{ + strlen/*dl_map_object*(Cond) + Memcheck:Cond + fun:strlen + fun:*dl_map_object* +} + +{ + strlen/*dl_open_worker*(Cond) + Memcheck:Cond + fun:strlen + fun:*dl_open_worker* +} +{ + strlen/_dl_sym/dlsym_doit + Memcheck:Cond + fun:strlen + fun:_dl_sym + fun:dlsym_doit +} +{ + realpath is inefficiently coded + Addrcheck,Memcheck:Overlap + fun:memcpy + fun:realpath* +} + +{ + realpath stupidity part II + Addrcheck,Memcheck:Overlap + fun:strcpy + fun:realpath* +} +{ + strlen/decompose_rpath/_dl_map_object + Memcheck:Cond + fun:strlen + fun:decompose_rpath + fun:*dl_map_object* +} +{ + stpcpy/_dl_sym* + Memcheck:Cond + fun:__stpcpy + fun:_dl_* +} + +#-------- For R H 8.0 +{ + elf_dynamic_do_rel.7/_dl_relocate_object_internal/dl_open_worker(Cond) + Memcheck:Cond + fun:elf_dynamic_do_rel.7 + fun:_dl_relocate_object_internal + fun:dl_open_worker +} +{ + dl_relocate/dl_open_worker + Memcheck:Cond + fun:_dl_relocate_object_internal + fun:dl_open_worker +} + +#-------- glibc 2.3.2/ Fedora Core 1 +{ + dl_relocate/dl_main + Memcheck:Cond + fun:_dl_relocate_object_internal + fun:dl_main +} + +#-------- glibc 2.3.3/ Fedora Core 2 +{ + dl_relocate_object/dl_main + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_main +} +{ + _dl_relocate_object/dl_open_worker + Memcheck:Cond + fun:_dl_relocate_object + fun:dl_open_worker +} + +#-------- Data races +{ + _dl_lookup_symbol_internal/fixup/_dl_runtime_resolve + Helgrind:Eraser + fun:_dl_lookup_symbol_internal + fun:fixup + fun:_dl_runtime_resolve +} +{ + _dl_lookup_symbol_x/fixup/_dl_runtime_resolve + Helgrind:Eraser + fun:_dl_lookup_symbol_x + fun:fixup + fun:_dl_runtime_resolve +} +{ + _dl_lookup_versioned_symbol_internal/fixup/_dl_runtime_resolve + Helgrind:Eraser + fun:_dl_lookup_versioned_symbol_internal + fun:fixup + fun:_dl_runtime_resolve +} +{ + _dl_lookup_versioned_symbol/ld-2.3.2.so/ld-2.3.2.so + Helgrind:Eraser + fun:_dl_lookup_versioned_symbol + obj:/lib/ld-2.3.2.so + obj:/lib/ld-2.3.2.so +} +{ + _dl_fini + Helgrind:Eraser + fun:_dl_fini +} + +#-------- Threading bugs? +# glibc 'knows' that destroying a locked mutex will unlock it +{ + pthread_error/pthread_mutex_destroy/__closedir + core:PThread + fun:pthread_error + fun:pthread_mutex_destroy + fun:__closedir +} + +{ + pthread_error/pthread_mutex_destroy/_IO_default_finish + core:PThread + fun:pthread_error + fun:pthread_mutex_destroy + fun:_IO_default_finish* +} + +{ + pthread_mutex_unlock/_IO_funlockfile + core:PThread + fun:pthread_mutex_unlock + fun:_IO_funlockfile +} + +##----------------------------------------------------------------------## +## For a leak in Valgrind's own libpthread.so :( +{ + my_malloc/get_or_allocate_specifics_ptr/pthread_key_create(Leak) + Memcheck:Leak + fun:malloc + fun:my_malloc + fun:get_or_allocate_specifics_ptr + fun:pthread_key_create +} + +##----------------------------------------------------------------------## +## Bugs in helper library supplied with Intel Icc 7.0 (65) +## in /opt/intel/compiler70/ia32/lib/libcxa.so.3 +{ + Intel compiler70/ia32/lib/libcxa.so.3 below-esp accesses + Addrcheck,Memcheck:Addr4 + obj:/opt/intel/compiler70/ia32/lib/libcxa.so.3 +} + +##----------------------------------------------------------------------## +## SuSE 9 after FV changes (post 2.1.0) + +{ + strlen/_dl_init_paths/dl_main/_dl_sysdep_start(Cond) + Memcheck:Cond + fun:strlen + fun:_dl_init_paths + fun:dl_main + fun:_dl_sysdep_start +} + +{ + Ugly strchr error in /lib/ld-2.3.2.so + Memcheck:Cond + obj:/lib/ld-2.3.2.so +} + +##----------------------------------------------------------------------## +## SuSE 9.1 with post 2.1.2 +{ + Ugly strchr error in /lib/ld-2.3.3.so + Memcheck:Cond + obj:/lib/ld-2.3.3.so +} diff --git a/VEX/head20041019/helgrind/.cvsignore b/VEX/head20041019/helgrind/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/helgrind/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/helgrind/CVS/Entries b/VEX/head20041019/helgrind/CVS/Entries new file mode 100644 index 0000000000..1b4161fbc7 --- /dev/null +++ b/VEX/head20041019/helgrind/CVS/Entries @@ -0,0 +1,6 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:32 2002// +/Makefile.am/1.49/Wed Sep 1 23:20:48 2004// +/helgrind.h/1.7/Sun Jan 4 16:43:22 2004// +/hg_main.c/1.84/Tue Sep 7 10:17:01 2004// +D/docs//// +D/tests//// diff --git a/VEX/head20041019/helgrind/CVS/Repository b/VEX/head20041019/helgrind/CVS/Repository new file mode 100644 index 0000000000..7b861a38de --- /dev/null +++ b/VEX/head20041019/helgrind/CVS/Repository @@ -0,0 +1 @@ +valgrind/helgrind diff --git a/VEX/head20041019/helgrind/CVS/Root b/VEX/head20041019/helgrind/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/helgrind/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/helgrind/CVS/Template b/VEX/head20041019/helgrind/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/Makefile.am b/VEX/head20041019/helgrind/Makefile.am new file mode 100644 index 0000000000..68aa37fd8f --- /dev/null +++ b/VEX/head20041019/helgrind/Makefile.am @@ -0,0 +1,16 @@ +include $(top_srcdir)/Makefile.tool.am + +val_PROGRAMS = vgskin_helgrind.so vgpreload_helgrind.so + +vgskin_helgrind_so_SOURCES = hg_main.c +vgskin_helgrind_so_LDFLAGS = -shared + +vgpreload_helgrind_so_SOURCES = +vgpreload_helgrind_so_LDADD = $(top_builddir)/coregrind/vg_replace_malloc.o +vgpreload_helgrind_so_DEPENDENCIES = $(top_builddir)/coregrind/vg_replace_malloc.o +vgpreload_helgrind_so_LDFLAGS = -shared -Wl,-z,interpose,-z,initfirst + +hgincludedir = $(includedir)/valgrind + +hginclude_HEADERS = helgrind.h + diff --git a/VEX/head20041019/helgrind/docs/.cvsignore b/VEX/head20041019/helgrind/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/helgrind/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/helgrind/docs/CVS/Entries b/VEX/head20041019/helgrind/docs/CVS/Entries new file mode 100644 index 0000000000..13e4e9f419 --- /dev/null +++ b/VEX/head20041019/helgrind/docs/CVS/Entries @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Thu Oct 3 10:07:33 2002// +/Makefile.am/1.3/Wed Aug 25 11:40:05 2004// +/hg_main.html/1.4/Fri Nov 14 17:47:53 2003// +D diff --git a/VEX/head20041019/helgrind/docs/CVS/Repository b/VEX/head20041019/helgrind/docs/CVS/Repository new file mode 100644 index 0000000000..ce80b6dabb --- /dev/null +++ b/VEX/head20041019/helgrind/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/helgrind/docs diff --git a/VEX/head20041019/helgrind/docs/CVS/Root b/VEX/head20041019/helgrind/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/helgrind/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/helgrind/docs/CVS/Template b/VEX/head20041019/helgrind/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/docs/Makefile.am b/VEX/head20041019/helgrind/docs/Makefile.am new file mode 100644 index 0000000000..54b4b1bd31 --- /dev/null +++ b/VEX/head20041019/helgrind/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = hg_main.html diff --git a/VEX/head20041019/helgrind/docs/hg_main.html b/VEX/head20041019/helgrind/docs/hg_main.html new file mode 100644 index 0000000000..74ee451e93 --- /dev/null +++ b/VEX/head20041019/helgrind/docs/hg_main.html @@ -0,0 +1,60 @@ + + + + Helgrind: a data-race detector + + + +

6  Helgrind: a data-race detector

+ +To use this tool, you must specify --tool=helgrind on the +Valgrind command line. +

+ +Helgrind is a Valgrind tool for detecting data races in C and C++ programs +that use the Pthreads library. +

+It uses the Eraser algorithm described in +

+ Eraser: A Dynamic Data Race Detector for Multithreaded Programs
+ Stefan Savage, Michael Burrows, Greg Nelson, Patrick Sobalvarro and + Thomas Anderson
+ ACM Transactions on Computer Systems, 15(4):391-411
+ November 1997. +
+ +We also incorporate significant improvements from this paper: + +
+ Runtime Checking of Multithreaded Applications with Visual Threads + Jerry J. Harrow, Jr.
+ Proceedings of the 7th International SPIN Workshop on Model Checking of + Software
+ Stanford, California, USA
+ August 2000
+ LNCS 1885, pp331--342
+ K. Havelund, J. Penix, and W. Visser, editors.
+
+ +

+Basically what Helgrind does is to look for memory locations which are +accessed by more than one thread. For each such location, Helgrind +records which of the program's (pthread_mutex_)locks were held by the +accessing thread at the time of the access. The hope is to discover +that there is indeed at least one lock which is used by all threads to +protect that location. If no such lock can be found, then there is +(apparently) no consistent locking strategy being applied for that +location, and so a possible data race might result. +

+Helgrind also allows for "thread segment lifetimes". If the execution of two +threads cannot overlap -- for example, if your main thread waits on another +thread with a pthread_join() operation -- they can both access the +same variable without holding a lock. +

+There's a lot of other sophistication in Helgrind, aimed at +reducing the number of false reports, and at producing useful error +reports. We hope to have more documentation one day... + + + + diff --git a/VEX/head20041019/helgrind/helgrind.h b/VEX/head20041019/helgrind/helgrind.h new file mode 100644 index 0000000000..593b8a82b4 --- /dev/null +++ b/VEX/head20041019/helgrind/helgrind.h @@ -0,0 +1,91 @@ +/* + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (helgrind.h) only. The entire rest of Valgrind is licensed + under the terms of the GNU General Public License, version 2. See + the COPYING file in the source distribution for details. + + ---------------------------------------------------------------- + + This file is part of helgrind, a Valgrind tool for detecting + data races in threaded programs. + + Copyright (C) 2002-2004 Nicholas Nethercote. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (helgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + +#ifndef __HELGRIND_H +#define __HELGRIND_H + +#include "valgrind.h" + +typedef + enum { + VG_USERREQ__HG_CLEAN_MEMORY = VG_USERREQ_SKIN_BASE('H','G'), + VG_USERREQ__HG_KNOWN_RACE + } Vg_HelgrindClientRequest; + +/* Clean memory state. This makes Helgrind forget everything it knew + about the specified memory range, and resets it to virgin. This is + particularly useful for memory allocators who wish to recycle + memory. */ +#define VALGRIND_HG_CLEAN_MEMORY(_qzz_start, _qzz_len) \ + do { \ + unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__HG_CLEAN_MEMORY, \ + _qzz_start, _qzz_len, 0, 0); \ + (void)0; \ + } while(0) + +/* Mark memory as known racy. This puts the memory range specified + into the error state, so that data race errors are not reported + against it. */ +#define VALGRIND_HG_KNOWN_RACE(_qzz_start, _qzz_len) \ + do { \ + unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__HG_KNOWN_RACE, \ + _qzz_start, _qzz_len, 0, 0); \ + (void)0; \ + } while(0) + +#endif /* __HELGRIND_H */ diff --git a/VEX/head20041019/helgrind/hg_main.c b/VEX/head20041019/helgrind/hg_main.c new file mode 100644 index 0000000000..ff869bd4ea --- /dev/null +++ b/VEX/head20041019/helgrind/hg_main.c @@ -0,0 +1,3419 @@ + +/*--------------------------------------------------------------------*/ +/*--- Helgrind: checking for data races in threaded programs. ---*/ +/*--- hg_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Helgrind, a Valgrind tool for detecting + data races in threaded programs. + + Copyright (C) 2002-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "tool.h" +#include "helgrind.h" + +static UInt n_eraser_warnings = 0; +static UInt n_lockorder_warnings = 0; + +/*------------------------------------------------------------*/ +/*--- Debug guff ---*/ +/*------------------------------------------------------------*/ + +#define DEBUG_LOCK_TABLE 0 /* Print lock table at end */ + +#define DEBUG_MAKE_ACCESSES 0 /* Print make_access() calls */ +#define DEBUG_LOCKS 0 /* Print lock()/unlock() calls and locksets */ +#define DEBUG_NEW_LOCKSETS 0 /* Print new locksets when created */ +#define DEBUG_ACCESSES 0 /* Print reads, writes */ +#define DEBUG_MEM_LOCKSET_CHANGES 0 + /* Print when an address's lockset + changes; only useful with + DEBUG_ACCESSES */ +#define SLOW_ASSERTS 0 /* do expensive asserts */ +#define DEBUG_VIRGIN_READS 0 /* Dump around address on VIRGIN reads */ + +#if SLOW_ASSERTS +#define SK_ASSERT(x) sk_assert(x) +#else +#define SK_ASSERT(x) +#endif + +/* heavyweight LockSet sanity checking: + 0 == never + 1 == after important ops + 2 == As 1 and also after pthread_mutex_* ops (excessively slow) + */ +#define LOCKSET_SANITY 0 + +/* Rotate an unsigned quantity left */ +#define ROTL(x, n) (((x) << (n)) | ((x) >> ((sizeof(x)*8)-(n)))) + +/* round a up to the next multiple of N. N must be a power of 2 */ +#define ROUNDUP(a, N) ((a + N - 1) & ~(N-1)) + +/* Round a down to the next multiple of N. N must be a power of 2 */ +#define ROUNDDN(a, N) ((a) & ~(N-1)) + +/*------------------------------------------------------------*/ +/*--- Command line options ---*/ +/*------------------------------------------------------------*/ + +static enum { + EC_None, + EC_Some, + EC_All +} clo_execontext = EC_None; + +static Bool clo_priv_stacks = False; + +/*------------------------------------------------------------*/ +/*--- Crude profiling machinery. ---*/ +/*------------------------------------------------------------*/ + +// PPP: work out if I want this + +#define PROF_EVENT(x) +#if 0 +#ifdef VG_PROFILE_MEMORY + +#define N_PROF_EVENTS 150 + +static UInt event_ctr[N_PROF_EVENTS]; + +void VGE_(done_prof_mem) ( void ) +{ + Int i; + for (i = 0; i < N_PROF_EVENTS; i++) { + if ((i % 10) == 0) + VG_(printf)("\n"); + if (event_ctr[i] > 0) + VG_(printf)( "prof mem event %2d: %d\n", i, event_ctr[i] ); + } + VG_(printf)("\n"); +} + +#define PROF_EVENT(ev) \ + do { sk_assert((ev) >= 0 && (ev) < N_PROF_EVENTS); \ + event_ctr[ev]++; \ + } while (False); + +#else + +//static void init_prof_mem ( void ) { } +// void VG_(done_prof_mem) ( void ) { } + +#define PROF_EVENT(ev) /* */ + +#endif /* VG_PROFILE_MEMORY */ + +/* Event index. If just the name of the fn is given, this means the + number of calls to the fn. Otherwise it is the specified event. + + [PPP: snip event numbers...] +*/ +#endif /* 0 */ + + +/*------------------------------------------------------------*/ +/*--- Data defns. ---*/ +/*------------------------------------------------------------*/ + +typedef + struct _HG_Chunk { + struct _HG_Chunk* next; + Addr data; /* ptr to actual block */ + Int size; /* size requested */ + ExeContext* where; /* where it was allocated */ + ThreadId tid; /* allocating thread */ + } + HG_Chunk; + +typedef enum + { Vge_VirginInit, Vge_NonVirginInit, Vge_SegmentInit, Vge_Error } + VgeInitStatus; + + +/* Should add up to 32 to fit in one word */ +#define OTHER_BITS 30 +#define STATE_BITS 2 + +#define ESEC_MAP_WORDS 16384 /* Words per secondary map */ + +/* This is for indicating that a memory block has been initialised but not + * really directly by a particular thread... (eg. text/data initialised + * automatically at startup). + * Must be different to virgin_word.other */ +#define TID_INDICATING_NONVIRGIN 1 + +/* Magic packed TLS used for error suppression; if word state is Excl + and tid is this, then it means all access are OK without changing + state and without raising any more errors */ +#define TLSP_INDICATING_ALL ((1 << OTHER_BITS) - 1) + +/* Number of entries must fit in STATE_BITS bits */ +typedef enum { Vge_Virgin, Vge_Excl, Vge_Shar, Vge_SharMod } pth_state; + +static inline const Char *pp_state(pth_state st) +{ + const Char *ret; + + switch(st) { + case Vge_Virgin: ret = "virgin"; break; + case Vge_Excl: ret = "exclusive"; break; + case Vge_Shar: ret = "shared RO"; break; + case Vge_SharMod: ret = "shared RW"; break; + default: ret = "???"; + } + return ret; +} + +typedef + struct { + /* gcc arranges this bitfield with state in the 2LSB and other + in the 30MSB, which is what we want */ + UInt state:STATE_BITS; + UInt other:OTHER_BITS; + } shadow_word; + +#define SW(st, other) ((shadow_word) { st, other }) + +typedef + struct { + shadow_word swords[ESEC_MAP_WORDS]; + } + ESecMap; + +static ESecMap* primary_map[ 65536 ]; +static ESecMap distinguished_secondary_map; + +static const shadow_word virgin_sword = SW(Vge_Virgin, 0); +static const shadow_word error_sword = SW(Vge_Excl, TLSP_INDICATING_ALL); + +#define VGE_IS_DISTINGUISHED_SM(smap) \ + ((smap) == &distinguished_secondary_map) + +#define ENSURE_MAPPABLE(addr,caller) \ + do { \ + if (VGE_IS_DISTINGUISHED_SM(primary_map[(addr) >> 16])) { \ + primary_map[(addr) >> 16] = alloc_secondary_map(caller); \ + /*VG_(printf)("new 2map because of %p\n", addr);*/ \ + } \ + } while(0) + + +/* Parallel map which contains execution contexts when words last + changed state (if required) */ + +typedef struct EC_EIP { + union u_ec_eip { + Addr eip; + ExeContext *ec; + } uu_ec_eip; + UInt state:STATE_BITS; + UInt tls:OTHER_BITS; /* packed TLS */ +} EC_EIP; + +#define NULL_EC_EIP ((EC_EIP){ { 0 }, 0, 0}) + +#define EIP(eip, prev, tls) ((EC_EIP) { (union u_ec_eip)(eip), (prev).state, packTLS(tls) }) +#define EC(ec, prev, tls) ((EC_EIP) { (union u_ec_eip)(ec), (prev).state, packTLS(tls) }) + +static inline UInt packEC(ExeContext *ec) +{ + SK_ASSERT(((UInt)ec & ((1 << STATE_BITS)-1)) == 0); + return ((UInt)ec) >> STATE_BITS; +} + +static inline ExeContext *unpackEC(UInt i) +{ + return (ExeContext *)(i << STATE_BITS); +} + +/* Lose 2 LSB of eip */ +static inline UInt packEIP(Addr eip) +{ + return ((UInt)eip) >> STATE_BITS; +} + +static inline Addr unpackEIP(UInt i) +{ + return (Addr)(i << STATE_BITS); +} + +typedef struct { + EC_EIP execontext[ESEC_MAP_WORDS]; +} ExeContextMap; + +static ExeContextMap** execontext_map; + +static inline void setExeContext(Addr a, EC_EIP ec) +{ + UInt idx = (a >> 16) & 0xffff; + UInt off = (a >> 2) & 0x3fff; + + if (execontext_map[idx] == NULL) { + execontext_map[idx] = VG_(malloc)(sizeof(ExeContextMap)); + VG_(memset)(execontext_map[idx], 0, sizeof(ExeContextMap)); + } + + execontext_map[idx]->execontext[off] = ec; +} + +static inline EC_EIP getExeContext(Addr a) +{ + UInt idx = (a >> 16) & 0xffff; + UInt off = (a >> 2) & 0x3fff; + EC_EIP ec = NULL_EC_EIP; + + if (execontext_map[idx] != NULL) + ec = execontext_map[idx]->execontext[off]; + + return ec; +} + +/*------------------------------------------------------------*/ +/*--- Thread lifetime segments ---*/ +/*------------------------------------------------------------*/ + +/* + * This mechanism deals with the common case of a parent thread + * creating a structure for a child thread, and then passing ownership + * of the structure to that thread. It similarly copes with a child + * thread passing information back to another thread waiting to join + * on it. + * + * Each thread's lifetime can be partitioned into segments. Those + * segments are arranged to form an interference graph which indicates + * whether two thread lifetime segments can possibly be concurrent. + * If not, then memory with is exclusively accessed by one TLS can be + * passed on to another TLS without an error occurring, and without + * moving it from Excl state. + * + * At present this only considers thread creation and join as + * synchronisation events for creating new lifetime segments, but + * others may be possible (like mutex operations). + */ + +typedef struct _ThreadLifeSeg ThreadLifeSeg; + +struct _ThreadLifeSeg { + ThreadId tid; + ThreadLifeSeg *prior[2]; /* Previous lifetime segments */ + UInt refcount; /* Number of memory locations pointing here */ + UInt mark; /* mark used for graph traversal */ + ThreadLifeSeg *next; /* list of all TLS */ +}; + +static ThreadLifeSeg *all_tls; +static UInt tls_since_gc; +#define TLS_SINCE_GC 10000 + +/* current mark used for TLS graph traversal */ +static UInt tlsmark; + +static ThreadLifeSeg *thread_seg[VG_N_THREADS]; + + +static void tls_gc(void) +{ + /* XXX later. Walk through all TLSs and look for ones with 0 + refcount and remove them from the structure and free them. + Could probably get rid of ThreadLifeSeg.refcount and simply use + mark-sweep from the shadow table. */ + VG_(printf)("WRITEME: TLS GC\n"); +} + +static void newTLS(ThreadId tid) +{ + static const Bool debug = False; + ThreadLifeSeg *tls; + + /* Initial NULL */ + if (thread_seg[tid] == NULL) { + tls = VG_(malloc)(sizeof(*tls)); + tls->tid = tid; + tls->prior[0] = tls->prior[1] = NULL; + tls->refcount = 0; + tls->mark = tlsmark-1; + + tls->next = all_tls; + all_tls = tls; + tls_since_gc++; + + thread_seg[tid] = tls; + return; + } + + /* Previous TLS was unused, so just recycle */ + if (thread_seg[tid]->refcount == 0) { + if (debug) + VG_(printf)("newTLS; recycling TLS %p for tid %u\n", + thread_seg[tid], tid); + return; + } + + /* Use existing TLS for this tid as a prior for new TLS */ + tls = VG_(malloc)(sizeof(*tls)); + tls->tid = tid; + tls->prior[0] = thread_seg[tid]; + tls->prior[1] = NULL; + tls->refcount = 0; + tls->mark = tlsmark-1; + + tls->next = all_tls; + all_tls = tls; + if (++tls_since_gc > TLS_SINCE_GC) { + tls_gc(); + tls_since_gc = 0; + } + + if (debug) + VG_(printf)("newTLS: made new TLS %p for tid %u (prior %p(%u))\n", + tls, tid, tls->prior[0], tls->prior[0]->tid); + + thread_seg[tid] = tls; +} + +/* clear out a TLS for a thread that's died */ +static void clearTLS(ThreadId tid) +{ + newTLS(tid); + + thread_seg[tid]->prior[0] = NULL; + thread_seg[tid]->prior[1] = NULL; +} + +static void addPriorTLS(ThreadId tid, ThreadId prior) +{ + static const Bool debug = False; + ThreadLifeSeg *tls = thread_seg[tid]; + + if (debug) + VG_(printf)("making TLS %p(%u) prior to TLS %p(%u)\n", + thread_seg[prior], prior, tls, tid); + + sk_assert(thread_seg[tid] != NULL); + sk_assert(thread_seg[prior] != NULL); + + if (tls->prior[0] == NULL) + tls->prior[0] = thread_seg[prior]; + else { + sk_assert(tls->prior[1] == NULL); + tls->prior[1] = thread_seg[prior]; + } +} + +/* Return True if prior is definitely not concurrent with tls */ +static Bool tlsIsDisjoint(const ThreadLifeSeg *tls, + const ThreadLifeSeg *prior) +{ + Bool isPrior(const ThreadLifeSeg *t) { + if (t == NULL || t->mark == tlsmark) + return False; + + if (t == prior) + return True; + + ((ThreadLifeSeg *)t)->mark = tlsmark; + + return isPrior(t->prior[0]) || isPrior(t->prior[1]); + } + tlsmark++; /* new traversal mark */ + + return isPrior(tls); +} + +static inline UInt packTLS(ThreadLifeSeg *tls) +{ + SK_ASSERT(((UInt)tls & ((1 << STATE_BITS)-1)) == 0); + return ((UInt)tls) >> STATE_BITS; +} + +static inline ThreadLifeSeg *unpackTLS(UInt i) +{ + return (ThreadLifeSeg *)(i << STATE_BITS); +} + +/*------------------------------------------------------------*/ +/*--- Low-level support for memory tracking. ---*/ +/*------------------------------------------------------------*/ + +/* + All reads and writes are recorded in the memory map, which + records the state of all memory in the process. The memory map is + organised like that for normal Valgrind, except each that everything + is done at word-level instead of byte-level, and each word has only + one word of shadow (instead of 36 bits). + + As for normal Valgrind there is a distinguished secondary map. But we're + working at word-granularity, so it has 16k word entries instead of 64k byte + entries. Lookup is done as follows: + + bits 31..16: primary map lookup + bits 15.. 2: secondary map lookup + bits 1.. 0: ignored +*/ + + +/*------------------------------------------------------------*/ +/*--- Basic bitmap management, reading and writing. ---*/ +/*------------------------------------------------------------*/ + +/* Allocate and initialise a secondary map, marking all words as virgin. */ + +/* Just a value that isn't a real pointer */ +#define SEC_MAP_ACCESS (shadow_word*)0x99 + + +static +ESecMap* alloc_secondary_map ( __attribute__ ((unused)) Char* caller ) +{ + ESecMap* map; + UInt i; + //PROF_EVENT(10); PPP + + // Mark all words as virgin. + map = (ESecMap *)VG_(shadow_alloc)(sizeof(ESecMap)); + for (i = 0; i < ESEC_MAP_WORDS; i++) + map->swords[i] = virgin_sword; + + return map; +} + + +/* Set a word. The byte give by 'a' could be anywhere in the word -- the whole + * word gets set. */ +static /* __inline__ */ +void set_sword ( Addr a, shadow_word sword ) +{ + ESecMap* sm; + shadow_word *oldsw; + + //PROF_EVENT(23); PPP + ENSURE_MAPPABLE(a, "VGE_(set_sword)"); + + /* Use bits 31..16 for primary, 15..2 for secondary lookup */ + sm = primary_map[a >> 16]; + sk_assert(sm != &distinguished_secondary_map); + oldsw = &sm->swords[(a & 0xFFFC) >> 2]; + if (oldsw->state == Vge_Excl && oldsw->other != TLSP_INDICATING_ALL) { + ThreadLifeSeg *tls = unpackTLS(oldsw->other); + tls->refcount--; + } + + if (sword.state == Vge_Excl && sword.other != TLSP_INDICATING_ALL) { + ThreadLifeSeg *tls = unpackTLS(sword.other); + tls->refcount++; + } + + sm->swords[(a & 0xFFFC) >> 2] = sword; + + if (VGE_IS_DISTINGUISHED_SM(sm)) { + VG_(printf)("wrote to distinguished 2ndary map! 0x%x\n", a); + // XXX: may be legit, but I want to know when it happens --njn + VG_(skin_panic)("wrote to distinguished 2ndary map!"); + } +} + + +static __inline__ +shadow_word* get_sword_addr ( Addr a ) +{ + /* Use bits 31..16 for primary, 15..2 for secondary lookup */ + ESecMap* sm = primary_map[a >> 16]; + UInt sm_off = (a & 0xFFFC) >> 2; + + if (VGE_IS_DISTINGUISHED_SM(sm)) { + VG_(printf)("accessed distinguished 2ndary map! 0x%x\n", a); + // XXX: may be legit, but I want to know when it happens --njn + //VG_(skin_panic)("accessed distinguished 2ndary map!"); + return SEC_MAP_ACCESS; + } + + //PROF_EVENT(21); PPP + return & (sm->swords[sm_off]); +} + + +// SSS: rename these so they're not so similar to memcheck, unless it's +// appropriate of course + +static __inline__ +void init_virgin_sword(Addr a) +{ + if (clo_execontext != EC_None) + setExeContext(a, NULL_EC_EIP); + set_sword(a, virgin_sword); +} + +static __inline__ +void init_error_sword(Addr a) +{ + set_sword(a, error_sword); +} + +static __inline__ +void init_nonvirgin_sword(Addr a) +{ + shadow_word sword; + ThreadId tid = VG_(get_current_or_recent_tid)(); + ThreadLifeSeg *tls; + + sk_assert(tid != VG_INVALID_THREADID); + tls = thread_seg[tid]; + + sword = SW(Vge_Excl, packTLS(tls)); + set_sword(a, sword); +} + + +/* In this case, we treat it for Eraser's sake like virgin (it hasn't + * been inited by a particular thread, it's just done automatically upon + * startup), but we mark its .state specially so it doesn't look like an + * uninited read. */ +static __inline__ +void init_magically_inited_sword(Addr a) +{ + shadow_word sword; + + sk_assert(VG_INVALID_THREADID == VG_(get_current_tid)()); + + sword = SW(Vge_Virgin, TID_INDICATING_NONVIRGIN); + + set_sword(a, virgin_sword); +} + + +/*------------------------------------------------------------*/ +/*--- Implementation of lock sets. ---*/ +/*------------------------------------------------------------*/ + +typedef struct _Mutex Mutex; /* forward decl */ +typedef struct _LockSet LockSet; + +typedef enum MutexState { + MxUnknown, /* don't know */ + MxUnlocked, /* unlocked */ + MxLocked, /* locked */ + MxDead /* destroyed */ +} MutexState; + +struct _Mutex { + Addr mutexp; + Mutex *next; + + MutexState state; /* mutex state */ + ThreadId tid; /* owner */ + ExeContext *location; /* where the last change happened */ + + const LockSet *lockdep; /* set of locks we depend on */ + UInt mark; /* mark for graph traversal */ +}; + +static inline Int mutex_cmp(const Mutex *a, const Mutex *b) +{ + return a->mutexp - b->mutexp; +} + +struct _LockSet { + Int setsize; /* number of members */ + UInt hash; /* hash code */ + LockSet *next; /* next in hash chain */ + const Mutex *mutex[0]; /* locks */ +}; + +static const LockSet *emptyset; + +/* Each one is an index into the lockset table. */ +static const LockSet *thread_locks[VG_N_THREADS]; + +#define LOCKSET_HASH_SZ 1021 + +static LockSet *lockset_hash[LOCKSET_HASH_SZ]; + +/* Pack and unpack a LockSet pointer into shadow_word.other */ +static inline UInt packLockSet(const LockSet *p) +{ + UInt id; + + SK_ASSERT(((UInt)p & ((1 << STATE_BITS)-1)) == 0); + id = ((UInt)p) >> STATE_BITS; + + return id; +} + +static inline const LockSet *unpackLockSet(UInt id) +{ + return (LockSet *)(id << STATE_BITS); +} + +static +void pp_LockSet(const LockSet* p) +{ + Int i; + VG_(printf)("{ "); + for(i = 0; i < p->setsize; i++) { + const Mutex *mx = p->mutex[i]; + + VG_(printf)("%p%(y ", mx->mutexp, mx->mutexp); + } + VG_(printf)("}\n"); +} + + +static void print_LockSet(const Char *s, const LockSet *ls) +{ + VG_(printf)("%s: ", s); + pp_LockSet(ls); +} + +/* Compute the hash of a LockSet */ +static UInt hash_LockSet_w_wo(const LockSet *ls, + const Mutex *with, + const Mutex *without) +{ + Int i; + UInt hash = ls->setsize + (with != NULL) - (without != NULL); + + sk_assert(with == NULL || with != without); + + for(i = 0; with != NULL || i < ls->setsize; i++) { + const Mutex *mx = i >= ls->setsize ? NULL : ls->mutex[i]; + + if (without && mutex_cmp(without, mx) == 0) + continue; + + if (with && (mx == NULL || mutex_cmp(with, mx) < 0)) { + mx = with; + with = NULL; + i--; + } + + hash = ROTL(hash, 17); + hash ^= (UInt)mx->mutexp; + } + + return hash % LOCKSET_HASH_SZ; +} + +static inline UInt hash_LockSet_with(const LockSet *ls, const Mutex *with) +{ + UInt hash = hash_LockSet_w_wo(ls, with, NULL); + + if (0) + VG_(printf)("hash_with %p+%p -> %d\n", ls, with->mutexp, hash); + + return hash; +} + +static inline UInt hash_LockSet_without(const LockSet *ls, const Mutex *without) +{ + UInt hash = hash_LockSet_w_wo(ls, NULL, without); + + if (0) + VG_(printf)("hash_with %p-%p -> %d\n", ls, without->mutexp, hash); + + return hash; +} + +static inline UInt hash_LockSet(const LockSet *ls) +{ + UInt hash = hash_LockSet_w_wo(ls, NULL, NULL); + + if (0) + VG_(printf)("hash %p -> %d\n", ls, hash); + + return hash; +} + +static +Bool structural_eq_LockSet(const LockSet* a, const LockSet* b) +{ + Int i; + + if (a == b) + return True; + if (a->setsize != b->setsize) + return False; + + for(i = 0; i < a->setsize; i++) { + if (mutex_cmp(a->mutex[i], b->mutex[i]) != 0) + return False; + } + + return True; +} + + +/* Tricky: equivalent to (compare(insert(missing_elem, a), b)), but + * doesn't do the insertion. Returns True if they match. + */ +static Bool +weird_LockSet_equals(const LockSet* a, const LockSet* b, + const Mutex *missing_mutex) +{ + static const Bool debug = False; + Int ia, ib; + + /* Idea is to try and match each element of b against either an + element of a, or missing_mutex. */ + + if (debug) { + print_LockSet("weird_LockSet_equals a", a); + print_LockSet(" b", b); + VG_(printf)( " missing: %p%(y\n", + missing_mutex->mutexp, missing_mutex->mutexp); + } + + if ((a->setsize + 1) != b->setsize) { + if (debug) + VG_(printf)(" fastpath length mismatch -> 0\n"); + return False; + } + + /* There are three phases to this compare: + 1 the section from the start of a up to missing_mutex + 2 missing mutex itself + 3 the section after missing_mutex to the end of a + */ + + ia = 0; + ib = 0; + + /* 1: up to missing_mutex */ + for(; ia < a->setsize && mutex_cmp(a->mutex[ia], missing_mutex) < 0; ia++, ib++) { + if (debug) { + print_LockSet(" 1:a", a); + print_LockSet(" 1:b", b); + } + if (ib == b->setsize || mutex_cmp(a->mutex[ia], b->mutex[ib]) != 0) + return False; + } + + /* 2: missing_mutex itself */ + if (debug) { + VG_(printf)( " 2:missing: %p%(y\n", + missing_mutex->mutexp, missing_mutex->mutexp); + print_LockSet(" 2: b", b); + } + + sk_assert(ia == a->setsize || mutex_cmp(a->mutex[ia], missing_mutex) >= 0); + + if (ib == b->setsize || mutex_cmp(missing_mutex, b->mutex[ib]) != 0) + return False; + + ib++; + + /* 3: after missing_mutex to end */ + + for(; ia < a->setsize && ib < b->setsize; ia++, ib++) { + if (debug) { + print_LockSet(" 3:a", a); + print_LockSet(" 3:b", b); + } + if (mutex_cmp(a->mutex[ia], b->mutex[ib]) != 0) + return False; + } + + if (debug) + VG_(printf)(" ia=%d ib=%d --> %d\n", ia, ib, ia == a->setsize && ib == b->setsize); + + return ia == a->setsize && ib == b->setsize; +} + + + +static const LockSet *lookup_LockSet(const LockSet *set) +{ + UInt bucket = set->hash; + LockSet *ret; + + for(ret = lockset_hash[bucket]; ret != NULL; ret = ret->next) + if (set == ret || structural_eq_LockSet(set, ret)) + return ret; + + return NULL; +} + +static const LockSet *lookup_LockSet_with(const LockSet *set, Mutex *mutex) +{ + UInt bucket = hash_LockSet_with(set, mutex); + const LockSet *ret; + + for(ret = lockset_hash[bucket]; ret != NULL; ret = ret->next) + if (weird_LockSet_equals(set, ret, mutex)) + return ret; + + return NULL; +} + +static const LockSet *lookup_LockSet_without(const LockSet *set, Mutex *mutex) +{ + UInt bucket = hash_LockSet_without(set, mutex); + const LockSet *ret; + + for(ret = lockset_hash[bucket]; ret != NULL; ret = ret->next) + if (weird_LockSet_equals(ret, set, mutex)) + return ret; + + return NULL; +} + +static void insert_LockSet(LockSet *set) +{ + UInt hash = hash_LockSet(set); + + set->hash = hash; + + sk_assert(lookup_LockSet(set) == NULL); + + set->next = lockset_hash[hash]; + lockset_hash[hash] = set; +} + +static inline +LockSet *alloc_LockSet(UInt setsize) +{ + LockSet *ret = VG_(malloc)(sizeof(*ret) + sizeof(Mutex *) * setsize); + ret->setsize = setsize; + return ret; +} + +static inline +void free_LockSet(LockSet *p) +{ + /* assert: not present in hash */ + VG_(free)(p); +} + +static +void pp_all_LockSets ( void ) +{ + Int i; + Int sets, buckets; + + sets = buckets = 0; + for (i = 0; i < LOCKSET_HASH_SZ; i++) { + const LockSet *ls = lockset_hash[i]; + Bool first = True; + + for(; ls != NULL; ls = ls->next) { + if (first) { + buckets++; + VG_(printf)("[%4d] = ", i); + } else + VG_(printf)(" "); + + sets++; + first = False; + pp_LockSet(ls); + } + } + + VG_(printf)("%d distinct LockSets in %d buckets\n", sets, buckets); +} + +static inline Bool isempty(const LockSet *ls) +{ + return ls == NULL || ls->setsize == 0; +} + +static Bool ismember(const LockSet *ls, const Mutex *mx) +{ + Int i; + + /* XXX use binary search */ + for(i = 0; i < ls->setsize; i++) + if (mutex_cmp(mx, ls->mutex[i]) == 0) + return True; + + return False; +} + +/* Check invariants: + - all locksets are unique + - each set is an array in strictly increasing order of mutex addr +*/ +static +void sanity_check_locksets ( const Char* caller ) +{ + Int i; + const Char *badness; + LockSet *ls; + + for(i = 0; i < LOCKSET_HASH_SZ; i++) { + + for(ls = lockset_hash[i]; ls != NULL; ls = ls->next) { + const Mutex *prev; + Int j; + + if (hash_LockSet(ls) != ls->hash) { + badness = "mismatched hash"; + goto bad; + } + if (ls->hash != (UInt)i) { + badness = "wrong bucket"; + goto bad; + } + if (lookup_LockSet(ls) != ls) { + badness = "non-unique set"; + goto bad; + } + + prev = ls->mutex[0]; + for(j = 1; j < ls->setsize; j++) { + if (mutex_cmp(prev, ls->mutex[j]) >= 0) { + badness = "mutexes out of order"; + goto bad; + } + } + } + } + return; + + bad: + VG_(printf)("sanity_check_locksets: " + "i = %d, ls=%p badness = %s, caller = %s\n", + i, ls, badness, caller); + pp_all_LockSets(); + VG_(skin_panic)("sanity_check_locksets"); +} + +static +LockSet *add_LockSet(const LockSet *ls, const Mutex *mx) +{ + static const Bool debug = False; + LockSet *ret = NULL; + Int i, j; + + if (debug || DEBUG_MEM_LOCKSET_CHANGES) { + VG_(printf)("add-IN mutex %p%(y\n", mx->mutexp, mx->mutexp); + print_LockSet("add-IN", ls); + } + + if (debug || LOCKSET_SANITY) + sanity_check_locksets("add-IN"); + + sk_assert(!ismember(ls, mx)); + + ret = alloc_LockSet(ls->setsize+1); + + for(i = j = 0; i < ls->setsize; i++) { + if (debug) + VG_(printf)("i=%d j=%d ls->mutex[i]=%p mx=%p\n", + i, j, ls->mutex[i]->mutexp, mx ? mx->mutexp : 0); + if (mx && mutex_cmp(mx, ls->mutex[i]) < 0) { + ret->mutex[j++] = mx; + mx = NULL; + } + ret->mutex[j++] = ls->mutex[i]; + } + + /* not added in loop - must be after */ + if (mx) + ret->mutex[j++] = mx; + + sk_assert(j == ret->setsize); + + if (debug || LOCKSET_SANITY) { + print_LockSet("add-OUT", ret); + sanity_check_locksets("add-OUT"); + } + return ret; +} + +/* Builds ls with mx removed. mx should actually be in ls! + (a checked assertion). Resulting set should not already + exist in the table (unchecked). +*/ +static +LockSet *remove_LockSet ( const LockSet *ls, const Mutex *mx ) +{ + static const Bool debug = False; + LockSet *ret = NULL; + Int i, j; + + if (debug || DEBUG_MEM_LOCKSET_CHANGES) { + print_LockSet("remove-IN", ls); + } + + if (debug || LOCKSET_SANITY) + sanity_check_locksets("remove-IN"); + + sk_assert(ismember(ls, mx)); + + ret = alloc_LockSet(ls->setsize-1); + + for(i = j = 0; i < ls->setsize; i++) { + if (mutex_cmp(ls->mutex[i], mx) == 0) + continue; + ret->mutex[j++] = ls->mutex[i]; + } + + sk_assert(j == ret->setsize); + + if (debug || LOCKSET_SANITY) { + print_LockSet("remove-OUT", ret); + sanity_check_locksets("remove-OUT"); + } + return ret; +} + + +/* Builds the intersection, and then unbuilds it if it's already in the table. + */ +static const LockSet *_intersect(const LockSet *a, const LockSet *b) +{ + static const Bool debug = False; + Int iret; + Int ia, ib; + Int size; + LockSet *ret; + const LockSet *found; + + if (debug || LOCKSET_SANITY) + sanity_check_locksets("intersect-IN"); + + if (debug || DEBUG_MEM_LOCKSET_CHANGES) { + print_LockSet("intersect a", a); + print_LockSet("intersect b", b); + } + + /* count the size of the new set */ + size = 0; + ia = ib = 0; + for(size = ia = ib = 0; ia < a->setsize && ib < b->setsize; ) { + if (mutex_cmp(a->mutex[ia], b->mutex[ib]) == 0) { + size++; + ia++; + ib++; + } else if (mutex_cmp(a->mutex[ia], b->mutex[ib]) < 0) { + ia++; + } else { + sk_assert(mutex_cmp(a->mutex[ia], b->mutex[ib]) > 0); + ib++; + } + } + + /* Build the intersection of the two sets */ + ret = alloc_LockSet(size); + for (iret = ia = ib = 0; ia < a->setsize && ib < b->setsize; ) { + if (mutex_cmp(a->mutex[ia], b->mutex[ib]) == 0) { + sk_assert(iret < ret->setsize); + ret->mutex[iret++] = a->mutex[ia]; + ia++; + ib++; + } else if (mutex_cmp(a->mutex[ia], b->mutex[ib]) < 0) { + ia++; + } else { + sk_assert(mutex_cmp(a->mutex[ia], b->mutex[ib]) > 0); + ib++; + } + } + + ret->hash = hash_LockSet(ret); + + /* Now search for it in the table, adding it if not seen before */ + found = lookup_LockSet(ret); + + if (found != NULL) { + free_LockSet(ret); + } else { + insert_LockSet(ret); + found = ret; + } + + if (debug || LOCKSET_SANITY) { + print_LockSet("intersect-OUT", found); + sanity_check_locksets("intersect-OUT"); + } + + return found; +} + +/* inline the fastpath */ +static inline const LockSet *intersect(const LockSet *a, const LockSet *b) +{ + static const Bool debug = False; + + /* Fast case -- when the two are the same */ + if (a == b) { + if (debug || DEBUG_MEM_LOCKSET_CHANGES) { + print_LockSet("intersect-same fastpath", a); + } + return a; + } + + if (isempty(a) || isempty(b)) { + if (debug) + VG_(printf)("intersect empty fastpath\n"); + return emptyset; + } + + return _intersect(a, b); +} + + +static const LockSet *ls_union(const LockSet *a, const LockSet *b) +{ + static const Bool debug = False; + Int iret; + Int ia, ib; + Int size; + LockSet *ret; + const LockSet *found; + + if (debug || LOCKSET_SANITY) + sanity_check_locksets("union-IN"); + + /* Fast case -- when the two are the same */ + if (a == b) { + if (debug || DEBUG_MEM_LOCKSET_CHANGES) { + print_LockSet("union-same fastpath", a); + } + return a; + } + + if (isempty(a)) { + if (debug) + print_LockSet("union a=empty b", b); + return b; + } + if (isempty(b)) { + if (debug) + print_LockSet("union b=empty a", a); + return a; + } + + if (debug || DEBUG_MEM_LOCKSET_CHANGES) { + print_LockSet("union a", a); + print_LockSet("union b", b); + } + + /* count the size of the new set */ + for(size = ia = ib = 0; (ia < a->setsize) || (ib < b->setsize); ) { + Int cmp; + + if ((ia < a->setsize) && (ib < b->setsize)) + cmp = mutex_cmp(a->mutex[ia], b->mutex[ib]); + else if (ia == a->setsize) + cmp = 1; + else + cmp = -1; + + if (cmp == 0) { + size++; + ia++; + ib++; + } else if (cmp < 0) { + size++; + ia++; + } else { + sk_assert(cmp > 0); + size++; + ib++; + } + } + + /* Build the intersection of the two sets */ + ret = alloc_LockSet(size); + for (iret = ia = ib = 0; (ia < a->setsize) || (ib < b->setsize); ) { + Int cmp; + sk_assert(iret < ret->setsize); + + if ((ia < a->setsize) && (ib < b->setsize)) + cmp = mutex_cmp(a->mutex[ia], b->mutex[ib]); + else if (ia == a->setsize) + cmp = 1; + else + cmp = -1; + + if (cmp == 0) { + ret->mutex[iret++] = a->mutex[ia]; + ia++; + ib++; + } else if (cmp < 0) { + ret->mutex[iret++] = a->mutex[ia]; + ia++; + } else { + sk_assert(cmp > 0); + ret->mutex[iret++] = b->mutex[ib]; + ib++; + } + } + + sk_assert(iret == ret->setsize); + + ret->hash = hash_LockSet(ret); + + /* Now search for it in the table, adding it if not seen before */ + found = lookup_LockSet(ret); + + if (found != NULL) { + if (debug) + print_LockSet("union found existing set", found); + free_LockSet(ret); + } else { + if (debug) + print_LockSet("union inserting new set", ret); + insert_LockSet(ret); + found = ret; + } + + if (debug || LOCKSET_SANITY) { + print_LockSet("union-OUT", found); + sanity_check_locksets("union-OUT"); + } + + return found; +} + +/*------------------------------------------------------------*/ +/*--- Implementation of mutex structure. ---*/ +/*------------------------------------------------------------*/ + +static UInt graph_mark; /* current mark we're using for graph traversal */ + +static void record_mutex_error(ThreadId tid, Mutex *mutex, + Char *str, ExeContext *ec); +static void record_lockgraph_error(ThreadId tid, Mutex *mutex, + const LockSet *lockset_holding, + const LockSet *lockset_prev); + +static void set_mutex_state(Mutex *mutex, MutexState state, ThreadId tid); + +#define M_MUTEX_HASHSZ 1021 + +static Mutex *mutex_hash[M_MUTEX_HASHSZ]; +static UInt total_mutexes; + +static const Char *pp_MutexState(MutexState st) +{ + switch(st) { + case MxLocked: return "Locked"; + case MxUnlocked: return "Unlocked"; + case MxDead: return "Dead"; + case MxUnknown: return "Unknown"; + } + return "???"; +} + +static void pp_all_mutexes() +{ + Int i; + Int locks, buckets; + + locks = buckets = 0; + for(i = 0; i < M_MUTEX_HASHSZ; i++) { + Mutex *mx; + Bool first = True; + + for(mx = mutex_hash[i]; mx != NULL; mx = mx->next) { + if (first) { + buckets++; + VG_(printf)("[%4d] = ", i); + } else + VG_(printf)(" "); + locks++; + first = False; + VG_(printf)("%p [%8s] -> %p%(y\n", + mx, pp_MutexState(mx->state), mx->mutexp, mx->mutexp); + } + } + + VG_(printf)("%d locks in %d buckets (%d allocated)\n", + locks, buckets, total_mutexes); +} + +/* find or create a Mutex for a program's mutex use */ +static Mutex *get_mutex(Addr mutexp) +{ + UInt bucket = ((UInt)mutexp) % M_MUTEX_HASHSZ; + Mutex *mp; + + for(mp = mutex_hash[bucket]; mp != NULL; mp = mp->next) + if (mp->mutexp == mutexp) + return mp; + + total_mutexes++; + + mp = VG_(malloc)(sizeof(*mp)); + mp->mutexp = mutexp; + mp->next = mutex_hash[bucket]; + mutex_hash[bucket] = mp; + + mp->state = MxUnknown; + mp->tid = VG_INVALID_THREADID; + mp->location = NULL; + + mp->lockdep = emptyset; + mp->mark = graph_mark - 1; + + return mp; +} + +/* Find all mutexes in a range of memory, and call the callback. + Remove the mutex from the hash if the callback returns True (mutex + structure itself is not freed, because it may be pointed to by a + LockSet. */ +static void find_mutex_range(Addr start, Addr end, Bool (*action)(Mutex *)) +{ + UInt first = start % M_MUTEX_HASHSZ; + UInt last = (end+1) % M_MUTEX_HASHSZ; + UInt i; + + /* Single pass over the hash table, looking for likely hashes */ + for(i = first; i != last; ) { + Mutex *mx; + Mutex **prev = &mutex_hash[i]; + + for(mx = mutex_hash[i]; mx != NULL; prev = &mx->next, mx = mx->next) { + if (mx->mutexp >= start && mx->mutexp < end && (*action)(mx)) + *prev = mx->next; + } + + if (++i == M_MUTEX_HASHSZ) + i = 0; + } +} + +#define MARK_LOOP (graph_mark+0) +#define MARK_DONE (graph_mark+1) + +static Bool check_cycle_inner(const Mutex *mutex, const LockSet *ls) +{ + static const Bool debug = False; + Int i; + + if (mutex->mark == MARK_LOOP) + return True; /* found cycle */ + if (mutex->mark == MARK_DONE) + return False; /* been here before, its OK */ + + ((Mutex*)mutex)->mark = MARK_LOOP; + + if (debug) + VG_(printf)("mark=%d visiting %p%(y mutex->lockset=%d\n", + graph_mark, mutex->mutexp, mutex->mutexp, mutex->lockdep); + for(i = 0; i < ls->setsize; i++) { + const Mutex *mx = ls->mutex[i]; + + if (debug) + VG_(printf)(" %y ls=%p (ls->mutex=%p%(y)\n", + mutex->mutexp, ls, + mx->mutexp, mx->mutexp); + if (check_cycle_inner(mx, mx->lockdep)) + return True; + } + ((Mutex*)mutex)->mark = MARK_DONE; + + return False; +} + +static Bool check_cycle(const Mutex *start, const LockSet* lockset) +{ + + graph_mark += 2; /* clear all marks */ + + return check_cycle_inner(start, lockset); +} + +/* test to see if a mutex state change would be problematic; this + makes no changes to the mutex state. This should be called before + the locking thread has actually blocked. */ +static void test_mutex_state(Mutex *mutex, MutexState state, ThreadId tid) +{ + static const Bool debug = False; + + if (mutex->state == MxDead) { + Char *str; + + switch(state) { + case MxLocked: str = "lock dead mutex"; break; + case MxUnlocked: str = "unlock dead mutex"; break; + default: str = "operate on dead mutex"; break; + } + + /* can't do anything legal to a destroyed mutex */ + record_mutex_error(tid, mutex, str, mutex->location); + return; + } + + switch(state) { + case MxLocked: + sk_assert(!check_cycle(mutex, mutex->lockdep)); + + if (debug) + print_LockSet("thread holding", thread_locks[tid]); + + if (check_cycle(mutex, thread_locks[tid])) + record_lockgraph_error(tid, mutex, thread_locks[tid], mutex->lockdep); + else { + mutex->lockdep = ls_union(mutex->lockdep, thread_locks[tid]); + + if (debug) { + VG_(printf)("giving mutex %p%(y lockdep = %p ", + mutex->mutexp, mutex->mutexp, mutex->lockdep); + print_LockSet("lockdep", mutex->lockdep); + } + } + break; + + case MxUnlocked: + if (debug) + print_LockSet("thread holding", thread_locks[tid]); + + if (mutex->state != MxLocked) { + record_mutex_error(tid, mutex, + "unlock non-locked mutex", mutex->location); + } + if (mutex->tid != tid) { + record_mutex_error(tid, mutex, + "unlock someone else's mutex", mutex->location); + } + break; + + case MxDead: + break; + + default: + break; + } +} + +/* Update a mutex state. Expects most error testing and reporting to + have happened in test_mutex_state(). The assumption is that no + client code is run by thread tid between test and set, either + because it is blocked or test and set are called together + atomically. + + Setting state to MxDead is the exception, since that can happen as + a result of any thread freeing memory; in this case set_mutex_state + does all the error reporting as well. +*/ +static void set_mutex_state(Mutex *mutex, MutexState state, ThreadId tid) +{ + static const Bool debug = False; + + if (debug) + VG_(printf)("\ntid %d changing mutex (%p)->%p%(y state %s -> %s\n", + tid, mutex, mutex->mutexp, mutex->mutexp, + pp_MutexState(mutex->state), pp_MutexState(state)); + + if (mutex->state == MxDead) { + /* can't do anything legal to a destroyed mutex */ + return; + } + + switch(state) { + case MxLocked: + if (mutex->state == MxLocked) { + if (mutex->tid != tid) + record_mutex_error(tid, mutex, "take lock held by someone else", + mutex->location); + else + record_mutex_error(tid, mutex, "take lock we already hold", + mutex->location); + + VG_(skin_panic)("core should have checked this\n"); + break; + } + + sk_assert(!check_cycle(mutex, mutex->lockdep)); + + mutex->tid = tid; + break; + + case MxUnlocked: + if (debug) + print_LockSet("thread holding", thread_locks[tid]); + + if (mutex->state != MxLocked || mutex->tid != tid) + break; + + mutex->tid = VG_INVALID_THREADID; + break; + + case MxDead: + if (mutex->state == MxLocked) { + /* forcably remove offending lock from thread's lockset */ + sk_assert(ismember(thread_locks[mutex->tid], mutex)); + thread_locks[mutex->tid] = remove_LockSet(thread_locks[mutex->tid], mutex); + mutex->tid = VG_INVALID_THREADID; + + record_mutex_error(tid, mutex, + "free locked mutex", mutex->location); + } + break; + + default: + break; + } + + mutex->location = VG_(get_ExeContext)(tid); + mutex->state = state; +} + +/*------------------------------------------------------------*/ +/*--- Setting and checking permissions. ---*/ +/*------------------------------------------------------------*/ + +/* only clean up dead mutexes */ +static +Bool cleanmx(Mutex *mx) { + return mx->state == MxDead; +} + +static +void set_address_range_state ( Addr a, UInt len /* in bytes */, + VgeInitStatus status ) +{ + Addr end; + +# if DEBUG_MAKE_ACCESSES + VG_(printf)("make_access: 0x%x, %u, status=%u\n", a, len, status); +# endif + //PROF_EVENT(30); PPP + + if (len == 0) + return; + + if (len > 100 * 1000 * 1000) + VG_(message)(Vg_UserMsg, + "Warning: set address range state: large range %d", + len); + + VGP_PUSHCC(VgpSARP); + + /* Remove mutexes in recycled memory range from hash */ + find_mutex_range(a, a+len, cleanmx); + + /* Memory block may not be aligned or a whole word multiple. In neat cases, + * we have to init len/4 words (len is in bytes). In nasty cases, it's + * len/4+1 words. This works out which it is by aligning the block and + * seeing if the end byte is in the same word as it is for the unaligned + * block; if not, it's the awkward case. */ + end = ROUNDUP(a + len, 4); + a = ROUNDDN(a, 4); + + /* Do it ... */ + switch (status) { + case Vge_VirginInit: + for ( ; a < end; a += 4) { + //PROF_EVENT(31); PPP + init_virgin_sword(a); + } + break; + + case Vge_NonVirginInit: + for ( ; a < end; a += 4) { + //PROF_EVENT(31); PPP + init_nonvirgin_sword(a); + } + break; + + case Vge_SegmentInit: + for ( ; a < end; a += 4) { + //PROF_EVENT(31); PPP + init_magically_inited_sword(a); + } + break; + + case Vge_Error: + for ( ; a < end; a += 4) { + //PROF_EVENT(31); PPP + init_error_sword(a); + } + break; + + default: + VG_(printf)("init_status = %u\n", status); + VG_(skin_panic)("Unexpected Vge_InitStatus"); + } + + /* Check that zero page and highest page have not been written to + -- this could happen with buggy syscall wrappers. Today + (2001-04-26) had precisely such a problem with + __NR_setitimer. */ + sk_assert(SK_(cheap_sanity_check)()); + VGP_POPCC(VgpSARP); +} + + +static void make_segment_readable ( Addr a, UInt len ) +{ + //PROF_EVENT(??); PPP + set_address_range_state ( a, len, Vge_SegmentInit ); +} + +static void make_writable ( Addr a, UInt len ) +{ + //PROF_EVENT(36); PPP + set_address_range_state( a, len, Vge_VirginInit ); +} + +static void make_readable ( Addr a, UInt len ) +{ + //PROF_EVENT(37); PPP + set_address_range_state( a, len, Vge_VirginInit ); +} + + +/* Block-copy states (needed for implementing realloc()). */ +static void copy_address_range_state(Addr src, Addr dst, UInt len) +{ + UInt i; + + //PROF_EVENT(40); PPP + for (i = 0; i < len; i += 4) { + shadow_word sword = *(get_sword_addr ( src+i )); + //PROF_EVENT(41); PPP + set_sword ( dst+i, sword ); + } +} + +// SSS: put these somewhere better +static void eraser_mem_read (Addr a, UInt data_size, ThreadId tid); +static void eraser_mem_write(Addr a, UInt data_size, ThreadId tid); + +static void eraser_mem_help_read_1(Addr a) REGPARM(1); +static void eraser_mem_help_read_2(Addr a) REGPARM(1); +static void eraser_mem_help_read_4(Addr a) REGPARM(1); +static void eraser_mem_help_read_N(Addr a, UInt size) REGPARM(2); + +static void eraser_mem_help_write_1(Addr a, UInt val) REGPARM(2); +static void eraser_mem_help_write_2(Addr a, UInt val) REGPARM(2); +static void eraser_mem_help_write_4(Addr a, UInt val) REGPARM(2); +static void eraser_mem_help_write_N(Addr a, UInt size) REGPARM(2); + +static void bus_lock(void); +static void bus_unlock(void); + +static +void eraser_pre_mem_read(CorePart part, ThreadId tid, + Char* s, Addr base, UInt size ) +{ + if (tid > 50) { VG_(printf)("pid = %d, s = `%s`, part = %d\n", tid, s, part); VG_(skin_panic)("a");} + eraser_mem_read(base, size, tid); +} + +static +void eraser_pre_mem_read_asciiz(CorePart part, ThreadId tid, + Char* s, Addr base ) +{ + eraser_mem_read(base, VG_(strlen)((Char*)base), tid); +} + +static +void eraser_pre_mem_write(CorePart part, ThreadId tid, + Char* s, Addr base, UInt size ) +{ + eraser_mem_write(base, size, tid); +} + + + +static +void eraser_new_mem_startup( Addr a, UInt len, Bool rr, Bool ww, Bool xx ) +{ + /* Ignore the permissions, just make it readable. Seems to work... */ + make_segment_readable(a, len); +} + + +static +void eraser_new_mem_heap ( Addr a, UInt len, Bool is_inited ) +{ + if (is_inited) { + make_readable(a, len); + } else { + make_writable(a, len); + } +} + +static +void eraser_set_perms (Addr a, UInt len, + Bool rr, Bool ww, Bool xx) +{ + if (rr) make_readable(a, len); + else if (ww) make_writable(a, len); + /* else do nothing */ +} + +static +void eraser_new_mem_stack_private(Addr a, UInt len) +{ + set_address_range_state(a, len, Vge_NonVirginInit); +} + +static +void eraser_new_mem_stack(Addr a, UInt len) +{ + set_address_range_state(a, len, Vge_VirginInit); +} + +/*--------------------------------------------------------------*/ +/*--- Initialise the memory audit system on program startup. ---*/ +/*--------------------------------------------------------------*/ + +static +void init_shadow_memory(void) +{ + Int i; + + for (i = 0; i < ESEC_MAP_WORDS; i++) + distinguished_secondary_map.swords[i] = virgin_sword; + + /* These entries gradually get overwritten as the used address + space expands. */ + for (i = 0; i < 65536; i++) + primary_map[i] = &distinguished_secondary_map; +} + + +/*------------------------------------------------------------*/ +/*--- malloc() et al replacements ---*/ +/*------------------------------------------------------------*/ + +static VgHashTable hg_malloc_list = NULL; + +#define N_FREED_CHUNKS 2 +static Int freechunkptr = 0; +static HG_Chunk *freechunks[N_FREED_CHUNKS]; + +/* Use a small redzone (paranoia) */ +UInt VG_(vg_malloc_redzone_szB) = 8; + + +/* Allocate a user-chunk of size bytes. Also allocate its shadow + block, make the shadow block point at the user block. Put the + shadow chunk on the appropriate list, and set all memory + protections correctly. */ + +static void add_HG_Chunk ( ThreadId tid, Addr p, UInt size ) +{ + HG_Chunk* hc; + + hc = VG_(malloc)(sizeof(HG_Chunk)); + hc->data = p; + hc->size = size; + hc->where = VG_(get_ExeContext)(tid); + hc->tid = tid; + + VG_(HT_add_node)( hg_malloc_list, (VgHashNode*)hc ); +} + +/* Allocate memory and note change in memory available */ +static __inline__ +void* alloc_and_new_mem ( Int size, UInt alignment, Bool is_zeroed ) +{ + Addr p; + + if (size < 0) return NULL; + + p = (Addr)VG_(cli_malloc)(alignment, size); + if (!p) { + return NULL; + } + if (is_zeroed) VG_(memset)((void*)p, 0, size); + add_HG_Chunk ( VG_(get_current_or_recent_tid)(), p, size ); + eraser_new_mem_heap( p, size, is_zeroed ); + + return (void*)p; +} + +void* SK_(malloc) ( Int n ) +{ + return alloc_and_new_mem ( n, VG_(clo_alignment), /*is_zeroed*/False ); +} + +void* SK_(__builtin_new) ( Int n ) +{ + return alloc_and_new_mem ( n, VG_(clo_alignment), /*is_zeroed*/False ); +} + +void* SK_(__builtin_vec_new) ( Int n ) +{ + return alloc_and_new_mem ( n, VG_(clo_alignment), /*is_zeroed*/False ); +} + +void* SK_(memalign) ( Int align, Int n ) +{ + return alloc_and_new_mem ( n, align, /*is_zeroed*/False ); +} + +void* SK_(calloc) ( Int nmemb, Int size ) +{ + return alloc_and_new_mem ( nmemb*size, VG_(clo_alignment), + /*is_zeroed*/True ); +} + +static ThreadId deadmx_tid; + +static +Bool deadmx(Mutex *mx) { + if (mx->state != MxDead) + set_mutex_state(mx, MxDead, deadmx_tid); + + return False; +} + +static +void die_and_free_mem ( ThreadId tid, HG_Chunk* hc, + HG_Chunk** prev_chunks_next_ptr ) +{ + Addr start = hc->data; + Addr end = start + hc->size; + + /* Remove hc from the malloclist using prev_chunks_next_ptr to + avoid repeating the hash table lookup. Can't remove until at least + after free and free_mismatch errors are done because they use + describe_addr() which looks for it in malloclist. */ + *prev_chunks_next_ptr = hc->next; + + /* Record where freed */ + hc->where = VG_(get_ExeContext) ( tid ); + + /* maintain a small window so that the error reporting machinery + knows about this memory */ + if (freechunks[freechunkptr] != NULL) { + /* free HG_Chunk */ + HG_Chunk* sc1 = freechunks[freechunkptr]; + VG_(cli_free) ( (void*)(sc1->data) ); + VG_(free) ( sc1 ); + } + + freechunks[freechunkptr] = hc; + + if (++freechunkptr == N_FREED_CHUNKS) + freechunkptr = 0; + + /* mark all mutexes in range dead */ + deadmx_tid = tid; + find_mutex_range(start, end, deadmx); +} + + +static __inline__ +void handle_free ( void* p ) +{ + HG_Chunk* hc; + HG_Chunk** prev_chunks_next_ptr; + + hc = (HG_Chunk*)VG_(HT_get_node) ( hg_malloc_list, (UInt)p, + (VgHashNode***)&prev_chunks_next_ptr ); + if (hc == NULL) { + return; + } + die_and_free_mem ( VG_(get_current_or_recent_tid)(), + hc, prev_chunks_next_ptr ); +} + +void SK_(free) ( void* p ) +{ + handle_free(p); +} + +void SK_(__builtin_delete) ( void* p ) +{ + handle_free(p); +} + +void SK_(__builtin_vec_delete) ( void* p ) +{ + handle_free(p); +} + +void* SK_(realloc) ( void* p, Int new_size ) +{ + HG_Chunk *hc; + HG_Chunk **prev_chunks_next_ptr; + Int i; + ThreadId tid = VG_(get_current_or_recent_tid)(); + + /* First try and find the block. */ + hc = (HG_Chunk*)VG_(HT_get_node) ( hg_malloc_list, (UInt)p, + (VgHashNode***)&prev_chunks_next_ptr ); + + if (hc == NULL) { + return NULL; + } + + if (hc->size == new_size) { + /* size unchanged */ + hc->where = VG_(get_ExeContext)(tid); + return p; + + } else if (hc->size > new_size) { + /* new size is smaller */ + hc->size = new_size; + hc->where = VG_(get_ExeContext)(tid); + return p; + + } else { + /* new size is bigger */ + Addr p_new; + + /* Get new memory */ + p_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size); + + /* First half kept and copied, second half new */ + copy_address_range_state( (Addr)p, p_new, hc->size ); + eraser_new_mem_heap ( p_new+hc->size, new_size-hc->size, + /*inited*/False ); + + /* Copy from old to new */ + for (i = 0; i < hc->size; i++) + ((UChar*)p_new)[i] = ((UChar*)p)[i]; + + /* Free old memory */ + die_and_free_mem ( tid, hc, prev_chunks_next_ptr ); + + /* this has to be after die_and_free_mem, otherwise the + former succeeds in shorting out the new block, not the + old, in the case when both are on the same list. */ + add_HG_Chunk ( tid, p_new, new_size ); + + return (void*)p_new; + } +} + +/*--------------------------------------------------------------*/ +/*--- Machinery to support sanity checking ---*/ +/*--------------------------------------------------------------*/ + +Bool SK_(cheap_sanity_check) ( void ) +{ + /* nothing useful we can rapidly check */ + return True; +} + +Bool SK_(expensive_sanity_check)(void) +{ + Int i; + + /* Make sure nobody changed the distinguished secondary. */ + for (i = 0; i < ESEC_MAP_WORDS; i++) + if (distinguished_secondary_map.swords[i].other != virgin_sword.other || + distinguished_secondary_map.swords[i].state != virgin_sword.state) + return False; + + return True; +} + + +/*--------------------------------------------------------------*/ +/*--- Instrumentation ---*/ +/*--------------------------------------------------------------*/ + +static UInt stk_ld, nonstk_ld, stk_st, nonstk_st; + +/* Create and return an instrumented version of cb_in. Free cb_in + before returning. */ +UCodeBlock* SK_(instrument) ( UCodeBlock* cb_in, Addr not_used ) +{ + UCodeBlock* cb; + Int i; + UInstr* u_in; + Int t_size = INVALID_TEMPREG; + Int ntemps; + Bool *stackref = NULL; + Bool locked = False; /* lock prefix */ + + cb = VG_(setup_UCodeBlock)(cb_in); + + /* stackref[] is used for super-simple value tracking to keep note + of which tempregs currently hold a value which is derived from + ESP or EBP, and is therefore likely stack-relative if used as + the address for LOAD or STORE. */ + ntemps = VG_(get_num_temps)(cb); + stackref = VG_(malloc)(sizeof(*stackref) * ntemps); + VG_(memset)(stackref, 0, sizeof(*stackref) * ntemps); + + for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { + u_in = VG_(get_instr)(cb_in, i); + + switch (u_in->opcode) { + + case NOP: case CALLM_S: case CALLM_E: + break; + + case LOCK: + locked = True; + uInstr0(cb, CCALL, 0); + uCCall(cb, (Addr)bus_lock, 0, 0, False); + break; + + case JMP: case INCEIP: + if (locked) { + uInstr0(cb, CCALL, 0); + uCCall(cb, (Addr)bus_unlock, 0, 0, False); + } + locked = False; + VG_(copy_UInstr)(cb, u_in); + break; + + case GET: + sk_assert(u_in->tag1 == ArchReg); + sk_assert(u_in->tag2 == TempReg); + sk_assert(u_in->val2 < ntemps); + + stackref[u_in->val2] = (u_in->size == 4 && + (u_in->val1 == R_ESP || u_in->val1 == R_EBP)); + VG_(copy_UInstr)(cb, u_in); + break; + + case MOV: + if (u_in->size == 4 && u_in->tag1 == TempReg) { + sk_assert(u_in->tag2 == TempReg); + stackref[u_in->val2] = stackref[u_in->val1]; + } + VG_(copy_UInstr)(cb, u_in); + break; + + case LEA1: + case ADD: case SUB: + if (u_in->size == 4 && u_in->tag1 == TempReg) { + sk_assert(u_in->tag2 == TempReg); + stackref[u_in->val2] |= stackref[u_in->val1]; + } + VG_(copy_UInstr)(cb, u_in); + break; + + case LOAD: { + void (*help)(Addr); + sk_assert(1 == u_in->size || 2 == u_in->size || 4 == u_in->size); + sk_assert(u_in->tag1 == TempReg); + + if (!clo_priv_stacks || !stackref[u_in->val1]) { + nonstk_ld++; + + switch(u_in->size) { + case 1: help = eraser_mem_help_read_1; break; + case 2: help = eraser_mem_help_read_2; break; + case 4: help = eraser_mem_help_read_4; break; + default: + VG_(skin_panic)("bad size"); + } + + /* XXX all registers should be flushed to baseblock + here */ + uInstr1(cb, CCALL, 0, TempReg, u_in->val1); + uCCall(cb, (Addr)help, 1, 1, False); + } else + stk_ld++; + + VG_(copy_UInstr)(cb, u_in); + t_size = INVALID_TEMPREG; + break; + } + + case MMX2_MemRd: + case FPU_R: { + sk_assert(1 == u_in->size || 2 == u_in->size || 4 == u_in->size || + 8 == u_in->size || 10 == u_in->size || 108 == u_in->size); + + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, (UInt)u_in->size); + + /* XXX all registers should be flushed to baseblock + here */ + uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, t_size); + uCCall(cb, (Addr) & eraser_mem_help_read_N, 2, 2, False); + + VG_(copy_UInstr)(cb, u_in); + t_size = INVALID_TEMPREG; + break; + } + + case MMX2a1_MemRd: { + sk_assert(8 == u_in->size); + + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, (UInt)u_in->size); + + /* XXX all registers should be flushed to baseblock + here */ + uInstr2(cb, CCALL, 0, TempReg, u_in->val3, TempReg, t_size); + uCCall(cb, (Addr) & eraser_mem_help_read_N, 2, 2, False); + + VG_(copy_UInstr)(cb, u_in); + t_size = INVALID_TEMPREG; + break; + } + + case SSE2a_MemRd: + case SSE2a1_MemRd: + case SSE3a_MemRd: + case SSE3a1_MemRd: + case SSE3ag_MemRd_RegWr: { + Int addr = (u_in->opcode == SSE3ag_MemRd_RegWr) ? u_in->val1 : u_in->val3; + + sk_assert(u_in->size == 4 || u_in->size == 8 || u_in->size == 16 || u_in->size == 512); + + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, (UInt)u_in->size); + + uInstr2(cb, CCALL, 0, TempReg, addr, TempReg, t_size); + uCCall(cb, (Addr) & eraser_mem_help_read_N, 2, 2, False); + + VG_(copy_UInstr)(cb, u_in); + t_size = INVALID_TEMPREG; + break; + } + + case STORE: { + void (*help)(Addr, UInt); + sk_assert(1 == u_in->size || 2 == u_in->size || 4 == u_in->size); + sk_assert(u_in->tag2 == TempReg); + + if (!clo_priv_stacks || !stackref[u_in->val2]) { + nonstk_st++; + + switch(u_in->size) { + case 1: help = eraser_mem_help_write_1; break; + case 2: help = eraser_mem_help_write_2; break; + case 4: help = eraser_mem_help_write_4; break; + default: + VG_(skin_panic)("bad size"); + } + + /* XXX all registers should be flushed to baseblock + here */ + uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, u_in->val1); + uCCall(cb, (Addr)help, 2, 2, False); + } else + stk_st++; + + VG_(copy_UInstr)(cb, u_in); + t_size = INVALID_TEMPREG; + break; + } + + case MMX2_MemWr: + case FPU_W: { + sk_assert(1 == u_in->size || 2 == u_in->size || 4 == u_in->size || + 8 == u_in->size || 10 == u_in->size || 108 == u_in->size); + + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, (UInt)u_in->size); + /* XXX all registers should be flushed to baseblock + here */ + uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, t_size); + uCCall(cb, (Addr) & eraser_mem_help_write_N, 2, 2, False); + + VG_(copy_UInstr)(cb, u_in); + t_size = INVALID_TEMPREG; + break; + } + + case SSE2a_MemWr: + case SSE3a_MemWr: { + sk_assert(4 == u_in->size || 8 == u_in->size || 16 == u_in->size || + 512 == u_in->size); + + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, (UInt)u_in->size); + /* XXX all registers should be flushed to baseblock + here */ + uInstr2(cb, CCALL, 0, TempReg, u_in->val3, TempReg, t_size); + uCCall(cb, (Addr) & eraser_mem_help_write_N, 2, 2, False); + + VG_(copy_UInstr)(cb, u_in); + t_size = INVALID_TEMPREG; + break; + } + + default: + /* conservative tromping */ + if (0 && u_in->tag1 == TempReg) /* can val1 ever be dest? */ + stackref[u_in->val1] = False; + if (u_in->tag2 == TempReg) + stackref[u_in->val2] = False; + if (u_in->tag3 == TempReg) + stackref[u_in->val3] = False; + VG_(copy_UInstr)(cb, u_in); + break; + } + } + + VG_(free)(stackref); + VG_(free_UCodeBlock)(cb_in); + return cb; +} + + +/*--------------------------------------------------------------------*/ +/*--- Error and suppression handling ---*/ +/*--------------------------------------------------------------------*/ + +typedef + enum { + /* Possible data race */ + EraserSupp + } + EraserSuppKind; + +/* What kind of error it is. */ +typedef + enum { + EraserErr, /* data-race */ + MutexErr, /* mutex operations */ + LockGraphErr, /* mutex order error */ + } + EraserErrorKind; + +/* The classification of a faulting address. */ +typedef + enum { Undescribed, /* as-yet unclassified */ + Stack, + Unknown, /* classification yielded nothing useful */ + Mallocd, + Freed, + Segment + } + AddrKind; +/* Records info about a faulting address. */ +typedef + struct { + /* ALL */ + AddrKind akind; + /* Freed, Mallocd */ + Int blksize; + /* Freed, Mallocd */ + Int rwoffset; + /* Freed, Mallocd */ + ExeContext* lastchange; + ThreadId lasttid; + /* Stack */ + ThreadId stack_tid; + /* Segment */ + const Char* filename; + const Char* section; + /* True if is just-below %esp -- could be a gcc bug. */ + Bool maybe_gcc; + /* symbolic address description */ + Char *expr; + } + AddrInfo; + +/* What kind of memory access is involved in the error? */ +typedef + enum { ReadAxs, WriteAxs, ExecAxs } + AxsKind; + +/* Extra context for memory errors */ +typedef + struct { + AxsKind axskind; + Int size; + AddrInfo addrinfo; + Bool isWrite; + shadow_word prevstate; + /* MutexErr, LockGraphErr */ + Mutex *mutex; + EC_EIP lasttouched; + ThreadId lasttid; + /* LockGraphErr */ + const LockSet *held_lockset; + const LockSet *prev_lockset; + } + HelgrindError; + +static __inline__ +void clear_AddrInfo ( AddrInfo* ai ) +{ + ai->akind = Unknown; + ai->blksize = 0; + ai->rwoffset = 0; + ai->lastchange = NULL; + ai->lasttid = VG_INVALID_THREADID; + ai->filename = NULL; + ai->section = "???"; + ai->stack_tid = VG_INVALID_THREADID; + ai->maybe_gcc = False; + ai->expr = NULL; +} + +static __inline__ +void clear_HelgrindError ( HelgrindError* err_extra ) +{ + err_extra->axskind = ReadAxs; + err_extra->size = 0; + err_extra->mutex = NULL; + err_extra->lasttouched= NULL_EC_EIP; + err_extra->lasttid = VG_INVALID_THREADID; + err_extra->prev_lockset = 0; + err_extra->held_lockset = 0; + err_extra->prevstate = SW(Vge_Virgin, 0); + clear_AddrInfo ( &err_extra->addrinfo ); + err_extra->isWrite = False; +} + + + +/* Describe an address as best you can, for error messages, + putting the result in ai. */ + +/* Callback for searching malloc'd and free'd lists */ +static Bool addr_is_in_block(VgHashNode *node, void *ap) +{ + HG_Chunk* hc2 = (HG_Chunk*)node; + Addr a = *(Addr *)ap; + + return (hc2->data <= a && a < hc2->data + hc2->size); +} + +static void describe_addr ( Addr a, AddrInfo* ai ) +{ + HG_Chunk* hc; + Int i; + + /* Search for it in segments */ + { + const SegInfo *seg; + + for(seg = VG_(next_seginfo)(NULL); + seg != NULL; + seg = VG_(next_seginfo)(seg)) { + Addr base = VG_(seg_start)(seg); + UInt size = VG_(seg_size)(seg); + const UChar *filename = VG_(seg_filename)(seg); + + if (a >= base && a < base+size) { + ai->akind = Segment; + ai->blksize = size; + ai->rwoffset = a - base; + ai->filename = filename; + + switch(VG_(seg_sect_kind)(a)) { + case Vg_SectText: ai->section = "text"; break; + case Vg_SectData: ai->section = "data"; break; + case Vg_SectBSS: ai->section = "BSS"; break; + case Vg_SectGOT: ai->section = "GOT"; break; + case Vg_SectPLT: ai->section = "PLT"; break; + case Vg_SectUnknown: + default: + ai->section = "???"; break; + } + + return; + } + } + } + + /* Search for a currently malloc'd block which might bracket it. */ + hc = (HG_Chunk*)VG_(HT_first_match)(hg_malloc_list, addr_is_in_block, &a); + if (NULL != hc) { + ai->akind = Mallocd; + ai->blksize = hc->size; + ai->rwoffset = (Int)a - (Int)(hc->data); + ai->lastchange = hc->where; + ai->lasttid = hc->tid; + return; + } + + /* Look in recently freed memory */ + for(i = 0; i < N_FREED_CHUNKS; i++) { + hc = freechunks[i]; + if (hc == NULL) + continue; + + if (a >= hc->data && a < hc->data + hc->size) { + ai->akind = Freed; + ai->blksize = hc->size; + ai->rwoffset = a - hc->data; + ai->lastchange = hc->where; + ai->lasttid = hc->tid; + return; + } + } + + /* Clueless ... */ + ai->akind = Unknown; + return; +} + + +/* Updates the copy with address info if necessary. */ +UInt SK_(update_extra)(Error* err) +{ + HelgrindError* extra; + + extra = (HelgrindError*)VG_(get_error_extra)(err); + if (extra != NULL && Undescribed == extra->addrinfo.akind) { + describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) ); + } + return sizeof(HelgrindError); +} + +static void record_eraser_error ( ThreadId tid, Addr a, Bool is_write, + shadow_word prevstate ) +{ + shadow_word *sw; + HelgrindError err_extra; + + n_eraser_warnings++; + + clear_HelgrindError(&err_extra); + err_extra.isWrite = is_write; + err_extra.addrinfo.akind = Undescribed; + err_extra.prevstate = prevstate; + if (clo_execontext) + err_extra.lasttouched = getExeContext(a); + err_extra.addrinfo.expr = VG_(describe_addr)(tid, a); + + VG_(maybe_record_error)( tid, EraserErr, a, + (is_write ? "writing" : "reading"), + &err_extra); + + sw = get_sword_addr(a); + if (sw->state == Vge_Excl && sw->other != TLSP_INDICATING_ALL) { + ThreadLifeSeg *tls = unpackTLS(sw->other); + tls->refcount--; + } + + set_sword(a, error_sword); +} + +static void record_mutex_error(ThreadId tid, Mutex *mutex, + Char *str, ExeContext *ec) +{ + HelgrindError err_extra; + + clear_HelgrindError(&err_extra); + err_extra.addrinfo.akind = Undescribed; + err_extra.mutex = mutex; + err_extra.lasttouched = EC(ec, virgin_sword, thread_seg[tid]); + err_extra.lasttid = tid; + + VG_(maybe_record_error)(tid, MutexErr, + (Addr)mutex->mutexp, str, &err_extra); +} + +static void record_lockgraph_error(ThreadId tid, Mutex *mutex, + const LockSet *lockset_holding, + const LockSet *lockset_prev) +{ + HelgrindError err_extra; + + n_lockorder_warnings++; + + clear_HelgrindError(&err_extra); + err_extra.addrinfo.akind = Undescribed; + err_extra.mutex = mutex; + + err_extra.lasttouched = EC(mutex->location, virgin_sword, 0); + err_extra.held_lockset = lockset_holding; + err_extra.prev_lockset = lockset_prev; + + VG_(maybe_record_error)(tid, LockGraphErr, mutex->mutexp, "", &err_extra); +} + +Bool SK_(eq_SkinError) ( VgRes not_used, Error* e1, Error* e2 ) +{ + Char *e1s, *e2s; + + sk_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); + + switch (VG_(get_error_kind)(e1)) { + case EraserErr: + return VG_(get_error_address)(e1) == VG_(get_error_address)(e2); + + case MutexErr: + return VG_(get_error_address)(e1) == VG_(get_error_address)(e2); + } + + e1s = VG_(get_error_string)(e1); + e2s = VG_(get_error_string)(e2); + if (e1s != e2s) return False; + if (0 != VG_(strcmp)(e1s, e2s)) return False; + return True; +} + +static void pp_AddrInfo ( Addr a, AddrInfo* ai ) +{ + if (ai->expr != NULL) + VG_(message)(Vg_UserMsg, + " Address %p == %s", a, ai->expr); + + switch (ai->akind) { + case Stack: + VG_(message)(Vg_UserMsg, + " Address %p is on thread %d's stack", + a, ai->stack_tid); + break; + case Unknown: + if (ai->expr != NULL) + break; + + /* maybe_gcc is never set to True! This is a hangover from code + in Memcheck */ + if (ai->maybe_gcc) { + VG_(message)(Vg_UserMsg, + " Address %p is just below %%esp. Possibly a bug in GCC/G++", + a); + VG_(message)(Vg_UserMsg, + " v 2.96 or 3.0.X. To suppress, use: --workaround-gcc296-bugs=yes"); + } else { + VG_(message)(Vg_UserMsg, + " Address %p is not stack'd, malloc'd or (recently) free'd", a); + } + break; + case Segment: + VG_(message)(Vg_UserMsg, + " Address %p is in %s section of %s", + a, ai->section, ai->filename); + break; + case Mallocd: + case Freed: { + UInt delta; + UChar* relative; + if (ai->rwoffset < 0) { + delta = (UInt)(- ai->rwoffset); + relative = "before"; + } else if (ai->rwoffset >= ai->blksize) { + delta = ai->rwoffset - ai->blksize; + relative = "after"; + } else { + delta = ai->rwoffset; + relative = "inside"; + } + VG_(message)(Vg_UserMsg, + " Address %p is %d bytes %s a block of size %d %s by thread %d", + a, delta, relative, + ai->blksize, + ai->akind == Mallocd ? "alloc'd" : "freed", + ai->lasttid); + + VG_(pp_ExeContext)(ai->lastchange); + break; + } + default: + VG_(skin_panic)("pp_AddrInfo"); + } +} + +static Char *lockset_str(const Char *prefix, const LockSet *lockset) +{ + Char *buf, *cp; + Int i; + + buf = VG_(malloc)((prefix == NULL ? 0 : VG_(strlen)(prefix)) + + lockset->setsize * 120 + + 1); + + cp = buf; + if (prefix) + cp += VG_(sprintf)(cp, "%s", prefix); + + for(i = 0; i < lockset->setsize; i++) + cp += VG_(sprintf)(cp, "%p%(y, ", lockset->mutex[i]->mutexp, + lockset->mutex[i]->mutexp); + + if (lockset->setsize) + cp[-2] = '\0'; + else + *cp = '\0'; + + return buf; +} + +void SK_(pp_SkinError) ( Error* err ) +{ + HelgrindError *extra = (HelgrindError *)VG_(get_error_extra)(err); + Char buf[100]; + Char *msg = buf; + const LockSet *ls; + + *msg = '\0'; + + switch(VG_(get_error_kind)(err)) { + case EraserErr: { + Addr err_addr = VG_(get_error_address)(err); + + VG_(message)(Vg_UserMsg, "Possible data race %s variable at %p %(y", + VG_(get_error_string)(err), err_addr, err_addr); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + pp_AddrInfo(err_addr, &extra->addrinfo); + + switch(extra->prevstate.state) { + case Vge_Virgin: + /* shouldn't be possible to go directly from virgin -> error */ + VG_(sprintf)(buf, "virgin!?"); + break; + + case Vge_Excl: { + ThreadLifeSeg *tls = unpackTLS(extra->prevstate.other); + + sk_assert(tls != unpackTLS(TLSP_INDICATING_ALL)); + VG_(sprintf)(buf, "exclusively owned by thread %u", tls->tid); + break; + } + + case Vge_Shar: + case Vge_SharMod: + ls = unpackLockSet(extra->prevstate.other); + + if (isempty(ls)) { + VG_(sprintf)(buf, "shared %s, no locks", + extra->prevstate.state == Vge_Shar ? "RO" : "RW"); + break; + } + + msg = lockset_str(extra->prevstate.state == Vge_Shar ? + "shared RO, locked by:" : + "shared RW, locked by:", ls); + + break; + } + + if (*msg) + VG_(message)(Vg_UserMsg, " Previous state: %s", msg); + + if (clo_execontext == EC_Some + && extra->lasttouched.uu_ec_eip.eip != 0) { + Char file[100]; + UInt line; + Addr eip = extra->lasttouched.uu_ec_eip.eip; + + VG_(message)(Vg_UserMsg, " Word at %p last changed state from %s by thread %u", + err_addr, + pp_state(extra->lasttouched.state), + unpackTLS(extra->lasttouched.tls)->tid); + + if (VG_(get_filename_linenum)(eip, file, sizeof(file), &line)) { + VG_(message)(Vg_UserMsg, " at %p: %y (%s:%u)", + eip, eip, file, line); + } else if (VG_(get_objname)(eip, file, sizeof(file))) { + VG_(message)(Vg_UserMsg, " at %p: %y (in %s)", + eip, eip, file); + } else { + VG_(message)(Vg_UserMsg, " at %p: %y", eip, eip); + } + } else if (clo_execontext == EC_All + && extra->lasttouched.uu_ec_eip.ec != NULL) { + VG_(message)(Vg_UserMsg, " Word at %p last changed state from %s in tid %u", + err_addr, + pp_state(extra->lasttouched.state), + unpackTLS(extra->lasttouched.tls)->tid); + VG_(pp_ExeContext)(extra->lasttouched.uu_ec_eip.ec); + } + break; + } + + case MutexErr: + VG_(message)(Vg_UserMsg, "Mutex problem at %p%(y trying to %s", + VG_(get_error_address)(err), + VG_(get_error_address)(err), + VG_(get_error_string)(err)); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + if (extra->lasttouched.uu_ec_eip.ec != NULL) { + VG_(message)(Vg_UserMsg, " last touched by thread %d", extra->lasttid); + VG_(pp_ExeContext)(extra->lasttouched.uu_ec_eip.ec); + } + pp_AddrInfo(VG_(get_error_address)(err), &extra->addrinfo); + break; + + case LockGraphErr: { + const LockSet *heldset = extra->held_lockset; + Addr err_addr = VG_(get_error_address)(err); + Int i; + + msg = lockset_str(NULL, heldset); + + VG_(message)(Vg_UserMsg, "Mutex %p%(y locked in inconsistent order", + err_addr, err_addr); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + VG_(message)(Vg_UserMsg, " while holding locks %s", msg); + + for(i = 0; i < heldset->setsize; i++) { + const Mutex *lsmx = heldset->mutex[i]; + + /* needs to be a recursive search+display */ + if (0 && !ismember(lsmx->lockdep, extra->mutex)) + continue; + + VG_(message)(Vg_UserMsg, " %p%(y last locked at", + lsmx->mutexp, lsmx->mutexp); + VG_(pp_ExeContext)(lsmx->location); + VG_(free)(msg); + msg = lockset_str(NULL, lsmx->lockdep); + VG_(message)(Vg_UserMsg, " while depending on locks %s", msg); + } + + break; + } + } + + if (msg != buf) + VG_(free)(msg); +} + + +Bool SK_(recognised_suppression) ( Char* name, Supp *su ) +{ + if (0 == VG_(strcmp)(name, "Eraser")) { + VG_(set_supp_kind)(su, EraserSupp); + return True; + } else { + return False; + } +} + + +Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, Supp* su ) +{ + /* do nothing -- no extra suppression info present. Return True to + indicate nothing bad happened. */ + return True; +} + + +Bool SK_(error_matches_suppression)(Error* err, Supp* su) +{ + sk_assert(VG_(get_supp_kind)(su) == EraserSupp); + + return (VG_(get_error_kind)(err) == EraserErr); +} + +extern Char* SK_(get_error_name) ( Error* err ) +{ + if (EraserErr == VG_(get_error_kind)(err)) { + return "Eraser"; + } else { + return NULL; /* Other errors types can't be suppressed */ + } +} + +extern void SK_(print_extra_suppression_info) ( Error* err ) +{ + /* Do nothing */ +} + +static void eraser_pre_mutex_lock(ThreadId tid, void* void_mutex) +{ + Mutex *mutex = get_mutex((Addr)void_mutex); + + test_mutex_state(mutex, MxLocked, tid); +} + +static void eraser_post_mutex_lock(ThreadId tid, void* void_mutex) +{ + static const Bool debug = False; + Mutex *mutex = get_mutex((Addr)void_mutex); + const LockSet* ls; + + set_mutex_state(mutex, MxLocked, tid); + +# if DEBUG_LOCKS + VG_(printf)("lock (%u, %p)\n", tid, mutex->mutexp); +# endif + + /* VG_(printf)("LOCK: held %d, new %p\n", thread_locks[tid], mutex); */ +# if LOCKSET_SANITY > 1 + sanity_check_locksets("eraser_post_mutex_lock-IN"); +# endif + + ls = lookup_LockSet_with(thread_locks[tid], mutex); + + if (ls == NULL) { + LockSet *newset = add_LockSet(thread_locks[tid], mutex); + insert_LockSet(newset); + ls = newset; + } + thread_locks[tid] = ls; + + if (debug || DEBUG_LOCKS) + VG_(printf)("tid %u now has lockset %p\n", tid, ls); + + if (debug || LOCKSET_SANITY > 1) + sanity_check_locksets("eraser_post_mutex_lock-OUT"); +} + + +static void eraser_post_mutex_unlock(ThreadId tid, void* void_mutex) +{ + static const Bool debug = False; + Int i = 0; + Mutex *mutex = get_mutex((Addr)void_mutex); + const LockSet *ls; + + test_mutex_state(mutex, MxUnlocked, tid); + set_mutex_state(mutex, MxUnlocked, tid); + + if (!ismember(thread_locks[tid], mutex)) + return; + + if (debug || DEBUG_LOCKS) + VG_(printf)("unlock(%u, %p%(y)\n", tid, mutex->mutexp, mutex->mutexp); + + if (debug || LOCKSET_SANITY > 1) + sanity_check_locksets("eraser_post_mutex_unlock-IN"); + + ls = lookup_LockSet_without(thread_locks[tid], mutex); + + if (ls == NULL) { + LockSet *newset = remove_LockSet(thread_locks[tid], mutex); + insert_LockSet(newset); + ls = newset; + } + + /* Update the thread's lock vector */ + if (debug || DEBUG_LOCKS) + VG_(printf)("tid %u reverts from %p to lockset %p\n", + tid, thread_locks[tid], i); + + thread_locks[tid] = ls; + + if (debug || LOCKSET_SANITY > 1) + sanity_check_locksets("eraser_post_mutex_unlock-OUT"); +} + + +/* --------------------------------------------------------------------- + Checking memory reads and writes + ------------------------------------------------------------------ */ + +/* Behaviour on reads and writes: + * + * VIR EXCL SHAR SH_MOD + * ---------------------------------------------------------------- + * rd/wr, 1st thread | - EXCL - - + * rd, new thread | - SHAR - - + * wr, new thread | - SH_MOD - - + * rd | error! - SHAR SH_MOD + * wr | EXCL - SH_MOD SH_MOD + * ---------------------------------------------------------------- + */ + +static inline +void dump_around_a(Addr a) +{ + UInt i; + shadow_word* sword; + VG_(printf)("NEARBY:\n"); + for (i = a - 12; i <= a + 12; i += 4) { + sword = get_sword_addr(i); + VG_(printf)(" %x -- tid: %u, state: %u\n", i, sword->other, sword->state); + } +} + +#if DEBUG_ACCESSES + #define DEBUG_STATE(args...) \ + VG_(printf)("(%u) ", size), \ + VG_(printf)(args) +#else + #define DEBUG_STATE(args...) +#endif + +static void eraser_mem_read_word(Addr a, ThreadId tid) +{ + shadow_word* sword /* egcs-2.91.66 complains uninit */ = NULL; + shadow_word prevstate; + ThreadLifeSeg *tls; + const LockSet *ls; + Bool statechange = False; + + static const void *const states[4] = { + [Vge_Virgin] &&st_virgin, + [Vge_Excl] &&st_excl, + [Vge_Shar] &&st_shar, + [Vge_SharMod] &&st_sharmod, + }; + + tls = thread_seg[tid]; + sk_assert(tls != NULL && tls->tid == tid); + + sword = get_sword_addr(a); + if (sword == SEC_MAP_ACCESS) { + VG_(printf)("read distinguished 2ndary map! 0x%x\n", a); + return; + } + + prevstate = *sword; + + goto *states[sword->state]; + + /* This looks like reading of unitialised memory, may be legit. Eg. + * calloc() zeroes its values, so untouched memory may actually be + * initialised. Leave that stuff to Valgrind. */ + st_virgin: + if (TID_INDICATING_NONVIRGIN == sword->other) { + DEBUG_STATE("Read VIRGIN --> EXCL: %8x, %u\n", a, tid); + if (DEBUG_VIRGIN_READS) + dump_around_a(a); + } else { + DEBUG_STATE("Read SPECIAL --> EXCL: %8x, %u\n", a, tid); + } + statechange = True; + *sword = SW(Vge_Excl, packTLS(tls)); /* remember exclusive owner */ + tls->refcount++; + goto done; + + st_excl: { + ThreadLifeSeg *sw_tls = unpackTLS(sword->other); + + if (tls == sw_tls) { + DEBUG_STATE("Read EXCL: %8x, %u\n", a, tid); + } else if (unpackTLS(TLSP_INDICATING_ALL) == sw_tls) { + DEBUG_STATE("Read EXCL/ERR: %8x, %u\n", a, tid); + } else if (tlsIsDisjoint(tls, sw_tls)) { + DEBUG_STATE("Read EXCL(%u) --> EXCL: %8x, %u\n", sw_tls->tid, a, tid); + statechange = True; + sword->other = packTLS(tls); + sw_tls->refcount--; + tls->refcount++; + } else { + DEBUG_STATE("Read EXCL(%u) --> SHAR: %8x, %u\n", sw_tls->tid, a, tid); + sw_tls->refcount--; + statechange = True; + *sword = SW(Vge_Shar, packLockSet(thread_locks[tid])); + + if (DEBUG_MEM_LOCKSET_CHANGES) + print_LockSet("excl read locks", unpackLockSet(sword->other)); + } + goto done; + } + + st_shar: + DEBUG_STATE("Read SHAR: %8x, %u\n", a, tid); + sword->other = packLockSet(intersect(unpackLockSet(sword->other), + thread_locks[tid])); + statechange = sword->other != prevstate.other; + goto done; + + st_sharmod: + DEBUG_STATE("Read SHAR_MOD: %8x, %u\n", a, tid); + ls = intersect(unpackLockSet(sword->other), + thread_locks[tid]); + sword->other = packLockSet(ls); + + statechange = sword->other != prevstate.other; + + if (isempty(ls)) { + record_eraser_error(tid, a, False /* !is_write */, prevstate); + } + goto done; + + done: + if (clo_execontext != EC_None && statechange) { + EC_EIP eceip; + + if (clo_execontext == EC_Some) + eceip = EIP(VG_(get_EIP)(tid), prevstate, tls); + else + eceip = EC(VG_(get_ExeContext)(tid), prevstate, tls); + setExeContext(a, eceip); + } +} + +static void eraser_mem_read(Addr a, UInt size, ThreadId tid) +{ + Addr end; + + end = ROUNDUP(a+size, 4); + a = ROUNDDN(a, 4); + + for ( ; a < end; a += 4) + eraser_mem_read_word(a, tid); +} + +static void eraser_mem_write_word(Addr a, ThreadId tid) +{ + ThreadLifeSeg *tls; + shadow_word* sword /* egcs-2.91.66 complains uninit */ = NULL; + shadow_word prevstate; + Bool statechange = False; + static const void *const states[4] = { + [Vge_Virgin] &&st_virgin, + [Vge_Excl] &&st_excl, + [Vge_Shar] &&st_shar, + [Vge_SharMod] &&st_sharmod, + }; + + tls = thread_seg[tid]; + sk_assert(tls != NULL && tls->tid == tid); + + sword = get_sword_addr(a); + if (sword == SEC_MAP_ACCESS) { + VG_(printf)("read distinguished 2ndary map! 0x%x\n", a); + return; + } + + prevstate = *sword; + + goto *states[sword->state]; + + st_virgin: + if (TID_INDICATING_NONVIRGIN == sword->other) + DEBUG_STATE("Write VIRGIN --> EXCL: %8x, %u\n", a, tid); + else + DEBUG_STATE("Write SPECIAL --> EXCL: %8x, %u\n", a, tid); + statechange = True; + *sword = SW(Vge_Excl, packTLS(tls));/* remember exclusive owner */ + tls->refcount++; + goto done; + + st_excl: { + ThreadLifeSeg *sw_tls = unpackTLS(sword->other); + + if (tls == sw_tls) { + DEBUG_STATE("Write EXCL: %8x, %u\n", a, tid); + goto done; + } else if (unpackTLS(TLSP_INDICATING_ALL) == sw_tls) { + DEBUG_STATE("Write EXCL/ERR: %8x, %u\n", a, tid); + goto done; + } else if (tlsIsDisjoint(tls, sw_tls)) { + DEBUG_STATE("Write EXCL(%u) --> EXCL: %8x, %u\n", sw_tls->tid, a, tid); + sword->other = packTLS(tls); + sw_tls->refcount--; + tls->refcount++; + goto done; + } else { + DEBUG_STATE("Write EXCL(%u) --> SHAR_MOD: %8x, %u\n", sw_tls->tid, a, tid); + statechange = True; + sw_tls->refcount--; + *sword = SW(Vge_SharMod, packLockSet(thread_locks[tid])); + if(DEBUG_MEM_LOCKSET_CHANGES) + print_LockSet("excl write locks", unpackLockSet(sword->other)); + goto SHARED_MODIFIED; + } + } + + st_shar: + DEBUG_STATE("Write SHAR --> SHAR_MOD: %8x, %u\n", a, tid); + sword->state = Vge_SharMod; + sword->other = packLockSet(intersect(unpackLockSet(sword->other), + thread_locks[tid])); + statechange = True; + goto SHARED_MODIFIED; + + st_sharmod: + DEBUG_STATE("Write SHAR_MOD: %8x, %u\n", a, tid); + sword->other = packLockSet(intersect(unpackLockSet(sword->other), + thread_locks[tid])); + statechange = sword->other != prevstate.other; + + SHARED_MODIFIED: + if (isempty(unpackLockSet(sword->other))) { + record_eraser_error(tid, a, True /* is_write */, prevstate); + } + goto done; + + done: + if (clo_execontext != EC_None && statechange) { + EC_EIP eceip; + + if (clo_execontext == EC_Some) + eceip = EIP(VG_(get_EIP)(tid), prevstate, tls); + else + eceip = EC(VG_(get_ExeContext)(tid), prevstate, tls); + setExeContext(a, eceip); + } +} + +static void eraser_mem_write(Addr a, UInt size, ThreadId tid) +{ + Addr end; + + end = ROUNDUP(a+size, 4); + a = ROUNDDN(a, 4); + + for ( ; a < end; a += 4) + eraser_mem_write_word(a, tid); +} + +#undef DEBUG_STATE + +REGPARM(1) static void eraser_mem_help_read_1(Addr a) +{ + eraser_mem_read(a, 1, VG_(get_current_tid)()); +} + +REGPARM(1) static void eraser_mem_help_read_2(Addr a) +{ + eraser_mem_read(a, 2, VG_(get_current_tid)()); +} + +REGPARM(1) static void eraser_mem_help_read_4(Addr a) +{ + eraser_mem_read(a, 4, VG_(get_current_tid)()); +} + +REGPARM(2) static void eraser_mem_help_read_N(Addr a, UInt size) +{ + eraser_mem_read(a, size, VG_(get_current_tid)()); +} + +REGPARM(2) static void eraser_mem_help_write_1(Addr a, UInt val) +{ + if (*(UChar *)a != val) + eraser_mem_write(a, 1, VG_(get_current_tid)()); +} +REGPARM(2) static void eraser_mem_help_write_2(Addr a, UInt val) +{ + if (*(UShort *)a != val) + eraser_mem_write(a, 2, VG_(get_current_tid)()); +} +REGPARM(2) static void eraser_mem_help_write_4(Addr a, UInt val) +{ + if (*(UInt *)a != val) + eraser_mem_write(a, 4, VG_(get_current_tid)()); +} +REGPARM(2) static void eraser_mem_help_write_N(Addr a, UInt size) +{ + eraser_mem_write(a, size, VG_(get_current_tid)()); +} + +static void hg_thread_create(ThreadId parent, ThreadId child) +{ + if (0) + VG_(printf)("CREATE: %u creating %u\n", parent, child); + + newTLS(child); + addPriorTLS(child, parent); + + newTLS(parent); +} + +static void hg_thread_join(ThreadId joiner, ThreadId joinee) +{ + if (0) + VG_(printf)("JOIN: %u joining on %u\n", joiner, joinee); + + newTLS(joiner); + addPriorTLS(joiner, joinee); + + clearTLS(joinee); +} + +static Int __BUS_HARDWARE_LOCK__; + +static void bus_lock(void) +{ + ThreadId tid = VG_(get_current_tid)(); + eraser_pre_mutex_lock(tid, &__BUS_HARDWARE_LOCK__); + eraser_post_mutex_lock(tid, &__BUS_HARDWARE_LOCK__); +} + +static void bus_unlock(void) +{ + ThreadId tid = VG_(get_current_tid)(); + eraser_post_mutex_unlock(tid, &__BUS_HARDWARE_LOCK__); +} + +/*--------------------------------------------------------------------*/ +/*--- Client requests ---*/ +/*--------------------------------------------------------------------*/ + +Bool SK_(handle_client_request)(ThreadId tid, UInt *args, UInt *ret) +{ + if (!VG_IS_SKIN_USERREQ('H','G',args[0])) + return False; + + switch(args[0]) { + case VG_USERREQ__HG_CLEAN_MEMORY: + set_address_range_state(args[1], args[2], Vge_VirginInit); + *ret = 0; /* meaningless */ + break; + + case VG_USERREQ__HG_KNOWN_RACE: + set_address_range_state(args[1], args[2], Vge_Error); + *ret = 0; /* meaningless */ + break; + + default: + return False; + } + + return True; +} + + +/*--------------------------------------------------------------------*/ +/*--- Setup ---*/ +/*--------------------------------------------------------------------*/ + +void SK_(pre_clo_init)(void) +{ + Int i; + LockSet *empty; + + VG_(details_name) ("Helgrind"); + VG_(details_version) (NULL); + VG_(details_description) ("a data race detector"); + VG_(details_copyright_author)( + "Copyright (C) 2002-2004, and GNU GPL'd, by Nicholas Nethercote et al."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + VG_(details_avg_translation_sizeB) ( 115 ); + + VG_(needs_core_errors)(); + VG_(needs_skin_errors)(); + VG_(needs_data_syms)(); + VG_(needs_client_requests)(); + VG_(needs_command_line_options)(); + VG_(needs_shadow_memory)(); + + VG_(init_new_mem_startup) (& eraser_new_mem_startup); + + /* stack ones not decided until VG_(post_clo_init)() */ + + VG_(init_new_mem_brk) (& make_writable); + VG_(init_new_mem_mmap) (& eraser_new_mem_startup); + + VG_(init_change_mem_mprotect) (& eraser_set_perms); + + VG_(init_ban_mem_stack) (NULL); + + VG_(init_die_mem_stack) (NULL); + VG_(init_die_mem_stack_signal) (NULL); + VG_(init_die_mem_brk) (NULL); + VG_(init_die_mem_munmap) (NULL); + + VG_(init_pre_mem_read) (& eraser_pre_mem_read); + VG_(init_pre_mem_read_asciiz) (& eraser_pre_mem_read_asciiz); + VG_(init_pre_mem_write) (& eraser_pre_mem_write); + VG_(init_post_mem_write) (NULL); + + VG_(init_post_thread_create) (& hg_thread_create); + VG_(init_post_thread_join) (& hg_thread_join); + + VG_(init_pre_mutex_lock) (& eraser_pre_mutex_lock); + VG_(init_post_mutex_lock) (& eraser_post_mutex_lock); + VG_(init_post_mutex_unlock) (& eraser_post_mutex_unlock); + + VG_(register_compact_helper)((Addr) & eraser_mem_help_read_1); + VG_(register_compact_helper)((Addr) & eraser_mem_help_read_2); + VG_(register_compact_helper)((Addr) & eraser_mem_help_read_4); + VG_(register_noncompact_helper)((Addr) & eraser_mem_help_read_N); + + VG_(register_compact_helper)((Addr) & eraser_mem_help_write_1); + VG_(register_compact_helper)((Addr) & eraser_mem_help_write_2); + VG_(register_compact_helper)((Addr) & eraser_mem_help_write_4); + VG_(register_noncompact_helper)((Addr) & eraser_mem_help_write_N); + + VG_(register_noncompact_helper)((Addr) & bus_lock); + VG_(register_noncompact_helper)((Addr) & bus_unlock); + + for(i = 0; i < LOCKSET_HASH_SZ; i++) + lockset_hash[i] = NULL; + + empty = alloc_LockSet(0); + insert_LockSet(empty); + emptyset = empty; + + /* Init lock table and thread segments */ + for (i = 0; i < VG_N_THREADS; i++) { + thread_locks[i] = empty; + + newTLS(i); + } + + init_shadow_memory(); + hg_malloc_list = VG_(HT_construct)(); +} + +Bool SK_(process_cmd_line_option)(Char* arg) +{ + if (VG_CLO_STREQ(arg, "--show-last-access=no")) + clo_execontext = EC_None; + else if (VG_CLO_STREQ(arg, "--show-last-access=some")) + clo_execontext = EC_Some; + else if (VG_CLO_STREQ(arg, "--show-last-access=all")) + clo_execontext = EC_All; + + else VG_BOOL_CLO("--private-stacks", clo_priv_stacks) + + else + return VG_(replacement_malloc_process_cmd_line_option)(arg); + + return True; +} + +void SK_(print_usage)(void) +{ + VG_(printf)( +" --private-stacks=yes|no assume thread stacks are used privately [no]\n" +" --show-last-access=no|some|all\n" +" show location of last word access on error [no]\n" + ); + VG_(replacement_malloc_print_usage)(); +} + +void SK_(print_debug_usage)(void) +{ + VG_(replacement_malloc_print_debug_usage)(); +} + +void SK_(post_clo_init)(void) +{ + void (*stack_tracker)(Addr a, UInt len); + + if (clo_execontext) { + execontext_map = VG_(malloc)(sizeof(ExeContextMap *) * 65536); + VG_(memset)(execontext_map, 0, sizeof(ExeContextMap *) * 65536); + } + + if (clo_priv_stacks) + stack_tracker = & eraser_new_mem_stack_private; + else + stack_tracker = & eraser_new_mem_stack; + + VG_(init_new_mem_stack) (stack_tracker); + VG_(init_new_mem_stack_signal) (stack_tracker); +} + + +void SK_(fini)(Int exitcode) +{ + if (DEBUG_LOCK_TABLE) { + pp_all_LockSets(); + pp_all_mutexes(); + } + + if (LOCKSET_SANITY) + sanity_check_locksets("SK_(fini)"); + + if (VG_(clo_verbosity) > 0) + VG_(message)(Vg_UserMsg, "%u possible data races found; %u lock order problems", + n_eraser_warnings, n_lockorder_warnings); + + if (0) + VG_(printf)("stk_ld:%u+stk_st:%u = %u nonstk_ld:%u+nonstk_st:%u = %u %u%%\n", + stk_ld, stk_st, stk_ld + stk_st, + nonstk_ld, nonstk_st, nonstk_ld + nonstk_st, + ((stk_ld+stk_st)*100) / (stk_ld + stk_st + nonstk_ld + nonstk_st)); +} + +/* Uses a 1:1 mapping */ +VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 1.0) + +/*--------------------------------------------------------------------*/ +/*--- end hg_main.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/helgrind/tests/.cvsignore b/VEX/head20041019/helgrind/tests/.cvsignore new file mode 100644 index 0000000000..d43164e029 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/.cvsignore @@ -0,0 +1,12 @@ +Makefile.in +Makefile +allok +deadlock +inherit +race +race2 +readshared +*.stdout.diff +*.stderr.diff +*.stdout.out +*.stderr.out diff --git a/VEX/head20041019/helgrind/tests/CVS/Entries b/VEX/head20041019/helgrind/tests/CVS/Entries new file mode 100644 index 0000000000..30468eaf0b --- /dev/null +++ b/VEX/head20041019/helgrind/tests/CVS/Entries @@ -0,0 +1,45 @@ +/.cvsignore/1.4/Wed Oct 13 14:55:07 2004// +/Makefile.am/1.8/Sat Jul 10 14:56:26 2004// +/allok.c/1.1/Thu Oct 16 06:09:41 2003// +/allok.stderr.exp/1.1/Wed Oct 15 22:15:37 2003// +/allok.vgtest/1.1/Wed Oct 15 22:15:37 2003// +/deadlock.c/1.2/Mon Sep 13 20:48:20 2004// +/deadlock.stderr.exp/1.5/Mon Dec 15 09:00:21 2003// +/deadlock.vgtest/1.1/Wed Oct 15 22:15:37 2003// +/filter_stderr/1.4/Thu Nov 6 11:34:52 2003// +/inherit.c/1.2/Fri Apr 16 07:22:42 2004// +/inherit.stderr.exp/1.4/Fri Apr 16 07:22:42 2004// +/inherit.vgtest/1.1/Wed Oct 15 22:15:37 2003// +/insn_basic.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_basic.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_basic.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_fpu.stderr.exp/1.1/Sat Mar 27 18:02:37 2004// +/insn_fpu.stdout.exp/1.4/Wed Mar 31 22:47:52 2004// +/insn_fpu.vgtest/1.1/Sat Mar 27 18:02:37 2004// +/insn_mmx.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmx.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmx.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmxext.stderr.exp/1.2/Tue Mar 9 08:50:02 2004// +/insn_mmxext.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_mmxext.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_sse.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse2.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse2.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_sse2.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/race.c/1.1/Thu Oct 16 06:09:41 2003// +/race.stderr.exp/1.4/Thu Nov 13 17:53:43 2003// +/race.vgtest/1.1/Wed Oct 15 22:15:37 2003// +/race2.c/1.1/Thu Oct 16 06:09:41 2003// +/race2.stderr.exp/1.4/Thu Nov 13 17:53:43 2003// +/race2.vgtest/1.1/Wed Oct 15 22:15:37 2003// +/readshared.c/1.1/Thu Oct 16 06:09:41 2003// +/readshared.stderr.exp/1.1/Wed Oct 15 22:15:37 2003// +/readshared.vgtest/1.1/Wed Oct 15 22:15:37 2003// +/toobig-allocs.stderr.exp/1.1/Sat Jul 10 14:56:26 2004// +/toobig-allocs.vgtest/1.1/Sat Jul 10 14:56:26 2004// +D diff --git a/VEX/head20041019/helgrind/tests/CVS/Repository b/VEX/head20041019/helgrind/tests/CVS/Repository new file mode 100644 index 0000000000..c11c4a19bc --- /dev/null +++ b/VEX/head20041019/helgrind/tests/CVS/Repository @@ -0,0 +1 @@ +valgrind/helgrind/tests diff --git a/VEX/head20041019/helgrind/tests/CVS/Root b/VEX/head20041019/helgrind/tests/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/helgrind/tests/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/helgrind/tests/CVS/Template b/VEX/head20041019/helgrind/tests/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/tests/Makefile.am b/VEX/head20041019/helgrind/tests/Makefile.am new file mode 100644 index 0000000000..16775a00b6 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/Makefile.am @@ -0,0 +1,29 @@ +noinst_SCRIPTS = filter_stderr + +INSN_TESTS=insn_basic insn_fpu insn_cmov insn_mmx insn_mmxext insn_sse insn_sse2 + +EXTRA_DIST = $(noinst_SCRIPTS) \ + allok.stderr.exp allok.vgtest \ + deadlock.stderr.exp deadlock.vgtest \ + inherit.stderr.exp inherit.vgtest \ + $(addsuffix .stderr.exp,$(INSN_TESTS)) \ + $(addsuffix .stdout.exp,$(INSN_TESTS)) \ + $(addsuffix .vgtest,$(INSN_TESTS)) \ + race.stderr.exp race.vgtest \ + race2.stderr.exp race2.vgtest \ + readshared.stderr.exp readshared.vgtest \ + toobig-allocs.stderr.exp toobig-allocs.vgtest + +check_PROGRAMS = \ + allok deadlock inherit race race2 readshared + +allok_SOURCES = allok.c +deadlock_SOURCES = deadlock.c +inherit_SOURCES = inherit.c +race_SOURCES = race.c +race2_SOURCES = race2.c +readshared_SOURCES = readshared.c + +# force -gstabs, because we don't print symaddr for DWARF yet +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -gstabs +LDADD = -lpthread diff --git a/VEX/head20041019/helgrind/tests/allok.c b/VEX/head20041019/helgrind/tests/allok.c new file mode 100644 index 0000000000..144ce608d1 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/allok.c @@ -0,0 +1,32 @@ +/* All OK */ + +#include + +static pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER; + +static int shared; + +static void *th(void *v) +{ + pthread_mutex_lock(&mx); + shared++; + pthread_mutex_unlock(&mx); + + return 0; +} + +int main() +{ + pthread_t a, b; + + pthread_mutex_lock(&mx); + pthread_mutex_unlock(&mx); + + pthread_create(&a, NULL, th, NULL); + pthread_create(&b, NULL, th, NULL); + + pthread_join(a, NULL); + pthread_join(b, NULL); + + return 0; +} diff --git a/VEX/head20041019/helgrind/tests/allok.stderr.exp b/VEX/head20041019/helgrind/tests/allok.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/helgrind/tests/allok.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/helgrind/tests/allok.vgtest b/VEX/head20041019/helgrind/tests/allok.vgtest new file mode 100644 index 0000000000..6063069f7b --- /dev/null +++ b/VEX/head20041019/helgrind/tests/allok.vgtest @@ -0,0 +1,2 @@ +prog: allok +vgopts: --optimise=no diff --git a/VEX/head20041019/helgrind/tests/deadlock.c b/VEX/head20041019/helgrind/tests/deadlock.c new file mode 100644 index 0000000000..1142f5cfe9 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/deadlock.c @@ -0,0 +1,43 @@ +/* Simple possible deadlock */ +#include + +static pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; + +static void *t1(void *v) +{ + pthread_mutex_lock(&m1); + pthread_mutex_lock(&m2); + pthread_mutex_unlock(&m1); + pthread_mutex_unlock(&m2); + + return 0; +} + +static void *t2(void *v) +{ + pthread_mutex_lock(&m2); + pthread_mutex_lock(&m1); + pthread_mutex_unlock(&m1); + pthread_mutex_unlock(&m2); + + return 0; +} + +int main() +{ + pthread_t a, b; + + /* prevent spurious messages from the dynamic linker */ + pthread_mutex_lock(&m1); + pthread_mutex_unlock(&m1); + + pthread_create(&a, NULL, t1, NULL); + pthread_create(&b, NULL, t2, NULL); + + pthread_join(a, NULL); + pthread_join(b, NULL); + + return 0; +} + diff --git a/VEX/head20041019/helgrind/tests/deadlock.stderr.exp b/VEX/head20041019/helgrind/tests/deadlock.stderr.exp new file mode 100644 index 0000000000..050d008789 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/deadlock.stderr.exp @@ -0,0 +1,15 @@ + +Thread 3: +Mutex 0x........(m1) locked in inconsistent order + at 0x........: pthread_mutex_lock (vg_libpthread.c:...) + by 0x........: t2 (deadlock.c:20) + by 0x........: thread_wrapper (vg_libpthread.c:...) + by 0x........: do__quit (vg_scheduler.c:...) + while holding locks 0x........(m2) + 0x........(m2) last locked at + at 0x........: pthread_mutex_lock (vg_libpthread.c:...) + by 0x........: t2 (deadlock.c:19) + by 0x........: thread_wrapper (vg_libpthread.c:...) + by 0x........: do__quit (vg_scheduler.c:...) + while depending on locks 0x........(m1) + diff --git a/VEX/head20041019/helgrind/tests/deadlock.vgtest b/VEX/head20041019/helgrind/tests/deadlock.vgtest new file mode 100644 index 0000000000..38418b486b --- /dev/null +++ b/VEX/head20041019/helgrind/tests/deadlock.vgtest @@ -0,0 +1,2 @@ +prog: deadlock +vgopts: --optimise=no diff --git a/VEX/head20041019/helgrind/tests/filter_stderr b/VEX/head20041019/helgrind/tests/filter_stderr new file mode 100755 index 0000000000..92ce7c1b66 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/filter_stderr @@ -0,0 +1,22 @@ +#! /bin/sh + +# Same as for MemCheck + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic | +$dir/../../tests/filter_addresses | + +# Anonymise paths like "section of /foo/bar/helgrind/tests/baz)" +sed "s/section of \/.*helgrind\/tests.*$/section of \/...helgrind\/tests.../" | + +# Anonymise line numbers in vg_scheduler.c +sed "s/vg_scheduler.c:[0-9]\+/vg_scheduler.c:.../" | + +# Output looks like... +# +# ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +# [0] = { } +# ==27116== 0 possible data races found +sed "/ERROR SUMMARY:/ , /0 possible data races found/ d" + diff --git a/VEX/head20041019/helgrind/tests/inherit.c b/VEX/head20041019/helgrind/tests/inherit.c new file mode 100644 index 0000000000..700ec4f5cb --- /dev/null +++ b/VEX/head20041019/helgrind/tests/inherit.c @@ -0,0 +1,55 @@ +/* test child thread inheriting data */ + +// *** +// +// Helgrind should detect an error on line 48 for this test, but it doesn't! +// +// *** + +#include +#include + +static volatile int shared[2]; + +static void *t1(void *v) +{ + volatile int *ip = (int *)v; + *ip += 44; + *ip *= 2; + sleep(1); + return 0; +} + +static void *t2(void *v) +{ + volatile int *ip = (int *)v; + *ip += 88; + *ip *= 3; + sleep(2); + return 0; +} + +int main() +{ + pthread_t a, b; + volatile int ret = 0; + + sleep(0); + + shared[0] = 22; + shared[1] = 77; + + pthread_create(&a, NULL, t1, (void *)&shared[0]); + pthread_create(&b, NULL, t2, (void *)&shared[1]); + + pthread_join(a, NULL); + + ret += shared[0]; /* no error - a is finished */ + ret += shared[1]; /* expect error - b has not finished, + so we can't touch shared[1] yet */ + + pthread_join(b, NULL); + + + return ret; +} diff --git a/VEX/head20041019/helgrind/tests/inherit.stderr.exp b/VEX/head20041019/helgrind/tests/inherit.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/helgrind/tests/inherit.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/helgrind/tests/inherit.vgtest b/VEX/head20041019/helgrind/tests/inherit.vgtest new file mode 100644 index 0000000000..5db4a74488 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/inherit.vgtest @@ -0,0 +1,2 @@ +prog: inherit +vgopts: --optimise=no diff --git a/VEX/head20041019/helgrind/tests/insn_basic.stderr.exp b/VEX/head20041019/helgrind/tests/insn_basic.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/tests/insn_basic.stdout.exp b/VEX/head20041019/helgrind/tests/insn_basic.stdout.exp new file mode 100644 index 0000000000..40cabbcd02 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_basic.stdout.exp @@ -0,0 +1,1083 @@ +aaa_1 ... ok +aaa_2 ... ok +aaa_3 ... ok +aaa_4 ... ok +aaa_5 ... ok +aaa_6 ... ok +aaa_7 ... ok +aaa_8 ... ok +aad_1 ... ok +aad_2 ... ok +aam_1 ... ok +aam_2 ... ok +aas_1 ... ok +aas_2 ... ok +aas_3 ... ok +aas_4 ... ok +aas_5 ... ok +aas_6 ... ok +aas_7 ... ok +aas_8 ... ok +adcb_1 ... ok +adcb_2 ... ok +adcb_3 ... ok +adcb_4 ... ok +adcb_5 ... ok +adcb_6 ... ok +adcb_7 ... ok +adcb_8 ... ok +adcb_9 ... ok +adcb_10 ... ok +adcb_11 ... ok +adcb_12 ... ok +adcw_1 ... ok +adcw_2 ... ok +adcw_3 ... ok +adcw_4 ... ok +adcw_5 ... ok +adcw_6 ... ok +adcw_7 ... ok +adcw_8 ... ok +adcw_9 ... ok +adcw_10 ... ok +adcw_11 ... ok +adcw_12 ... ok +adcw_13 ... ok +adcw_14 ... ok +adcl_1 ... ok +adcl_2 ... ok +adcl_3 ... ok +adcl_4 ... ok +adcl_5 ... ok +adcl_6 ... ok +adcl_7 ... ok +adcl_8 ... ok +adcl_9 ... ok +adcl_10 ... ok +adcl_11 ... ok +adcl_12 ... ok +adcl_13 ... ok +adcl_14 ... ok +addb_1 ... ok +addb_2 ... ok +addb_3 ... ok +addb_4 ... ok +addb_5 ... ok +addb_6 ... ok +addw_1 ... ok +addw_2 ... ok +addw_3 ... ok +addw_4 ... ok +addw_5 ... ok +addw_6 ... ok +addw_7 ... ok +addl_1 ... ok +addl_2 ... ok +addl_3 ... ok +addl_4 ... ok +addl_5 ... ok +addl_6 ... ok +addl_7 ... ok +andb_1 ... ok +andb_2 ... ok +andb_3 ... ok +andb_4 ... ok +andb_5 ... ok +andb_6 ... ok +andw_1 ... ok +andw_2 ... ok +andw_3 ... ok +andw_4 ... ok +andw_5 ... ok +andw_6 ... ok +andw_7 ... ok +andl_1 ... ok +andl_2 ... ok +andl_3 ... ok +andl_4 ... ok +andl_5 ... ok +andl_6 ... ok +andl_7 ... ok +bsfw_1 ... ok +bsfw_2 ... ok +bsfl_1 ... ok +bsfl_2 ... ok +bsrw_1 ... ok +bsrw_2 ... ok +bsrl_1 ... ok +bsrl_2 ... ok +bswapl_1 ... ok +btw_1 ... ok +btw_2 ... ok +btw_3 ... ok +btw_4 ... ok +btw_5 ... ok +btw_6 ... ok +btw_7 ... ok +btw_8 ... ok +btl_1 ... ok +btl_2 ... ok +btl_3 ... ok +btl_4 ... ok +btl_5 ... ok +btl_6 ... ok +btl_7 ... ok +btl_8 ... ok +btcw_1 ... ok +btcw_2 ... ok +btcw_3 ... ok +btcw_4 ... ok +btcw_5 ... ok +btcw_6 ... ok +btcw_7 ... ok +btcw_8 ... ok +btcl_1 ... ok +btcl_2 ... ok +btcl_3 ... ok +btcl_4 ... ok +btcl_5 ... ok +btcl_6 ... ok +btcl_7 ... ok +btcl_8 ... ok +btrw_1 ... ok +btrw_2 ... ok +btrw_3 ... ok +btrw_4 ... ok +btrw_5 ... ok +btrw_6 ... ok +btrw_7 ... ok +btrw_8 ... ok +btrl_1 ... ok +btrl_2 ... ok +btrl_3 ... ok +btrl_4 ... ok +btrl_5 ... ok +btrl_6 ... ok +btrl_7 ... ok +btrl_8 ... ok +btsw_1 ... ok +btsw_2 ... ok +btsw_3 ... ok +btsw_4 ... ok +btsw_5 ... ok +btsw_6 ... ok +btsw_7 ... ok +btsw_8 ... ok +btsl_1 ... ok +btsl_2 ... ok +btsl_3 ... ok +btsl_4 ... ok +btsl_5 ... ok +btsl_6 ... ok +btsl_7 ... ok +btsl_8 ... ok +cbw_1 ... ok +cbw_2 ... ok +cdq_1 ... ok +cdq_2 ... ok +clc_1 ... ok +clc_2 ... ok +cld_1 ... ok +cld_2 ... ok +cmc_1 ... ok +cmc_2 ... ok +cmpb_1 ... ok +cmpb_2 ... ok +cmpb_3 ... ok +cmpb_4 ... ok +cmpb_5 ... ok +cmpb_6 ... ok +cmpb_7 ... ok +cmpb_8 ... ok +cmpb_9 ... ok +cmpb_10 ... ok +cmpb_11 ... ok +cmpb_12 ... ok +cmpb_13 ... ok +cmpb_14 ... ok +cmpb_15 ... ok +cmpb_16 ... ok +cmpb_17 ... ok +cmpb_18 ... ok +cmpb_19 ... ok +cmpb_20 ... ok +cmpb_21 ... ok +cmpb_22 ... ok +cmpb_23 ... ok +cmpb_24 ... ok +cmpb_25 ... ok +cmpb_26 ... ok +cmpb_27 ... ok +cmpb_28 ... ok +cmpb_29 ... ok +cmpb_30 ... ok +cmpb_31 ... ok +cmpb_32 ... ok +cmpb_33 ... ok +cmpb_34 ... ok +cmpb_35 ... ok +cmpb_36 ... ok +cmpb_37 ... ok +cmpb_38 ... ok +cmpb_39 ... ok +cmpb_40 ... ok +cmpb_41 ... ok +cmpb_42 ... ok +cmpb_43 ... ok +cmpb_44 ... ok +cmpb_45 ... ok +cmpb_46 ... ok +cmpb_47 ... ok +cmpb_48 ... ok +cmpb_49 ... ok +cmpb_50 ... ok +cmpb_51 ... ok +cmpb_52 ... ok +cmpb_53 ... ok +cmpb_54 ... ok +cmpb_55 ... ok +cmpb_56 ... ok +cmpb_57 ... ok +cmpb_58 ... ok +cmpb_59 ... ok +cmpb_60 ... ok +cmpw_1 ... ok +cmpw_2 ... ok +cmpw_3 ... ok +cmpw_4 ... ok +cmpw_5 ... ok +cmpw_6 ... ok +cmpw_7 ... ok +cmpw_8 ... ok +cmpw_9 ... ok +cmpw_10 ... ok +cmpw_11 ... ok +cmpw_12 ... ok +cmpw_13 ... ok +cmpw_14 ... ok +cmpw_15 ... ok +cmpw_16 ... ok +cmpw_17 ... ok +cmpw_18 ... ok +cmpw_19 ... ok +cmpw_20 ... ok +cmpw_21 ... ok +cmpw_22 ... ok +cmpw_23 ... ok +cmpw_24 ... ok +cmpw_25 ... ok +cmpw_26 ... ok +cmpw_27 ... ok +cmpw_28 ... ok +cmpw_29 ... ok +cmpw_30 ... ok +cmpw_31 ... ok +cmpw_32 ... ok +cmpw_33 ... ok +cmpw_34 ... ok +cmpw_35 ... ok +cmpw_36 ... ok +cmpw_37 ... ok +cmpw_38 ... ok +cmpw_39 ... ok +cmpw_40 ... ok +cmpw_41 ... ok +cmpw_42 ... ok +cmpw_43 ... ok +cmpw_44 ... ok +cmpw_45 ... ok +cmpw_46 ... ok +cmpw_47 ... ok +cmpw_48 ... ok +cmpw_49 ... ok +cmpw_50 ... ok +cmpw_51 ... ok +cmpw_52 ... ok +cmpw_53 ... ok +cmpw_54 ... ok +cmpw_55 ... ok +cmpw_56 ... ok +cmpw_57 ... ok +cmpw_58 ... ok +cmpw_59 ... ok +cmpw_60 ... ok +cmpw_61 ... ok +cmpw_62 ... ok +cmpw_63 ... ok +cmpw_64 ... ok +cmpw_65 ... ok +cmpw_66 ... ok +cmpw_67 ... ok +cmpw_68 ... ok +cmpw_69 ... ok +cmpw_70 ... ok +cmpw_71 ... ok +cmpw_72 ... ok +cmpw_73 ... ok +cmpw_74 ... ok +cmpw_75 ... ok +cmpw_76 ... ok +cmpw_77 ... ok +cmpw_78 ... ok +cmpw_79 ... ok +cmpw_80 ... ok +cmpl_1 ... ok +cmpl_2 ... ok +cmpl_3 ... ok +cmpl_4 ... ok +cmpl_5 ... ok +cmpl_6 ... ok +cmpl_7 ... ok +cmpl_8 ... ok +cmpl_9 ... ok +cmpl_10 ... ok +cmpl_11 ... ok +cmpl_12 ... ok +cmpl_13 ... ok +cmpl_14 ... ok +cmpl_15 ... ok +cmpl_16 ... ok +cmpl_17 ... ok +cmpl_18 ... ok +cmpl_19 ... ok +cmpl_20 ... ok +cmpl_21 ... ok +cmpl_22 ... ok +cmpl_23 ... ok +cmpl_24 ... ok +cmpl_25 ... ok +cmpl_26 ... ok +cmpl_27 ... ok +cmpl_28 ... ok +cmpl_29 ... ok +cmpl_30 ... ok +cmpl_31 ... ok +cmpl_32 ... ok +cmpl_33 ... ok +cmpl_34 ... ok +cmpl_35 ... ok +cmpl_36 ... ok +cmpl_37 ... ok +cmpl_38 ... ok +cmpl_39 ... ok +cmpl_40 ... ok +cmpl_41 ... ok +cmpl_42 ... ok +cmpl_43 ... ok +cmpl_44 ... ok +cmpl_45 ... ok +cmpl_46 ... ok +cmpl_47 ... ok +cmpl_48 ... ok +cmpl_49 ... ok +cmpl_50 ... ok +cmpl_51 ... ok +cmpl_52 ... ok +cmpl_53 ... ok +cmpl_54 ... ok +cmpl_55 ... ok +cmpl_56 ... ok +cmpl_57 ... ok +cmpl_58 ... ok +cmpl_59 ... ok +cmpl_60 ... ok +cmpl_61 ... ok +cmpl_62 ... ok +cmpl_63 ... ok +cmpl_64 ... ok +cmpl_65 ... ok +cmpl_66 ... ok +cmpl_67 ... ok +cmpl_68 ... ok +cmpl_69 ... ok +cmpl_70 ... ok +cmpl_71 ... ok +cmpl_72 ... ok +cmpl_73 ... ok +cmpl_74 ... ok +cmpl_75 ... ok +cmpl_76 ... ok +cmpl_77 ... ok +cmpl_78 ... ok +cmpl_79 ... ok +cmpl_80 ... ok +cmpxchgb_1 ... ok +cmpxchgb_2 ... ok +cmpxchgb_3 ... ok +cmpxchgb_4 ... ok +cmpxchgw_1 ... ok +cmpxchgw_2 ... ok +cmpxchgw_3 ... ok +cmpxchgw_4 ... ok +cmpxchgl_1 ... ok +cmpxchgl_2 ... ok +cmpxchgl_3 ... ok +cmpxchgl_4 ... ok +cwd_1 ... ok +cwd_2 ... ok +cwde_1 ... ok +cwde_2 ... ok +daa_1 ... ok +daa_2 ... ok +das_1 ... ok +decb_1 ... ok +decb_2 ... ok +decw_1 ... ok +decw_2 ... ok +decl_1 ... ok +decl_2 ... ok +divb_1 ... ok +divb_2 ... ok +divw_1 ... ok +divw_2 ... ok +divl_1 ... ok +divl_2 ... ok +idivb_1 ... ok +idivb_2 ... ok +idivw_1 ... ok +idivw_2 ... ok +idivl_1 ... ok +idivl_2 ... ok +imulb_1 ... ok +imulb_2 ... ok +imulw_1 ... ok +imulw_2 ... ok +imull_1 ... ok +imull_2 ... ok +imulw_3 ... ok +imulw_4 ... ok +imulw_5 ... ok +imulw_6 ... ok +imulw_7 ... ok +imulw_8 ... ok +imulw_9 ... ok +imulw_10 ... ok +imull_3 ... ok +imull_4 ... ok +imull_5 ... ok +imull_6 ... ok +imull_7 ... ok +imull_8 ... ok +imull_9 ... ok +imull_10 ... ok +incb_1 ... ok +incb_2 ... ok +incw_1 ... ok +incw_2 ... ok +incl_1 ... ok +incl_2 ... ok +lahf_1 ... ok +lahf_2 ... ok +movb_1 ... ok +movb_2 ... ok +movb_3 ... ok +movb_4 ... ok +movb_5 ... ok +movw_1 ... ok +movw_2 ... ok +movw_3 ... ok +movw_4 ... ok +movw_5 ... ok +movl_1 ... ok +movl_2 ... ok +movl_3 ... ok +movl_4 ... ok +movl_5 ... ok +movsbw_1 ... ok +movsbw_2 ... ok +movsbl_1 ... ok +movsbl_2 ... ok +movswl_1 ... ok +movswl_2 ... ok +movzbw_1 ... ok +movzbw_2 ... ok +movzbl_1 ... ok +movzbl_2 ... ok +movzwl_1 ... ok +movzwl_2 ... ok +mulb_1 ... ok +mulb_2 ... ok +mulw_1 ... ok +mulw_2 ... ok +mull_1 ... ok +mull_2 ... ok +negb_1 ... ok +negb_2 ... ok +negw_1 ... ok +negw_2 ... ok +negl_1 ... ok +negl_2 ... ok +notb_1 ... ok +notb_2 ... ok +notw_1 ... ok +notw_2 ... ok +notl_1 ... ok +notl_2 ... ok +orb_1 ... ok +orb_2 ... ok +orb_3 ... ok +orb_4 ... ok +orb_5 ... ok +orb_6 ... ok +orw_1 ... ok +orw_2 ... ok +orw_3 ... ok +orw_4 ... ok +orw_5 ... ok +orw_6 ... ok +orw_7 ... ok +orl_1 ... ok +orl_2 ... ok +orl_3 ... ok +orl_4 ... ok +orl_5 ... ok +orl_6 ... ok +orl_7 ... ok +rclb_1 ... ok +rclb_2 ... ok +rclb_3 ... ok +rclb_4 ... ok +rclb_5 ... ok +rclb_6 ... ok +rclw_1 ... ok +rclw_2 ... ok +rclw_3 ... ok +rclw_4 ... ok +rclw_5 ... ok +rclw_6 ... ok +rcll_1 ... ok +rcll_2 ... ok +rcll_3 ... ok +rcll_4 ... ok +rcll_5 ... ok +rcll_6 ... ok +rcrb_1 ... ok +rcrb_2 ... ok +rcrb_3 ... ok +rcrb_4 ... ok +rcrb_5 ... ok +rcrb_6 ... ok +rcrw_1 ... ok +rcrw_2 ... ok +rcrw_3 ... ok +rcrw_4 ... ok +rcrw_5 ... ok +rcrw_6 ... ok +rcrl_1 ... ok +rcrl_2 ... ok +rcrl_3 ... ok +rcrl_4 ... ok +rcrl_5 ... ok +rcrl_6 ... ok +rolb_1 ... ok +rolb_2 ... ok +rolb_3 ... ok +rolb_4 ... ok +rolb_5 ... ok +rolb_6 ... ok +rolw_1 ... ok +rolw_2 ... ok +rolw_3 ... ok +rolw_4 ... ok +rolw_5 ... ok +rolw_6 ... ok +roll_1 ... ok +roll_2 ... ok +roll_3 ... ok +roll_4 ... ok +roll_5 ... ok +roll_6 ... ok +rorb_1 ... ok +rorb_2 ... ok +rorb_3 ... ok +rorb_4 ... ok +rorb_5 ... ok +rorb_6 ... ok +rorw_1 ... ok +rorw_2 ... ok +rorw_3 ... ok +rorw_4 ... ok +rorw_5 ... ok +rorw_6 ... ok +rorl_1 ... ok +rorl_2 ... ok +rorl_3 ... ok +rorl_4 ... ok +rorl_5 ... ok +rorl_6 ... ok +sahf_1 ... ok +sahf_2 ... ok +salb_1 ... ok +salb_2 ... ok +salb_3 ... ok +salb_4 ... ok +salb_5 ... ok +salb_6 ... ok +salw_1 ... ok +salw_2 ... ok +salw_3 ... ok +salw_4 ... ok +salw_5 ... ok +salw_6 ... ok +sall_1 ... ok +sall_2 ... ok +sall_3 ... ok +sall_4 ... ok +sall_5 ... ok +sall_6 ... ok +sarb_1 ... ok +sarb_2 ... ok +sarb_3 ... ok +sarb_4 ... ok +sarb_5 ... ok +sarb_6 ... ok +sarw_1 ... ok +sarw_2 ... ok +sarw_3 ... ok +sarw_4 ... ok +sarw_5 ... ok +sarw_6 ... ok +sarl_1 ... ok +sarl_2 ... ok +sarl_3 ... ok +sarl_4 ... ok +sarl_5 ... ok +sarl_6 ... ok +sbbb_1 ... ok +sbbb_2 ... ok +sbbb_3 ... ok +sbbb_4 ... ok +sbbb_5 ... ok +sbbb_6 ... ok +sbbb_7 ... ok +sbbb_8 ... ok +sbbb_9 ... ok +sbbb_10 ... ok +sbbb_11 ... ok +sbbb_12 ... ok +sbbw_1 ... ok +sbbw_2 ... ok +sbbw_3 ... ok +sbbw_4 ... ok +sbbw_5 ... ok +sbbw_6 ... ok +sbbw_7 ... ok +sbbw_8 ... ok +sbbw_9 ... ok +sbbw_10 ... ok +sbbw_11 ... ok +sbbw_12 ... ok +sbbw_13 ... ok +sbbw_14 ... ok +sbbl_1 ... ok +sbbl_2 ... ok +sbbl_3 ... ok +sbbl_4 ... ok +sbbl_5 ... ok +sbbl_6 ... ok +sbbl_7 ... ok +sbbl_8 ... ok +sbbl_9 ... ok +sbbl_10 ... ok +sbbl_11 ... ok +sbbl_12 ... ok +sbbl_13 ... ok +sbbl_14 ... ok +seta_1 ... ok +seta_2 ... ok +seta_3 ... ok +seta_4 ... ok +seta_5 ... ok +seta_6 ... ok +seta_7 ... ok +seta_8 ... ok +setae_1 ... ok +setae_2 ... ok +setae_3 ... ok +setae_4 ... ok +setb_1 ... ok +setb_2 ... ok +setb_3 ... ok +setb_4 ... ok +setbe_1 ... ok +setbe_2 ... ok +setbe_3 ... ok +setbe_4 ... ok +setbe_5 ... ok +setbe_6 ... ok +setbe_7 ... ok +setbe_8 ... ok +setc_1 ... ok +setc_2 ... ok +setc_3 ... ok +setc_4 ... ok +sete_1 ... ok +sete_2 ... ok +sete_3 ... ok +sete_4 ... ok +setg_1 ... ok +setg_2 ... ok +setg_3 ... ok +setg_4 ... ok +setg_5 ... ok +setg_6 ... ok +setg_7 ... ok +setg_8 ... ok +setg_9 ... ok +setg_10 ... ok +setg_11 ... ok +setg_12 ... ok +setg_13 ... ok +setg_14 ... ok +setg_15 ... ok +setg_16 ... ok +setge_1 ... ok +setge_2 ... ok +setge_3 ... ok +setge_4 ... ok +setge_5 ... ok +setge_6 ... ok +setge_7 ... ok +setge_8 ... ok +setl_1 ... ok +setl_2 ... ok +setl_3 ... ok +setl_4 ... ok +setl_5 ... ok +setl_6 ... ok +setl_7 ... ok +setl_8 ... ok +setle_1 ... ok +setle_2 ... ok +setle_3 ... ok +setle_4 ... ok +setle_5 ... ok +setle_6 ... ok +setle_7 ... ok +setle_8 ... ok +setle_9 ... ok +setle_10 ... ok +setle_11 ... ok +setle_12 ... ok +setle_13 ... ok +setle_14 ... ok +setle_15 ... ok +setle_16 ... ok +setna_1 ... ok +setna_2 ... ok +setna_3 ... ok +setna_4 ... ok +setna_5 ... ok +setna_6 ... ok +setna_7 ... ok +setna_8 ... ok +setnae_1 ... ok +setnae_2 ... ok +setnae_3 ... ok +setnae_4 ... ok +setnb_1 ... ok +setnb_2 ... ok +setnb_3 ... ok +setnb_4 ... ok +setnbe_1 ... ok +setnbe_2 ... ok +setnbe_3 ... ok +setnbe_4 ... ok +setnbe_5 ... ok +setnbe_6 ... ok +setnbe_7 ... ok +setnbe_8 ... ok +setnc_1 ... ok +setnc_2 ... ok +setnc_3 ... ok +setnc_4 ... ok +setne_1 ... ok +setne_2 ... ok +setne_3 ... ok +setne_4 ... ok +setng_1 ... ok +setng_2 ... ok +setng_3 ... ok +setng_4 ... ok +setng_5 ... ok +setng_6 ... ok +setng_7 ... ok +setng_8 ... ok +setng_9 ... ok +setng_10 ... ok +setng_11 ... ok +setng_12 ... ok +setng_13 ... ok +setng_14 ... ok +setng_15 ... ok +setng_16 ... ok +setnge_1 ... ok +setnge_2 ... ok +setnge_3 ... ok +setnge_4 ... ok +setnge_5 ... ok +setnge_6 ... ok +setnge_7 ... ok +setnge_8 ... ok +setnl_1 ... ok +setnl_2 ... ok +setnl_3 ... ok +setnl_4 ... ok +setnl_5 ... ok +setnl_6 ... ok +setnl_7 ... ok +setnl_8 ... ok +setnle_1 ... ok +setnle_2 ... ok +setnle_3 ... ok +setnle_4 ... ok +setnle_5 ... ok +setnle_6 ... ok +setnle_7 ... ok +setnle_8 ... ok +setnle_9 ... ok +setnle_10 ... ok +setnle_11 ... ok +setnle_12 ... ok +setnle_13 ... ok +setnle_14 ... ok +setnle_15 ... ok +setnle_16 ... ok +setno_1 ... ok +setno_2 ... ok +setno_3 ... ok +setno_4 ... ok +setnp_1 ... ok +setnp_2 ... ok +setnp_3 ... ok +setnp_4 ... ok +setns_1 ... ok +setns_2 ... ok +setns_3 ... ok +setns_4 ... ok +setnz_1 ... ok +setnz_2 ... ok +setnz_3 ... ok +setnz_4 ... ok +seto_1 ... ok +seto_2 ... ok +seto_3 ... ok +seto_4 ... ok +setp_1 ... ok +setp_2 ... ok +setp_3 ... ok +setp_4 ... ok +sets_1 ... ok +sets_2 ... ok +sets_3 ... ok +sets_4 ... ok +setz_1 ... ok +setz_2 ... ok +setz_3 ... ok +setz_4 ... ok +shlb_1 ... ok +shlb_2 ... ok +shlb_3 ... ok +shlb_4 ... ok +shlb_5 ... ok +shlb_6 ... ok +shlw_1 ... ok +shlw_2 ... ok +shlw_3 ... ok +shlw_4 ... ok +shlw_5 ... ok +shlw_6 ... ok +shll_1 ... ok +shll_2 ... ok +shll_3 ... ok +shll_4 ... ok +shll_5 ... ok +shll_6 ... ok +shrb_1 ... ok +shrb_2 ... ok +shrb_3 ... ok +shrb_4 ... ok +shrb_5 ... ok +shrb_6 ... ok +shrw_1 ... ok +shrw_2 ... ok +shrw_3 ... ok +shrw_4 ... ok +shrw_5 ... ok +shrw_6 ... ok +shrl_1 ... ok +shrl_2 ... ok +shrl_3 ... ok +shrl_4 ... ok +shrl_5 ... ok +shrl_6 ... ok +shldw_1 ... ok +shldw_2 ... ok +shldw_3 ... ok +shldw_4 ... ok +shldw_5 ... ok +shldw_6 ... ok +shldw_7 ... ok +shldw_8 ... ok +shldl_1 ... ok +shldl_2 ... ok +shldl_3 ... ok +shldl_4 ... ok +shldl_5 ... ok +shldl_6 ... ok +shldl_7 ... ok +shldl_8 ... ok +shrdw_1 ... ok +shrdw_2 ... ok +shrdw_3 ... ok +shrdw_4 ... ok +shrdw_5 ... ok +shrdw_6 ... ok +shrdw_7 ... ok +shrdw_8 ... ok +shrdl_1 ... ok +shrdl_2 ... ok +shrdl_3 ... ok +shrdl_4 ... ok +shrdl_5 ... ok +shrdl_6 ... ok +shrdl_7 ... ok +shrdl_8 ... ok +stc_1 ... ok +stc_2 ... ok +std_1 ... ok +std_2 ... ok +subb_1 ... ok +subb_2 ... ok +subb_3 ... ok +subb_4 ... ok +subb_5 ... ok +subb_6 ... ok +subw_1 ... ok +subw_2 ... ok +subw_3 ... ok +subw_4 ... ok +subw_5 ... ok +subw_6 ... ok +subw_7 ... ok +subl_1 ... ok +subl_2 ... ok +subl_3 ... ok +subl_4 ... ok +subl_5 ... ok +subl_6 ... ok +subl_7 ... ok +testb_1 ... ok +testb_2 ... ok +testb_3 ... ok +testb_4 ... ok +testb_5 ... ok +testb_6 ... ok +testb_7 ... ok +testb_8 ... ok +testb_9 ... ok +testb_10 ... ok +testb_11 ... ok +testb_12 ... ok +testb_13 ... ok +testb_14 ... ok +testb_15 ... ok +testb_16 ... ok +testb_17 ... ok +testb_18 ... ok +testb_19 ... ok +testb_20 ... ok +testb_21 ... ok +testb_22 ... ok +testb_23 ... ok +testb_24 ... ok +testb_25 ... ok +testw_1 ... ok +testw_2 ... ok +testw_3 ... ok +testw_4 ... ok +testw_5 ... ok +testw_6 ... ok +testw_7 ... ok +testw_8 ... ok +testw_9 ... ok +testw_10 ... ok +testw_11 ... ok +testw_12 ... ok +testw_13 ... ok +testw_14 ... ok +testw_15 ... ok +testw_16 ... ok +testw_17 ... ok +testw_18 ... ok +testw_19 ... ok +testw_20 ... ok +testw_21 ... ok +testw_22 ... ok +testw_23 ... ok +testw_24 ... ok +testw_25 ... ok +testl_1 ... ok +testl_2 ... ok +testl_3 ... ok +testl_4 ... ok +testl_5 ... ok +testl_6 ... ok +testl_7 ... ok +testl_8 ... ok +testl_9 ... ok +testl_10 ... ok +testl_11 ... ok +testl_12 ... ok +testl_13 ... ok +testl_14 ... ok +testl_15 ... ok +testl_16 ... ok +testl_17 ... ok +testl_18 ... ok +testl_19 ... ok +testl_20 ... ok +testl_21 ... ok +testl_22 ... ok +testl_23 ... ok +testl_24 ... ok +testl_25 ... ok +xaddb_1 ... ok +xaddb_2 ... ok +xaddw_1 ... ok +xaddw_2 ... ok +xaddl_1 ... ok +xaddl_2 ... ok +xchgb_1 ... ok +xchgb_2 ... ok +xchgb_3 ... ok +xchgw_1 ... ok +xchgw_2 ... ok +xchgw_3 ... ok +xchgw_4 ... ok +xchgw_5 ... ok +xchgl_1 ... ok +xchgl_2 ... ok +xchgl_3 ... ok +xchgl_4 ... ok +xchgl_5 ... ok +xorb_1 ... ok +xorb_2 ... ok +xorb_3 ... ok +xorb_4 ... ok +xorb_5 ... ok +xorb_6 ... ok +xorw_1 ... ok +xorw_2 ... ok +xorw_3 ... ok +xorw_4 ... ok +xorw_5 ... ok +xorw_6 ... ok +xorw_7 ... ok +xorl_1 ... ok +xorl_2 ... ok +xorl_3 ... ok +xorl_4 ... ok +xorl_5 ... ok +xorl_6 ... ok +xorl_7 ... ok diff --git a/VEX/head20041019/helgrind/tests/insn_basic.vgtest b/VEX/head20041019/helgrind/tests/insn_basic.vgtest new file mode 100644 index 0000000000..f5329ea810 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_basic.vgtest @@ -0,0 +1,2 @@ +vgopts: -q +prog: ../../none/tests/insn_basic diff --git a/VEX/head20041019/helgrind/tests/insn_cmov.stderr.exp b/VEX/head20041019/helgrind/tests/insn_cmov.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/tests/insn_cmov.stdout.exp b/VEX/head20041019/helgrind/tests/insn_cmov.stdout.exp new file mode 100644 index 0000000000..31ac17204d --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_cmov.stdout.exp @@ -0,0 +1,384 @@ +cmova_1 ... ok +cmova_2 ... ok +cmova_3 ... ok +cmova_4 ... ok +cmova_5 ... ok +cmova_6 ... ok +cmova_7 ... ok +cmova_8 ... ok +cmovae_1 ... ok +cmovae_2 ... ok +cmovae_3 ... ok +cmovae_4 ... ok +cmovb_1 ... ok +cmovb_2 ... ok +cmovb_3 ... ok +cmovb_4 ... ok +cmovbe_1 ... ok +cmovbe_2 ... ok +cmovbe_3 ... ok +cmovbe_4 ... ok +cmovbe_5 ... ok +cmovbe_6 ... ok +cmovbe_7 ... ok +cmovbe_8 ... ok +cmovc_1 ... ok +cmovc_2 ... ok +cmovc_3 ... ok +cmovc_4 ... ok +cmove_1 ... ok +cmove_2 ... ok +cmove_3 ... ok +cmove_4 ... ok +cmovg_1 ... ok +cmovg_2 ... ok +cmovg_3 ... ok +cmovg_4 ... ok +cmovg_5 ... ok +cmovg_6 ... ok +cmovg_7 ... ok +cmovg_8 ... ok +cmovg_9 ... ok +cmovg_10 ... ok +cmovg_11 ... ok +cmovg_12 ... ok +cmovg_13 ... ok +cmovg_14 ... ok +cmovg_15 ... ok +cmovg_16 ... ok +cmovge_1 ... ok +cmovge_2 ... ok +cmovge_3 ... ok +cmovge_4 ... ok +cmovge_5 ... ok +cmovge_6 ... ok +cmovge_7 ... ok +cmovge_8 ... ok +cmovl_1 ... ok +cmovl_2 ... ok +cmovl_3 ... ok +cmovl_4 ... ok +cmovl_5 ... ok +cmovl_6 ... ok +cmovl_7 ... ok +cmovl_8 ... ok +cmovle_1 ... ok +cmovle_2 ... ok +cmovle_3 ... ok +cmovle_4 ... ok +cmovle_5 ... ok +cmovle_6 ... ok +cmovle_7 ... ok +cmovle_8 ... ok +cmovle_9 ... ok +cmovle_10 ... ok +cmovle_11 ... ok +cmovle_12 ... ok +cmovle_13 ... ok +cmovle_14 ... ok +cmovle_15 ... ok +cmovle_16 ... ok +cmovna_1 ... ok +cmovna_2 ... ok +cmovna_3 ... ok +cmovna_4 ... ok +cmovna_5 ... ok +cmovna_6 ... ok +cmovna_7 ... ok +cmovna_8 ... ok +cmovnae_1 ... ok +cmovnae_2 ... ok +cmovnae_3 ... ok +cmovnae_4 ... ok +cmovnb_1 ... ok +cmovnb_2 ... ok +cmovnb_3 ... ok +cmovnb_4 ... ok +cmovnbe_1 ... ok +cmovnbe_2 ... ok +cmovnbe_3 ... ok +cmovnbe_4 ... ok +cmovnbe_5 ... ok +cmovnbe_6 ... ok +cmovnbe_7 ... ok +cmovnbe_8 ... ok +cmovnc_1 ... ok +cmovnc_2 ... ok +cmovnc_3 ... ok +cmovnc_4 ... ok +cmovne_1 ... ok +cmovne_2 ... ok +cmovne_3 ... ok +cmovne_4 ... ok +cmovng_1 ... ok +cmovng_2 ... ok +cmovng_3 ... ok +cmovng_4 ... ok +cmovng_5 ... ok +cmovng_6 ... ok +cmovng_7 ... ok +cmovng_8 ... ok +cmovng_9 ... ok +cmovng_10 ... ok +cmovng_11 ... ok +cmovng_12 ... ok +cmovng_13 ... ok +cmovng_14 ... ok +cmovng_15 ... ok +cmovng_16 ... ok +cmovnge_1 ... ok +cmovnge_2 ... ok +cmovnge_3 ... ok +cmovnge_4 ... ok +cmovnge_5 ... ok +cmovnge_6 ... ok +cmovnge_7 ... ok +cmovnge_8 ... ok +cmovnl_1 ... ok +cmovnl_2 ... ok +cmovnl_3 ... ok +cmovnl_4 ... ok +cmovnl_5 ... ok +cmovnl_6 ... ok +cmovnl_7 ... ok +cmovnl_8 ... ok +cmovnle_1 ... ok +cmovnle_2 ... ok +cmovnle_3 ... ok +cmovnle_4 ... ok +cmovnle_5 ... ok +cmovnle_6 ... ok +cmovnle_7 ... ok +cmovnle_8 ... ok +cmovnle_9 ... ok +cmovnle_10 ... ok +cmovnle_11 ... ok +cmovnle_12 ... ok +cmovnle_13 ... ok +cmovnle_14 ... ok +cmovnle_15 ... ok +cmovnle_16 ... ok +cmovno_1 ... ok +cmovno_2 ... ok +cmovno_3 ... ok +cmovno_4 ... ok +cmovnp_1 ... ok +cmovnp_2 ... ok +cmovnp_3 ... ok +cmovnp_4 ... ok +cmovns_1 ... ok +cmovns_2 ... ok +cmovns_3 ... ok +cmovns_4 ... ok +cmovnz_1 ... ok +cmovnz_2 ... ok +cmovnz_3 ... ok +cmovnz_4 ... ok +cmovo_1 ... ok +cmovo_2 ... ok +cmovo_3 ... ok +cmovo_4 ... ok +cmovp_1 ... ok +cmovp_2 ... ok +cmovp_3 ... ok +cmovp_4 ... ok +cmovs_1 ... ok +cmovs_2 ... ok +cmovs_3 ... ok +cmovs_4 ... ok +cmovz_1 ... ok +cmovz_2 ... ok +cmovz_3 ... ok +cmovz_4 ... ok +cmova_9 ... ok +cmova_10 ... ok +cmova_11 ... ok +cmova_12 ... ok +cmova_13 ... ok +cmova_14 ... ok +cmova_15 ... ok +cmova_16 ... ok +cmovae_5 ... ok +cmovae_6 ... ok +cmovae_7 ... ok +cmovae_8 ... ok +cmovb_5 ... ok +cmovb_6 ... ok +cmovb_7 ... ok +cmovb_8 ... ok +cmovbe_9 ... ok +cmovbe_10 ... ok +cmovbe_11 ... ok +cmovbe_12 ... ok +cmovbe_13 ... ok +cmovbe_14 ... ok +cmovbe_15 ... ok +cmovbe_16 ... ok +cmovc_5 ... ok +cmovc_6 ... ok +cmovc_7 ... ok +cmovc_8 ... ok +cmove_5 ... ok +cmove_6 ... ok +cmove_7 ... ok +cmove_8 ... ok +cmovg_17 ... ok +cmovg_18 ... ok +cmovg_19 ... ok +cmovg_20 ... ok +cmovg_21 ... ok +cmovg_22 ... ok +cmovg_23 ... ok +cmovg_24 ... ok +cmovg_25 ... ok +cmovg_26 ... ok +cmovg_27 ... ok +cmovg_28 ... ok +cmovg_29 ... ok +cmovg_30 ... ok +cmovg_31 ... ok +cmovg_32 ... ok +cmovge_9 ... ok +cmovge_10 ... ok +cmovge_11 ... ok +cmovge_12 ... ok +cmovge_13 ... ok +cmovge_14 ... ok +cmovge_15 ... ok +cmovge_16 ... ok +cmovl_9 ... ok +cmovl_10 ... ok +cmovl_11 ... ok +cmovl_12 ... ok +cmovl_13 ... ok +cmovl_14 ... ok +cmovl_15 ... ok +cmovl_16 ... ok +cmovle_17 ... ok +cmovle_18 ... ok +cmovle_19 ... ok +cmovle_20 ... ok +cmovle_21 ... ok +cmovle_22 ... ok +cmovle_23 ... ok +cmovle_24 ... ok +cmovle_25 ... ok +cmovle_26 ... ok +cmovle_27 ... ok +cmovle_28 ... ok +cmovle_29 ... ok +cmovle_30 ... ok +cmovle_31 ... ok +cmovle_32 ... ok +cmovna_9 ... ok +cmovna_10 ... ok +cmovna_11 ... ok +cmovna_12 ... ok +cmovna_13 ... ok +cmovna_14 ... ok +cmovna_15 ... ok +cmovna_16 ... ok +cmovnae_5 ... ok +cmovnae_6 ... ok +cmovnae_7 ... ok +cmovnae_8 ... ok +cmovnb_5 ... ok +cmovnb_6 ... ok +cmovnb_7 ... ok +cmovnb_8 ... ok +cmovnbe_9 ... ok +cmovnbe_10 ... ok +cmovnbe_11 ... ok +cmovnbe_12 ... ok +cmovnbe_13 ... ok +cmovnbe_14 ... ok +cmovnbe_15 ... ok +cmovnbe_16 ... ok +cmovnc_5 ... ok +cmovnc_6 ... ok +cmovnc_7 ... ok +cmovnc_8 ... ok +cmovne_5 ... ok +cmovne_6 ... ok +cmovne_7 ... ok +cmovne_8 ... ok +cmovng_17 ... ok +cmovng_18 ... ok +cmovng_19 ... ok +cmovng_20 ... ok +cmovng_21 ... ok +cmovng_22 ... ok +cmovng_23 ... ok +cmovng_24 ... ok +cmovng_25 ... ok +cmovng_26 ... ok +cmovng_27 ... ok +cmovng_28 ... ok +cmovng_29 ... ok +cmovng_30 ... ok +cmovng_31 ... ok +cmovng_32 ... ok +cmovnge_9 ... ok +cmovnge_10 ... ok +cmovnge_11 ... ok +cmovnge_12 ... ok +cmovnge_13 ... ok +cmovnge_14 ... ok +cmovnge_15 ... ok +cmovnge_16 ... ok +cmovnl_9 ... ok +cmovnl_10 ... ok +cmovnl_11 ... ok +cmovnl_12 ... ok +cmovnl_13 ... ok +cmovnl_14 ... ok +cmovnl_15 ... ok +cmovnl_16 ... ok +cmovnle_17 ... ok +cmovnle_18 ... ok +cmovnle_19 ... ok +cmovnle_20 ... ok +cmovnle_21 ... ok +cmovnle_22 ... ok +cmovnle_23 ... ok +cmovnle_24 ... ok +cmovnle_25 ... ok +cmovnle_26 ... ok +cmovnle_27 ... ok +cmovnle_28 ... ok +cmovnle_29 ... ok +cmovnle_30 ... ok +cmovnle_31 ... ok +cmovnle_32 ... ok +cmovno_5 ... ok +cmovno_6 ... ok +cmovno_7 ... ok +cmovno_8 ... ok +cmovnp_5 ... ok +cmovnp_6 ... ok +cmovnp_7 ... ok +cmovnp_8 ... ok +cmovns_5 ... ok +cmovns_6 ... ok +cmovns_7 ... ok +cmovns_8 ... ok +cmovnz_5 ... ok +cmovnz_6 ... ok +cmovnz_7 ... ok +cmovnz_8 ... ok +cmovo_5 ... ok +cmovo_6 ... ok +cmovo_7 ... ok +cmovo_8 ... ok +cmovp_5 ... ok +cmovp_6 ... ok +cmovp_7 ... ok +cmovp_8 ... ok +cmovs_5 ... ok +cmovs_6 ... ok +cmovs_7 ... ok +cmovs_8 ... ok +cmovz_5 ... ok +cmovz_6 ... ok +cmovz_7 ... ok +cmovz_8 ... ok diff --git a/VEX/head20041019/helgrind/tests/insn_cmov.vgtest b/VEX/head20041019/helgrind/tests/insn_cmov.vgtest new file mode 100644 index 0000000000..0321a3ca84 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_cmov.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_cmov +cpu_test: cmov diff --git a/VEX/head20041019/helgrind/tests/insn_fpu.stderr.exp b/VEX/head20041019/helgrind/tests/insn_fpu.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/tests/insn_fpu.stdout.exp b/VEX/head20041019/helgrind/tests/insn_fpu.stdout.exp new file mode 100644 index 0000000000..2dbaa07ce0 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_fpu.stdout.exp @@ -0,0 +1,452 @@ +fabs_1 ... ok +fabs_2 ... ok +fabs_3 ... ok +fabs_4 ... ok +fadds_1 ... ok +fadds_2 ... ok +fadds_3 ... ok +fadds_4 ... ok +faddl_1 ... ok +faddl_2 ... ok +faddl_3 ... ok +faddl_4 ... ok +fadd_1 ... ok +fadd_2 ... ok +fadd_3 ... ok +fadd_4 ... ok +fadd_5 ... ok +fadd_6 ... ok +fadd_7 ... ok +fadd_8 ... ok +fadd_9 ... ok +fadd_10 ... ok +fadd_11 ... ok +fadd_12 ... ok +fadd_13 ... ok +fadd_14 ... ok +fadd_15 ... ok +fadd_16 ... ok +faddp_1 ... ok +faddp_2 ... ok +faddp_3 ... ok +faddp_4 ... ok +faddp_5 ... ok +faddp_6 ... ok +faddp_7 ... ok +faddp_8 ... ok +faddp_9 ... ok +faddp_10 ... ok +faddp_11 ... ok +faddp_12 ... ok +faddp_13 ... ok +faddp_14 ... ok +faddp_15 ... ok +faddp_16 ... ok +fiadds_1 ... ok +fiadds_2 ... ok +fiadds_3 ... ok +fiadds_4 ... ok +fiadds_5 ... ok +fiadds_6 ... ok +fiadds_7 ... ok +fiadds_8 ... ok +fiaddl_1 ... ok +fiaddl_2 ... ok +fiaddl_3 ... ok +fiaddl_4 ... ok +fiaddl_5 ... ok +fiaddl_6 ... ok +fiaddl_7 ... ok +fiaddl_8 ... ok +fcomi_1 ... ok +fcomi_2 ... ok +fcomi_3 ... ok +fcomi_4 ... ok +fcomi_5 ... ok +fcomi_6 ... ok +fcomip_1 ... ok +fcomip_2 ... ok +fcomip_3 ... ok +fcomip_4 ... ok +fcomip_5 ... ok +fcomip_6 ... ok +fucomi_1 ... ok +fucomi_2 ... ok +fucomi_3 ... ok +fucomi_4 ... ok +fucomi_5 ... ok +fucomi_6 ... ok +fucomip_1 ... ok +fucomip_2 ... ok +fucomip_3 ... ok +fucomip_4 ... ok +fucomip_5 ... ok +fucomip_6 ... ok +fchs_1 ... ok +fchs_2 ... ok +fchs_3 ... ok +fchs_4 ... ok +fdivs_1 ... ok +fdivs_2 ... ok +fdivs_3 ... ok +fdivs_4 ... ok +fdivl_1 ... ok +fdivl_2 ... ok +fdivl_3 ... ok +fdivl_4 ... ok +fdiv_1 ... ok +fdiv_2 ... ok +fdiv_3 ... ok +fdiv_4 ... ok +fdiv_5 ... ok +fdiv_6 ... ok +fdiv_7 ... ok +fdiv_8 ... ok +fdiv_9 ... ok +fdiv_10 ... ok +fdiv_11 ... ok +fdiv_12 ... ok +fdiv_13 ... ok +fdiv_14 ... ok +fdiv_15 ... ok +fdiv_16 ... ok +fdivp_1 ... ok +fdivp_2 ... ok +fdivp_3 ... ok +fdivp_4 ... ok +fdivp_5 ... ok +fdivp_6 ... ok +fdivp_7 ... ok +fdivp_8 ... ok +fdivp_9 ... ok +fdivp_10 ... ok +fdivp_11 ... ok +fdivp_12 ... ok +fdivp_13 ... ok +fdivp_14 ... ok +fdivp_15 ... ok +fdivp_16 ... ok +fidivs_1 ... ok +fidivs_2 ... ok +fidivs_3 ... ok +fidivs_4 ... ok +fidivs_5 ... ok +fidivs_6 ... ok +fidivs_7 ... ok +fidivs_8 ... ok +fidivl_1 ... ok +fidivl_2 ... ok +fidivl_3 ... ok +fidivl_4 ... ok +fidivl_5 ... ok +fidivl_6 ... ok +fidivl_7 ... ok +fidivl_8 ... ok +fdivrs_1 ... ok +fdivrs_2 ... ok +fdivrs_3 ... ok +fdivrs_4 ... ok +fdivrl_1 ... ok +fdivrl_2 ... ok +fdivrl_3 ... ok +fdivrl_4 ... ok +fdivr_1 ... ok +fdivr_2 ... ok +fdivr_3 ... ok +fdivr_4 ... ok +fdivr_5 ... ok +fdivr_6 ... ok +fdivr_7 ... ok +fdivr_8 ... ok +fdivr_9 ... ok +fdivr_10 ... ok +fdivr_11 ... ok +fdivr_12 ... ok +fdivr_13 ... ok +fdivr_14 ... ok +fdivr_15 ... ok +fdivr_16 ... ok +fdivrp_1 ... ok +fdivrp_2 ... ok +fdivrp_3 ... ok +fdivrp_4 ... ok +fdivrp_5 ... ok +fdivrp_6 ... ok +fdivrp_7 ... ok +fdivrp_8 ... ok +fdivrp_9 ... ok +fdivrp_10 ... ok +fdivrp_11 ... ok +fdivrp_12 ... ok +fdivrp_13 ... ok +fdivrp_14 ... ok +fdivrp_15 ... ok +fdivrp_16 ... ok +fidivrs_1 ... ok +fidivrs_2 ... ok +fidivrs_3 ... ok +fidivrs_4 ... ok +fidivrs_5 ... ok +fidivrs_6 ... ok +fidivrs_7 ... ok +fidivrs_8 ... ok +fidivrl_1 ... ok +fidivrl_2 ... ok +fidivrl_3 ... ok +fidivrl_4 ... ok +fidivrl_5 ... ok +fidivrl_6 ... ok +fidivrl_7 ... ok +fidivrl_8 ... ok +filds_1 ... ok +filds_2 ... ok +filds_3 ... ok +filds_4 ... ok +fildl_1 ... ok +fildl_2 ... ok +fildl_3 ... ok +fildl_4 ... ok +fildq_1 ... ok +fildq_2 ... ok +fildq_3 ... ok +fildq_4 ... ok +fists_1 ... ok +fists_2 ... ok +fists_3 ... ok +fists_4 ... ok +fists_5 ... ok +fists_6 ... ok +fists_7 ... ok +fists_8 ... ok +fistl_1 ... ok +fistl_2 ... ok +fistl_3 ... ok +fistl_4 ... ok +fistl_5 ... ok +fistl_6 ... ok +fistl_7 ... ok +fistl_8 ... ok +fistps_1 ... ok +fistps_2 ... ok +fistps_3 ... ok +fistps_4 ... ok +fistps_5 ... ok +fistps_6 ... ok +fistps_7 ... ok +fistps_8 ... ok +fistpl_1 ... ok +fistpl_2 ... ok +fistpl_3 ... ok +fistpl_4 ... ok +fistpl_5 ... ok +fistpl_6 ... ok +fistpl_7 ... ok +fistpl_8 ... ok +fistpq_1 ... ok +fistpq_2 ... ok +fistpq_3 ... ok +fistpq_4 ... ok +fistpq_5 ... ok +fistpq_6 ... ok +fistpq_7 ... ok +fistpq_8 ... ok +flds_1 ... ok +flds_2 ... ok +fldl_1 ... ok +fldl_2 ... ok +fld_1 ... ok +fld_2 ... ok +fld_3 ... ok +fld1_1 ... ok +fldl2t_1 ... ok +fldl2e_1 ... ok +fldpi_1 ... ok +fldlg2_1 ... ok +fldln2_1 ... ok +fldz_1 ... ok +fmuls_1 ... ok +fmuls_2 ... ok +fmuls_3 ... ok +fmuls_4 ... ok +fmull_1 ... ok +fmull_2 ... ok +fmull_3 ... ok +fmull_4 ... ok +fmul_1 ... ok +fmul_2 ... ok +fmul_3 ... ok +fmul_4 ... ok +fmul_5 ... ok +fmul_6 ... ok +fmul_7 ... ok +fmul_8 ... ok +fmul_9 ... ok +fmul_10 ... ok +fmul_11 ... ok +fmul_12 ... ok +fmul_13 ... ok +fmul_14 ... ok +fmul_15 ... ok +fmul_16 ... ok +fmulp_1 ... ok +fmulp_2 ... ok +fmulp_3 ... ok +fmulp_4 ... ok +fmulp_5 ... ok +fmulp_6 ... ok +fmulp_7 ... ok +fmulp_8 ... ok +fmulp_9 ... ok +fmulp_10 ... ok +fmulp_11 ... ok +fmulp_12 ... ok +fmulp_13 ... ok +fmulp_14 ... ok +fmulp_15 ... ok +fmulp_16 ... ok +fimuls_1 ... ok +fimuls_2 ... ok +fimuls_3 ... ok +fimuls_4 ... ok +fimuls_5 ... ok +fimuls_6 ... ok +fimuls_7 ... ok +fimuls_8 ... ok +fimull_1 ... ok +fimull_2 ... ok +fimull_3 ... ok +fimull_4 ... ok +fimull_5 ... ok +fimull_6 ... ok +fimull_7 ... ok +fimull_8 ... ok +frndint_1 ... ok +frndint_2 ... ok +frndint_3 ... ok +frndint_4 ... ok +frndint_5 ... ok +frndint_6 ... ok +frndint_7 ... ok +frndint_8 ... ok +frndint_9 ... ok +frndint_10 ... ok +frndint_11 ... ok +frndint_12 ... ok +frndint_13 ... ok +frndint_14 ... ok +frndint_15 ... ok +frndint_16 ... ok +fsubs_1 ... ok +fsubs_2 ... ok +fsubs_3 ... ok +fsubs_4 ... ok +fsubl_1 ... ok +fsubl_2 ... ok +fsubl_3 ... ok +fsubl_4 ... ok +fsub_1 ... ok +fsub_2 ... ok +fsub_3 ... ok +fsub_4 ... ok +fsub_5 ... ok +fsub_6 ... ok +fsub_7 ... ok +fsub_8 ... ok +fsub_9 ... ok +fsub_10 ... ok +fsub_11 ... ok +fsub_12 ... ok +fsub_13 ... ok +fsub_14 ... ok +fsub_15 ... ok +fsub_16 ... ok +fsubp_1 ... ok +fsubp_2 ... ok +fsubp_3 ... ok +fsubp_4 ... ok +fsubp_5 ... ok +fsubp_6 ... ok +fsubp_7 ... ok +fsubp_8 ... ok +fsubp_9 ... ok +fsubp_10 ... ok +fsubp_11 ... ok +fsubp_12 ... ok +fsubp_13 ... ok +fsubp_14 ... ok +fsubp_15 ... ok +fsubp_16 ... ok +fisubs_1 ... ok +fisubs_2 ... ok +fisubs_3 ... ok +fisubs_4 ... ok +fisubs_5 ... ok +fisubs_6 ... ok +fisubs_7 ... ok +fisubs_8 ... ok +fisubl_1 ... ok +fisubl_2 ... ok +fisubl_3 ... ok +fisubl_4 ... ok +fisubl_5 ... ok +fisubl_6 ... ok +fisubl_7 ... ok +fisubl_8 ... ok +fsubrs_1 ... ok +fsubrs_2 ... ok +fsubrs_3 ... ok +fsubrs_4 ... ok +fsubrl_1 ... ok +fsubrl_2 ... ok +fsubrl_3 ... ok +fsubrl_4 ... ok +fsubr_1 ... ok +fsubr_2 ... ok +fsubr_3 ... ok +fsubr_4 ... ok +fsubr_5 ... ok +fsubr_6 ... ok +fsubr_7 ... ok +fsubr_8 ... ok +fsubr_9 ... ok +fsubr_10 ... ok +fsubr_11 ... ok +fsubr_12 ... ok +fsubr_13 ... ok +fsubr_14 ... ok +fsubr_15 ... ok +fsubr_16 ... ok +fsubrp_1 ... ok +fsubrp_2 ... ok +fsubrp_3 ... ok +fsubrp_4 ... ok +fsubrp_5 ... ok +fsubrp_6 ... ok +fsubrp_7 ... ok +fsubrp_8 ... ok +fsubrp_9 ... ok +fsubrp_10 ... ok +fsubrp_11 ... ok +fsubrp_12 ... ok +fsubrp_13 ... ok +fsubrp_14 ... ok +fsubrp_15 ... ok +fsubrp_16 ... ok +fisubrs_1 ... ok +fisubrs_2 ... ok +fisubrs_3 ... ok +fisubrs_4 ... ok +fisubrs_5 ... ok +fisubrs_6 ... ok +fisubrs_7 ... ok +fisubrs_8 ... ok +fisubrl_1 ... ok +fisubrl_2 ... ok +fisubrl_3 ... ok +fisubrl_4 ... ok +fisubrl_5 ... ok +fisubrl_6 ... ok +fisubrl_7 ... ok +fisubrl_8 ... ok +fxch_1 ... ok +fxch_2 ... ok diff --git a/VEX/head20041019/helgrind/tests/insn_fpu.vgtest b/VEX/head20041019/helgrind/tests/insn_fpu.vgtest new file mode 100644 index 0000000000..1b9546f54a --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_fpu.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_fpu +cpu_test: fpu diff --git a/VEX/head20041019/helgrind/tests/insn_mmx.stderr.exp b/VEX/head20041019/helgrind/tests/insn_mmx.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/tests/insn_mmx.stdout.exp b/VEX/head20041019/helgrind/tests/insn_mmx.stdout.exp new file mode 100644 index 0000000000..95cbae1603 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_mmx.stdout.exp @@ -0,0 +1,103 @@ +movd_1 ... ok +movd_2 ... ok +movd_3 ... ok +movd_4 ... ok +movq_1 ... ok +movq_2 ... ok +movq_3 ... ok +packssdw_1 ... ok +packssdw_2 ... ok +packsswb_1 ... ok +packsswb_2 ... ok +packuswb_1 ... ok +packuswb_2 ... ok +paddb_1 ... ok +paddb_2 ... ok +paddd_1 ... ok +paddd_2 ... ok +paddsb_1 ... ok +paddsb_2 ... ok +paddsw_1 ... ok +paddsw_2 ... ok +paddusb_1 ... ok +paddusb_2 ... ok +paddusw_1 ... ok +paddusw_2 ... ok +paddw_1 ... ok +paddw_2 ... ok +pand_1 ... ok +pand_2 ... ok +pandn_1 ... ok +pandn_2 ... ok +pcmpeqb_1 ... ok +pcmpeqb_2 ... ok +pcmpeqd_1 ... ok +pcmpeqd_2 ... ok +pcmpeqw_1 ... ok +pcmpeqw_2 ... ok +pcmpgtb_1 ... ok +pcmpgtb_2 ... ok +pcmpgtd_1 ... ok +pcmpgtd_2 ... ok +pcmpgtw_1 ... ok +pcmpgtw_2 ... ok +pmaddwd_1 ... ok +pmaddwd_2 ... ok +pmulhw_1 ... ok +pmulhw_2 ... ok +pmullw_1 ... ok +pmullw_2 ... ok +por_1 ... ok +por_2 ... ok +pslld_1 ... ok +pslld_2 ... ok +pslld_3 ... ok +psllq_1 ... ok +psllq_2 ... ok +psllq_3 ... ok +psllw_1 ... ok +psllw_2 ... ok +psllw_3 ... ok +psrad_1 ... ok +psrad_2 ... ok +psrad_3 ... ok +psraw_1 ... ok +psraw_2 ... ok +psraw_3 ... ok +psrld_1 ... ok +psrld_2 ... ok +psrld_3 ... ok +psrlq_1 ... ok +psrlq_2 ... ok +psrlq_3 ... ok +psrlw_1 ... ok +psrlw_2 ... ok +psrlw_3 ... ok +psubb_1 ... ok +psubb_2 ... ok +psubd_1 ... ok +psubd_2 ... ok +psubsb_1 ... ok +psubsb_2 ... ok +psubsw_1 ... ok +psubsw_2 ... ok +psubusb_1 ... ok +psubusb_2 ... ok +psubusw_1 ... ok +psubusw_2 ... ok +psubw_1 ... ok +psubw_2 ... ok +punpckhbw_1 ... ok +punpckhbw_2 ... ok +punpckhdq_1 ... ok +punpckhdq_2 ... ok +punpckhwd_1 ... ok +punpckhwd_2 ... ok +punpcklbw_1 ... ok +punpcklbw_2 ... ok +punpckldq_1 ... ok +punpckldq_2 ... ok +punpcklwd_1 ... ok +punpcklwd_2 ... ok +pxor_1 ... ok +pxor_2 ... ok diff --git a/VEX/head20041019/helgrind/tests/insn_mmx.vgtest b/VEX/head20041019/helgrind/tests/insn_mmx.vgtest new file mode 100644 index 0000000000..ddbb977263 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_mmx.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_mmx +cpu_test: mmx diff --git a/VEX/head20041019/helgrind/tests/insn_mmxext.stderr.exp b/VEX/head20041019/helgrind/tests/insn_mmxext.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/tests/insn_mmxext.stdout.exp b/VEX/head20041019/helgrind/tests/insn_mmxext.stdout.exp new file mode 100644 index 0000000000..23b2e55abd --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_mmxext.stdout.exp @@ -0,0 +1,29 @@ +movntq_1 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufw_1 ... ok +pshufw_2 ... ok +sfence_1 ... ok diff --git a/VEX/head20041019/helgrind/tests/insn_mmxext.vgtest b/VEX/head20041019/helgrind/tests/insn_mmxext.vgtest new file mode 100644 index 0000000000..bb667097fb --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_mmxext.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_mmxext +cpu_test: mmxext diff --git a/VEX/head20041019/helgrind/tests/insn_sse.stderr.exp b/VEX/head20041019/helgrind/tests/insn_sse.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/tests/insn_sse.stdout.exp b/VEX/head20041019/helgrind/tests/insn_sse.stdout.exp new file mode 100644 index 0000000000..f15bd81f0b --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_sse.stdout.exp @@ -0,0 +1,142 @@ +addps_1 ... ok +addps_2 ... ok +addss_1 ... ok +addss_2 ... ok +andnps_1 ... ok +andnps_2 ... ok +andps_1 ... ok +andps_2 ... ok +cmpeqps_1 ... ok +cmpeqps_2 ... ok +cmpeqss_1 ... ok +cmpeqss_2 ... ok +cmpleps_1 ... ok +cmpleps_2 ... ok +cmpless_1 ... ok +cmpless_2 ... ok +cmpltps_1 ... ok +cmpltps_2 ... ok +cmpltss_1 ... ok +cmpltss_2 ... ok +cmpneqps_1 ... ok +cmpneqps_2 ... ok +cmpneqss_1 ... ok +cmpneqss_2 ... ok +cmpnleps_1 ... ok +cmpnleps_2 ... ok +cmpnless_1 ... ok +cmpnless_2 ... ok +cmpnltps_1 ... ok +cmpnltps_2 ... ok +cmpnltss_1 ... ok +cmpnltss_2 ... ok +comiss_1 ... ok +comiss_2 ... ok +comiss_3 ... ok +comiss_4 ... ok +comiss_5 ... ok +comiss_6 ... ok +cvtpi2ps_1 ... ok +cvtpi2ps_2 ... ok +cvtps2pi_1 ... ok +cvtps2pi_2 ... ok +cvtsi2ss_1 ... ok +cvtsi2ss_2 ... ok +cvtss2si_1 ... ok +cvtss2si_2 ... ok +cvttps2pi_1 ... ok +cvttps2pi_2 ... ok +cvttss2si_1 ... ok +cvttss2si_2 ... ok +divps_1 ... ok +divps_2 ... ok +divss_1 ... ok +divss_2 ... ok +maxps_1 ... ok +maxps_2 ... ok +maxss_1 ... ok +maxss_2 ... ok +minps_1 ... ok +minps_2 ... ok +minss_1 ... ok +minss_2 ... ok +movaps_1 ... ok +movaps_2 ... ok +movhlps_1 ... ok +movhps_1 ... ok +movhps_2 ... ok +movlhps_1 ... ok +movlps_1 ... ok +movlps_2 ... ok +movmskps_1 ... ok +movntps_1 ... ok +movntq_1 ... ok +movss_1 ... ok +movss_2 ... ok +movss_3 ... ok +movups_1 ... ok +movups_2 ... ok +mulps_1 ... ok +mulps_2 ... ok +mulss_1 ... ok +mulss_2 ... ok +orps_1 ... ok +orps_2 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufw_1 ... ok +pshufw_2 ... ok +rcpps_1 ... ok +rcpps_2 ... ok +rcpss_1 ... ok +rcpss_2 ... ok +rsqrtps_1 ... ok +rsqrtps_2 ... ok +rsqrtss_1 ... ok +rsqrtss_2 ... ok +sfence_1 ... ok +shufps_1 ... ok +shufps_2 ... ok +sqrtps_1 ... ok +sqrtps_2 ... ok +sqrtss_1 ... ok +sqrtss_2 ... ok +subps_1 ... ok +subps_2 ... ok +subss_1 ... ok +subss_2 ... ok +ucomiss_1 ... ok +ucomiss_2 ... ok +ucomiss_3 ... ok +ucomiss_4 ... ok +ucomiss_5 ... ok +ucomiss_6 ... ok +unpckhps_1 ... ok +unpckhps_2 ... ok +unpcklps_1 ... ok +unpcklps_2 ... ok +xorps_1 ... ok +xorps_2 ... ok diff --git a/VEX/head20041019/helgrind/tests/insn_sse.vgtest b/VEX/head20041019/helgrind/tests/insn_sse.vgtest new file mode 100644 index 0000000000..167c8e290b --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_sse.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_sse +cpu_test: sse diff --git a/VEX/head20041019/helgrind/tests/insn_sse2.stderr.exp b/VEX/head20041019/helgrind/tests/insn_sse2.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/helgrind/tests/insn_sse2.stdout.exp b/VEX/head20041019/helgrind/tests/insn_sse2.stdout.exp new file mode 100644 index 0000000000..9c24f72621 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_sse2.stdout.exp @@ -0,0 +1,294 @@ +addpd_1 ... ok +addpd_2 ... ok +addsd_1 ... ok +addsd_2 ... ok +andpd_1 ... ok +andpd_2 ... ok +andnpd_1 ... ok +andnpd_2 ... ok +cmpeqpd_1 ... ok +cmpeqpd_2 ... ok +cmpltpd_1 ... ok +cmpltpd_2 ... ok +cmplepd_1 ... ok +cmplepd_2 ... ok +cmpneqpd_1 ... ok +cmpneqpd_2 ... ok +cmpnltpd_1 ... ok +cmpnltpd_2 ... ok +cmpnlepd_1 ... ok +cmpnlepd_2 ... ok +cmpeqsd_1 ... ok +cmpeqsd_2 ... ok +cmpltsd_1 ... ok +cmpltsd_2 ... ok +cmplesd_1 ... ok +cmplesd_2 ... ok +cmpneqsd_1 ... ok +cmpneqsd_2 ... ok +cmpnltsd_1 ... ok +cmpnltsd_2 ... ok +cmpnlesd_1 ... ok +cmpnlesd_2 ... ok +comisd_1 ... ok +comisd_2 ... ok +comisd_3 ... ok +comisd_4 ... ok +comisd_5 ... ok +comisd_6 ... ok +cvtdq2pd_1 ... ok +cvtdq2pd_2 ... ok +cvtdq2ps_1 ... ok +cvtdq2ps_2 ... ok +cvtpd2dq_1 ... ok +cvtpd2dq_2 ... ok +cvtpd2pi_1 ... ok +cvtpd2pi_2 ... ok +cvtpd2ps_1 ... ok +cvtpd2ps_2 ... ok +cvtpi2pd_1 ... ok +cvtpi2pd_2 ... ok +cvtps2dq_1 ... ok +cvtps2dq_2 ... ok +cvtps2pd_1 ... ok +cvtps2pd_2 ... ok +cvtsd2si_1 ... ok +cvtsd2si_2 ... ok +cvtsd2ss_1 ... ok +cvtsd2ss_2 ... ok +cvtsi2sd_1 ... ok +cvtsi2sd_2 ... ok +cvtss2sd_1 ... ok +cvtss2sd_2 ... ok +cvttpd2pi_1 ... ok +cvttpd2pi_2 ... ok +cvttpd2dq_1 ... ok +cvttpd2dq_2 ... ok +cvttps2dq_1 ... ok +cvttps2dq_2 ... ok +cvttsd2si_1 ... ok +cvttsd2si_2 ... ok +divpd_1 ... ok +divpd_2 ... ok +divsd_1 ... ok +divsd_2 ... ok +lfence_1 ... ok +maxpd_1 ... ok +maxpd_2 ... ok +maxsd_1 ... ok +maxsd_2 ... ok +mfence_1 ... ok +minpd_1 ... ok +minpd_2 ... ok +minsd_1 ... ok +minsd_2 ... ok +movapd_1 ... ok +movapd_2 ... ok +movd_1 ... ok +movd_2 ... ok +movd_3 ... ok +movd_4 ... ok +movdqa_1 ... ok +movdqa_2 ... ok +movdqa_3 ... ok +movdqu_1 ... ok +movdqu_2 ... ok +movdqu_3 ... ok +movdq2q_1 ... ok +movhpd_1 ... ok +movhpd_2 ... ok +movlpd_1 ... ok +movlpd_2 ... ok +movmskpd_1 ... ok +movntdq_1 ... ok +movnti_1 ... ok +movntpd_1 ... ok +movq2dq_1 ... ok +movsd_1 ... ok +movsd_2 ... ok +movsd_3 ... ok +movupd_1 ... ok +movupd_2 ... ok +mulpd_1 ... ok +mulpd_2 ... ok +mulsd_1 ... ok +mulsd_2 ... ok +orpd_1 ... ok +orpd_2 ... ok +packssdw_1 ... ok +packssdw_2 ... ok +packsswb_1 ... ok +packsswb_2 ... ok +packuswb_1 ... ok +packuswb_2 ... ok +paddb_1 ... ok +paddb_2 ... ok +paddd_1 ... ok +paddd_2 ... ok +paddq_1 ... ok +paddq_2 ... ok +paddq_3 ... ok +paddq_4 ... ok +paddsb_1 ... ok +paddsb_2 ... ok +paddsw_1 ... ok +paddsw_2 ... ok +paddusb_1 ... ok +paddusb_2 ... ok +paddusw_1 ... ok +paddusw_2 ... ok +paddw_1 ... ok +paddw_2 ... ok +pand_1 ... ok +pand_2 ... ok +pandn_1 ... ok +pandn_2 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pcmpeqb_1 ... ok +pcmpeqb_2 ... ok +pcmpeqd_1 ... ok +pcmpeqd_2 ... ok +pcmpeqw_1 ... ok +pcmpeqw_2 ... ok +pcmpgtb_1 ... ok +pcmpgtb_2 ... ok +pcmpgtd_1 ... ok +pcmpgtd_2 ... ok +pcmpgtw_1 ... ok +pcmpgtw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pextrw_5 ... ok +pextrw_6 ... ok +pextrw_7 ... ok +pextrw_8 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pinsrw_5 ... ok +pinsrw_6 ... ok +pinsrw_7 ... ok +pinsrw_8 ... ok +pmaddwd_1 ... ok +pmaddwd_2 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +pmulhw_1 ... ok +pmulhw_2 ... ok +pmullw_1 ... ok +pmullw_2 ... ok +pmuludq_1 ... ok +pmuludq_2 ... ok +pmuludq_3 ... ok +pmuludq_4 ... ok +por_1 ... ok +por_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufd_1 ... ok +pshufd_2 ... ok +pshufhw_1 ... ok +pshufhw_2 ... ok +pshuflw_1 ... ok +pshuflw_2 ... ok +pslld_1 ... ok +pslld_2 ... ok +pslld_3 ... ok +pslldq_1 ... ok +pslldq_2 ... ok +psllq_1 ... ok +psllq_2 ... ok +psllq_3 ... ok +psllw_1 ... ok +psllw_2 ... ok +psllw_3 ... ok +psrad_1 ... ok +psrad_2 ... ok +psrad_3 ... ok +psraw_1 ... ok +psraw_2 ... ok +psraw_3 ... ok +psrld_1 ... ok +psrld_2 ... ok +psrld_3 ... ok +psrldq_1 ... ok +psrldq_2 ... ok +psrlq_1 ... ok +psrlq_2 ... ok +psrlq_3 ... ok +psrlw_1 ... ok +psrlw_2 ... ok +psrlw_3 ... ok +psubb_1 ... ok +psubb_2 ... ok +psubd_1 ... ok +psubd_2 ... ok +psubq_1 ... ok +psubq_2 ... ok +psubq_3 ... ok +psubq_4 ... ok +psubsb_1 ... ok +psubsb_2 ... ok +psubsw_1 ... ok +psubsw_2 ... ok +psubusb_1 ... ok +psubusb_2 ... ok +psubusw_1 ... ok +psubusw_2 ... ok +psubw_1 ... ok +psubw_2 ... ok +punpckhbw_1 ... ok +punpckhbw_2 ... ok +punpckhdq_1 ... ok +punpckhdq_2 ... ok +punpckhqdq_1 ... ok +punpckhqdq_2 ... ok +punpckhwd_1 ... ok +punpckhwd_2 ... ok +punpcklbw_1 ... ok +punpcklbw_2 ... ok +punpckldq_1 ... ok +punpckldq_2 ... ok +punpcklqdq_1 ... ok +punpcklqdq_2 ... ok +punpcklwd_1 ... ok +punpcklwd_2 ... ok +pxor_1 ... ok +pxor_2 ... ok +shufpd_1 ... ok +shufpd_2 ... ok +sqrtpd_1 ... ok +sqrtpd_2 ... ok +sqrtsd_1 ... ok +sqrtsd_2 ... ok +subpd_1 ... ok +subpd_2 ... ok +subsd_1 ... ok +subsd_2 ... ok +ucomisd_1 ... ok +ucomisd_2 ... ok +ucomisd_3 ... ok +ucomisd_4 ... ok +ucomisd_5 ... ok +ucomisd_6 ... ok +unpckhpd_1 ... ok +unpckhpd_2 ... ok +unpcklpd_1 ... ok +unpcklpd_2 ... ok +xorpd_1 ... ok +xorpd_2 ... ok diff --git a/VEX/head20041019/helgrind/tests/insn_sse2.vgtest b/VEX/head20041019/helgrind/tests/insn_sse2.vgtest new file mode 100644 index 0000000000..42e82f38d3 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/insn_sse2.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_sse2 +cpu_test: sse2 diff --git a/VEX/head20041019/helgrind/tests/race.c b/VEX/head20041019/helgrind/tests/race.c new file mode 100644 index 0000000000..111195bf90 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/race.c @@ -0,0 +1,27 @@ +/* A simple race */ + +#include +#include + +static int shared; + +static void *th(void *v) +{ + shared++; + + return 0; +} + +int main() +{ + pthread_t a, b; + + pthread_create(&a, NULL, th, NULL); + sleep(1); /* force ordering */ + pthread_create(&b, NULL, th, NULL); + + pthread_join(a, NULL); + pthread_join(b, NULL); + + return 0; +} diff --git a/VEX/head20041019/helgrind/tests/race.stderr.exp b/VEX/head20041019/helgrind/tests/race.stderr.exp new file mode 100644 index 0000000000..96832e62a9 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/race.stderr.exp @@ -0,0 +1,9 @@ + +Thread 3: +Possible data race writing variable at 0x........ (shared) + at 0x........: th (race.c:10) + by 0x........: thread_wrapper (vg_libpthread.c:...) + by 0x........: do__quit (vg_scheduler.c:...) + Address 0x........ is in BSS section of /...helgrind/tests... + Previous state: shared RO, no locks + diff --git a/VEX/head20041019/helgrind/tests/race.vgtest b/VEX/head20041019/helgrind/tests/race.vgtest new file mode 100644 index 0000000000..ca127cdac0 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/race.vgtest @@ -0,0 +1,2 @@ +prog: race +vgopts: --optimise=no diff --git a/VEX/head20041019/helgrind/tests/race2.c b/VEX/head20041019/helgrind/tests/race2.c new file mode 100644 index 0000000000..de01f2c490 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/race2.c @@ -0,0 +1,35 @@ +/* A simple race - test symaddr */ + +#include +#include + +struct foo { + struct bar { + int plop[22]; + char biff; + } poot[11]; +}; + +static void *th(void *v) +{ + struct foo *f = (struct foo *)v; + + f->poot[5].plop[11]++; + + return 0; +} + +int main() +{ + struct foo foo; + pthread_t a, b; + + pthread_create(&a, NULL, th, &foo); + sleep(1); /* force ordering */ + pthread_create(&b, NULL, th, &foo); + + pthread_join(a, NULL); + pthread_join(b, NULL); + + return 0; +} diff --git a/VEX/head20041019/helgrind/tests/race2.stderr.exp b/VEX/head20041019/helgrind/tests/race2.stderr.exp new file mode 100644 index 0000000000..9a7d7e642e --- /dev/null +++ b/VEX/head20041019/helgrind/tests/race2.stderr.exp @@ -0,0 +1,9 @@ + +Thread 3: +Possible data race writing variable at 0x........ + at 0x........: th (race2.c:17) + by 0x........: thread_wrapper (vg_libpthread.c:...) + by 0x........: do__quit (vg_scheduler.c:...) + Address 0x........ == &(f->poot[5].plop[11]) at race2.c:17 + Previous state: shared RO, no locks + diff --git a/VEX/head20041019/helgrind/tests/race2.vgtest b/VEX/head20041019/helgrind/tests/race2.vgtest new file mode 100644 index 0000000000..a67c27ae4a --- /dev/null +++ b/VEX/head20041019/helgrind/tests/race2.vgtest @@ -0,0 +1,2 @@ +prog: race2 +vgopts: --optimise=no diff --git a/VEX/head20041019/helgrind/tests/readshared.c b/VEX/head20041019/helgrind/tests/readshared.c new file mode 100644 index 0000000000..0541e7def6 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/readshared.c @@ -0,0 +1,33 @@ +/* All OK - test allowed read sharing */ + +#include +#include + +static int shared; + +static void *t1(void *v) +{ + return (void *)(shared + 44); +} + +static void *t2(void *v) +{ + return (void *)(shared + 55); +} + +int main() +{ + pthread_t a, b; + + shared = 22; + + pthread_create(&a, NULL, t1, NULL); + pthread_create(&b, NULL, t2, NULL); + + pthread_join(a, NULL); + pthread_join(b, NULL); + + assert(shared == 22); + + return 0; +} diff --git a/VEX/head20041019/helgrind/tests/readshared.stderr.exp b/VEX/head20041019/helgrind/tests/readshared.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/helgrind/tests/readshared.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/helgrind/tests/readshared.vgtest b/VEX/head20041019/helgrind/tests/readshared.vgtest new file mode 100644 index 0000000000..2d91bee035 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/readshared.vgtest @@ -0,0 +1,2 @@ +prog: readshared +vgopts: --optimise=no diff --git a/VEX/head20041019/helgrind/tests/toobig-allocs.stderr.exp b/VEX/head20041019/helgrind/tests/toobig-allocs.stderr.exp new file mode 100644 index 0000000000..28c2b9ee04 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/toobig-allocs.stderr.exp @@ -0,0 +1,4 @@ + +Attempting too-big malloc()... +Attempting too-big mmap()... + diff --git a/VEX/head20041019/helgrind/tests/toobig-allocs.vgtest b/VEX/head20041019/helgrind/tests/toobig-allocs.vgtest new file mode 100644 index 0000000000..186cf5f901 --- /dev/null +++ b/VEX/head20041019/helgrind/tests/toobig-allocs.vgtest @@ -0,0 +1 @@ +prog: ../../tests/toobig-allocs diff --git a/VEX/head20041019/include/.cvsignore b/VEX/head20041019/include/.cvsignore new file mode 100644 index 0000000000..3e26277d69 --- /dev/null +++ b/VEX/head20041019/include/.cvsignore @@ -0,0 +1,3 @@ +Makefile.in +Makefile +tool.h diff --git a/VEX/head20041019/include/CVS/Entries b/VEX/head20041019/include/CVS/Entries new file mode 100644 index 0000000000..4fb60480c5 --- /dev/null +++ b/VEX/head20041019/include/CVS/Entries @@ -0,0 +1,9 @@ +/.cvsignore/1.3/Thu Sep 2 08:54:27 2004// +/Makefile.am/1.9/Mon Oct 18 18:07:49 2004// +/tool.h.base/1.7/Tue Sep 7 10:17:02 2004// +/tool_asm.h/1.2/Fri Sep 3 13:45:28 2004// +/valgrind.h.in/1.1/Mon Oct 18 18:07:49 2004// +/vg_kerneliface.h/1.25/Tue Sep 14 11:55:36 2004// +/vg_profile.c/1.12/Thu Sep 2 08:51:42 2004// +/vg_skin.h/1.104/Thu Sep 2 08:55:34 2004// +D/x86//// diff --git a/VEX/head20041019/include/CVS/Repository b/VEX/head20041019/include/CVS/Repository new file mode 100644 index 0000000000..9ed88dbe95 --- /dev/null +++ b/VEX/head20041019/include/CVS/Repository @@ -0,0 +1 @@ +valgrind/include diff --git a/VEX/head20041019/include/CVS/Root b/VEX/head20041019/include/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/include/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/include/CVS/Template b/VEX/head20041019/include/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/include/Makefile.am b/VEX/head20041019/include/Makefile.am new file mode 100644 index 0000000000..ad8370e547 --- /dev/null +++ b/VEX/head20041019/include/Makefile.am @@ -0,0 +1,26 @@ + +SUBDIRS = $(VG_ARCH) . + +EXTRA_DIST = \ + tool.h.base \ + valgrind.h.in \ + vg_profile.c + +incincdir = $(includedir)/valgrind + +incinc_HEADERS = \ + tool.h \ + tool_asm.h \ + valgrind.h \ + vg_kerneliface.h \ + vg_skin.h + +BUILT_SOURCES = tool.h valgrind.h +CLEANFILES = tool.h valgrind.h + +tool.h: $(srcdir)/tool.h.base \ + $(top_srcdir)/coregrind/gen_toolint.pl $(top_srcdir)/coregrind/toolfuncs.def + rm -f $@ + cat $(srcdir)/tool.h.base > $@ + $(PERL) $(top_srcdir)/coregrind/gen_toolint.pl toolproto < $(top_srcdir)/coregrind/toolfuncs.def >> $@ || rm -f $@ + $(PERL) $(top_srcdir)/coregrind/gen_toolint.pl initproto < $(top_srcdir)/coregrind/toolfuncs.def >> $@ || rm -f $@ diff --git a/VEX/head20041019/include/tool.h.base b/VEX/head20041019/include/tool.h.base new file mode 100644 index 0000000000..4f9133100f --- /dev/null +++ b/VEX/head20041019/include/tool.h.base @@ -0,0 +1,1926 @@ +/*-*- c -*- ----------------------------------------------------------*/ +/*--- The only header your tool will ever need to #include... ---*/ +/*--- tool.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __TOOL_H +#define __TOOL_H + +#include /* ANSI varargs stuff */ +#include /* for jmp_buf */ + +#include "tool_asm.h" // asm stuff + +/*====================================================================*/ +/*=== Basic types ===*/ +/*====================================================================*/ + +// By choosing the right types, we can get these right for 32-bit and 64-bit +// platforms without having to do any conditional compilation or anything. +// +// Size in bits on: 32-bit archs 64-bit archs +// ------------ ------------ +typedef unsigned char UChar; // 8 8 +typedef unsigned short UShort; // 16 16 +typedef unsigned int UInt; // 32 32 +typedef unsigned long UWord; // 32 64 +typedef unsigned long long ULong; // 64 64 + +typedef signed char Char; // 8 8 +typedef signed short Short; // 16 16 +typedef signed int Int; // 32 32 +typedef signed long Word; // 32 64 +typedef signed long long Long; // 64 64 + +typedef UWord Addr; // 32 64 + +typedef UChar Bool; // 8 8 +#define False ((Bool)0) +#define True ((Bool)1) + +/* --------------------------------------------------------------------- + Now the basic types are set up, we can haul in the kernel-interface + definitions and tool_arch.h + ------------------------------------------------------------------ */ + +#include "tool_arch.h" // arch-specific tool stuff +#include "vg_kerneliface.h" + +/* --------------------------------------------------------------------- + Where to send bug reports to. + ------------------------------------------------------------------ */ + +#define VG_BUGS_TO "valgrind.kde.org" + + +/*====================================================================*/ +/*=== Build options and table sizes. ===*/ +/*====================================================================*/ + +/* You should be able to change these options or sizes, recompile, and + still have a working system. */ + +/* The maximum number of pthreads that we support. This is + deliberately not very high since our implementation of some of the + scheduler algorithms is surely O(N) in the number of threads, since + that's simple, at least. And (in practice) we hope that most + programs do not need many threads. */ +#define VG_N_THREADS 100 + +/* Maximum number of pthread keys available. Again, we start low until + the need for a higher number presents itself. */ +#define VG_N_THREAD_KEYS 50 + + +/*====================================================================*/ +/*=== Useful macros ===*/ +/*====================================================================*/ + +#define mycat_wrk(aaa,bbb) aaa##bbb +#define mycat(aaa,bbb) mycat_wrk(aaa,bbb) + +/* No, really. I _am_ that strange. */ +#define OINK(nnn) VG_(message)(Vg_DebugMsg, "OINK %d",nnn) + +/* Path to all our library/aux files */ +extern const Char *VG_(libdir); + + +/*====================================================================*/ +/*=== Core/tool interface version ===*/ +/*====================================================================*/ + +/* The major version number indicates binary-incompatible changes to the + interface; if the core and tool major versions don't match, Valgrind + will abort. The minor version indicates binary-compatible changes. +*/ +#define VG_CORE_INTERFACE_MAJOR_VERSION 6 +#define VG_CORE_INTERFACE_MINOR_VERSION 0 + +typedef struct _ToolInfo { + Int sizeof_ToolInfo; + Int interface_major_version; + Int interface_minor_version; + + /* Initialise tool. Must do the following: + - initialise the `details' struct, via the VG_(details_*)() functions + - register any helpers called by generated code + + May do the following: + - initialise the `needs' struct to indicate certain requirements, via + the VG_(needs_*)() functions + - initialize all the tool's entrypoints via the VG_(init_*)() functions + - register any tool-specific profiling events + - any other tool-specific initialisation + */ + void (*sk_pre_clo_init) ( void ); + + /* Specifies how big the shadow segment should be as a ratio to the + client address space. 0 for no shadow segment. */ + float shadow_ratio; +} ToolInfo; + +/* Every tool must include this macro somewhere, exactly once. */ +#define VG_DETERMINE_INTERFACE_VERSION(pre_clo_init, shadow) \ + const ToolInfo SK_(tool_info) = { \ + .sizeof_ToolInfo = sizeof(ToolInfo), \ + .interface_major_version = VG_CORE_INTERFACE_MAJOR_VERSION, \ + .interface_minor_version = VG_CORE_INTERFACE_MINOR_VERSION, \ + .sk_pre_clo_init = pre_clo_init, \ + .shadow_ratio = shadow, \ + }; + +/*====================================================================*/ +/*=== Command-line options ===*/ +/*====================================================================*/ + +/* Use this for normal null-termination-style string comparison */ +#define VG_STREQ(s1,s2) (s1 != NULL && s2 != NULL \ + && VG_(strcmp)((s1),(s2))==0) + +/* Use these for recognising tool command line options -- stops comparing + once whitespace is reached. */ +#define VG_CLO_STREQ(s1,s2) (0==VG_(strcmp_ws)((s1),(s2))) +#define VG_CLO_STREQN(nn,s1,s2) (0==VG_(strncmp_ws)((s1),(s2),(nn))) + +// Higher-level command-line option recognisers; use in if/else chains + +#define VG_BOOL_CLO(qq_option, qq_var) \ + if (VG_CLO_STREQ(arg, qq_option"=yes")) { (qq_var) = True; } \ + else if (VG_CLO_STREQ(arg, qq_option"=no")) { (qq_var) = False; } + +#define VG_STR_CLO(qq_option, qq_var) \ + if (VG_CLO_STREQN(VG_(strlen)(qq_option)+1, arg, qq_option"=")) { \ + (qq_var) = &arg[ VG_(strlen)(qq_option)+1 ]; \ + } + +#define VG_NUM_CLO(qq_option, qq_var) \ + if (VG_CLO_STREQN(VG_(strlen)(qq_option)+1, arg, qq_option"=")) { \ + (qq_var) = (Int)VG_(atoll)( &arg[ VG_(strlen)(qq_option)+1 ] ); \ + } + +// Bounded integer arg +#define VG_BNUM_CLO(qq_option, qq_var, qq_lo, qq_hi) \ + if (VG_CLO_STREQN(VG_(strlen)(qq_option)+1, arg, qq_option"=")) { \ + (qq_var) = (Int)VG_(atoll)( &arg[ VG_(strlen)(qq_option)+1 ] ); \ + if ((qq_var) < (qq_lo)) (qq_var) = (qq_lo); \ + if ((qq_var) > (qq_hi)) (qq_var) = (qq_hi); \ + } + + +/* Verbosity level: 0 = silent, 1 (default), > 1 = more verbose. */ +extern Int VG_(clo_verbosity); + +/* Profile? */ +extern Bool VG_(clo_profile); + +/* Call this if a recognised option was bad for some reason. + Note: don't use it just because an option was unrecognised -- return 'False' + from SKN_(process_cmd_line_option) to indicate that. */ +extern void VG_(bad_option) ( Char* opt ); + +/* Client args */ +extern Int VG_(client_argc); +extern Char** VG_(client_argv); + +/* Client environment. Can be inspected with VG_(getenv)() */ +extern Char** VG_(client_envp); + + +/*====================================================================*/ +/*=== Printing messages for the user ===*/ +/*====================================================================*/ + +/* Print a message prefixed by "???? "; '?' depends on the VgMsgKind. + Should be used for all user output. */ + +typedef + enum { Vg_UserMsg, /* '?' == '=' */ + Vg_DebugMsg, /* '?' == '-' */ + Vg_DebugExtraMsg, /* '?' == '+' */ + Vg_ClientMsg, /* '?' == '*' */ + } + VgMsgKind; + +/* Functions for building a message from multiple parts. */ +extern int VG_(start_msg) ( VgMsgKind kind ); +extern int VG_(add_to_msg) ( Char* format, ... ); +/* Ends and prints the message. Appends a newline. */ +extern int VG_(end_msg) ( void ); + +/* Send a single-part message. Appends a newline. */ +extern int VG_(message) ( VgMsgKind kind, Char* format, ... ); +extern int VG_(vmessage) ( VgMsgKind kind, Char* format, va_list vargs ); + + +/*====================================================================*/ +/*=== Profiling ===*/ +/*====================================================================*/ + +/* Nb: VGP_(register_profile_event)() relies on VgpUnc being the first one */ +#define VGP_CORE_LIST \ + /* These ones depend on the core */ \ + VGP_PAIR(VgpUnc, "unclassified"), \ + VGP_PAIR(VgpStartup, "startup"), \ + VGP_PAIR(VgpRun, "running"), \ + VGP_PAIR(VgpSched, "scheduler"), \ + VGP_PAIR(VgpMalloc, "low-lev malloc/free"), \ + VGP_PAIR(VgpCliMalloc, "client malloc/free"), \ + VGP_PAIR(VgpTranslate, "translate-main"), \ + VGP_PAIR(VgpToUCode, "to-ucode"), \ + VGP_PAIR(VgpFromUcode, "from-ucode"), \ + VGP_PAIR(VgpImprove, "improve"), \ + VGP_PAIR(VgpESPUpdate, "ESP-update"), \ + VGP_PAIR(VgpRegAlloc, "reg-alloc"), \ + VGP_PAIR(VgpLiveness, "liveness-analysis"), \ + VGP_PAIR(VgpDoLRU, "do-lru"), \ + VGP_PAIR(VgpSlowFindT, "slow-search-transtab"), \ + VGP_PAIR(VgpExeContext, "exe-context"), \ + VGP_PAIR(VgpReadSyms, "read-syms"), \ + VGP_PAIR(VgpSearchSyms, "search-syms"), \ + VGP_PAIR(VgpAddToT, "add-to-transtab"), \ + VGP_PAIR(VgpCoreSysWrap, "core-syscall-wrapper"), \ + VGP_PAIR(VgpDemangle, "demangle"), \ + VGP_PAIR(VgpCoreCheapSanity, "core-cheap-sanity"), \ + VGP_PAIR(VgpCoreExpensiveSanity, "core-expensive-sanity"), \ + /* These ones depend on the tool */ \ + VGP_PAIR(VgpPreCloInit, "pre-clo-init"), \ + VGP_PAIR(VgpPostCloInit, "post-clo-init"), \ + VGP_PAIR(VgpInstrument, "instrument"), \ + VGP_PAIR(VgpSkinSysWrap, "tool-syscall-wrapper"), \ + VGP_PAIR(VgpSkinCheapSanity, "tool-cheap-sanity"), \ + VGP_PAIR(VgpSkinExpensiveSanity, "tool-expensive-sanity"), \ + VGP_PAIR(VgpFini, "fini") + +#define VGP_PAIR(n,name) n +typedef enum { VGP_CORE_LIST } VgpCoreCC; +#undef VGP_PAIR + +/* When registering tool profiling events, ensure that the 'n' value is in + * the range (VgpFini+1..) */ +extern void VGP_(register_profile_event) ( Int n, Char* name ); + +extern void VGP_(pushcc) ( UInt cc ); +extern void VGP_(popcc) ( UInt cc ); + +/* Define them only if they haven't already been defined by vg_profile.c */ +#ifndef VGP_PUSHCC +# define VGP_PUSHCC(x) +#endif +#ifndef VGP_POPCC +# define VGP_POPCC(x) +#endif + + +/*====================================================================*/ +/*=== Useful stuff to call from generated code ===*/ +/*====================================================================*/ + +/* ------------------------------------------------------------------ */ +/* General stuff */ + +/* 64-bit counter for the number of basic blocks done. */ +extern ULong VG_(bbs_done); + +/* Get the simulated %esp */ +extern Addr VG_(get_stack_pointer) ( void ); + +/* Check if an address is 4-byte aligned */ +#define IS_ALIGNED4_ADDR(aaa_p) (0 == (((UInt)(aaa_p)) & 3)) +#define IS_ALIGNED8_ADDR(aaa_p) (0 == (((UInt)(aaa_p)) & 7)) + + +/* ------------------------------------------------------------------ */ +/* Thread-related stuff */ + +/* Special magic value for an invalid ThreadId. It corresponds to + LinuxThreads using zero as the initial value for + pthread_mutex_t.__m_owner and pthread_cond_t.__c_waiting. */ +#define VG_INVALID_THREADID ((ThreadId)(0)) + +/* ThreadIds are simply indices into the VG_(threads)[] array. */ +typedef + UInt + ThreadId; + +/* When looking for the current ThreadId, this is the safe option and + probably the one you want. + + Details: Use this one from non-generated code, eg. from functions called + on events like 'new_mem_heap'. In such a case, the "current" thread is + temporarily suspended as Valgrind's dispatcher is running. This function + is also suitable to be called from generated code (ie. from UCode, or a C + function called directly from UCode). + + If you use VG_(get_current_tid)() from non-generated code, it will return + 0 signifying the invalid thread, which is probably not what you want. */ +extern ThreadId VG_(get_current_or_recent_tid) ( void ); + +/* When looking for the current ThreadId, only use this one if you know what + you are doing. + + Details: Use this one from generated code, eg. from C functions called + from UCode. (VG_(get_current_or_recent_tid)() is also suitable in that + case.) If you use this function from non-generated code, it will return + 0 signifying the invalid thread, which is probably not what you want. */ +extern ThreadId VG_(get_current_tid) ( void ); + +/* Searches through all thread's stacks to see if any match. Returns + VG_INVALID_THREADID if none match. */ +extern ThreadId VG_(first_matching_thread_stack) + ( Bool (*p) ( Addr stack_min, Addr stack_max, void* d ), + void* d ); + + +/*====================================================================*/ +/*=== Valgrind's version of libc ===*/ +/*====================================================================*/ + +/* Valgrind doesn't use libc at all, for good reasons (trust us). So here + are its own versions of C library functions, but with VG_ prefixes. Note + that the types of some are slightly different to the real ones. Some + additional useful functions are provided too; descriptions of how they + work are given below. */ + +#if !defined(NULL) +# define NULL ((void*)0) +#endif + + +/* ------------------------------------------------------------------ */ +/* stdio.h + * + * Note that they all output to the file descriptor given by the + * --log-fd/--log-file/--log-socket argument, which defaults to 2 (stderr). + * Hence no need for VG_(fprintf)(). + */ +extern UInt VG_(printf) ( const char *format, ... ); +/* too noisy ... __attribute__ ((format (printf, 1, 2))) ; */ +extern UInt VG_(sprintf) ( Char* buf, Char *format, ... ); +extern UInt VG_(vprintf) ( void(*send)(Char), + const Char *format, va_list vargs ); + +extern Int VG_(rename) ( Char* old_name, Char* new_name ); + +/* ------------------------------------------------------------------ */ +/* stdlib.h */ + +extern void* VG_(malloc) ( Int nbytes ); +extern void VG_(free) ( void* p ); +extern void* VG_(calloc) ( Int n, Int nbytes ); +extern void* VG_(realloc) ( void* p, Int size ); +extern void* VG_(malloc_aligned) ( Int align_bytes, Int nbytes ); + +extern void VG_(print_malloc_stats) ( void ); + + +extern void VG_(exit)( Int status ) + __attribute__ ((__noreturn__)); +/* Prints a panic message (a constant string), appends newline and bug + reporting info, aborts. */ +__attribute__ ((__noreturn__)) +extern void VG_(skin_panic) ( Char* str ); + +/* Looks up VG_(client_envp) */ +extern Char* VG_(getenv) ( Char* name ); + +/* Get client resource limit*/ +extern Int VG_(getrlimit) ( Int resource, struct vki_rlimit *rlim ); + +/* Set client resource limit*/ +extern Int VG_(setrlimit) ( Int resource, struct vki_rlimit *rlim ); + +/* Crude stand-in for the glibc system() call. */ +extern Int VG_(system) ( Char* cmd ); + +extern Long VG_(atoll) ( Char* str ); + +/* Like atoll(), but converts a number of base 16 */ +extern Long VG_(atoll16) ( Char* str ); + +/* Like atoll(), but converts a number of base 2..36 */ +extern Long VG_(atoll36) ( UInt base, Char* str ); + +/* Like qsort(), but does shell-sort. The size==1/2/4 cases are specialised. */ +extern void VG_(ssort)( void* base, UInt nmemb, UInt size, + Int (*compar)(void*, void*) ); + + +/* ------------------------------------------------------------------ */ +/* ctype.h */ +extern Bool VG_(isspace) ( Char c ); +extern Bool VG_(isdigit) ( Char c ); +extern Char VG_(toupper) ( Char c ); + + +/* ------------------------------------------------------------------ */ +/* string.h */ +extern Int VG_(strlen) ( const Char* str ); +extern Char* VG_(strcat) ( Char* dest, const Char* src ); +extern Char* VG_(strncat) ( Char* dest, const Char* src, Int n ); +extern Char* VG_(strpbrk) ( const Char* s, const Char* accept ); +extern Char* VG_(strcpy) ( Char* dest, const Char* src ); +extern Char* VG_(strncpy) ( Char* dest, const Char* src, Int ndest ); +extern Int VG_(strcmp) ( const Char* s1, const Char* s2 ); +extern Int VG_(strncmp) ( const Char* s1, const Char* s2, Int nmax ); +extern Char* VG_(strstr) ( const Char* haystack, Char* needle ); +extern Char* VG_(strchr) ( const Char* s, Char c ); +extern Char* VG_(strrchr) ( const Char* s, Char c ); +extern Char* VG_(strdup) ( const Char* s); +extern void* VG_(memcpy) ( void *d, const void *s, Int sz ); +extern void* VG_(memset) ( void *s, Int c, Int sz ); +extern Int VG_(memcmp) ( const void* s1, const void* s2, Int n ); + +/* Like strcmp() and strncmp(), but stop comparing at any whitespace. */ +extern Int VG_(strcmp_ws) ( const Char* s1, const Char* s2 ); +extern Int VG_(strncmp_ws) ( const Char* s1, const Char* s2, Int nmax ); + +/* Like strncpy(), but if 'src' is longer than 'ndest' inserts a '\0' as the + last character. */ +extern void VG_(strncpy_safely) ( Char* dest, const Char* src, Int ndest ); + +/* Mini-regexp function. Searches for 'pat' in 'str'. Supports + * meta-symbols '*' and '?'. '\' escapes meta-symbols. */ +extern Bool VG_(string_match) ( const Char* pat, const Char* str ); + + +/* ------------------------------------------------------------------ */ +/* math.h */ +/* Returns the base-2 logarithm of x. */ +extern Int VG_(log2) ( Int x ); + + +/* ------------------------------------------------------------------ */ +/* unistd.h, fcntl.h, sys/stat.h */ +extern Int VG_(getdents)( UInt fd, struct vki_dirent *dirp, UInt count ); +extern Int VG_(readlink)( Char* path, Char* buf, UInt bufsize ); +extern Int VG_(getpid) ( void ); +extern Int VG_(getppid) ( void ); +extern Int VG_(getpgrp) ( void ); +extern Int VG_(gettid) ( void ); +extern Int VG_(setpgid) ( Int pid, Int pgrp ); + +extern Int VG_(open) ( const Char* pathname, Int flags, Int mode ); +extern Int VG_(read) ( Int fd, void* buf, Int count); +extern Int VG_(write) ( Int fd, const void* buf, Int count); +extern Int VG_(lseek) ( Int fd, Long offset, Int whence); +extern void VG_(close) ( Int fd ); + +extern Int VG_(pipe) ( Int fd[2] ); + +/* Nb: VG_(rename)() declared in stdio.h section above */ +extern Int VG_(unlink) ( Char* file_name ); +extern Int VG_(stat) ( Char* file_name, struct vki_stat* buf ); +extern Int VG_(fstat) ( Int fd, struct vki_stat* buf ); +extern Int VG_(dup2) ( Int oldfd, Int newfd ); + +extern Char* VG_(getcwd) ( Char* buf, Int size ); + +/* Easier to use than VG_(getcwd)() -- does the buffer fiddling itself. + String put into 'cwd' is VG_(malloc)'d, and should be VG_(free)'d. + Returns False if it fails. Will fail if the pathname is > 65535 bytes. */ +extern Bool VG_(getcwd_alloc) ( Char** cwd ); + +/* ------------------------------------------------------------------ */ +/* assert.h */ +/* Asserts permanently enabled -- no turning off with NDEBUG. Hurrah! */ +#define VG__STRING(__str) #__str + +#define sk_assert(expr) \ + ((void) ((expr) ? 0 : \ + (VG_(skin_assert_fail) (VG__STRING(expr), \ + __FILE__, __LINE__, \ + __PRETTY_FUNCTION__), 0))) + +__attribute__ ((__noreturn__)) +extern void VG_(skin_assert_fail) ( const Char* expr, const Char* file, + Int line, const Char* fn ); + + +/* ------------------------------------------------------------------ */ +/* Get memory by anonymous mmap. */ +extern void* VG_(get_memory_from_mmap) ( Int nBytes, Char* who ); + +extern Bool VG_(is_client_addr) (Addr a); +extern Addr VG_(get_client_base)(void); +extern Addr VG_(get_client_end) (void); +extern Addr VG_(get_client_size)(void); + +extern Bool VG_(is_shadow_addr) (Addr a); +extern Addr VG_(get_shadow_base)(void); +extern Addr VG_(get_shadow_end) (void); +extern Addr VG_(get_shadow_size)(void); + +extern void *VG_(shadow_alloc)(UInt size); + +extern Bool VG_(is_addressable)(Addr p, Int sz); + +extern Addr VG_(client_alloc)(Addr base, UInt len, UInt prot, UInt flags); +extern void VG_(client_free)(Addr addr); + +extern Bool VG_(is_valgrind_addr)(Addr a); + +/* initialize shadow pages in the range [p, p+sz) This calls + init_shadow_page for each one. It should be a lot more efficient + for bulk-initializing shadow pages than faulting on each one. +*/ +extern void VG_(init_shadow_range)(Addr p, UInt sz, Bool call_init); + +/* ------------------------------------------------------------------ */ +/* signal.h. + + Note that these use the vk_ (kernel) structure + definitions, which are different in places from those that glibc + defines -- hence the 'k' prefix. Since we're operating right at the + kernel interface, glibc's view of the world is entirely irrelevant. */ + +/* --- Signal set ops --- */ +extern Int VG_(ksigfillset) ( vki_ksigset_t* set ); +extern Int VG_(ksigemptyset) ( vki_ksigset_t* set ); + +extern Bool VG_(kisfullsigset) ( vki_ksigset_t* set ); +extern Bool VG_(kisemptysigset) ( vki_ksigset_t* set ); + +extern Int VG_(ksigaddset) ( vki_ksigset_t* set, Int signum ); +extern Int VG_(ksigdelset) ( vki_ksigset_t* set, Int signum ); +extern Int VG_(ksigismember) ( vki_ksigset_t* set, Int signum ); + +extern void VG_(ksigaddset_from_set) ( vki_ksigset_t* dst, vki_ksigset_t* src ); +extern void VG_(ksigdelset_from_set) ( vki_ksigset_t* dst, vki_ksigset_t* src ); + +/* --- Mess with the kernel's sig state --- */ +extern Int VG_(ksigprocmask) ( Int how, const vki_ksigset_t* set, + vki_ksigset_t* oldset ); +extern Int VG_(ksigaction) ( Int signum, + const vki_ksigaction* act, + vki_ksigaction* oldact ); + +extern Int VG_(ksigtimedwait)( const vki_ksigset_t *, vki_ksiginfo_t *, + const struct vki_timespec * ); + +extern Int VG_(ksignal) ( Int signum, void (*sighandler)(Int) ); +extern Int VG_(ksigaltstack) ( const vki_kstack_t* ss, vki_kstack_t* oss ); + +extern Int VG_(kkill) ( Int pid, Int signo ); +extern Int VG_(ktkill) ( Int pid, Int signo ); +extern Int VG_(ksigpending) ( vki_ksigset_t* set ); + +extern Int VG_(waitpid) ( Int pid, Int *status, Int options ); + +/* ------------------------------------------------------------------ */ +/* socket.h. */ + +extern Int VG_(getsockname) ( Int sd, struct vki_sockaddr *name, Int *namelen); +extern Int VG_(getpeername) ( Int sd, struct vki_sockaddr *name, Int *namelen); +extern Int VG_(getsockopt) ( Int sd, Int level, Int optname, void *optval, + Int *optlen); + +/* ------------------------------------------------------------------ */ +/* other, randomly useful functions */ +extern UInt VG_(read_millisecond_timer) ( void ); + +extern void VG_(cpuid) ( UInt eax, + UInt *eax_ret, UInt *ebx_ret, + UInt *ecx_ret, UInt *edx_ret ); + +/*====================================================================*/ +/*=== UCode definition ===*/ +/*====================================================================*/ + +/* Tags which describe what operands are. Must fit into 4 bits, which + they clearly do. */ +typedef +enum { TempReg =0, /* virtual temp-reg */ + ArchReg =1, /* simulated integer reg */ + ArchRegS =2, /* simulated segment reg */ + RealReg =3, /* real machine's real reg */ + SpillNo =4, /* spill slot location */ + Literal =5, /* literal; .lit32 field has actual value */ + Lit16 =6, /* literal; .val[123] field has actual value */ + NoValue =7 /* operand not in use */ + } + Tag; + +/* Invalid register numbers (can't be negative) */ +#define INVALID_TEMPREG 999999999 +#define INVALID_REALREG 999999999 + +/* Microinstruction opcodes. */ +typedef + enum { + NOP, /* Null op */ + + LOCK, /* Indicate the existence of a LOCK prefix (functionally NOP) */ + + /* Moving values around */ + GET, PUT, /* simulated register <--> TempReg */ + GETF, PUTF, /* simulated %eflags <--> TempReg */ + LOAD, STORE, /* memory <--> TempReg */ + MOV, /* TempReg <--> TempReg */ + CMOV, /* Used for cmpxchg and cmov */ + + /* Arithmetic/logical ops */ + MUL, UMUL, /* Multiply */ + ADD, ADC, SUB, SBB, /* Add/subtract (w/wo carry) */ + AND, OR, XOR, NOT, /* Boolean ops */ + SHL, SHR, SAR, ROL, ROR, RCL, RCR, /* Shift/rotate (w/wo carry) */ + NEG, /* Negate */ + INC, DEC, /* Increment/decrement */ + BSWAP, /* Big-endian <--> little-endian */ + CC2VAL, /* Condition code --> 0 or 1 */ + WIDEN, /* Signed or unsigned widening */ + + /* Conditional or unconditional jump */ + JMP, + + /* FPU ops */ + FPU, /* Doesn't touch memory */ + FPU_R, FPU_W, /* Reads/writes memory */ + + /* ------------ MMX ops ------------ */ + /* In this and the SSE encoding, bytes at higher addresses are + held in bits [7:0] in these 16-bit words. I guess this means + it is a big-endian encoding. */ + + /* 1 byte, no memrefs, no iregdefs, copy exactly to the + output. Held in val1[7:0]. */ + MMX1, + + /* 2 bytes, no memrefs, no iregdefs, copy exactly to the + output. Held in val1[15:0]. */ + MMX2, + + /* 3 bytes, no memrefs, no iregdefs, copy exactly to the + output. Held in val1[15:0] and val2[7:0]. */ + MMX3, + + /* 2 bytes, reads/writes mem. Insns of the form + bbbbbbbb:mod mmxreg r/m. + Held in val1[15:0], and mod and rm are to be replaced + at codegen time by a reference to the Temp/RealReg holding + the address. Arg2 holds this Temp/Real Reg. + Transfer is always at size 8. + */ + MMX2_MemRd, + MMX2_MemWr, + + /* 3 bytes, reads/writes mem. Insns of the form + bbbbbbbb:mod mmxreg r/m:bbbbbbbb + Held in val1[15:0] and val2[7:0], and mod and rm are to be + replaced at codegen time by a reference to the Temp/RealReg + holding the address. Arg2 holds this Temp/Real Reg. + Transfer is always at size 8. + */ + MMX2a1_MemRd, + + /* 2 bytes, reads/writes an integer ("E") register. Insns of the form + bbbbbbbb:11 mmxreg ireg. + Held in val1[15:0], and ireg is to be replaced + at codegen time by a reference to the relevant RealReg. + Transfer is always at size 4. Arg2 holds this Temp/Real Reg. + */ + MMX2_ERegRd, + MMX2_ERegWr, + + /* ------------ SSE/SSE2 ops ------------ */ + /* In the following: + + a digit N indicates the next N bytes are to be copied exactly + to the output. + + 'a' indicates a mod-xmmreg-rm byte, where the mod-rm part is + to be replaced at codegen time to a Temp/RealReg holding the + address. + + 'e' indicates a byte of the form '11 xmmreg ireg', where ireg + is read or written, and is to be replaced at codegen time by + a reference to the relevant RealReg. 'e' because it's the E + reg in Intel encoding parlance. + + 'g' indicates a byte of the form '11 ireg xmmreg', where ireg + is read or written, and is to be replaced at codegen time by + a reference to the relevant RealReg. 'g' because it's called + G in Intel parlance. */ + + /* 3 bytes, no memrefs, no iregdefs, copy exactly to the + output. Held in val1[15:0] and val2[7:0]. */ + SSE3, + + /* 3 bytes, reads/writes mem. Insns of the form + bbbbbbbb:bbbbbbbb:mod mmxreg r/m. + Held in val1[15:0] and val2[7:0], and mod and rm are to be + replaced at codegen time by a reference to the Temp/RealReg + holding the address. Arg3 holds this Temp/Real Reg. + Transfer is usually, but not always, at size 16. */ + SSE2a_MemRd, + SSE2a_MemWr, + + /* 4 bytes, writes an integer register. Insns of the form + bbbbbbbb:bbbbbbbb:11 ireg bbb. + Held in val1[15:0] and val2[7:0], and ireg is to be replaced + at codegen time by a reference to the relevant RealReg. + Transfer is always at size 4. Arg3 holds this Temp/Real Reg. + */ + SSE2g_RegWr, + + /* 5 bytes, writes an integer register. Insns of the form + bbbbbbbb:bbbbbbbb:11 ireg bbb :bbbbbbbb. Held in + val1[15:0] and val2[7:0] and lit32[7:0], and ireg is to be + replaced at codegen time by a reference to the relevant + RealReg. Transfer is always at size 4. Arg3 holds this + Temp/Real Reg. + */ + SSE2g1_RegWr, + + /* 5 bytes, reads an integer register. Insns of the form + bbbbbbbb:bbbbbbbb:11 bbb ireg :bbbbbbbb. Held in + val1[15:0] and val2[7:0] and lit32[7:0], and ireg is to be + replaced at codegen time by a reference to the relevant + RealReg. Transfer is always at size 4. Arg3 holds this + Temp/Real Reg. + */ + SSE2e1_RegRd, + + /* 4 bytes, no memrefs, no iregdefs, copy exactly to the + output. Held in val1[15:0] and val2[15:0]. */ + SSE4, + + /* 4 bytes, reads/writes mem. Insns of the form + bbbbbbbb:bbbbbbbb:bbbbbbbb:mod mmxreg r/m. + Held in val1[15:0] and val2[15:0], and mod and rm are to be + replaced at codegen time by a reference to the Temp/RealReg + holding the address. Arg3 holds this Temp/Real Reg. + Transfer is at stated size. */ + SSE3a_MemRd, + SSE3a_MemWr, + + /* 4 bytes, reads/writes mem. Insns of the form + bbbbbbbb:bbbbbbbb:mod mmxreg r/m:bbbbbbbb + Held in val1[15:0] and val2[15:0], and mod and rm are to be + replaced at codegen time by a reference to the Temp/RealReg + holding the address. Arg3 holds this Temp/Real Reg. + Transfer is at stated size. */ + SSE2a1_MemRd, + + /* 4 bytes, writes an integer register. Insns of the form + bbbbbbbb:bbbbbbbb:bbbbbbbb:11 ireg bbb. + Held in val1[15:0] and val2[15:0], and ireg is to be replaced + at codegen time by a reference to the relevant RealReg. + Transfer is always at size 4. Arg3 holds this Temp/Real Reg. + */ + SSE3g_RegWr, + + /* 5 bytes, writes an integer register. Insns of the form + bbbbbbbb:bbbbbbbb:bbbbbbbb: 11 ireg bbb :bbbbbbbb. Held in + val1[15:0] and val2[15:0] and lit32[7:0], and ireg is to be + replaced at codegen time by a reference to the relevant + RealReg. Transfer is always at size 4. Arg3 holds this + Temp/Real Reg. + */ + SSE3g1_RegWr, + + /* 4 bytes, reads an integer register. Insns of the form + bbbbbbbb:bbbbbbbb:bbbbbbbb:11 bbb ireg. + Held in val1[15:0] and val2[15:0], and ireg is to be replaced + at codegen time by a reference to the relevant RealReg. + Transfer is always at size 4. Arg3 holds this Temp/Real Reg. + */ + SSE3e_RegRd, + SSE3e_RegWr, /* variant that writes Ereg, not reads it */ + + /* 5 bytes, reads an integer register. Insns of the form + bbbbbbbb:bbbbbbbb:bbbbbbbb: 11 bbb ireg :bbbbbbbb. Held in + val1[15:0] and val2[15:0] and lit32[7:0], and ireg is to be + replaced at codegen time by a reference to the relevant + RealReg. Transfer is always at size 4. Arg3 holds this + Temp/Real Reg. + */ + SSE3e1_RegRd, + + /* 4 bytes, reads memory, writes an integer register, but is + nevertheless an SSE insn. The insn is of the form + bbbbbbbb:bbbbbbbb:bbbbbbbb:mod ireg rm where mod indicates + memory (ie is not 11b) and ireg is the int reg written. The + first 4 bytes are held in lit32[31:0] since there is + insufficient space elsewhere. mod and rm are to be replaced + at codegen time by a reference to the Temp/RealReg holding + the address. Arg1 holds this Temp/RealReg. ireg is to be + replaced at codegen time by a reference to the relevant + RealReg in which the answer is to be written. Arg2 holds + this Temp/RealReg. Transfer to the destination reg is always + at size 4. However the memory read can be at sizes 4 or 8 + and so this is what the sz field holds. Note that the 4th + byte of the instruction (the modrm byte) is redundant, but we + store it anyway so as to be consistent with all other SSE + uinstrs. + */ + SSE3ag_MemRd_RegWr, + + /* 5 bytes, no memrefs, no iregdefs, copy exactly to the + output. Held in val1[15:0], val2[15:0] and val3[7:0]. */ + SSE5, + + /* 5 bytes, reads/writes mem. Insns of the form + bbbbbbbb:bbbbbbbb:bbbbbbbb:mod mmxreg r/m:bbbbbbbb + Held in val1[15:0], val2[15:0], lit32[7:0]. + mod and rm are to be replaced at codegen time by a reference + to the Temp/RealReg holding the address. Arg3 holds this + Temp/Real Reg. Transfer is always at size 16. */ + SSE3a1_MemRd, + + /* ------------------------ */ + + /* Not strictly needed, but improve address calculation translations. */ + LEA1, /* reg2 := const + reg1 */ + LEA2, /* reg3 := const + reg1 + reg2 * 1,2,4 or 8 */ + + /* Hack for x86 REP insns. Jump to literal if TempReg/RealReg + is zero. */ + JIFZ, + + /* Advance the simulated %eip by some small (< 128) number. */ + INCEIP, + + /* Dealing with segment registers */ + GETSEG, PUTSEG, /* simulated segment register <--> TempReg */ + USESEG, /* (LDT/GDT index, virtual addr) --> linear addr */ + + /* Not for translating x86 calls -- only to call helpers */ + CALLM_S, CALLM_E, /* Mark start/end of CALLM push/pop sequence */ + PUSH, POP, CLEAR, /* Add/remove/zap args for helpers */ + CALLM, /* Call assembly-code helper */ + + /* Not for translating x86 calls -- only to call C helper functions of + up to three arguments (or two if the functions has a return value). + Arguments and return value must be word-sized. More arguments can + be faked with global variables (eg. use VG_(lit_to_globvar)()). + + Seven possibilities: 'arg[123]' show where args go, 'ret' shows + where return value goes (if present). + + CCALL(-, -, - ) void f(void) + CCALL(arg1, -, - ) void f(UInt arg1) + CCALL(arg1, arg2, - ) void f(UInt arg1, UInt arg2) + CCALL(arg1, arg2, arg3) void f(UInt arg1, UInt arg2, UInt arg3) + CCALL(-, -, ret ) UInt f(UInt) + CCALL(arg1, -, ret ) UInt f(UInt arg1) + CCALL(arg1, arg2, ret ) UInt f(UInt arg1, UInt arg2) */ + CCALL, + + /* This opcode makes it easy for tools that extend UCode to do this to + avoid opcode overlap: + + enum { EU_OP1 = DUMMY_FINAL_UOPCODE + 1, ... } + + WARNING: Do not add new opcodes after this one! They can be added + before, though. */ + DUMMY_FINAL_UOPCODE + } + Opcode; + + +/* Condition codes, using the Intel encoding. CondAlways is an extra. */ +typedef + enum { + CondO = 0, /* overflow */ + CondNO = 1, /* no overflow */ + CondB = 2, /* below */ + CondNB = 3, /* not below */ + CondZ = 4, /* zero */ + CondNZ = 5, /* not zero */ + CondBE = 6, /* below or equal */ + CondNBE = 7, /* not below or equal */ + CondS = 8, /* negative */ + CondNS = 9, /* not negative */ + CondP = 10, /* parity even */ + CondNP = 11, /* not parity even */ + CondL = 12, /* jump less */ + CondNL = 13, /* not less */ + CondLE = 14, /* less or equal */ + CondNLE = 15, /* not less or equal */ + CondAlways = 16 /* Jump always */ + } + Condcode; + + +/* Descriptions of additional properties of *unconditional* jumps. */ +typedef + enum { + JmpBoring=0, /* boring unconditional jump */ + JmpCall=1, /* jump due to an x86 call insn */ + JmpRet=2, /* jump due to an x86 ret insn */ + JmpSyscall=3, /* do a system call, then jump */ + JmpClientReq=4,/* do a client request, then jump */ + JmpYield=5 /* do a yield, then jump */ + } + JmpKind; + + +/* Flags. User-level code can only read/write O(verflow), S(ign), + Z(ero), A(ux-carry), C(arry), P(arity), and may also write + D(irection). That's a total of 7 flags. A FlagSet is a bitset, + thusly: + 76543210 + DOSZACP + and bit 7 must always be zero since it is unused. + + Note: these Flag? values are **not** the positions in the actual + %eflags register. */ + +typedef UChar FlagSet; + +#define FlagD (1<<6) +#define FlagO (1<<5) +#define FlagS (1<<4) +#define FlagZ (1<<3) +#define FlagA (1<<2) +#define FlagC (1<<1) +#define FlagP (1<<0) + +#define FlagsOSZACP (FlagO | FlagS | FlagZ | FlagA | FlagC | FlagP) +#define FlagsOSZAP (FlagO | FlagS | FlagZ | FlagA | FlagP) +#define FlagsOSZCP (FlagO | FlagS | FlagZ | FlagC | FlagP) +#define FlagsOSACP (FlagO | FlagS | FlagA | FlagC | FlagP) +#define FlagsSZACP ( FlagS | FlagZ | FlagA | FlagC | FlagP) +#define FlagsSZAP ( FlagS | FlagZ | FlagA | FlagP) +#define FlagsSZP ( FlagS | FlagZ | FlagP) +#define FlagsZCP ( FlagZ | FlagC | FlagP) +#define FlagsOC (FlagO | FlagC ) +#define FlagsAC ( FlagA | FlagC ) + +#define FlagsALL (FlagsOSZACP | FlagD) +#define FlagsEmpty (FlagSet)0 + + +/* flag positions in eflags */ +#define EFlagC (1 << 0) /* carry */ +#define EFlagP (1 << 2) /* parity */ +#define EFlagA (1 << 4) /* aux carry */ +#define EFlagZ (1 << 6) /* zero */ +#define EFlagS (1 << 7) /* sign */ +#define EFlagD (1 << 10) /* direction */ +#define EFlagO (1 << 11) /* overflow */ +#define EFlagID (1 << 21) /* changable if CPUID exists */ + +/* Liveness of general purpose registers, useful for code generation. + Reg rank order 0..N-1 corresponds to bits 0..N-1, ie. first + reg's liveness in bit 0, last reg's in bit N-1. Note that + these rankings don't match the Intel register ordering. */ +typedef UInt RRegSet; + +#define ALL_RREGS_DEAD 0 /* 0000...00b */ +#define ALL_RREGS_LIVE ((1 << VG_MAX_REALREGS)-1) /* 0011...11b */ +#define UNIT_RREGSET(rank) (1 << (rank)) + +#define IS_RREG_LIVE(rank,rregs_live) (rregs_live & UNIT_RREGSET(rank)) +#define SET_RREG_LIVENESS(rank,rregs_live,b) \ + do { RRegSet unit = UNIT_RREGSET(rank); \ + if (b) rregs_live |= unit; \ + else rregs_live &= ~unit; \ + } while(0) + + +/* A Micro (u)-instruction. */ +typedef + struct { + /* word 1 */ + UInt lit32; /* 32-bit literal */ + + /* word 2 */ + UShort val1; /* first operand */ + UShort val2; /* second operand */ + + /* word 3 */ + UShort val3; /* third operand */ + UChar opcode; /* opcode */ + UShort size; /* data transfer size */ + + /* word 4 */ + FlagSet flags_r; /* :: FlagSet */ + FlagSet flags_w; /* :: FlagSet */ + UChar tag1:4; /* first operand tag */ + UChar tag2:4; /* second operand tag */ + UChar tag3:4; /* third operand tag */ + UChar extra4b:4; /* Spare field, used by WIDEN for src + -size, and by LEA2 for scale (1,2,4 or 8), + and by JMPs for original x86 instr size */ + + /* word 5 */ + UChar cond; /* condition, for jumps */ + Bool signed_widen:1; /* signed or unsigned WIDEN ? */ + JmpKind jmpkind:3; /* additional properties of unconditional JMP */ + + /* Additional properties for UInstrs that call C functions: + - CCALL + - PUT (when %ESP is the target) + - possibly tool-specific UInstrs + */ + UChar argc:2; /* Number of args, max 3 */ + UChar regparms_n:2; /* Number of args passed in registers */ + Bool has_ret_val:1; /* Function has return value? */ + + /* RealReg liveness; only sensical after reg alloc and liveness + analysis done. This info is a little bit arch-specific -- + VG_MAX_REALREGS can vary on different architectures. Note that + to use this information requires converting between register ranks + and the Intel register numbers, using VG_(realreg_to_rank)() + and/or VG_(rank_to_realreg)() */ + RRegSet regs_live_after:VG_MAX_REALREGS; + } + UInstr; + + +typedef + struct _UCodeBlock + UCodeBlock; + +extern Int VG_(get_num_instrs) (UCodeBlock* cb); +extern Int VG_(get_num_temps) (UCodeBlock* cb); + +extern UInstr* VG_(get_instr) (UCodeBlock* cb, Int i); +extern UInstr* VG_(get_last_instr) (UCodeBlock* cb); + + +/*====================================================================*/ +/*=== Instrumenting UCode ===*/ +/*====================================================================*/ + +/* Maximum number of registers read or written by a single UInstruction. */ +#define VG_MAX_REGS_USED 3 + +/* Find what this instruction does to its regs, useful for + analysis/optimisation passes. `tag' indicates whether we're considering + TempRegs (pre-reg-alloc) or RealRegs (post-reg-alloc). `regs' is filled + with the affected register numbers, `isWrites' parallels it and indicates + if the reg is read or written. If a reg is read and written, it will + appear twice in `regs'. `regs' and `isWrites' must be able to fit + VG_MAX_REGS_USED elements. */ +extern Int VG_(get_reg_usage) ( UInstr* u, Tag tag, Int* regs, Bool* isWrites ); + + +/* Used to register helper functions to be called from generated code. A + limited number of compact helpers can be registered; the code generated + to call them is slightly shorter -- so register the mostly frequently + called helpers as compact. */ +extern void VG_(register_compact_helper) ( Addr a ); +extern void VG_(register_noncompact_helper) ( Addr a ); + + +/* ------------------------------------------------------------------ */ +/* Virtual register allocation */ + +/* Get a new virtual register */ +extern Int VG_(get_new_temp) ( UCodeBlock* cb ); + +/* Get a new virtual shadow register */ +extern Int VG_(get_new_shadow) ( UCodeBlock* cb ); + +/* Get a virtual register's corresponding virtual shadow register */ +#define SHADOW(tempreg) ((tempreg)+1) + + +/* ------------------------------------------------------------------ */ +/* Low-level UInstr builders */ +extern void VG_(new_NOP) ( UInstr* u ); +extern void VG_(new_UInstr0) ( UCodeBlock* cb, Opcode opcode, Int sz ); +extern void VG_(new_UInstr1) ( UCodeBlock* cb, Opcode opcode, Int sz, + Tag tag1, UInt val1 ); +extern void VG_(new_UInstr2) ( UCodeBlock* cb, Opcode opcode, Int sz, + Tag tag1, UInt val1, + Tag tag2, UInt val2 ); +extern void VG_(new_UInstr3) ( UCodeBlock* cb, Opcode opcode, Int sz, + Tag tag1, UInt val1, + Tag tag2, UInt val2, + Tag tag3, UInt val3 ); + +/* Set read/write/undefined flags. Undefined flags are treaten as written, + but it's worth keeping them logically distinct. */ +extern void VG_(set_flag_fields) ( UCodeBlock* cb, FlagSet fr, FlagSet fw, + FlagSet fu); +extern void VG_(set_lit_field) ( UCodeBlock* cb, UInt lit32 ); +extern void VG_(set_ccall_fields) ( UCodeBlock* cb, Addr fn, UChar argc, + UChar regparms_n, Bool has_ret_val ); +extern void VG_(set_cond_field) ( UCodeBlock* cb, Condcode code ); +extern void VG_(set_widen_fields) ( UCodeBlock* cb, UInt szs, Bool is_signed ); + +extern void VG_(copy_UInstr) ( UCodeBlock* cb, UInstr* instr ); + +extern Bool VG_(any_flag_use)( UInstr* u ); + +/* Macro versions of the above; just shorter to type. */ +#define uInstr0 VG_(new_UInstr0) +#define uInstr1 VG_(new_UInstr1) +#define uInstr2 VG_(new_UInstr2) +#define uInstr3 VG_(new_UInstr3) +#define uLiteral VG_(set_lit_field) +#define uCCall VG_(set_ccall_fields) +#define uCond VG_(set_cond_field) +#define uWiden VG_(set_widen_fields) +#define uFlagsRWU VG_(set_flag_fields) +#define newTemp VG_(get_new_temp) +#define newShadow VG_(get_new_shadow) + +/* Refer to `the last instruction stuffed in' (can be lvalue). */ +#define LAST_UINSTR(cb) (cb)->instrs[(cb)->used-1] + + +/* ------------------------------------------------------------------ */ +/* Higher-level UInstr sequence builders */ + +extern void VG_(lit_to_reg) ( UCodeBlock* cb, UInt lit, UInt t ); +extern UInt VG_(lit_to_newreg) ( UCodeBlock* cb, UInt lit ); + +#define CB_F UCodeBlock* cb, Addr f +#define EV extern void +#define RPn UInt regparms_n + +/* Various CCALL builders, of the form "ccall__". 'R' + represents a TempReg, 'L' represents a literal, '0' represents nothing + (ie. no args, or no return value). */ + +EV VG_(ccall_0_0) ( CB_F ); + +EV VG_(ccall_R_0) ( CB_F, UInt R1, RPn ); +EV VG_(ccall_L_0) ( CB_F, UInt L1, RPn ); +EV VG_(ccall_R_R) ( CB_F, UInt R1, UInt R_ret, RPn ); +EV VG_(ccall_L_R) ( CB_F, UInt L1, UInt R_ret, RPn ); + +EV VG_(ccall_RR_0) ( CB_F, UInt R1, UInt R2, RPn ); +EV VG_(ccall_RL_0) ( CB_F, UInt R1, UInt RL, RPn ); +EV VG_(ccall_LR_0) ( CB_F, UInt L1, UInt R2, RPn ); +EV VG_(ccall_LL_0) ( CB_F, UInt L1, UInt L2, RPn ); +EV VG_(ccall_RR_R) ( CB_F, UInt R1, UInt R2, UInt R_ret, RPn ); +EV VG_(ccall_RL_R) ( CB_F, UInt R1, UInt L2, UInt R_ret, RPn ); +EV VG_(ccall_LR_R) ( CB_F, UInt L1, UInt R2, UInt R_ret, RPn ); +EV VG_(ccall_LL_R) ( CB_F, UInt L1, UInt L2, UInt R_ret, RPn ); + +EV VG_(ccall_RRR_0) ( CB_F, UInt R1, UInt R2, UInt R3, RPn ); +EV VG_(ccall_RLL_0) ( CB_F, UInt R1, UInt L2, UInt L3, RPn ); +EV VG_(ccall_LRR_0) ( CB_F, UInt L1, UInt R2, UInt R3, RPn ); +EV VG_(ccall_LLR_0) ( CB_F, UInt L1, UInt L2, UInt R3, RPn ); +EV VG_(ccall_LLL_0) ( CB_F, UInt L1, UInt L2, UInt L3, RPn ); + +#undef CB_F +#undef EV +#undef RPn + +/* One way around the 3-arg C function limit is to pass args via global + * variables... ugly, but it works. */ +void VG_(reg_to_globvar)(UCodeBlock* cb, UInt t, UInt* globvar_ptr); +void VG_(lit_to_globvar)(UCodeBlock* cb, UInt lit, UInt* globvar_ptr); + + +/* Old, deprecated versions of some of the helpers (DO NOT USE) */ +extern void VG_(call_helper_0_0) ( UCodeBlock* cb, Addr f); +extern void VG_(call_helper_1_0) ( UCodeBlock* cb, Addr f, UInt arg1, + UInt regparms_n); +extern void VG_(call_helper_2_0) ( UCodeBlock* cb, Addr f, UInt arg1, UInt arg2, + UInt regparms_n); +extern void VG_(set_global_var) ( UCodeBlock* cb, Addr globvar_ptr, UInt val); +extern void VG_(set_global_var_tempreg) ( UCodeBlock* cb, Addr globvar_ptr, + UInt t_val); + +/* ------------------------------------------------------------------ */ +/* Allocating/freeing basic blocks of UCode */ +extern UCodeBlock* VG_(setup_UCodeBlock) ( UCodeBlock* cb ); +extern void VG_(free_UCodeBlock) ( UCodeBlock* cb ); + +/* ------------------------------------------------------------------ */ +/* UCode pretty/ugly printing. Probably only useful to call from a tool + if VG_(needs).extended_UCode == True. */ + +/* When True, all generated code is/should be printed. */ +extern Bool VG_(print_codegen); + +/* Pretty/ugly printing functions */ +extern void VG_(pp_UCodeBlock) ( UCodeBlock* cb, Char* title ); +extern void VG_(pp_UInstr) ( Int instrNo, UInstr* u ); +extern void VG_(pp_UInstr_regs) ( Int instrNo, UInstr* u ); +extern void VG_(up_UInstr) ( Int instrNo, UInstr* u ); +extern Char* VG_(name_UOpcode) ( Bool upper, Opcode opc ); +extern Char* VG_(name_UCondcode) ( Condcode cond ); +extern void VG_(pp_UOperand) ( UInstr* u, Int operandNo, + Int sz, Bool parens ); + +/* ------------------------------------------------------------------ */ +/* Accessing archregs and their shadows */ +extern UInt VG_(get_archreg) ( UInt archreg ); +extern UInt VG_(get_thread_archreg) ( ThreadId tid, UInt archreg ); + +extern UInt VG_(get_shadow_archreg) ( UInt archreg ); +extern void VG_(set_shadow_archreg) ( UInt archreg, UInt val ); +extern void VG_(set_shadow_eflags) ( UInt val ); +extern Addr VG_(shadow_archreg_address) ( UInt archreg ); + +extern UInt VG_(get_thread_shadow_archreg) ( ThreadId tid, UInt archreg ); +extern void VG_(set_thread_shadow_archreg) ( ThreadId tid, UInt archreg, + UInt val ); + +/*====================================================================*/ +/*=== Generating x86 code from UCode ===*/ +/*====================================================================*/ + +/* All this only necessary for tools with VG_(needs).extends_UCode == True. */ + +/* This is the Intel register encoding -- integer regs. */ +#define R_EAX 0 +#define R_ECX 1 +#define R_EDX 2 +#define R_EBX 3 +#define R_ESP 4 +#define R_EBP 5 +#define R_ESI 6 +#define R_EDI 7 + +#define R_AL (0+R_EAX) +#define R_CL (0+R_ECX) +#define R_DL (0+R_EDX) +#define R_BL (0+R_EBX) +#define R_AH (4+R_EAX) +#define R_CH (4+R_ECX) +#define R_DH (4+R_EDX) +#define R_BH (4+R_EBX) + +/* This is the Intel register encoding -- segment regs. */ +#define R_ES 0 +#define R_CS 1 +#define R_SS 2 +#define R_DS 3 +#define R_FS 4 +#define R_GS 5 + +/* For pretty printing x86 code */ +extern const Char* VG_(name_of_mmx_gran) ( UChar gran ); +extern const Char* VG_(name_of_mmx_reg) ( Int mmxreg ); +extern const Char* VG_(name_of_seg_reg) ( Int sreg ); +extern const Char* VG_(name_of_int_reg) ( Int size, Int reg ); +extern const Char VG_(name_of_int_size) ( Int size ); + +/* Shorter macros for convenience */ +#define nameIReg VG_(name_of_int_reg) +#define nameISize VG_(name_of_int_size) +#define nameSReg VG_(name_of_seg_reg) +#define nameMMXReg VG_(name_of_mmx_reg) +#define nameMMXGran VG_(name_of_mmx_gran) +#define nameXMMReg VG_(name_of_xmm_reg) + +/* Randomly useful things */ +extern UInt VG_(extend_s_8to32) ( UInt x ); + +/* Code emitters */ +extern void VG_(emitB) ( UInt b ); +extern void VG_(emitW) ( UInt w ); +extern void VG_(emitL) ( UInt l ); +extern void VG_(new_emit) ( Bool upd_cc, FlagSet uses_flags, FlagSet sets_flags ); + +/* Finding offsets */ +extern Int VG_(helper_offset) ( Addr a ); +extern Int VG_(shadow_reg_offset) ( Int arch ); +extern Int VG_(shadow_flags_offset) ( void ); + +/* Convert reg ranks <-> Intel register ordering, for using register + liveness information. */ +extern Int VG_(realreg_to_rank) ( Int realreg ); +extern Int VG_(rank_to_realreg) ( Int rank ); + +/* Call a subroutine. Does no argument passing, stack manipulations, etc. */ +extern void VG_(synth_call) ( Bool ensure_shortform, Int word_offset, + Bool upd_cc, FlagSet use_flags, FlagSet set_flags ); + +/* For calling C functions -- saves caller save regs, pushes args, calls, + clears the stack, restores caller save regs. `fn' must be registered in + the baseBlock first. Acceptable tags are RealReg and Literal. Optimises + things, eg. by not preserving non-live caller-save registers. + + WARNING: a UInstr should *not* be translated with synth_ccall() followed + by some other x86 assembly code; this will invalidate the results of + vg_realreg_liveness_analysis() and everything will fall over. */ +extern void VG_(synth_ccall) ( Addr fn, Int argc, Int regparms_n, UInt argv[], + Tag tagv[], Int ret_reg, + RRegSet regs_live_before, + RRegSet regs_live_after ); + +/* Addressing modes */ +extern void VG_(emit_amode_offregmem_reg)( Int off, Int regmem, Int reg ); +extern void VG_(emit_amode_ereg_greg) ( Int e_reg, Int g_reg ); + +/* v-size (4, or 2 with OSO) insn emitters */ +extern void VG_(emit_movv_offregmem_reg) ( Int sz, Int off, Int areg, Int reg ); +extern void VG_(emit_movv_reg_offregmem) ( Int sz, Int reg, Int off, Int areg ); +extern void VG_(emit_movv_reg_reg) ( Int sz, Int reg1, Int reg2 ); +extern void VG_(emit_nonshiftopv_lit_reg)( Bool upd_cc, Int sz, Opcode opc, UInt lit, + Int reg ); +extern void VG_(emit_shiftopv_lit_reg) ( Bool upd_cc, Int sz, Opcode opc, UInt lit, + Int reg ); +extern void VG_(emit_nonshiftopv_reg_reg)( Bool upd_cc, Int sz, Opcode opc, + Int reg1, Int reg2 ); +extern void VG_(emit_movv_lit_reg) ( Int sz, UInt lit, Int reg ); +extern void VG_(emit_unaryopv_reg) ( Bool upd_cc, Int sz, Opcode opc, Int reg ); +extern void VG_(emit_pushv_reg) ( Int sz, Int reg ); +extern void VG_(emit_popv_reg) ( Int sz, Int reg ); + +extern void VG_(emit_pushl_lit32) ( UInt int32 ); +extern void VG_(emit_pushl_lit8) ( Int lit8 ); +extern void VG_(emit_cmpl_zero_reg) ( Bool upd_cc, Int reg ); +extern void VG_(emit_swapl_reg_EAX) ( Int reg ); +extern void VG_(emit_movv_lit_offregmem) ( Int sz, UInt lit, Int off, + Int memreg ); + +/* b-size (1 byte) instruction emitters */ +extern void VG_(emit_movb_lit_offregmem) ( UInt lit, Int off, Int memreg ); +extern void VG_(emit_movb_reg_offregmem) ( Int reg, Int off, Int areg ); +extern void VG_(emit_unaryopb_reg) ( Bool upd_cc, Opcode opc, Int reg ); +extern void VG_(emit_testb_lit_reg) ( Bool upd_cc, UInt lit, Int reg ); + +/* zero-extended load emitters */ +extern void VG_(emit_movzbl_offregmem_reg) ( Bool bounds, Int off, Int regmem, Int reg ); +extern void VG_(emit_movzwl_offregmem_reg) ( Bool bounds, Int off, Int areg, Int reg ); +extern void VG_(emit_movzwl_regmem_reg) ( Bool bounds, Int reg1, Int reg2 ); + +/* misc instruction emitters */ +extern void VG_(emit_call_reg) ( Int reg ); +extern void VG_(emit_add_lit_to_esp) ( Int lit ); +extern void VG_(emit_pushal) ( void ); +extern void VG_(emit_popal) ( void ); +extern void VG_(emit_AMD_prefetch_reg) ( Int reg ); + +/* jump emitters */ +extern void VG_(init_target) ( Int *tgt ); + +extern void VG_(target_back) ( Int *tgt ); +extern void VG_(target_forward) ( Int *tgt ); +extern void VG_(emit_target_delta) ( Int *tgt ); + +typedef enum { + JP_NONE, /* no prediction */ + JP_TAKEN, /* predict taken */ + JP_NOT_TAKEN, /* predict not taken */ +} JumpPred; + +extern void VG_(emit_jcondshort_delta) ( Bool simd_cc, Condcode cond, Int delta, JumpPred ); +extern void VG_(emit_jcondshort_target)( Bool simd_cc, Condcode cond, Int *tgt, JumpPred ); + + +/*====================================================================*/ +/*=== Execution contexts ===*/ +/*====================================================================*/ + +/* Generic resolution type used in a few different ways, such as deciding + how closely to compare two errors for equality. */ +typedef + enum { Vg_LowRes, Vg_MedRes, Vg_HighRes } + VgRes; + +typedef + struct _ExeContext + ExeContext; + +/* Compare two ExeContexts. Number of callers considered depends on `res': + Vg_LowRes: 2 + Vg_MedRes: 4 + Vg_HighRes: all */ +extern Bool VG_(eq_ExeContext) ( VgRes res, + ExeContext* e1, ExeContext* e2 ); + +/* Print an ExeContext. */ +extern void VG_(pp_ExeContext) ( ExeContext* ); + +/* Take a snapshot of the client's stack. Search our collection of + ExeContexts to see if we already have it, and if not, allocate a + new one. Either way, return a pointer to the context. Context size + controlled by --num-callers option. + + If called from generated code, use VG_(get_current_tid)() to get the + current ThreadId. If called from non-generated code, the current + ThreadId should be passed in by the core. +*/ +extern ExeContext* VG_(get_ExeContext) ( ThreadId tid ); + +/* Get the nth IP from the ExeContext. 0 is the IP of the top function, 1 + is its caller, etc. Returns 0 if there isn't one, or if n is greater + than VG_(clo_backtrace_size), set by the --num-callers option. */ +extern Addr VG_(get_EIP_from_ExeContext) ( ExeContext* e, UInt n ); + +/* Just grab the client's IP, as a much smaller and cheaper + indication of where they are. Use is basically same as for + VG_(get_ExeContext)() above. +*/ +extern Addr VG_(get_EIP)( ThreadId tid ); + +/* For tools needing more control over stack traces: walks the stack to get + instruction pointers from the top stack frames for thread 'tid'. Maximum of + 'n_ips' addresses put into 'ips'; 0 is the top of the stack, 1 is its + caller, etc. */ +extern UInt VG_(stack_snapshot) ( ThreadId tid, Addr* ips, UInt n_ips ); + +/* Does the same thing as VG_(pp_ExeContext)(), just with slightly + different input. */ +extern void VG_(mini_stack_dump) ( Addr ips[], UInt n_ips ); + + +/*====================================================================*/ +/*=== Error reporting ===*/ +/*====================================================================*/ + +/* ------------------------------------------------------------------ */ +/* Suppressions describe errors which we want to suppress, ie, not + show the user, usually because it is caused by a problem in a library + which we can't fix, replace or work around. Suppressions are read from + a file at startup time. This gives flexibility so that new + suppressions can be added to the file as and when needed. +*/ + +typedef + Int /* Do not make this unsigned! */ + SuppKind; + +/* The tool-relevant parts of a suppression are: + kind: what kind of suppression; must be in the range (0..) + string: use is optional. NULL by default. + extra: use is optional. NULL by default. void* so it's extensible. +*/ +typedef + struct _Supp + Supp; + +/* Useful in SK_(error_matches_suppression)() */ +SuppKind VG_(get_supp_kind) ( Supp* su ); +Char* VG_(get_supp_string) ( Supp* su ); +void* VG_(get_supp_extra) ( Supp* su ); + +/* Must be used in VG_(recognised_suppression)() */ +void VG_(set_supp_kind) ( Supp* su, SuppKind suppkind ); +/* May be used in VG_(read_extra_suppression_info)() */ +void VG_(set_supp_string) ( Supp* su, Char* string ); +void VG_(set_supp_extra) ( Supp* su, void* extra ); + + +/* ------------------------------------------------------------------ */ +/* Error records contain enough info to generate an error report. The idea + is that (typically) the same few points in the program generate thousands + of errors, and we don't want to spew out a fresh error message for each + one. Instead, we use these structures to common up duplicates. +*/ + +typedef + Int /* Do not make this unsigned! */ + ErrorKind; + +/* The tool-relevant parts of an Error are: + kind: what kind of error; must be in the range (0..) + addr: use is optional. 0 by default. + string: use is optional. NULL by default. + extra: use is optional. NULL by default. void* so it's extensible. +*/ +typedef + struct _Error + Error; + +/* Useful in SK_(error_matches_suppression)(), SK_(pp_SkinError)(), etc */ +ExeContext* VG_(get_error_where) ( Error* err ); +SuppKind VG_(get_error_kind) ( Error* err ); +Addr VG_(get_error_address) ( Error* err ); +Char* VG_(get_error_string) ( Error* err ); +void* VG_(get_error_extra) ( Error* err ); + +/* Call this when an error occurs. It will be recorded if it hasn't been + seen before. If it has, the existing error record will have its count + incremented. + + 'tid' can be found as for VG_(get_ExeContext)(). The `extra' field can + be stack-allocated; it will be copied by the core if needed (but it + won't be copied if it's NULL). + + If no 'a', 's' or 'extra' of interest needs to be recorded, just use + NULL for them. */ +extern void VG_(maybe_record_error) ( ThreadId tid, ErrorKind ekind, + Addr a, Char* s, void* extra ); + +/* Similar to VG_(maybe_record_error)(), except this one doesn't record the + error -- useful for errors that can only happen once. The errors can be + suppressed, though. Return value is True if it was suppressed. + `print_error' dictates whether to print the error, which is a bit of a + hack that's useful sometimes if you just want to know if the error would + be suppressed without possibly printing it. `count_error' dictates + whether to add the error in the error total count (another mild hack). */ +extern Bool VG_(unique_error) ( ThreadId tid, ErrorKind ekind, + Addr a, Char* s, void* extra, + ExeContext* where, Bool print_error, + Bool allow_GDB_attach, Bool count_error ); + +/* Gets a non-blank, non-comment line of at most nBuf chars from fd. + Skips leading spaces on the line. Returns True if EOF was hit instead. + Useful for reading in extra tool-specific suppression lines. */ +extern Bool VG_(get_line) ( Int fd, Char* buf, Int nBuf ); + + +/*====================================================================*/ +/*=== Obtaining debug information ===*/ +/*====================================================================*/ + +/* Get the file/function/line number of the instruction at address + 'a'. For these four, if debug info for the address is found, it + copies the info into the buffer/UInt and returns True. If not, it + returns False and nothing is copied. VG_(get_fnname) always + demangles C++ function names. VG_(get_fnname_w_offset) is the + same, except it appends "+N" to symbol names to indicate offsets. */ +extern Bool VG_(get_filename) ( Addr a, Char* filename, Int n_filename ); +extern Bool VG_(get_fnname) ( Addr a, Char* fnname, Int n_fnname ); +extern Bool VG_(get_linenum) ( Addr a, UInt* linenum ); +extern Bool VG_(get_fnname_w_offset) + ( Addr a, Char* fnname, Int n_fnname ); + +/* This one is more efficient if getting both filename and line number, + because the two lookups are done together. */ +extern Bool VG_(get_filename_linenum) + ( Addr a, Char* filename, Int n_filename, + UInt* linenum ); + +/* Succeeds only if we find from debug info that 'a' is the address of the + first instruction in a function -- as opposed to VG_(get_fnname) which + succeeds if we find from debug info that 'a' is the address of any + instruction in a function. Use this to instrument the start of + a particular function. Nb: if an executable/shared object is stripped + of its symbols, this function will not be able to recognise function + entry points within it. */ +extern Bool VG_(get_fnname_if_entry) ( Addr a, Char* fnname, Int n_fnname ); + +/* Succeeds if the address is within a shared object or the main executable. + It doesn't matter if debug info is present or not. */ +extern Bool VG_(get_objname) ( Addr a, Char* objname, Int n_objname ); + +/* Puts into 'buf' info about the code address %eip: the address, function + name (if known) and filename/line number (if known), like this: + + 0x4001BF05: realloc (vg_replace_malloc.c:339) + + 'n_buf' gives length of 'buf'. Returns 'buf'. +*/ +extern Char* VG_(describe_eip)(Addr eip, Char* buf, Int n_buf); + +/* Returns a string containing an expression for the given + address. String is malloced with VG_(malloc)() */ +Char *VG_(describe_addr)(ThreadId, Addr); + +/* A way to get information about what segments are mapped */ +typedef struct _SegInfo SegInfo; + +/* Returns NULL if the SegInfo isn't found. It doesn't matter if debug info + is present or not. */ +extern SegInfo* VG_(get_obj) ( Addr a ); + +extern const SegInfo* VG_(next_seginfo) ( const SegInfo *seg ); +extern Addr VG_(seg_start) ( const SegInfo *seg ); +extern UInt VG_(seg_size) ( const SegInfo *seg ); +extern const UChar* VG_(seg_filename) ( const SegInfo *seg ); +extern UInt VG_(seg_sym_offset)( const SegInfo *seg ); + +typedef + enum { + Vg_SectUnknown, + Vg_SectText, + Vg_SectData, + Vg_SectBSS, + Vg_SectGOT, + Vg_SectPLT, + } + VgSectKind; + +extern VgSectKind VG_(seg_sect_kind)(Addr); + + +/*====================================================================*/ +/*=== Generic hash table ===*/ +/*====================================================================*/ + +/* Generic type for a separately-chained hash table. Via a kind of dodgy + C-as-C++ style inheritance, tools can extend the VgHashNode type, so long + as the first two fields match the sizes of these two fields. Requires + a bit of casting by the tool. */ +typedef + struct _VgHashNode { + struct _VgHashNode * next; + UInt key; + } + VgHashNode; + +typedef + VgHashNode** + VgHashTable; + +/* Make a new table. */ +extern VgHashTable VG_(HT_construct) ( void ); + +/* Count the number of nodes in a table. */ +extern Int VG_(HT_count_nodes) ( VgHashTable table ); + +/* Add a node to the table. */ +extern void VG_(HT_add_node) ( VgHashTable t, VgHashNode* node ); + +/* Looks up a node in the hash table. Also returns the address of the + previous node's `next' pointer which allows it to be removed from the + list later without having to look it up again. */ +extern VgHashNode* VG_(HT_get_node) ( VgHashTable t, UInt key, + /*OUT*/VgHashNode*** next_ptr ); + +/* Allocates an array of pointers to all the shadow chunks of malloc'd + blocks. Must be freed with VG_(free)(). */ +extern VgHashNode** VG_(HT_to_array) ( VgHashTable t, /*OUT*/ UInt* n_shadows ); + +/* Returns first node that matches predicate `p', or NULL if none do. + Extra arguments can be implicitly passed to `p' using `d' which is an + opaque pointer passed to `p' each time it is called. */ +extern VgHashNode* VG_(HT_first_match) ( VgHashTable t, + Bool (*p)(VgHashNode*, void*), + void* d ); + +/* Applies a function f() once to each node. Again, `d' can be used + to pass extra information to the function. */ +extern void VG_(HT_apply_to_all_nodes)( VgHashTable t, + void (*f)(VgHashNode*, void*), + void* d ); + +/* Destroy a table. */ +extern void VG_(HT_destruct) ( VgHashTable t ); + + +/*====================================================================*/ +/*=== A generic skiplist ===*/ +/*====================================================================*/ + +/* + The idea here is that the skiplist puts its per-element data at the + end of the structure. When you initialize the skiplist, you tell + it what structure your list elements are going to be. Then you + should allocate them with VG_(SkipNode_Alloc), which will allocate + enough memory for the extra bits. + */ +#include /* for offsetof */ + +typedef struct _SkipList SkipList; +typedef struct _SkipNode SkipNode; + +typedef Int (*SkipCmp_t)(const void *key1, const void *key2); + +struct _SkipList { + const Short arena; /* allocation arena */ + const UShort size; /* structure size (not including SkipNode) */ + const UShort keyoff; /* key offset */ + const SkipCmp_t cmp; /* compare two keys */ + Char * (*strkey)(void *); /* stringify a key (for debugging) */ + SkipNode *head; /* list head */ +}; + +/* Use this macro to initialize your skiplist head. The arguments are pretty self explanitory: + _type is the type of your element structure + _key is the field within that type which you want to use as the key + _cmp is the comparison function for keys - it gets two typeof(_key) pointers as args + _strkey is a function which can return a string of your key - it's only used for debugging + _arena is the arena to use for allocation - -1 is the default + */ +#define SKIPLIST_INIT(_type, _key, _cmp, _strkey, _arena) \ + { \ + .arena = _arena, \ + .size = sizeof(_type), \ + .keyoff = offsetof(_type, _key), \ + .cmp = _cmp, \ + .strkey = _strkey, \ + .head = NULL, \ + } + +/* List operations: + SkipList_Find searchs a list. If it can't find an exact match, it either + returns NULL or a pointer to the element before where k would go + SkipList_Insert inserts a new element into the list. Duplicates are + forbidden. The element must have been created with SkipList_Alloc! + SkipList_Remove removes an element from the list and returns it. It + doesn't free the memory. +*/ +extern void *VG_(SkipList_Find) (const SkipList *l, void *key); +extern void VG_(SkipList_Insert)( SkipList *l, void *data); +extern void *VG_(SkipList_Remove)( SkipList *l, void *key); + +/* Node (element) operations: + SkipNode_Alloc: allocate memory for a new element on the list. Must be + used before an element can be inserted! Returns NULL if not enough + memory. + SkipNode_Free: free memory allocated above + SkipNode_First: return the first element on the list + SkipNode_Next: return the next element after "data" on the list - + NULL for none + + You can iterate through a SkipList like this: + + for(x = VG_(SkipNode_First)(&list); // or SkipList_Find + x != NULL; + x = VG_(SkipNode_Next)(&list, x)) { ... } +*/ +extern void *VG_(SkipNode_Alloc) (const SkipList *l); +extern void VG_(SkipNode_Free) (const SkipList *l, void *p); +extern void *VG_(SkipNode_First) (const SkipList *l); +extern void *VG_(SkipNode_Next) (const SkipList *l, void *data); + +/*====================================================================*/ +/*=== Functions for shadow registers ===*/ +/*====================================================================*/ + +/* Nb: make sure the shadow_regs 'need' is set before using these! */ + +/* This one lets you override the shadow of the return value register for a + syscall. Call it from SK_(post_syscall)() (not SK_(pre_syscall)()!) to + override the default shadow register value. */ +extern void VG_(set_return_from_syscall_shadow) ( ThreadId tid, + UInt ret_shadow ); + +/* This can be called from SK_(fini)() to find the shadow of the argument + to exit(), ie. the shadow of the program's return value. */ +extern UInt VG_(get_exit_status_shadow) ( void ); + + +/*====================================================================*/ +/*=== Specific stuff for replacing malloc() and friends ===*/ +/*====================================================================*/ + +/* If a tool replaces malloc() et al, the easiest way to do so is to + link with vg_replace_malloc.o into its vgpreload_*.so file, and + follow the following instructions. You can do it from scratch, + though, if you enjoy that sort of thing. */ + +/* Arena size for valgrind's own malloc(); default value is 0, but can + be overridden by tool -- but must be done so *statically*, eg: + + Int VG_(vg_malloc_redzone_szB) = 4; + + It can't be done from a function like SK_(pre_clo_init)(). So it can't, + for example, be controlled with a command line option, unfortunately. */ +extern UInt VG_(vg_malloc_redzone_szB); + +/* Can be called from SK_(malloc) et al to do the actual alloc/freeing. */ +extern void* VG_(cli_malloc) ( UInt align, Int nbytes ); +extern void VG_(cli_free) ( void* p ); + +/* Check if an address is within a range, allowing for redzones at edges */ +extern Bool VG_(addr_is_in_block)( Addr a, Addr start, UInt size ); + +/* ------------------------------------------------------------------ */ +/* Some options that can be used by a tool if malloc() et al are replaced. + The tool should call the functions in the appropriate places to give + control over these aspects of Valgrind's version of malloc(). */ + +/* Round malloc sizes upwards to integral number of words? default: NO */ +extern Bool VG_(clo_sloppy_malloc); +/* DEBUG: print malloc details? default: NO */ +extern Bool VG_(clo_trace_malloc); +/* Minimum alignment in functions that don't specify alignment explicitly. + default: 0, i.e. use default of the machine (== 4) */ +extern Int VG_(clo_alignment); + +extern Bool VG_(replacement_malloc_process_cmd_line_option) ( Char* arg ); +extern void VG_(replacement_malloc_print_usage) ( void ); +extern void VG_(replacement_malloc_print_debug_usage) ( void ); + + +/*====================================================================*/ +/*=== Tool-specific stuff ===*/ +/*====================================================================*/ + +/* ------------------------------------------------------------------ */ +/* Details */ + +/* Default value for avg_translations_sizeB (in bytes), indicating typical + code expansion of about 6:1. */ +#define VG_DEFAULT_TRANS_SIZEB 100 + +/* Information used in the startup message. `name' also determines the + string used for identifying suppressions in a suppression file as + belonging to this tool. `version' can be NULL, in which case (not + surprisingly) no version info is printed; this mechanism is designed for + tools distributed with Valgrind that share a version number with + Valgrind. Other tools not distributed as part of Valgrind should + probably have their own version number. */ +extern void VG_(details_name) ( Char* name ); +extern void VG_(details_version) ( Char* version ); +extern void VG_(details_description) ( Char* description ); +extern void VG_(details_copyright_author) ( Char* copyright_author ); + +/* Average size of a translation, in bytes, so that the translation + storage machinery can allocate memory appropriately. Not critical, + setting is optional. */ +extern void VG_(details_avg_translation_sizeB) ( UInt size ); + +/* String printed if an `sk_assert' assertion fails or VG_(skin_panic) + is called. Should probably be an email address. */ +extern void VG_(details_bug_reports_to) ( Char* bug_reports_to ); + +/* ------------------------------------------------------------------ */ +/* Needs */ + +/* Booleans that decide core behaviour, but don't require extra + operations to be defined if `True' */ + +/* Should __libc_freeres() be run? Bugs in it can crash the tool. */ +extern void VG_(needs_libc_freeres) ( void ); + +/* Want to have errors detected by Valgrind's core reported? Includes: + - pthread API errors (many; eg. unlocking a non-locked mutex) + - invalid file descriptors to blocking syscalls read() and write() + - bad signal numbers passed to sigaction() + - attempt to install signal handler for SIGKILL or SIGSTOP */ +extern void VG_(needs_core_errors) ( void ); + +/* Booleans that indicate extra operations are defined; if these are True, + the corresponding template functions (given below) must be defined. A + lot like being a member of a type class. */ + +/* Want to report errors from tool? This implies use of suppressions, too. */ +extern void VG_(needs_skin_errors) ( void ); + +/* Is information kept about specific individual basic blocks? (Eg. for + cachegrind there are cost-centres for every instruction, stored at a + basic block level.) If so, it sometimes has to be discarded, because + .so mmap/munmap-ping or self-modifying code (informed by the + DISCARD_TRANSLATIONS user request) can cause one instruction address + to be used for more than one instruction in one program run... */ +extern void VG_(needs_basic_block_discards) ( void ); + +/* Tool maintains information about each register? */ +extern void VG_(needs_shadow_regs) ( void ); + +/* Tool defines its own command line options? */ +extern void VG_(needs_command_line_options) ( void ); + +/* Tool defines its own client requests? */ +extern void VG_(needs_client_requests) ( void ); + +/* Tool defines its own UInstrs? */ +extern void VG_(needs_extended_UCode) ( void ); + +/* Tool does stuff before and/or after system calls? */ +extern void VG_(needs_syscall_wrapper) ( void ); + +/* Are tool-state sanity checks performed? */ +extern void VG_(needs_sanity_checks) ( void ); + +/* Do we need to see data symbols? */ +extern void VG_(needs_data_syms) ( void ); + +/* Does the tool need shadow memory allocated (if you set this, you must also statically initialize + float SK_(shadow_ratio) = n./m; + to define how many shadow bits you need per client address space bit. +*/ +extern void VG_(needs_shadow_memory)( void ); +extern float SK_(shadow_ratio); + +/* ------------------------------------------------------------------ */ +/* Core events to track */ + +/* Part of the core from which this call was made. Useful for determining + what kind of error message should be emitted. */ +typedef + enum { Vg_CorePThread, Vg_CoreSignal, Vg_CoreSysCall, Vg_CoreTranslate } + CorePart; + +/* Useful to use in VG_(get_Xreg_usage)() */ +#define VG_UINSTR_READS_REG(ono,regs,isWrites) \ + { if (mycat(u->tag,ono) == tag) \ + { regs[n] = mycat(u->val,ono); \ + isWrites[n] = False; \ + n++; \ + } \ + } +#define VG_UINSTR_WRITES_REG(ono,regs,isWrites) \ + { if (mycat(u->tag,ono) == tag) \ + { regs[n] = mycat(u->val,ono); \ + isWrites[n] = True; \ + n++; \ + } \ + } + +#endif /* NDEF __TOOL_H */ + +/* gen_toolint.pl will put the VG_(init_*)() functions here: */ diff --git a/VEX/head20041019/include/tool_asm.h b/VEX/head20041019/include/tool_asm.h new file mode 100644 index 0000000000..9e0ea2acb9 --- /dev/null +++ b/VEX/head20041019/include/tool_asm.h @@ -0,0 +1,61 @@ + +/*--------------------------------------------------------------------*/ +/*--- Tool-specific, asm-specific includes. tool_asm.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __TOOL_ASM_H +#define __TOOL_ASM_H + + +/* All symbols externally visible from valgrind.so are prefixed + as specified here. The prefix can be changed, so as to avoid + namespace conflict problems. +*/ +#define VGAPPEND(str1,str2) str1##str2 + +/* These macros should add different prefixes so the same base + name can safely be used across different macros. */ +#define VG_(str) VGAPPEND(vgPlain_,str) +#define VGP_(str) VGAPPEND(vgProf_,str) +#define VGOFF_(str) VGAPPEND(vgOff_,str) +#define VGA_(str) VGAPPEND(vgArch_,str) + +/* Tool-specific ones. Note that final name still starts with "vg". */ +#define SK_(str) VGAPPEND(vgSkin_,str) + +/* This is specifically for stringifying VG_(x) function names. We + need to do two macroexpansions to get the VG_ macro expanded before + stringifying */ +#define _STR(x) #x +#define STR(x) _STR(x) + +#endif /* ndef __TOOL_ASM_H */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/include/valgrind.h.in b/VEX/head20041019/include/valgrind.h.in new file mode 100644 index 0000000000..d7f57d76b0 --- /dev/null +++ b/VEX/head20041019/include/valgrind.h.in @@ -0,0 +1,379 @@ + +/* + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (valgrind.h) only. The entire rest of Valgrind is licensed + under the terms of the GNU General Public License, version 2. See + the COPYING file in the source distribution for details. + + ---------------------------------------------------------------- + + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (valgrind.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +#ifndef __VALGRIND_H +#define __VALGRIND_H + +#include + +#define __@VG_ARCH@__ // Architecture we're installed on + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query Valgrind's + execution inside your own programs. + + The resulting executables will still run without Valgrind, just a + little bit more slowly than they otherwise would, but otherwise + unchanged. When not running on valgrind, each client request + consumes very few (eg. < 10) instructions, so the resulting performance + loss is negligible unless you plan to execute client requests + millions of times per second. Nevertheless, if that is still a + problem, you can compile with the NVALGRIND symbol defined (gcc + -DNVALGRIND) so that client requests are not even compiled in. */ + +#ifndef NVALGRIND + +/* The following defines the magic code sequence which the JITter spots and + handles magically. Don't look too closely at this; it will rot + your brain. We must ensure that the default value gets put in the return + slot, so that everything works when this is executed not under Valgrind. + Args are passed in a memory block, and so there's no intrinsic limit to + the number that could be passed, but it's currently four. + + Nb: we put the assembly code sequences for all architectures in this one + file. This is because this file must be stand-alone, so we can't rely on + eg. x86/ subdirectories like we do within the rest of Valgrind. +*/ + +#ifdef __x86__ +/* This defines the magic code sequence which the JITter spots and + handles magically. Don't look too closely at this; it will rot + your brain. Valgrind dumps the result value in %EDX, so we first + copy the default value there, so that it is returned when not + running on Valgrind. Since %EAX points to a block of mem + containing the args, you can pass as many args as you want like + this. Currently this is set up to deal with 4 args since that's + the max that we appear to need (pthread_create). +*/ +#define VALGRIND_MAGIC_SEQUENCE( \ + _zzq_rlval, /* result lvalue */ \ + _zzq_default, /* result returned when running on real CPU */ \ + _zzq_request, /* request code */ \ + _zzq_arg1, /* request first param */ \ + _zzq_arg2, /* request second param */ \ + _zzq_arg3, /* request third param */ \ + _zzq_arg4 /* request fourth param */ ) \ + \ + { volatile unsigned int _zzq_args[5]; \ + _zzq_args[0] = (volatile unsigned int)(_zzq_request); \ + _zzq_args[1] = (volatile unsigned int)(_zzq_arg1); \ + _zzq_args[2] = (volatile unsigned int)(_zzq_arg2); \ + _zzq_args[3] = (volatile unsigned int)(_zzq_arg3); \ + _zzq_args[4] = (volatile unsigned int)(_zzq_arg4); \ + asm volatile("movl %1, %%eax\n\t" \ + "movl %2, %%edx\n\t" \ + "roll $29, %%eax ; roll $3, %%eax\n\t" \ + "rorl $27, %%eax ; rorl $5, %%eax\n\t" \ + "roll $13, %%eax ; roll $19, %%eax\n\t" \ + "movl %%edx, %0\t" \ + : "=r" (_zzq_rlval) \ + : "r" (&_zzq_args[0]), "r" (_zzq_default) \ + : "eax", "edx", "cc", "memory" \ + ); \ + } +#endif // __x86__ + +// Insert assembly code for other architectures here... + +#else /* NVALGRIND */ +/* Define NVALGRIND to completely remove the Valgrind magic sequence + from the compiled code (analogous to NDEBUG's effects on + assert()) */ +#define VALGRIND_MAGIC_SEQUENCE( \ + _zzq_rlval, /* result lvalue */ \ + _zzq_default, /* result returned when running on real CPU */ \ + _zzq_request, /* request code */ \ + _zzq_arg1, /* request first param */ \ + _zzq_arg2, /* request second param */ \ + _zzq_arg3, /* request third param */ \ + _zzq_arg4 /* request fourth param */ ) \ + { \ + (_zzq_rlval) = (_zzq_default); \ + } +#endif /* NVALGRIND */ + +/* Some request codes. There are many more of these, but most are not + exposed to end-user view. These are the public ones, all of the + form 0x1000 + small_number. + + Core ones are in the range 0x00000000--0x0000ffff. The non-public ones + start at 0x2000. +*/ + +#define VG_USERREQ_SKIN_BASE(a,b) \ + ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) +#define VG_IS_SKIN_USERREQ(a, b, v) \ + (VG_USERREQ_SKIN_BASE(a,b) == ((v) & 0xffff0000)) + +typedef + enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, + VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, + + /* These allow any function of 0--3 args to be called from the + simulated CPU but run on the real CPU */ + VG_USERREQ__CLIENT_CALL0 = 0x1101, + VG_USERREQ__CLIENT_CALL1 = 0x1102, + VG_USERREQ__CLIENT_CALL2 = 0x1103, + VG_USERREQ__CLIENT_CALL3 = 0x1104, + + /* Can be useful in regression testing suites -- eg. can send + Valgrind's output to /dev/null and still count errors. */ + VG_USERREQ__COUNT_ERRORS = 0x1201, + + /* These are useful and can be interpreted by any tool that tracks + malloc() et al, by using vg_replace_malloc.c. */ + VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, + VG_USERREQ__FREELIKE_BLOCK = 0x1302, + /* Memory pool support. */ + VG_USERREQ__CREATE_MEMPOOL = 0x1303, + VG_USERREQ__DESTROY_MEMPOOL = 0x1304, + VG_USERREQ__MEMPOOL_ALLOC = 0x1305, + VG_USERREQ__MEMPOOL_FREE = 0x1306, + + /* Allow printfs to valgrind log. */ + VG_USERREQ__PRINTF = 0x1401, + VG_USERREQ__PRINTF_BACKTRACE = 0x1402 + } Vg_ClientRequest; + +#ifndef __GNUC__ +#define __extension__ +#endif + +/* Returns 1 if running on Valgrind, 0 if running on the real CPU. + Currently implemented but untested. */ +#define RUNNING_ON_VALGRIND __extension__ \ + ({unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* returned if not */, \ + VG_USERREQ__RUNNING_ON_VALGRIND, \ + 0, 0, 0, 0); \ + _qzz_res; \ + }) + + +/* Discard translation of code in the range [_qzz_addr .. _qzz_addr + + _qzz_len - 1]. Useful if you are debugging a JITter or some such, + since it provides a way to make sure valgrind will retranslate the + invalidated area. Returns no value. */ +#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__DISCARD_TRANSLATIONS, \ + _qzz_addr, _qzz_len, 0, 0); \ + } + +#ifndef NVALGRIND + +int VALGRIND_PRINTF(const char *format, ...) + __attribute__((format(__printf__, 1, 2))); +__attribute__((weak)) +int +VALGRIND_PRINTF(const char *format, ...) +{ + unsigned int _qzz_res; + va_list vargs; + va_start(vargs, format); + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__PRINTF, + (unsigned int)format, (unsigned int)vargs, 0, 0); + va_end(vargs); + return _qzz_res; +} + +int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) + __attribute__((format(__printf__, 1, 2))); +__attribute__((weak)) +int +VALGRIND_PRINTF_BACKTRACE(const char *format, ...) +{ + unsigned int _qzz_res; + va_list vargs; + va_start(vargs, format); + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, VG_USERREQ__PRINTF_BACKTRACE, + (unsigned int)format, (unsigned int)vargs, 0, 0); + va_end(vargs); + return _qzz_res; +} + +#else /* NVALGRIND */ + +#define VALGRIND_PRINTF(...) +#define VALGRIND_PRINTF_BACKTRACE(...) + +#endif /* NVALGRIND */ + +/* These requests allow control to move from the simulated CPU to the + real CPU, calling an arbitary function */ +#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ + ({unsigned int _qyy_res; \ + VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL0, \ + _qyy_fn, \ + 0, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ + ({unsigned int _qyy_res; \ + VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL1, \ + _qyy_fn, \ + _qyy_arg1, 0, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ + ({unsigned int _qyy_res; \ + VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL2, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, 0); \ + _qyy_res; \ + }) + +#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ + ({unsigned int _qyy_res; \ + VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ + VG_USERREQ__CLIENT_CALL3, \ + _qyy_fn, \ + _qyy_arg1, _qyy_arg2, _qyy_arg3); \ + _qyy_res; \ + }) + + +/* Counts the number of errors that have been recorded by a tool. Nb: + the tool must record the errors with VG_(maybe_record_error)() or + VG_(unique_error)() for them to be counted. */ +#define VALGRIND_COUNT_ERRORS \ + ({unsigned int _qyy_res; \ + VALGRIND_MAGIC_SEQUENCE(_qyy_res, 0 /* default return */, \ + VG_USERREQ__COUNT_ERRORS, \ + 0, 0, 0, 0); \ + _qyy_res; \ + }) + +/* Mark a block of memory as having been allocated by a malloc()-like + function. `addr' is the start of the usable block (ie. after any + redzone) `rzB' is redzone size if the allocator can apply redzones; + use '0' if not. Adding redzones makes it more likely Valgrind will spot + block overruns. `is_zeroed' indicates if the memory is zeroed, as it is + for calloc(). Put it immediately after the point where a block is + allocated. + + If you're allocating memory via superblocks, and then handing out small + chunks of each superblock, if you don't have redzones on your small + blocks, it's worth marking the superblock with VALGRIND_MAKE_NOACCESS + when it's created, so that block overruns are detected. But if you can + put redzones on, it's probably better to not do this, so that messages + for small overruns are described in terms of the small block rather than + the superblock (but if you have a big overrun that skips over a redzone, + you could miss an error this way). See memcheck/tests/custom_alloc.c + for an example. + + Nb: block must be freed via a free()-like function specified + with VALGRIND_FREELIKE_BLOCK or mismatch errors will occur. */ +#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed); \ + } + +/* Mark a block of memory as having been freed by a free()-like function. + `rzB' is redzone size; it must match that given to + VALGRIND_MALLOCLIKE_BLOCK. Memory not freed will be detected by the leak + checker. Put it immediately after the point where the block is freed. */ +#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0); \ + } + +/* Create a memory pool. */ +#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__CREATE_MEMPOOL, \ + pool, rzB, is_zeroed, 0); \ + } + +/* Destroy a memory pool. */ +#define VALGRIND_DESTROY_MEMPOOL(pool) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__DESTROY_MEMPOOL, \ + pool, 0, 0, 0); \ + } + +/* Associate a piece of memory with a memory pool. */ +#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_ALLOC, \ + pool, addr, size, 0); \ + } + +/* Disassociate a piece of memory from a memory pool. */ +#define VALGRIND_MEMPOOL_FREE(pool, addr) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__MEMPOOL_FREE, \ + pool, addr, 0, 0); \ + } + +#endif /* __VALGRIND_H */ diff --git a/VEX/head20041019/include/vg_kerneliface.h b/VEX/head20041019/include/vg_kerneliface.h new file mode 100644 index 0000000000..3d50b977e2 --- /dev/null +++ b/VEX/head20041019/include/vg_kerneliface.h @@ -0,0 +1,943 @@ + +/*--------------------------------------------------------------------*/ +/*--- A header file defining structures and constants which are ---*/ +/*--- important at the kernel boundary for this platform. ---*/ +/*--- vg_kerneliface.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __VG_KERNELIFACE_H +#define __VG_KERNELIFACE_H + +/* This file is ONLY to be included into core.h. Do not include + it directly into valgrind source .c files. This file defines types + and constants for the kernel interface, and to make that clear + everything is prefixed VKI. */ + +/*--- All the following stuff is correct for Linux kernels 2.2.X and + 2.4.X. +---*/ + +/* Should really get this from an include file somewhere. */ +#define VKI_BYTES_PER_PAGE_BITS 12 +#define VKI_BYTES_PER_PAGE (1 << VKI_BYTES_PER_PAGE_BITS) + +#define VKI_BYTES_PER_WORD 4 +#define VKI_WORDS_PER_PAGE (VKI_BYTES_PER_PAGE / VKI_BYTES_PER_WORD) + + +/* An implementation of signal sets. These are the same as the sigset + implementations in the relevant Linux kernels. Note carefully that + this has nothing to do with glibc's signal sets. We work entirely + at the kernel boundary, so the libc stuff is invisible and + irrelevant. */ + +/* The following is copied from + /usr/src/linux-2.4.9-13/include/asm-i386/signal.h */ +#define VKI_KNSIG 64 /* true for linux 2.2.X and 2.4.X */ +#define VKI_KNSIG_BPW 32 /* since we're using UInts */ +#define VKI_KNSIG_WORDS (VKI_KNSIG / VKI_KNSIG_BPW) + +typedef + struct { + UInt ws[VKI_KNSIG_WORDS]; + } + vki_ksigset_t; + + +typedef + struct { + void* ksa_handler; + unsigned long ksa_flags; + void (*ksa_restorer)(void); + vki_ksigset_t ksa_mask; + } + vki_ksigaction; + +typedef + struct { + void* ss_sp; + Int ss_flags; + UInt ss_size; + } + vki_kstack_t; + +#define SI_MAX_SIZE 128 +#define SI_PAD_SIZE ((SI_MAX_SIZE/sizeof(int)) - 3) + +union vki_sigval { + Int sival_int; + void *sival_ptr; +}; + +typedef + struct { + Int si_signo; + Int si_errno; + Int si_code; + + union { + Int _pad[SI_PAD_SIZE]; + + /* kill() */ + struct { + Int _pid; /* sender's pid */ + Short _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + UInt _timer1; + UInt _timer2; + } _timer; + + /* POSIX.1b signals */ + struct { + Int _pid; /* sender's pid */ + UShort _uid; /* sender's uid */ + union vki_sigval _sigval; + } _rt; + + /* SIGCHLD */ + struct { + Int _pid; /* which child */ + UShort _uid; /* sender's uid */ + Int _status; /* exit code */ + Int _utime; + Int _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + void *_addr; /* faulting insn/memory ref. */ + } _sigfault; + + /* SIGPOLL */ + struct { + Int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + Int _fd; + } _sigpoll; + } _sifields; + } vki_ksiginfo_t; + +/* linux-2.6/include/asm-generic/siginfo.h */ + +#define VKI_SI_USER 0 +#define VKI_SI_QUEUE -1 +#define VKI_SI_TKILL -6 + +struct vki_fpreg { + UShort significand[4]; + UShort exponent; +}; + +struct vki_fpxreg { + UShort significand[4]; + UShort exponent; + UShort padding[3]; +}; + +struct vki_xmmreg { + UInt element[4]; +}; + +struct vki_fpstate { + /* Regular FPU environment */ + unsigned long cw; + unsigned long sw; + unsigned long tag; + unsigned long ipoff; + unsigned long cssel; + unsigned long dataoff; + unsigned long datasel; + struct vki_fpreg _st[8]; + unsigned short status; + unsigned short magic; /* 0xffff = regular FPU data only */ + + /* FXSR FPU environment */ + unsigned long _fxsr_env[6]; /* FXSR FPU env is ignored */ + unsigned long mxcsr; + unsigned long reserved; + struct vki_fpxreg _fxsr_st[8]; /* FXSR FPU reg data is ignored */ + struct vki_xmmreg _xmm[8]; + unsigned long padding[56]; +}; + +#define X86_FXSR_MAGIC 0x0000 + +struct vki_sigcontext { + UShort gs, __gsh; + UShort fs, __fsh; + UShort es, __esh; + UShort ds, __dsh; + UInt edi; + UInt esi; + UInt ebp; + UInt esp; + UInt ebx; + UInt edx; + UInt ecx; + UInt eax; + UInt trapno; + UInt err; + UInt eip; + UShort cs, __csh; + UInt eflags; + UInt esp_at_signal; + UShort ss, __ssh; + struct vki_fpstate * fpstate; + UInt oldmask; + UInt cr2; +}; + +struct vki_ucontext { + UInt uc_flags; + struct vki_ucontext *uc_link; + vki_kstack_t uc_stack; + struct vki_sigcontext uc_mcontext; + vki_ksigset_t uc_sigmask; /* mask last for extensibility */ +}; + + +/* sigaltstack controls */ +#define VKI_SS_ONSTACK 1 +#define VKI_SS_DISABLE 2 + +#define VKI_MINSIGSTKSZ 2048 +#define VKI_SIGSTKSZ 8192 + + + +#define VKI_SIG_BLOCK 0 /* for blocking signals */ +#define VKI_SIG_UNBLOCK 1 /* for unblocking signals */ +#define VKI_SIG_SETMASK 2 /* for setting the signal mask */ + +#define VKI_SIG_DFL ((void*)0) /* default signal handling */ +#define VKI_SIG_IGN ((void*)1) /* ignore signal */ +#define VKI_SIG_ERR ((void*)-1) /* error return from signal */ + +#define VKI_SA_ONSTACK 0x08000000 +#define VKI_SA_RESTART 0x10000000 +#define VKI_SA_NOCLDSTOP 0x00000001 +#define VKI_SA_SIGINFO 0x00000004 +#define VKI_SA_RESETHAND 0x80000000 +#define VKI_SA_ONESHOT VKI_SA_RESETHAND +#define VKI_SA_NODEFER 0x40000000 +#define VKI_SA_NOMASK VKI_SA_NODEFER +#define VKI_SA_NOCLDWAIT 0x00000002 +#define VKI_SA_RESTORER 0x04000000 +#if 0 +#define VKI_SA_INTERRUPT 0x20000000 /* dummy -- ignored */ +#endif + +/* extra wait flags */ +#define VKI_WNOHANG 1 /* Don't block waiting. */ +#define VKI_WUNTRACED 2 /* Report status of stopped children. */ +#define VKI__WALL 0x40000000 /* Wait for any child. */ +#define VKI__WCLONE 0x80000000 /* Wait for cloned process. */ + +#define VKI_SIGHUP 1 /* Hangup (POSIX). */ +#define VKI_SIGINT 2 /* Interrupt (ANSI). */ +#define VKI_SIGQUIT 3 /* Quit (POSIX). */ +#define VKI_SIGILL 4 /* Illegal instruction (ANSI). */ +#define VKI_SIGTRAP 5 /* Trace trap (POSIX). */ +#define VKI_SIGABRT 6 /* Abort (ANSI). */ +#define VKI_SIGIOT 6 /* IOT trap (4.2 BSD). */ +#define VKI_SIGBUS 7 /* BUS error (4.2 BSD). */ +#define VKI_SIGFPE 8 /* Floating-point exception (ANSI). */ +#define VKI_SIGKILL 9 /* Kill, unblockable (POSIX). */ +#define VKI_SIGUSR1 10 /* User-defined signal 1 (POSIX). */ +#define VKI_SIGSEGV 11 /* Segmentation violation (ANSI). */ +#define VKI_SIGUSR2 12 /* User-defined signal 2 (POSIX). */ +#define VKI_SIGPIPE 13 /* Broken pipe (POSIX). */ +#define VKI_SIGALRM 14 /* Alarm clock (POSIX). */ +#define VKI_SIGTERM 15 /* Termination (ANSI). */ +#define VKI_SIGSTKFLT 16 /* Stack fault. */ +#define VKI_SIGCLD SIGCHLD /* Same as SIGCHLD (System V). */ +#define VKI_SIGCHLD 17 /* Child status has changed (POSIX). */ +#define VKI_SIGCONT 18 /* Continue (POSIX). */ +#define VKI_SIGSTOP 19 /* Stop, unblockable (POSIX). */ +#define VKI_SIGTSTP 20 /* Keyboard stop (POSIX). */ +#define VKI_SIGTTIN 21 /* Background read from tty (POSIX). */ +#define VKI_SIGTTOU 22 /* Background write to tty (POSIX). */ +#define VKI_SIGURG 23 /* Urgent condition on socket (4.2 BSD). */ +#define VKI_SIGXCPU 24 /* CPU limit exceeded (4.2 BSD). */ +#define VKI_SIGXFSZ 25 /* File size limit exceeded (4.2 BSD). */ +#define VKI_SIGVTALRM 26 /* Virtual alarm clock (4.2 BSD). */ +#define VKI_SIGPROF 27 /* Profiling alarm clock (4.2 BSD). */ +#define VKI_SIGWINCH 28 /* Window size change (4.3 BSD, Sun). */ +#define VKI_SIGPOLL SIGIO /* Pollable event occurred (System V). */ +#define VKI_SIGIO 29 /* I/O now possible (4.2 BSD). */ +#define VKI_SIGPWR 30 /* Power failure restart (System V). */ +#define VKI_SIGSYS 31 /* Bad system call. */ +#define VKI_SIGUNUSED 31 + +#define VKI_SIGRTMIN 32 +#define VKI_SIGRTMAX 63 + +#define VKI_SIGVGINT (VKI_SIGRTMIN+0) /* signal for internal use - interrupt */ +#define VKI_SIGVGKILL (VKI_SIGRTMIN+1) /* signal for internal use - kill */ +#define VKI_SIGRTUSERMIN (VKI_SIGRTMIN+2) /* first user-usable RT signal */ + +/* The following are copied from include/asm-i386/mman.h .*/ + +#define VKI_PROT_NONE 0x0 /* No page permissions */ +#define VKI_PROT_READ 0x1 /* Page can be read. */ +#define VKI_PROT_WRITE 0x2 /* Page can be written. */ +#define VKI_PROT_EXEC 0x4 /* Page can be executed. */ +#define VKI_MAP_ANONYMOUS 0x20 /* Don't use a file. */ +#define VKI_MAP_SHARED 0x01 /* Share changes. */ +#define VKI_MAP_PRIVATE 0x02 /* Changes are private. */ +#define VKI_MAP_FIXED 0x10 /* Interpret addr exactly */ +#define VKI_MAP_NOSYMS 0x40000000 /* internal pseudo-flag to disable symbol loading */ +#define VKI_MAP_CLIENT 0x80000000 /* internal pseudo-flag to distinguish client mappings */ + +/* linux/mman.h */ +#define VKI_MREMAP_MAYMOVE 1 +#define VKI_MREMAP_FIXED 2 + +/* Copied from linux-2.4.19/include/asm-i386/fcntl.h */ + +#define VKI_O_ACCMODE 0003 +#define VKI_O_RDONLY 00 +#define VKI_O_WRONLY 01 +#define VKI_O_RDWR 02 +#define VKI_O_CREAT 0100 /* not fcntl */ +#define VKI_O_EXCL 0200 /* not fcntl */ +#define VKI_O_TRUNC 01000 /* not fcntl */ +#define VKI_O_APPEND 02000 +#define VKI_O_NONBLOCK 04000 +#define VKI_O_SYNC 010000 +#define VKI_FASYNC 020000 /* fcntl, for BSD compatibility */ +#define VKI_O_DIRECT 040000 /* direct disk access hint */ +#define VKI_O_LARGEFILE 0100000 +#define VKI_O_DIRECTORY 0200000 /* must be a directory */ +#define VKI_O_NOFOLLOW 0400000 /* don't follow links */ + +#define VKI_SEEK_SET 0 +#define VKI_SEEK_CUR 1 +#define VKI_SEEK_END 2 + +/* Copied from linux-2.4.19/include/linux/stat.h */ + +#define VKI_S_IRWXU 00700 +#define VKI_S_IRUSR 00400 +#define VKI_S_IWUSR 00200 +#define VKI_S_IXUSR 00100 + +#define VKI_S_IRWXG 00070 +#define VKI_S_IRGRP 00040 +#define VKI_S_IWGRP 00020 +#define VKI_S_IXGRP 00010 + +#define VKI_S_IRWXO 00007 +#define VKI_S_IROTH 00004 +#define VKI_S_IWOTH 00002 +#define VKI_S_IXOTH 00001 + + +/* Copied from /usr/src/linux-2.4.9-13/include/asm/errno.h */ + +#define VKI_EPERM 1 /* Operation not permitted */ +#define VKI_ENOENT 2 /* No such file or directory */ +#define VKI_ESRCH 3 /* No such process */ +#define VKI_EINTR 4 /* Interrupted system call */ +#define VKI_EBADF 9 /* Bad file number */ +#define VKI_ENOMEM 12 /* Out of memory */ +#define VKI_EWOULDBLOCK VKI_EAGAIN /* Operation would block */ +#define VKI_EAGAIN 11 /* Try again */ +#define VKI_EACCES 13 /* Permission denied */ +#define VKI_EFAULT 14 /* Bad address */ +#define VKI_EEXIST 17 /* File exists */ +#define VKI_EINVAL 22 /* Invalid argument */ +#define VKI_ENFILE 23 /* File table overflow */ +#define VKI_EMFILE 24 /* Too many open files */ +#define VKI_ENOSYS 38 /* Function not implemented */ + +#define VKI_ERESTARTSYS 512 /* Restart the syscall */ + +/* Copied from linux/isdn.h */ + +#define VKI_IIOCGETCPS _IO( 'I',21 ) +#define VKI_IIOCNETGPN _IO( 'I',34 ) + +#define ISDN_MSNLEN 32 + +typedef struct { + char name[ 10 ]; + char phone[ ISDN_MSNLEN ]; + int outgoing; +} isdn_net_ioctl_phone; + + +/* Gawd ... hack ... */ + +typedef struct vki__user_cap_header_struct { + UInt version; + int pid; +} vki_cap_user_header_t; + +typedef struct vki__user_cap_data_struct { + UInt effective; + UInt permitted; + UInt inheritable; +} vki_cap_user_data_t; + + +/* "Byrial Jensen" says: + [various] ioctls take a pointer to a "struct + termios" but this is another and shorter "struct + termios" than the one defined in and used + by tcgetattr(3) and tcsetattr(3) and other library + functions. GNU libc translate between its library + termios and the kernel termios. +*/ + +#define VKI_SIZEOF_STRUCT_TERMIOS 36 + +/* Adam Gundy , 20 Mar 2002, says: */ +#define VKI_SIZEOF_STRUCT_TERMIO 17 + + +/* File descriptor sets, for doing select(). Copied from + /usr/src/linux-2.4.9-31/include/linux/posix_types.h +*/ +/* + * This allows for 1024 file descriptors: if NR_OPEN is ever grown + * beyond that you'll have to change this too. But 1024 fd's seem to be + * enough even for such "real" unices like OSF/1, so hopefully this is + * one limit that doesn't have to be changed [again]. + * + * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in + * (and thus ) - but this is a more logical + * place for them. Solved by having dummy defines in . + */ + +/* + * Those macros may have been defined in . But we always + * use the ones here. + */ +#undef VKI_NFDBITS +#define VKI_NFDBITS (8 * sizeof(unsigned long)) + +#undef VKI_FD_SETSIZE +#define VKI_FD_SETSIZE 1024 + +#undef VKI_FDSET_LONGS +#define VKI_FDSET_LONGS (VKI_FD_SETSIZE/VKI_NFDBITS) + +#undef VKI_FDELT +#define VKI_FDELT(d) ((d) / VKI_NFDBITS) + +#undef VKI_FDMASK +#define VKI_FDMASK(d) (1UL << ((d) % VKI_NFDBITS)) + +typedef struct { + unsigned long vki_fds_bits [VKI_FDSET_LONGS]; +} vki_fd_set; + + +struct vki_pollfd { + Int fd; + Short events; + Short revents; +}; + +/* asm/poll.h */ +#define VKI_POLLIN 0x0001 +#define VKI_POLLPRI 0x0002 +#define VKI_POLLOUT 0x0004 +#define VKI_POLLERR 0x0008 +#define VKI_POLLHUP 0x0010 +#define VKI_POLLNVAL 0x0020 + + +/* sys/epoll.h */ +typedef union vki_epoll_data { + void *ptr; + Int fd; + UInt u32; + ULong u64; +} vki_epoll_data_t; + +struct vki_epoll_event { + UInt events; /* Epoll events */ + vki_epoll_data_t data; /* User data variable */ +}; + + +/* +./include/asm-i386/posix_types.h:typedef long __kernel_suseconds_t; +./include/linux/types.h:typedef __kernel_suseconds_t suseconds_t; + +./include/asm-i386/posix_types.h:typedef long __kernel_time_t; +./include/linux/types.h:typedef __kernel_time_t time_t; +*/ + +struct vki_timeval { + /* time_t */ long tv_sec; /* seconds */ + /* suseconds_t */ long tv_usec; /* microseconds */ +}; + + + +/* For fcntl on fds .. + from ./include/asm-i386/fcntl.h */ +#define VKI_F_DUPFD 0 /* dup */ +#define VKI_F_GETFD 1 /* get close_on_exec */ +#define VKI_F_SETFD 2 /* set/clear close_on_exec */ +#define VKI_F_GETFL 3 /* get file->f_flags */ +#define VKI_F_SETFL 4 /* set file->f_flags */ +#define VKI_F_GETLK 5 +#define VKI_F_SETLK 6 +#define VKI_F_SETLKW 7 +#define VKI_F_GETLK64 12 /* using 'struct flock64' */ +#define VKI_F_SETLK64 13 +#define VKI_F_SETLKW64 14 + +/* for F_[GET|SET]FL */ +#define VKI_FD_CLOEXEC 1 /* actually anything with low bit set goes */ + +#define VKI_O_NONBLOCK 04000 + +/* For nanosleep ... + from ./include/linux/time.h */ +struct vki_timespec { + /* time_t */ long tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +}; + +/* POSIX.1b structure for timer start values and intervals. */ +struct vki_itimerspec { + struct vki_timespec it_interval; + struct vki_timespec it_value; +}; + +/* STAT stuff + from /usr/src/linux-2.4.9-31/include/asm-i386/stat.h */ +struct vki_stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime; + unsigned long __unused1; + unsigned long st_mtime; + unsigned long __unused2; + unsigned long st_ctime; + unsigned long __unused3; + unsigned long __unused4; + unsigned long __unused5; +}; + + +/* To do with the ELF frame constructed by the kernel on a process' + stack just before it transfers control to the program's interpreter + (to use the ELF parlance). + Constants from /usr/src/linux-2.4.9-31/include/linux/elf.h + Logic from /usr/src/linux-2.4.9-31/fs/binfmt_elf.c + and its counterpart in the 2.2.14 kernel sources + in Red Hat 6.2. */ +#define VKI_AT_NULL 0 +#define VKI_AT_SYSINFO 32 /* address of system info page */ +#define VKI_AT_CLKTCK 17 /* frequency at which times() increments */ +#define VKI_AT_HWCAP 16 /* arch dependent hints at CPU capabilities */ +#define VKI_AT_BASE 7 /* base address of interpreter */ +#define VKI_AT_PAGESZ 6 /* system page size */ +#define VKI_AT_PHNUM 5 /* number of program headers */ +#define VKI_AT_PHENT 4 /* size of program header entry */ +#define VKI_AT_PHDR 3 /* program headers for program */ +#define VKI_AT_USER_AUX_SEGMENT 23 /* tell glibc what address segment + 0x3B points to. (Needed for + Red Hat Limbo, 7.3.92) */ + +/* Including leads to loads of hassle because then we + need sometimes (RedHat 7.3) and that is a + kernel-only header which deliberately #errors on gcc-3.1. Mucho + hassle considering that we only want to know sizeof(struct module). + Hence ... + + #include + #include + #include + + int main ( void ) + { + printf ("sizeof(struct module) = %d\n", sizeof(struct module) ); + return 0; + } +*/ + +#define VKI_SIZEOF_STRUCT_MODULE 96 + + +/* This is the structure passed to the modify_ldt syscall. Just so as + to confuse and annoy everyone, this is _not_ the same as an + VgLdtEntry and has to be translated into such. The logic for doing + so, in vg_ldt.c, is copied from the kernel sources. */ +/* + * ldt.h + * + * Definitions of structures used with the modify_ldt system call. + */ +typedef struct vki_modify_ldt_ldt_s { + unsigned int entry_number; + unsigned long base_addr; + unsigned int limit; + unsigned int seg_32bit:1; + unsigned int contents:2; + unsigned int read_exec_only:1; + unsigned int limit_in_pages:1; + unsigned int seg_not_present:1; + unsigned int useable:1; + unsigned int reserved:25; +} vki_modify_ldt_t; + +#define VKI_MODIFY_LDT_CONTENTS_DATA 0 +#define VKI_MODIFY_LDT_CONTENTS_STACK 1 +#define VKI_MODIFY_LDT_CONTENTS_CODE 2 + +#define VKI_GDT_TLS_ENTRIES 3 +#define VKI_GDT_TLS_MIN 6 +#define VKI_GDT_TLS_MAX (VKI_GDT_TLS_MIN + VKI_GDT_TLS_ENTRIES) + +/* Flags for clone() */ +/* linux/sched.h */ +#define VKI_CSIGNAL 0x000000ff /* signal mask to be sent at exit */ +#define VKI_CLONE_VM 0x00000100 /* set if VM shared between processes */ +#define VKI_CLONE_FS 0x00000200 /* set if fs info shared between processes */ +#define VKI_CLONE_FILES 0x00000400 /* set if open files shared between processes */ +#define VKI_CLONE_SIGHAND 0x00000800 /* set if signal handlers and blocked signals shared */ +#define VKI_CLONE_IDLETASK 0x00001000 /* set if new pid should be 0 (kernel only)*/ +#define VKI_CLONE_PTRACE 0x00002000 /* set if we want to let tracing continue on the child too */ +#define VKI_CLONE_VFORK 0x00004000 /* set if the parent wants the child to wake it up on mm_release */ +#define VKI_CLONE_PARENT 0x00008000 /* set if we want to have the same parent as the cloner */ +#define VKI_CLONE_THREAD 0x00010000 /* Same thread group? */ +#define VKI_CLONE_NEWNS 0x00020000 /* New namespace group? */ +#define VKI_CLONE_SYSVSEM 0x00040000 /* share system V SEM_UNDO semantics */ +#define VKI_CLONE_SETTLS 0x00080000 /* create a new TLS for the child */ +#define VKI_CLONE_PARENT_SETTID 0x00100000 /* set the TID in the parent */ +#define VKI_CLONE_CHILD_CLEARTID 0x00200000 /* clear the TID in the child */ +#define VKI_CLONE_DETACHED 0x00400000 /* parent wants no child-exit signal */ +#define VKI_CLONE_UNTRACED 0x00800000 /* set if the tracing process can't force VKI_CLONE_PTRACE on this clone */ +#define VKI_CLONE_CHILD_SETTID 0x01000000 /* set the TID in the child */ + +/* This is the structure passed to the getdents syscall. */ +/* + * linux/dirent.h + */ +typedef struct vki_dirent { + long d_ino; + long d_off; + unsigned short d_reclen; + char d_name[256]; +} vki_dirent; + + + +/* This is the structure passed to the getrlimit syscall. */ +/* + * bits/resource.h + */ +typedef struct vki_rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +} vki_rlimit; + +#define VKI_RLIMIT_CPU 0 /* CPU time in ms */ +#define VKI_RLIMIT_FSIZE 1 /* Maximum filesize */ +#define VKI_RLIMIT_DATA 2 /* max data size */ +#define VKI_RLIMIT_STACK 3 /* max stack size */ +#define VKI_RLIMIT_CORE 4 /* max core file size */ +#define VKI_RLIMIT_RSS 5 /* max resident set size */ +#define VKI_RLIMIT_NPROC 6 /* max number of processes */ +#define VKI_RLIMIT_NOFILE 7 /* max number of open files */ +#define VKI_RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */ +#define VKI_RLIMIT_AS 9 /* address space limit */ +#define VKI_RLIMIT_LOCKS 10 /* maximum file locks held */ + +/* Socket stuff. */ +/* + * sys/socket.h + */ +typedef unsigned short vki_sa_family_t; +struct vki_sockaddr { + vki_sa_family_t sa_family; /* Address family. */ + char sa_data[14]; /* Address data. */ +}; + +/* statfs structs */ +/* + * bits/statfs.h + */ + +struct vki_statfs { + unsigned int f_type; + unsigned int f_bsize; + unsigned int f_blocks; + unsigned int f_bfree; + unsigned int f_bavail; + unsigned int f_files; + unsigned int f_ffree; + int f_fsid[ 2 ]; + unsigned int f_namelen; + unsigned int f_frsize; + unsigned int f_spare[5]; +}; + +struct vki_statfs64 { + unsigned int f_type; + unsigned int f_bsize; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_bavail; + unsigned long long f_files; + unsigned long long f_ffree; + int f_fsid[ 2 ]; + unsigned int f_namelen; + unsigned int f_frsize; + unsigned int f_spare[5]; +}; + +/* + * linux/futex.h + */ + +#define VKI_FUTEX_WAIT 0 +#define VKI_FUTEX_WAKE 1 +#define VKI_FUTEX_FD 2 +#define VKI_FUTEX_REQUEUE 3 + +/* + * linux/ipc.h + */ + +#define VKI_IPC_CREAT 00001000 /* create if key is nonexistent */ +#define VKI_IPC_EXCL 00002000 /* fail if key exists */ +#define VKI_IPC_NOWAIT 00004000 /* return error on wait */ + +/* + * linux/elfcore.h + */ + +struct elf_siginfo +{ + int si_signo; /* signal number */ + int si_code; /* extra code */ + int si_errno; /* errno */ +}; + +/* + * This is the old layout of "struct pt_regs", and + * is still the layout used by user mode (the new + * pt_regs doesn't have all registers as the kernel + * doesn't use the extra segment registers) + */ +struct user_regs_struct { + long ebx, ecx, edx, esi, edi, ebp, eax; + unsigned short ds, __ds, es, __es; + unsigned short fs, __fs, gs, __gs; + long orig_eax, eip; + unsigned short cs, __cs; + long eflags, esp; + unsigned short ss, __ss; +}; + +struct user_i387_struct { + long cwd; + long swd; + long twd; + long fip; + long fcs; + long foo; + long fos; + long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */ +}; + +struct user_fxsr_struct { + unsigned short cwd; + unsigned short swd; + unsigned short twd; + unsigned short fop; + long fip; + long fcs; + long foo; + long fos; + long mxcsr; + long reserved; + long st_space[32]; /* 8*16 bytes for each FP-reg = 128 bytes */ + long xmm_space[32]; /* 8*16 bytes for each XMM-reg = 128 bytes */ + long padding[56]; +}; + +typedef unsigned long elf_greg_t; + +#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t)) +typedef elf_greg_t elf_gregset_t[ELF_NGREG]; + +typedef struct user_i387_struct elf_fpregset_t; +typedef struct user_fxsr_struct elf_fpxregset_t; + + +/* + * Definitions to generate Intel SVR4-like core files. + * These mostly have the same names as the SVR4 types with "elf_" + * tacked on the front to prevent clashes with linux definitions, + * and the typedef forms have been avoided. This is mostly like + * the SVR4 structure, but more Linuxy, with things that Linux does + * not support and which gdb doesn't really use excluded. + * Fields present but not used are marked with "XXX". + */ +struct elf_prstatus +{ +#if 0 + long pr_flags; /* XXX Process flags */ + short pr_why; /* XXX Reason for process halt */ + short pr_what; /* XXX More detailed reason */ +#endif + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ +#if 0 + struct sigaltstack pr_altstack; /* Alternate stack info */ + struct sigaction pr_action; /* Signal action for current sig */ +#endif + Int pr_pid; + Int pr_ppid; + Int pr_pgrp; + Int pr_sid; + struct vki_timeval pr_utime; /* User time */ + struct vki_timeval pr_stime; /* System time */ + struct vki_timeval pr_cutime; /* Cumulative user time */ + struct vki_timeval pr_cstime; /* Cumulative system time */ +#if 0 + long pr_instr; /* Current instruction */ +#endif + elf_gregset_t pr_reg; /* GP registers */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + +#define ELF_PRARGSZ (80) /* Number of chars for args */ + +struct elf_prpsinfo +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + unsigned long pr_flag; /* flags */ + Int pr_uid; + Int pr_gid; + Int pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +/* + * linux/aio_abi.h + */ + +typedef struct { + unsigned id; /* kernel internal index number */ + unsigned nr; /* number of io_events */ + unsigned head; + unsigned tail; + + unsigned magic; + unsigned compat_features; + unsigned incompat_features; + unsigned header_length; /* size of aio_ring */ +} vki_aio_ring ; + +typedef vki_aio_ring *vki_aio_context_t; + +typedef struct { + ULong data; + ULong obj; + Long result; + Long result2; +} vki_io_event; + +typedef struct { + /* these are internal to the kernel/libc. */ + ULong aio_data; /* data to be returned in event's data */ + ULong aio_key; + /* the kernel sets aio_key to the req # */ + + /* common fields */ + UShort aio_lio_opcode; /* see IOCB_CMD_ above */ + UShort aio_reqprio; + UInt aio_fildes; + + ULong aio_buf; + ULong aio_nbytes; + Long aio_offset; + + /* extra parameters */ + ULong aio_reserved2; /* TODO: use this for a (struct sigevent *) */ + ULong aio_reserved3; +} vki_iocb; /* 64 bytes */ + +enum { + VKI_IOCB_CMD_PREAD = 0, + VKI_IOCB_CMD_PWRITE = 1, + VKI_IOCB_CMD_FSYNC = 2, + VKI_IOCB_CMD_FDSYNC = 3, + /* These two are experimental. + * IOCB_CMD_PREADX = 4, + * IOCB_CMD_POLL = 5, + */ + VKI_IOCB_CMD_NOOP = 6, +}; + +/* + * linux/mqueue.h + */ + +struct vki_mq_attr { + long mq_flags; /* message queue flags */ + long mq_maxmsg; /* maximum number of messages */ + long mq_msgsize; /* maximum message size */ + long mq_curmsgs; /* number of messages currently queued */ + long __reserved[4]; /* ignored for input, zeroed for output */ +}; + +#endif /* __VG_KERNELIFACE_H */ + +/*--------------------------------------------------------------------*/ +/*--- end vg_kerneliface.h ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/include/vg_profile.c b/VEX/head20041019/include/vg_profile.c new file mode 100644 index 0000000000..a5860ddc00 --- /dev/null +++ b/VEX/head20041019/include/vg_profile.c @@ -0,0 +1,173 @@ + +/*--------------------------------------------------------------------*/ +/*--- Profiling machinery. #include this file into a tool to ---*/ +/*--- enable --profile=yes, but not for release versions of tools, ---*/ +/*--- because it uses glibc code. ---*/ +/*--- vg_profile.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __VG_PROFILE_C +#define __VG_PROFILE_C + +#include "tool.h" + +/* get rid of these, if possible */ +#include +#include + +/* Override the empty definitions from tool.h */ +#undef VGP_PUSHCC +#undef VGP_POPCC +#define VGP_PUSHCC(x) if (VG_(clo_profile)) VGP_(pushcc)(x) +#define VGP_POPCC(x) if (VG_(clo_profile)) VGP_(popcc)(x) + +#define VGP_M_STACK 20 +#define VGP_MAX_CCS 50 + + +/* All zeroed initially because they're static */ +static Int vgp_nticks; + +static Int vgp_counts [VGP_MAX_CCS]; +static Int vgp_entries[VGP_MAX_CCS]; +static Char* vgp_names [VGP_MAX_CCS]; + +static Int vgp_sp; +static UInt vgp_stack[VGP_M_STACK]; + +/* These definitions override the panicking ones in vg_profile.c */ + +void VGP_(register_profile_event) ( Int n, Char* name ) +{ + /* Adjust for negative values */ + n += VgpUnc; + if (n >= VGP_MAX_CCS) { + VG_(printf)("\nProfile event #%d higher than VGP_MAX_CCS of %d.\n" + "If you really need this many profile events, increase\n" + "VGP_MAX_CCS and recompile Valgrind.\n", + n, VGP_MAX_CCS); + VG_(skin_panic)("profile event too high"); + } + if (vgp_names[n] != NULL) { + VG_(printf)("\nProfile event #%d being registered as `%s'\n" + "already registered as `%s'.\n" + "Note that tool and core event numbers must not overlap.\n", + n, name, vgp_names[n]); + VG_(skin_panic)("profile event already registered"); + } + + vgp_names[n] = name; +} + +void VGP_(tick) ( int sigNo ) +{ + Int cc; + vgp_nticks++; + cc = vgp_stack[vgp_sp]; + sk_assert(cc >= 0 && cc < VGP_MAX_CCS); + vgp_counts[ cc ]++; +} + +void VGP_(init_profiling) ( void ) +{ + struct itimerval value; + Int ret; + + /* Register core events... tricky macro definition causes + VGP_(register_profile_event)() to be called once for each core event + in VGP_CORE_LIST. */ + sk_assert(VgpUnc == 0); +# define VGP_PAIR(n,name) VGP_(register_profile_event)(n,name) + VGP_CORE_LIST; +# undef VGP_PAIR + + vgp_sp = -1; + VGP_(pushcc) ( VgpUnc ); + + value.it_interval.tv_sec = 0; + value.it_interval.tv_usec = 10 * 1000; + value.it_value = value.it_interval; + + signal(SIGPROF, VGP_(tick) ); + ret = setitimer(ITIMER_PROF, &value, NULL); + if (ret != 0) VG_(skin_panic)("vgp_init_profiling"); +} + +void VGP_(done_profiling) ( void ) +{ + Int i; + VG_(printf)("\nProfiling done, %d ticks\n", vgp_nticks); + for (i = 0; i < VGP_MAX_CCS; i++) + if (NULL != vgp_names[i]) + VG_(printf)( + "%2d: %4d (%3d %%%%) ticks, %10d entries for %s\n", + i, vgp_counts[i], + (Int)(1000.0 * (double)vgp_counts[i] / (double)vgp_nticks), + vgp_entries[i], vgp_names[i] ); +} + +void VGP_(pushcc) ( UInt cc ) +{ + if (vgp_sp >= VGP_M_STACK-1) { + VG_(printf)( + "\nMaximum profile stack depth (%d) reached for event #%d (`%s').\n" + "This is probably due to a VGP_(pushcc)() without a matching\n" + "VGP_(popcc)(). Make sure they all match.\n" + "Or if you are nesting profiling events very deeply, increase\n" + "VGP_M_STACK and recompile Valgrind.\n", + VGP_M_STACK, cc, vgp_names[cc]); + VG_(skin_panic)("Profiling stack overflow"); + } + vgp_sp++; + vgp_stack[vgp_sp] = cc; + vgp_entries[ cc ] ++; +} + +void VGP_(popcc) ( UInt cc ) +{ + if (vgp_sp <= 0) { + VG_(printf)( + "\nProfile stack underflow. This is due to a VGP_(popcc)() without\n" + "a matching VGP_(pushcc)(). Make sure they all match.\n"); + VG_(skin_panic)("Profiling stack underflow"); + } + if (vgp_stack[vgp_sp] != cc) { + Int i; + VG_(printf)("popping %s, stack looks like:\n", vgp_names[cc]); + for (i = vgp_sp; i >= 0; i--) + VG_(printf)("%2d: %s\n", i, vgp_names[vgp_stack[i]]); + VG_(exit)(1); + } + vgp_sp--; +} + +#endif /* __VG_PROFILE_C */ + +/*--------------------------------------------------------------------*/ +/*--- end vg_profile.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/include/vg_skin.h b/VEX/head20041019/include/vg_skin.h new file mode 100644 index 0000000000..0628888d96 --- /dev/null +++ b/VEX/head20041019/include/vg_skin.h @@ -0,0 +1,3 @@ +// "vg_skin.h" was renamed to "tool.h"; this is for backward compatibility +// with old external tools. +#include "tool.h" diff --git a/VEX/head20041019/include/x86/.cvsignore b/VEX/head20041019/include/x86/.cvsignore new file mode 100644 index 0000000000..282522db03 --- /dev/null +++ b/VEX/head20041019/include/x86/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/VEX/head20041019/include/x86/CVS/Entries b/VEX/head20041019/include/x86/CVS/Entries new file mode 100644 index 0000000000..5b45ed13ce --- /dev/null +++ b/VEX/head20041019/include/x86/CVS/Entries @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Fri Sep 3 13:45:29 2004// +/Makefile.am/1.1/Fri Sep 3 13:45:29 2004// +/tool_arch.h/1.4/Tue Sep 7 10:17:02 2004// +D diff --git a/VEX/head20041019/include/x86/CVS/Repository b/VEX/head20041019/include/x86/CVS/Repository new file mode 100644 index 0000000000..ca641eb5f4 --- /dev/null +++ b/VEX/head20041019/include/x86/CVS/Repository @@ -0,0 +1 @@ +valgrind/include/x86 diff --git a/VEX/head20041019/include/x86/CVS/Root b/VEX/head20041019/include/x86/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/include/x86/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/include/x86/CVS/Template b/VEX/head20041019/include/x86/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/include/x86/Makefile.am b/VEX/head20041019/include/x86/Makefile.am new file mode 100644 index 0000000000..7d61728860 --- /dev/null +++ b/VEX/head20041019/include/x86/Makefile.am @@ -0,0 +1,4 @@ +incincdir = $(includedir)/valgrind/x86 + +incinc_HEADERS = tool_arch.h + diff --git a/VEX/head20041019/include/x86/tool_arch.h b/VEX/head20041019/include/x86/tool_arch.h new file mode 100644 index 0000000000..34c1cd2d30 --- /dev/null +++ b/VEX/head20041019/include/x86/tool_arch.h @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------*/ +/*--- x86/tool_arch.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Valgrind, an extensible x86 protected-mode + emulator for monitoring program execution on x86-Unixes. + + Copyright (C) 2000-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __X86_TOOL_ARCH_H +#define __X86_TOOL_ARCH_H + +// XXX: eventually a lot of the stuff in this file can be made private to +// the x86/ subdir, and not visible to the core. But as long as the core +// still refers to them, they'll have to stay in here. + +/*====================================================================*/ +/*=== Registers, etc ===*/ +/*====================================================================*/ + +#define REGPARM(n) __attribute__((regparm(n))) + +#define FIRST_ARCH_REG R_EAX +#define LAST_ARCH_REG R_EDI + +#define N_ARCH_REGS 8 + +#define MIN_INSTR_SIZE 1 +#define MAX_INSTR_SIZE 16 + +/* Total number of integer registers available for allocation -- all of + them except %esp (points to Valgrind's stack) and %ebp (permanently + points at the baseBlock). + + If you increase this you'll have to also change at least these: + - VG_(rank_to_realreg)() + - VG_(realreg_to_rank)() + - ppRegsLiveness() + - the RegsLive type (maybe -- RegsLive type must have more than + VG_MAX_REALREGS bits) + + You can decrease it, and performance will drop because more spills will + occur. If you decrease it too much, everything will fall over. + + Do not change this unless you really know what you are doing! */ +#define VG_MAX_REALREGS 6 + + +/*====================================================================*/ +/*=== Instrumenting UCode ===*/ +/*====================================================================*/ + +/* ------------------------------------------------------------------ */ +/* Offsets of addresses of helper functions. A "helper" function is one + which is called from generated code via CALLM. */ + +// XXX: eventually these should be private to the x86 part, not visible to +// tools, and the IR should provide a better way than this to see what the +// original instruction was. + +extern Int VGOFF_(helper_idiv_64_32); +extern Int VGOFF_(helper_div_64_32); +extern Int VGOFF_(helper_idiv_32_16); +extern Int VGOFF_(helper_div_32_16); +extern Int VGOFF_(helper_idiv_16_8); +extern Int VGOFF_(helper_div_16_8); + +extern Int VGOFF_(helper_imul_32_64); +extern Int VGOFF_(helper_mul_32_64); +extern Int VGOFF_(helper_imul_16_32); +extern Int VGOFF_(helper_mul_16_32); +extern Int VGOFF_(helper_imul_8_16); +extern Int VGOFF_(helper_mul_8_16); + +extern Int VGOFF_(helper_CLD); +extern Int VGOFF_(helper_STD); +extern Int VGOFF_(helper_get_dirflag); + +extern Int VGOFF_(helper_CLC); +extern Int VGOFF_(helper_STC); +extern Int VGOFF_(helper_CMC); + +extern Int VGOFF_(helper_shldl); +extern Int VGOFF_(helper_shldw); +extern Int VGOFF_(helper_shrdl); +extern Int VGOFF_(helper_shrdw); + +extern Int VGOFF_(helper_RDTSC); +extern Int VGOFF_(helper_CPUID); + +extern Int VGOFF_(helper_IN); +extern Int VGOFF_(helper_OUT); + +extern Int VGOFF_(helper_bsfw); +extern Int VGOFF_(helper_bsfl); +extern Int VGOFF_(helper_bsrw); +extern Int VGOFF_(helper_bsrl); + +extern Int VGOFF_(helper_fstsw_AX); +extern Int VGOFF_(helper_SAHF); +extern Int VGOFF_(helper_LAHF); +extern Int VGOFF_(helper_DAS); +extern Int VGOFF_(helper_DAA); +extern Int VGOFF_(helper_AAS); +extern Int VGOFF_(helper_AAA); +extern Int VGOFF_(helper_AAD); +extern Int VGOFF_(helper_AAM); + +extern Int VGOFF_(helper_cmpxchg8b); + + +#endif // __X86_TOOL_ARCH_H + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/lackey/.cvsignore b/VEX/head20041019/lackey/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/lackey/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/lackey/CVS/Entries b/VEX/head20041019/lackey/CVS/Entries new file mode 100644 index 0000000000..325e851deb --- /dev/null +++ b/VEX/head20041019/lackey/CVS/Entries @@ -0,0 +1,5 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:33 2002// +/Makefile.am/1.46/Wed Sep 1 23:20:48 2004// +/lk_main.c/1.24/Thu Sep 2 08:51:42 2004// +D/docs//// +D/tests//// diff --git a/VEX/head20041019/lackey/CVS/Repository b/VEX/head20041019/lackey/CVS/Repository new file mode 100644 index 0000000000..a3bb075cd9 --- /dev/null +++ b/VEX/head20041019/lackey/CVS/Repository @@ -0,0 +1 @@ +valgrind/lackey diff --git a/VEX/head20041019/lackey/CVS/Root b/VEX/head20041019/lackey/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/lackey/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/lackey/CVS/Template b/VEX/head20041019/lackey/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/lackey/Makefile.am b/VEX/head20041019/lackey/Makefile.am new file mode 100644 index 0000000000..a21574b9e4 --- /dev/null +++ b/VEX/head20041019/lackey/Makefile.am @@ -0,0 +1,7 @@ +include $(top_srcdir)/Makefile.tool.am + +val_PROGRAMS = vgskin_lackey.so + +vgskin_lackey_so_SOURCES = lk_main.c +vgskin_lackey_so_LDFLAGS = -shared + diff --git a/VEX/head20041019/lackey/docs/.cvsignore b/VEX/head20041019/lackey/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/lackey/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/lackey/docs/CVS/Entries b/VEX/head20041019/lackey/docs/CVS/Entries new file mode 100644 index 0000000000..0455f9b22c --- /dev/null +++ b/VEX/head20041019/lackey/docs/CVS/Entries @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Thu Oct 3 10:07:34 2002// +/Makefile.am/1.3/Wed Aug 25 11:40:06 2004// +/lk_main.html/1.4/Sun Jan 4 16:43:22 2004// +D diff --git a/VEX/head20041019/lackey/docs/CVS/Repository b/VEX/head20041019/lackey/docs/CVS/Repository new file mode 100644 index 0000000000..d58e8e9549 --- /dev/null +++ b/VEX/head20041019/lackey/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/lackey/docs diff --git a/VEX/head20041019/lackey/docs/CVS/Root b/VEX/head20041019/lackey/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/lackey/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/lackey/docs/CVS/Template b/VEX/head20041019/lackey/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/lackey/docs/Makefile.am b/VEX/head20041019/lackey/docs/Makefile.am new file mode 100644 index 0000000000..4872f33446 --- /dev/null +++ b/VEX/head20041019/lackey/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = lk_main.html diff --git a/VEX/head20041019/lackey/docs/lk_main.html b/VEX/head20041019/lackey/docs/lk_main.html new file mode 100644 index 0000000000..a6f22a00c1 --- /dev/null +++ b/VEX/head20041019/lackey/docs/lk_main.html @@ -0,0 +1,68 @@ + + + + Cachegrind + + + + + +

Lackey

+
This manual was last updated on 2002-10-03
+

+ +

+njn25@cam.ac.uk
+Copyright © 2002-2004 Nicholas Nethercote +

+Lackey is licensed under the GNU General Public License, +version 2
+Lackey is an example Valgrind tool that does some very basic program +measurement. +

+ +

+ +

1  Lackey

+ +Lackey is a simple Valgrind tool that does some basic program measurement. +It adds quite a lot of simple instrumentation to the program's code. It is +primarily intended to be of use as an example tool. +

+It measures three things: + +

    +
  1. The number of calls to _dl_runtime_resolve(), the function + in glibc's dynamic linker that resolves function lookups into shared + objects.

    + +

  2. The number of UCode instructions (UCode is Valgrind's RISC-like + intermediate language), x86 instructions, and basic blocks executed by the + program, and some ratios between the three counts.

    + +

  3. The number of conditional branches encountered and the proportion of those + taken.

    +

+ +
+ + + diff --git a/VEX/head20041019/lackey/lk_main.c b/VEX/head20041019/lackey/lk_main.c new file mode 100644 index 0000000000..1d3aefd8b8 --- /dev/null +++ b/VEX/head20041019/lackey/lk_main.c @@ -0,0 +1,231 @@ + +/*--------------------------------------------------------------------*/ +/*--- Simple tool for counting UInstrs, using a C helper. ---*/ +/*--- lk_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Lackey, an example Valgrind tool that does + some simple program measurement. + + Copyright (C) 2002-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "tool.h" + +/* Nb: use ULongs because the numbers can get very big */ +static ULong n_dlrr_calls = 0; +static ULong n_BBs = 0; +static ULong n_UInstrs = 0; +static ULong n_x86_instrs = 0; +static ULong n_Jccs = 0; +static ULong n_Jccs_untaken = 0; + +static void add_one_dlrr_call(void) +{ + n_dlrr_calls++; +} + +/* See comment above SK_(instrument) for reason why n_x86_instrs is + incremented here. */ +static void add_one_BB(void) +{ + n_BBs++; + n_x86_instrs++; +} + +static void add_one_UInstr(void) +{ + n_UInstrs++; +} + +static void add_one_x86_instr(void) +{ + n_x86_instrs++; +} + +static void add_one_Jcc(void) +{ + n_Jccs++; +} + +static void add_one_Jcc_untaken(void) +{ + n_Jccs_untaken++; +} + +void SK_(pre_clo_init)(void) +{ + VG_(details_name) ("Lackey"); + VG_(details_version) (NULL); + VG_(details_description) ("an example Valgrind tool"); + VG_(details_copyright_author)( + "Copyright (C) 2002-2004, and GNU GPL'd, by Nicholas Nethercote."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + VG_(details_avg_translation_sizeB) ( 175 ); + + VG_(register_compact_helper)((Addr) & add_one_dlrr_call); + VG_(register_compact_helper)((Addr) & add_one_BB); + VG_(register_compact_helper)((Addr) & add_one_x86_instr); + VG_(register_compact_helper)((Addr) & add_one_UInstr); + VG_(register_compact_helper)((Addr) & add_one_Jcc); + VG_(register_compact_helper)((Addr) & add_one_Jcc_untaken); +} + +void SK_(post_clo_init)(void) +{ +} + +/* Note: x86 instructions are marked by an INCEIP at the end of each one, + except for the final one in the basic block which ends in an + unconditional JMP. Sometimes the final unconditional JMP is preceded by + a conditional JMP (Jcc), and thus it isn't reached. Eg: + + + INCEIP ... + + + Jcc ... + JMP ... (will not be reached if Jcc succeeds) + + If we simplemindedly added calls to add_one_x86_instr() before INCEIPs + and unconditional JMPs, we'd sometimes miss the final call (when a + preceding conditional JMP succeeds), underestimating the x86 instruction + count. + + + call add_one_x86_instr() + INCEIP ... + + + Jcc ... + call add_one_x86_instr() + JMP ... + + Instead we add a call before each INCEIP, and also one at the start of the + block, but not one at the end, viz: + + call add_one_x86_instr() + + + call add_one_x86_instr() + INCEIP ... + + + Jcc ... + JMP ... + + Which gives us the right answer. And just to avoid two C calls, we fold + the basic-block-beginning call in with add_one_BB(). Phew. +*/ +UCodeBlock* SK_(instrument)(UCodeBlock* cb_in, Addr orig_addr) +{ + UCodeBlock* cb; + Int i; + UInstr* u; + Char fnname[100]; + + cb = VG_(setup_UCodeBlock)(cb_in); + + /* Count call to dlrr(), if this BB is dlrr()'s entry point */ + if (VG_(get_fnname_if_entry)(orig_addr, fnname, 100) && + 0 == VG_(strcmp)(fnname, "_dl_runtime_resolve")) + { + VG_(call_helper_0_0)(cb, (Addr) & add_one_dlrr_call); + } + + /* Count basic block */ + VG_(call_helper_0_0)(cb, (Addr) & add_one_BB); + + for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { + u = VG_(get_instr)(cb_in, i); + + switch (u->opcode) { + case NOP: case LOCK: case CALLM_S: case CALLM_E: + break; + + case INCEIP: + /* Count x86 instr */ + VG_(call_helper_0_0)(cb, (Addr) & add_one_x86_instr); + VG_(copy_UInstr)(cb, u); + break; + + case JMP: + if (u->cond != CondAlways) { + /* Count Jcc */ + VG_(call_helper_0_0)(cb, (Addr) & add_one_Jcc); + VG_(copy_UInstr)(cb, u); + /* Count non-taken Jcc */ + VG_(call_helper_0_0)(cb, (Addr) & add_one_Jcc_untaken); + } else { + VG_(copy_UInstr)(cb, u); + } + break; + + default: + /* Count UInstr */ + VG_(call_helper_0_0)(cb, (Addr) & add_one_UInstr); + VG_(copy_UInstr)(cb, u); + break; + } + } + + VG_(free_UCodeBlock)(cb_in); + return cb; +} + +void SK_(fini)(Int exitcode) +{ + VG_(message)(Vg_UserMsg, + "Counted %d calls to _dl_runtime_resolve()", n_dlrr_calls); + + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, "Executed:"); + VG_(message)(Vg_UserMsg, " BBs: %u", n_BBs); + VG_(message)(Vg_UserMsg, " x86 instrs: %u", n_x86_instrs); + VG_(message)(Vg_UserMsg, " UInstrs: %u", n_UInstrs); + + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, "Jccs:"); + VG_(message)(Vg_UserMsg, " total: %u", n_Jccs); + VG_(message)(Vg_UserMsg, " %% taken: %u%%", + (n_Jccs - n_Jccs_untaken)*100 / n_Jccs); + + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, "Ratios:"); + VG_(message)(Vg_UserMsg, " x86 instrs : BB = %3u : 10", + 10 * n_x86_instrs / n_BBs); + VG_(message)(Vg_UserMsg, " UInstrs : BB = %3u : 10", + 10 * n_UInstrs / n_BBs); + VG_(message)(Vg_UserMsg, " UInstrs : x86_instr = %3u : 10", + 10 * n_UInstrs / n_x86_instrs); + + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode); +} + +VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 0) + + +/*--------------------------------------------------------------------*/ +/*--- end lk_main.c ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/VEX/head20041019/lackey/tests/.cvsignore b/VEX/head20041019/lackey/tests/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/lackey/tests/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/lackey/tests/CVS/Entries b/VEX/head20041019/lackey/tests/CVS/Entries new file mode 100644 index 0000000000..967349db77 --- /dev/null +++ b/VEX/head20041019/lackey/tests/CVS/Entries @@ -0,0 +1,6 @@ +/.cvsignore/1.1/Fri Oct 4 11:35:47 2002// +/Makefile.am/1.3/Thu Jun 12 14:12:58 2003// +/filter_stderr/1.2/Tue Apr 22 21:41:38 2003// +/true.stderr.exp/1.1/Fri Oct 4 11:35:47 2002// +/true.vgtest/1.1/Fri Oct 4 11:35:47 2002// +D diff --git a/VEX/head20041019/lackey/tests/CVS/Repository b/VEX/head20041019/lackey/tests/CVS/Repository new file mode 100644 index 0000000000..4848b644f6 --- /dev/null +++ b/VEX/head20041019/lackey/tests/CVS/Repository @@ -0,0 +1 @@ +valgrind/lackey/tests diff --git a/VEX/head20041019/lackey/tests/CVS/Root b/VEX/head20041019/lackey/tests/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/lackey/tests/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/lackey/tests/CVS/Template b/VEX/head20041019/lackey/tests/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/lackey/tests/Makefile.am b/VEX/head20041019/lackey/tests/Makefile.am new file mode 100644 index 0000000000..e586af521e --- /dev/null +++ b/VEX/head20041019/lackey/tests/Makefile.am @@ -0,0 +1,4 @@ +noinst_SCRIPTS = filter_stderr + +EXTRA_DIST = $(noinst_SCRIPTS) \ + true.stderr.exp true.vgtest diff --git a/VEX/head20041019/lackey/tests/filter_stderr b/VEX/head20041019/lackey/tests/filter_stderr new file mode 100755 index 0000000000..1f415be4b4 --- /dev/null +++ b/VEX/head20041019/lackey/tests/filter_stderr @@ -0,0 +1,31 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic | + +# Output looks like this... +# +# Counted 53 calls to _dl_runtime_resolve() +# +# Executed: +# BBs: 47131 +# x86 instrs: 193330 +# UInstrs: 523996 +# +# Jccs: +# total: 36368 +# % taken: 58% +# +# Ratios: +# x86 instrs : BB = 41 : 10 +# UInstrs : BB = 111 : 10 +# UInstrs : x86_instr = 27 : 10 +# +# Exit code: 0 +# +# ...so chop all lines between first and last (inclusive) +sed "/^Counted [0-9]\+ calls to _dl_runtime_resolve()$/ , \ + /UInstrs : x86_instrs = [0-9]\+ : [0-9]\+/ \ + d" + diff --git a/VEX/head20041019/lackey/tests/true.stderr.exp b/VEX/head20041019/lackey/tests/true.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/lackey/tests/true.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/lackey/tests/true.vgtest b/VEX/head20041019/lackey/tests/true.vgtest new file mode 100644 index 0000000000..24fc0a48c5 --- /dev/null +++ b/VEX/head20041019/lackey/tests/true.vgtest @@ -0,0 +1 @@ +prog: ../../tests/true diff --git a/VEX/head20041019/make-uninstall-docs b/VEX/head20041019/make-uninstall-docs new file mode 100755 index 0000000000..cc0db79bfd --- /dev/null +++ b/VEX/head20041019/make-uninstall-docs @@ -0,0 +1,21 @@ +#!/bin/sh + +# small tool to help documentation writers. +# Copy docs out of an installation tree (`pwd`/Inst) back to the build tree +# since it is a lot easier to edit them in the installation tree. +# Use with care! + +cp Inst/share/doc/valgrind/coregrind_core.html coregrind/docs +cp Inst/share/doc/valgrind/coregrind_intro.html coregrind/docs +cp Inst/share/doc/valgrind/coregrind_tools.html coregrind/docs +cp Inst/share/doc/valgrind/manual.html docs +cp Inst/share/doc/valgrind/ac_main.html addrcheck/docs +cp Inst/share/doc/valgrind/mc_main.html memcheck/docs +cp Inst/share/doc/valgrind/mc_techdocs.html memcheck/docs +cp Inst/share/doc/valgrind/cg_main.html cachegrind/docs +cp Inst/share/doc/valgrind/cg_techdocs.html cachegrind/docs +cp Inst/share/doc/valgrind/cc_main.html corecheck/docs +cp Inst/share/doc/valgrind/hg_main.html helgrind/docs +cp Inst/share/doc/valgrind/lk_main.html lackey/docs +cp Inst/share/doc/valgrind/nl_main.html none/docs + diff --git a/VEX/head20041019/massif/.cvsignore b/VEX/head20041019/massif/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/massif/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/massif/CVS/Entries b/VEX/head20041019/massif/CVS/Entries new file mode 100644 index 0000000000..7e5db8aace --- /dev/null +++ b/VEX/head20041019/massif/CVS/Entries @@ -0,0 +1,6 @@ +/.cvsignore/1.1/Sun Feb 22 19:34:55 2004// +/Makefile.am/1.5/Wed Sep 1 23:20:48 2004// +/ms_main.c/1.16/Mon Sep 13 13:27:30 2004// +D/docs//// +D/hp2ps//// +D/tests//// diff --git a/VEX/head20041019/massif/CVS/Repository b/VEX/head20041019/massif/CVS/Repository new file mode 100644 index 0000000000..0d85ece015 --- /dev/null +++ b/VEX/head20041019/massif/CVS/Repository @@ -0,0 +1 @@ +valgrind/massif diff --git a/VEX/head20041019/massif/CVS/Root b/VEX/head20041019/massif/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/massif/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/massif/CVS/Template b/VEX/head20041019/massif/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/massif/Makefile.am b/VEX/head20041019/massif/Makefile.am new file mode 100644 index 0000000000..620c4f1d87 --- /dev/null +++ b/VEX/head20041019/massif/Makefile.am @@ -0,0 +1,14 @@ +include $(top_srcdir)/Makefile.tool.am + +SUBDIRS += hp2ps + +val_PROGRAMS = vgskin_massif.so vgpreload_massif.so + +vgskin_massif_so_SOURCES = ms_main.c +vgskin_massif_so_LDFLAGS = -shared + +vgpreload_massif_so_SOURCES = +vgpreload_massif_so_LDADD = $(top_builddir)/coregrind/vg_replace_malloc.o +vgpreload_massif_so_DEPENDENCIES = $(top_builddir)/coregrind/vg_replace_malloc.o +vgpreload_massif_so_LDFLAGS = -shared -Wl,-z,interpose,-z,initfirst + diff --git a/VEX/head20041019/massif/docs/.cvsignore b/VEX/head20041019/massif/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/massif/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/massif/docs/CVS/Entries b/VEX/head20041019/massif/docs/CVS/Entries new file mode 100644 index 0000000000..dd8835ecf2 --- /dev/null +++ b/VEX/head20041019/massif/docs/CVS/Entries @@ -0,0 +1,5 @@ +/.cvsignore/1.1/Sun Feb 22 19:34:55 2004// +/Makefile.am/1.2/Wed Aug 25 11:40:06 2004// +/date.gif/1.1/Sat Feb 14 16:40:01 2004/-kb/ +/ms_main.html/1.1/Sat Feb 14 16:40:01 2004// +D diff --git a/VEX/head20041019/massif/docs/CVS/Repository b/VEX/head20041019/massif/docs/CVS/Repository new file mode 100644 index 0000000000..23a9639a1f --- /dev/null +++ b/VEX/head20041019/massif/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/massif/docs diff --git a/VEX/head20041019/massif/docs/CVS/Root b/VEX/head20041019/massif/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/massif/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/massif/docs/CVS/Template b/VEX/head20041019/massif/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/massif/docs/Makefile.am b/VEX/head20041019/massif/docs/Makefile.am new file mode 100644 index 0000000000..a53c352321 --- /dev/null +++ b/VEX/head20041019/massif/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = ms_main.html date.gif diff --git a/VEX/head20041019/massif/docs/date.gif b/VEX/head20041019/massif/docs/date.gif new file mode 100644 index 0000000000..eff527acec Binary files /dev/null and b/VEX/head20041019/massif/docs/date.gif differ diff --git a/VEX/head20041019/massif/docs/ms_main.html b/VEX/head20041019/massif/docs/ms_main.html new file mode 100644 index 0000000000..87d1abcf95 --- /dev/null +++ b/VEX/head20041019/massif/docs/ms_main.html @@ -0,0 +1,331 @@ + + + Massif: a heap profiler + + + + +

Massif: a heap profiler

+ +To use this tool, you must specify --tool=massif +on the Valgrind command line. + + +

7.1  Heap profiling

+Massif is a heap profiler, i.e. it measures how much heap memory programs use. +In particular, it can give you information about: +
    +
  • Heap blocks; +
  • Heap administration blocks; +
  • Stack sizes. +
+ +Heap profiling is useful to help you reduce the amount of memory your program +uses. On modern machines with virtual memory, this provides the following +benefits: +
    +
  • It can speed up your program -- a smaller program will interact better + with your machine's caches, avoid paging, and so on. + +
  • If your program uses lots of memory, it will reduce the chance that it + exhausts your machine's swap space. +
+ +Also, there are certain space leaks that aren't detected by traditional +leak-checkers, such as Memcheck's. That's because the memory isn't ever +actually lost -- a pointer remains to it -- but it's not in use. Programs +that have leaks like this can unnecessarily increase the amount of memory +they are using over time. +

+ + + +

7.2  Why Use a Heap Profiler?

+ +Everybody knows how useful time profilers are for speeding up programs. They +are particularly useful because people are notoriously bad at predicting where +are the bottlenecks in their programs. +

+But the story is different for heap profilers. Some programming languages, +particularly lazy functional languages like Haskell, have quite sophisticated heap +profilers. But there are few tools as powerful for profiling C and C++ +programs. +

+Why is this? Maybe it's because C and C++ programmers must think that +they know where the memory is being allocated. After all, you can see all the +calls to malloc() and new and new[], +right? But, in a big program, do you really know which heap allocations are +being executed, how many times, and how large each allocation is? Can you give +even a vague estimate of the memory footprint for your program? Do you know +this for all the libraries your program uses? What about administration bytes +required by the heap allocator to track heap blocks -- have you thought about +them? What about the stack? If you are unsure about any of these things, +maybe you should think about heap profiling. +

+Massif can tell you these things. +

+Or maybe it's because it's relatively easy to add basic heap profiling +functionality into a program, to tell you how many bytes you have allocated for +certain objects, or similar. But this information might only be simple like +total counts for the whole program's execution. What about space usage at +different points in the program's execution, for example? And reimplementing +heap profiling code for each project is a pain. +

+Massif can save you this effort. +

+ + + +

7.3  Overview

+First off, as for normal Valgrind use, you probably want to compile with +debugging info (the -g flag). But, as opposed to Memcheck, +you probably do want to turn optimisation on, since you should profile +your program as it will be normally run. +

+Then, run your program with valgrind --tool=massif in front of the +normal command line invocation. When the program finishes, Massif will print +summary space statistics. It also creates a graph representing the program's +heap usage in a file called massif.pid.ps, which can +be read by any PostScript viewer, such as Ghostview. +

+It also puts detailed information about heap consumption in a file file +massif.pid.txt (text format) or +massif.pid.html (HTML format), where +pid is the program's process id. +

+ + + +

7.4  Basic Results of Profiling

+ +To gather heap profiling information about the program prog, +type: +

+

+valgrind --tool=massif prog +
+

+The program will execute (slowly). Upon completion, summary statistics +that look like this will be printed: + +

+==27519== Total spacetime:   2,258,106 ms.B
+==27519== heap:              24.0%
+==27519== heap admin:         2.2%
+==27519== stack(s):          73.7%
+
+ +All measurements are done in spacetime, i.e. space (in bytes) multiplied +by time (in milliseconds). Note that because Massif slows a program down a +lot, the actual spacetime figure is fairly meaningless; it's the relative +values that are interesting. +

+Which entries you see in the breakdown depends on the command line options +given. The above example measures all the possible parts of memory: +

    +
  • Heap: number of words allocated on the heap, via malloc(), + new and new[]. +

    +

  • Heap admin: each heap block allocated requires some administration data, + which lets the allocator track certain things about the block. It is easy + to forget about this, and if your program allocates lots of small blocks, + it can add up. This value is an estimate of the space required for this + administration data. +

    +

  • Stack(s): the spacetime used by the programs' stack(s). (Threaded programs + can have multiple stacks.) This includes signal handler stacks. +

    +

+

+ + + +

7.5  Spacetime Graphs

+As well as printing summary information, Massif also creates a file +representing a spacetime graph, massif.pid.hp. +It will produce a file called massif.pid.ps, which can be +viewed in a PostScript viewer. +

+Massif uses a program called hp2ps to convert the raw data into +the PostScript graph. It's distributed with Massif, but came originally +from the Glasgow Haskell +Compiler. You shouldn't need to worry about this at all. However, if +the graph creation fails for any reason, Massif tell you, and will leave +behind a file named massif.pid.hp, containing the raw +heap profiling data. +

+Here's an example graph:
+ spacetime graph +

+The graph is broken into several bands. Most bands represent a single line of +your program that does some heap allocation; each such band represents all +the allocations and deallocations done from that line. Up to twenty bands are +shown; less significant allocation sites are merged into "other" and/or "OTHER" +bands. The accompanying text/HTML file produced by Massif has more detail +about these heap allocation bands. Then there are single bands for the +stack(s) and heap admin bytes. +

+Note: it's the height of a band that's important. Don't let the ups and downs +caused by other bands confuse you. For example, the +read_alias_file band in the example has the same height all the +time it's in existence. +

+The triangles on the x-axis show each point at which a memory census was taken. +These aren't necessarily evenly spread; Massif only takes a census when +memory is allocated or deallocated. The time on the x-axis is wallclock +time, which is not ideal because you can get different graphs for different +executions of the same program, due to random OS delays. But it's not too +bad, and it becomes less of a problem the longer a program runs. +

+Massif takes censuses at an appropriate timescale; censuses take place less +frequently as the program runs for longer. There is no point having more +than 100-200 censuses on a single graph. +

+The graphs give a good overview of where your program's space use comes from, +and how that varies over time. The accompanying text/HTML file gives a lot +more information about heap use. + + +

7.6  Details of Heap Allocations

+ +The text/HTML file contains information to help interpret the heap bands of the +graph. It also contains a lot of extra information about heap allocations that you don't see in the graph. +

+Here's part of the information that accompanies the above graph. + +


+== 0 ===========================
+Heap allocation functions accounted for 50.8% of measured spacetime
+

+Called from: +

    +
  • 22.1%: 0x401767D0: _nl_intern_locale_data (in /lib/i686/libc-2.3.2.so) +
  • 8.6%: 0x4017C393: read_alias_file (in /lib/i686/libc-2.3.2.so) + +
  • (several entries omitted) + +
  • and 6 other insignificant places
  • +
+
+The first part shows the total spacetime due to heap allocations, and the +places in the program where most memory was allocated (nb: if this program had +been compiled with -g, actual line numbers would be given). These +places are sorted, from most significant to least, and correspond to the bands +seen in the graph. Insignificant sites (accounting for less than 0.5% of total +spacetime) are omitted. +

+That alone can be useful, but often isn't enough. What if one of these +functions was called from several different places in the program? Which one +of these is responsible for most of the memory used? For +_nl_intern_locale_data(), this question is answered by clicking on +the 22.1% link, which takes us to the following part +of the file. + +


+

== 1 ===========================
+Context accounted for 22.1% of measured spacetime
+   0x401767D0: _nl_intern_locale_data (in /lib/i686/libc-2.3.2.so)
+

+Called from: +

    +
  • 22.1%: 0x40176F95: _nl_load_locale_from_archive (in /lib/i686/libc-2.3.2.so) +
+
+ +At this level, we can see all the places from which +_nl_load_locale_from_archive() was called such that it allocated +memory at 0x401767D0. (We can click on the top 22.1% +link to go back to the parent entry.) At this level, we have moved beyond the +information presented in the graph. In this case, it is only called from one +place. We can again follow the link for more detail, moving to the following +part of the file. + +
+

== 2 ===========================
+Context accounted for 22.1% of measured spacetime
+   0x401767D0: _nl_intern_locale_data (in /lib/i686/libc-2.3.2.so)
+   0x40176F95: _nl_load_locale_from_archive (in /lib/i686/libc-2.3.2.so)
+

+Called from: +

    +
  • 22.1%: 0x40176184: _nl_find_locale (in /lib/i686/libc-2.3.2.so) +
+
+ +In this way we can dig deeper into the call stack, to work out exactly what +sequence of calls led to some memory being allocated. At this point, with a +call depth of 3, the information runs out (thus the address of the child entry, +0x40176184, isn't a link). We could rerun the program with a greater +--depth value if we wanted more information. +

+Sometimes you will get a code location like this: +

    +
  • 30.8% : 0xFFFFFFFF: ??? +
+The code address isn't really 0xFFFFFFFF -- that's impossible. This is what +Massif does when it can't work out what the real code address is. +

+Massif produces this information in a plain text file by default, or HTML with +the --format=html option. The plain text version obviously +doesn't have the links, but a similar effect can be achieved by searching on +the code addresses. (In Vim, the '*' and '#' searches are ideal for this.) + + + +

7.7  Massif options

+ +Massif-specific options are: + +
    +
  • --heap=no
    + --heap=yes [default]
    + When enabled, profile heap usage in detail. Without it, the + massif.pid.txt or + massif.pid.html will be very short. +

    +

  • --heap-admin=n [default: 8]
    + The number of admin bytes per block to use. This can only be an + estimate of the average, since it may vary. The allocator used by + glibc requires somewhere between 4--15 bytes per block, + depending on various factors. It also requires admin space for freed + blocks, although Massif does not count this. +

    +

  • --stacks=no
    + --stacks=yes [default]
    + When enabled, include stack(s) in the profile. Threaded programs can + have multiple stacks. +

    +

  • --depth=n [default: 3]
    + Depth of call chains to present in the detailed heap information. + Increasing it will give more information, but Massif will run the program + more slowly, using more memory, and produce a bigger + .txt/.hp file. +

    +

  • --alloc-fn=name
    + Specify a function that allocates memory. This is useful for functions + that are wrappers to malloc(), which can fill up the context + information uselessly (and give very uninformative bands on the graph). + Functions specified will be ignored in contexts, i.e. treated as though + they were malloc(). This option can be specified multiple + times on the command line, to name multiple functions. +

    +

  • --format=text [default]
    + --format=html
    + Produce the detailed heap information in text or HTML format. The file + suffix used will be either .txt or .html. +

    +

+ + +

7.8  Accuracy

+The information should be pretty accurate. Some approximations made might +cause some allocation contexts to be attributed with less memory than they +actually allocated, but the amounts should be miniscule. +

+The heap admin spacetime figure is an approximation, as described above. If +anyone knows how to improve its accuracy, please let us know. + + + + diff --git a/VEX/head20041019/massif/hp2ps/.cvsignore b/VEX/head20041019/massif/hp2ps/.cvsignore new file mode 100644 index 0000000000..fa57f32c34 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/.cvsignore @@ -0,0 +1,3 @@ +Makefile.in +Makefile +hp2ps diff --git a/VEX/head20041019/massif/hp2ps/AreaBelow.c b/VEX/head20041019/massif/hp2ps/AreaBelow.c new file mode 100644 index 0000000000..e14ed5ebed --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/AreaBelow.c @@ -0,0 +1,66 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Error.h" +#include "HpFile.h" +#include "Utilities.h" + +/* own stuff */ +#include "AreaBelow.h" + +/* + * Return the area enclosed by all of the curves. The algorithm + * used is the same as the trapizoidal rule for integration. + */ + +floatish +AreaBelow() +{ + intish i; + intish j; + intish bucket; + floatish value; + struct chunk *ch; + floatish area; + floatish trap; + floatish base; + floatish *maxima; + + maxima = (floatish *) xmalloc(nsamples * sizeof(floatish)); + for (i = 0; i < nsamples; i++) { + maxima[i] = 0.0; + } + + for (i = 0; i < nidents; i++) { + for (ch = identtable[i]->chk; ch; ch = ch->next) { + for (j = 0; j < ch->nd; j++) { + bucket = ch->d[j].bucket; + value = ch->d[j].value; + if (bucket >= nsamples) + Disaster("bucket out of range"); + maxima[ bucket ] += value; + } + } + } + + area = 0.0; + + for (i = 1; i < nsamples; i++) { + base = samplemap[i] - samplemap[i-1]; + if (maxima[i] > maxima[i-1]) { + trap = base * maxima[i-1] + ((base * (maxima[i] - maxima[i-1]))/ 2.0); + } else { + trap = base * maxima[i] + ((base * (maxima[i-1] - maxima[i]))/ 2.0); + } + + area += trap; + } + + free(maxima); + return area; +} diff --git a/VEX/head20041019/massif/hp2ps/AreaBelow.h b/VEX/head20041019/massif/hp2ps/AreaBelow.h new file mode 100644 index 0000000000..a4c20d9345 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/AreaBelow.h @@ -0,0 +1,10 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef AREA_BELOW_H +#define AREA_BELOW_H + +floatish AreaBelow PROTO((void)); + +#endif /* AREA_BELOW_H */ diff --git a/VEX/head20041019/massif/hp2ps/AuxFile.c b/VEX/head20041019/massif/hp2ps/AuxFile.c new file mode 100644 index 0000000000..dfc26cdb1d --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/AuxFile.c @@ -0,0 +1,172 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Shade.h" +#include "Error.h" +#include "HpFile.h" +#include "Reorder.h" + +/* own stuff */ +#include "AuxFile.h" + +static void GetAuxLine PROTO((FILE *)); /* forward */ +static void GetAuxTok PROTO((FILE *)); /* forward */ + +void +GetAuxFile(auxfp) + FILE* auxfp; +{ + g_ch = ' '; + endfile = 0; + linenum = 1; + + GetAuxTok(auxfp); + + while (endfile == 0) { + GetAuxLine(auxfp); + } + + fclose(auxfp); +} + + + +/* + * Read the next line from the aux file, check the syntax, and + * perform the appropriate action. + */ + +static void +GetAuxLine(auxfp) + FILE* auxfp; +{ + switch (thetok) { + case X_RANGE_TOK: + GetAuxTok(auxfp); + if (thetok != FLOAT_TOK) { + Error("%s, line %d, floating point number must follow X_RANGE", + auxfile, linenum); + } + auxxrange = thefloatish; + GetAuxTok(auxfp); + break; + case Y_RANGE_TOK: + GetAuxTok(auxfp); + if (thetok != FLOAT_TOK) { + Error("%s, line %d, floating point number must follow Y_RANGE", + auxfile, linenum); + } + auxyrange = thefloatish; + GetAuxTok(auxfp); + break; + case ORDER_TOK: + GetAuxTok(auxfp); + if (thetok != IDENTIFIER_TOK) { + Error("%s, line %d: identifier must follow ORDER", + auxfile, linenum); + } + GetAuxTok(auxfp); + if (thetok != INTEGER_TOK) { + Error("%s, line %d: identifier and integer must follow ORDER", + auxfile, linenum); + } + OrderFor(theident, theinteger); + GetAuxTok(auxfp); + break; + case SHADE_TOK: + GetAuxTok(auxfp); + if (thetok != IDENTIFIER_TOK) { + Error("%s, line %d: identifier must follow SHADE", + auxfile, linenum); + } + GetAuxTok(auxfp); + if (thetok != FLOAT_TOK) { + Error("%s, line %d: identifier and floating point number must follow SHADE", + auxfile, linenum); + } + ShadeFor(theident, thefloatish); + GetAuxTok(auxfp); + break; + case EOF_TOK: + endfile = 1; + break; + default: + Error("%s, line %d: %s unexpected", auxfile, linenum, + TokenToString(thetok)); + break; + } +} + + + +/* + * Read the next token from the input and assign its value + * to the global variable "thetok". In the case of numbers, + * the corresponding value is also assigned to "thefloatish"; + * in the case of identifiers it is assigned to "theident". + */ + +static void GetAuxTok(auxfp) +FILE* auxfp; +{ + + while (isspace(g_ch)) { /* skip whitespace */ + if (g_ch == '\n') linenum++; + g_ch = getc(auxfp); + } + + if (g_ch == EOF) { + thetok = EOF_TOK; + return; + } + + if (isdigit(g_ch)) { + thetok = GetNumber(auxfp); + return; + } else if (IsIdChar(g_ch)) { /* g_ch can't be a digit here */ + GetIdent(auxfp); + if (!isupper(theident[0])) { + thetok = IDENTIFIER_TOK; + } else if (strcmp(theident, "X_RANGE") == 0) { + thetok = X_RANGE_TOK; + } else if (strcmp(theident, "Y_RANGE") == 0) { + thetok = Y_RANGE_TOK; + } else if (strcmp(theident, "ORDER") == 0) { + thetok = ORDER_TOK; + } else if (strcmp(theident, "SHADE") == 0) { + thetok = SHADE_TOK; + } else { + thetok = IDENTIFIER_TOK; + } + return; + } else { + Error("%s, line %d: strange character (%c)", auxfile, linenum, g_ch); + } +} + +void +PutAuxFile(auxfp) + FILE* auxfp; +{ + int i; + + fprintf(auxfp, "X_RANGE %.2f\n", xrange); + fprintf(auxfp, "Y_RANGE %.2f\n", yrange); + + for (i = 0; i < nidents; i++) { + fprintf(auxfp, "ORDER %s %d\n", identtable[i]->name, i+1); + } + + for (i = 0; i < nidents; i++) { + fprintf(auxfp, "SHADE %s %.2f\n", identtable[i]->name, + ShadeOf(identtable[i]->name)); + } + + fclose(auxfp); +} diff --git a/VEX/head20041019/massif/hp2ps/AuxFile.h b/VEX/head20041019/massif/hp2ps/AuxFile.h new file mode 100644 index 0000000000..be3fe11076 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/AuxFile.h @@ -0,0 +1,11 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef AUX_FILE_H +#define AUX_FILE_H + +void PutAuxFile PROTO((FILE *)); +void GetAuxFile PROTO((FILE *)); + +#endif /* AUX_FILE_H */ diff --git a/VEX/head20041019/massif/hp2ps/Axes.c b/VEX/head20041019/massif/hp2ps/Axes.c new file mode 100644 index 0000000000..b8036100ee --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Axes.c @@ -0,0 +1,245 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Curves.h" +#include "Defines.h" +#include "Dimensions.h" +#include "HpFile.h" +#include "Utilities.h" + +/* own stuff */ +#include "Axes.h" + +typedef enum {MEGABYTE, KILOBYTE, BYTE} mkb; + +static void XAxis PROTO((void)); /* forward */ +static void YAxis PROTO((void)); /* forward */ + +static void XAxisMark PROTO((floatish, floatish)); /* forward */ +static void YAxisMark PROTO((floatish, floatish, mkb)); /* forward */ + +static floatish Round PROTO((floatish)); /* forward */ + +void +Axes() +{ + XAxis(); + YAxis(); +} + +static void +XAxisMark(x, num) + floatish x; floatish num; +{ + /* calibration mark */ + fprintf(psfp, "%f %f moveto\n", xpage(x), ypage(0.0)); + fprintf(psfp, "0 -4 rlineto\n"); + fprintf(psfp, "stroke\n"); + + /* number */ + fprintf(psfp, "HE%d setfont\n", NORMAL_FONT); + fprintf(psfp, "(%.1f)\n", num); + fprintf(psfp, "dup stringwidth pop\n"); + fprintf(psfp, "2 div\n"); + fprintf(psfp, "%f exch sub\n", xpage(x)); + fprintf(psfp, "%f moveto\n", borderspace); + fprintf(psfp, "show\n"); +} + + +#define N_X_MARKS 7 +#define XFUDGE 15 + +extern floatish xrange; +extern char *sampleunitstring; + +static void +XAxis() +{ + floatish increment, i; + floatish t, x; + floatish legendlen; + + /* draw the x axis line */ + fprintf(psfp, "%f %f moveto\n", xpage(0.0), ypage(0.0)); + fprintf(psfp, "%f 0 rlineto\n", graphwidth); + fprintf(psfp, "%f setlinewidth\n", borderthick); + fprintf(psfp, "stroke\n"); + + /* draw x axis legend */ + fprintf(psfp, "HE%d setfont\n", NORMAL_FONT); + fprintf(psfp, "(%s)\n", sampleunitstring); + fprintf(psfp, "dup stringwidth pop\n"); + fprintf(psfp, "%f\n", xpage(0.0) + graphwidth); + fprintf(psfp, "exch sub\n"); + fprintf(psfp, "%f moveto\n", borderspace); + fprintf(psfp, "show\n"); + + + /* draw x axis scaling */ + + increment = Round(xrange / (floatish) N_X_MARKS); + + t = graphwidth / xrange; + legendlen = StringSize(sampleunitstring) + (floatish) XFUDGE; + + for (i = samplemap[0]; i < samplemap[nsamples - 1]; i += increment) { + x = (i - samplemap[0]) * t; + + if (x < (graphwidth - legendlen)) { + XAxisMark(x,i); + } + } +} + +static void +YAxisMark(y, num, unit) + floatish y; floatish num; mkb unit; +{ + /* calibration mark */ + fprintf(psfp, "%f %f moveto\n", xpage(0.0), ypage(y)); + fprintf(psfp, "-4 0 rlineto\n"); + fprintf(psfp, "stroke\n"); + + /* number */ + fprintf(psfp, "HE%d setfont\n", NORMAL_FONT); + + switch (unit) { + case MEGABYTE : + fprintf(psfp, "("); + CommaPrint(psfp, (intish) (num / 1e6 + 0.5)); + fprintf(psfp, "M)\n"); + break; + case KILOBYTE : + fprintf(psfp, "("); + CommaPrint(psfp, (intish) (num / 1e3 + 0.5)); + fprintf(psfp, "k)\n"); + break; + case BYTE: + fprintf(psfp, "("); + CommaPrint(psfp, (intish) (num + 0.5)); + fprintf(psfp, ")\n"); + break; + } + + fprintf(psfp, "dup stringwidth\n"); + fprintf(psfp, "2 div\n"); + fprintf(psfp, "%f exch sub\n", ypage(y)); + + fprintf(psfp, "exch\n"); + fprintf(psfp, "%f exch sub\n", graphx0 - borderspace); + + fprintf(psfp, "exch\n"); + fprintf(psfp, "moveto\n"); + fprintf(psfp, "show\n"); +} + +#define N_Y_MARKS 7 +#define YFUDGE 15 + +extern floatish yrange; +extern char *valueunitstring; + +static void +YAxis() +{ + floatish increment, i; + floatish t, y; + floatish legendlen; + mkb unit; + + /* draw the y axis line */ + fprintf(psfp, "%f %f moveto\n", xpage(0.0), ypage(0.0)); + fprintf(psfp, "0 %f rlineto\n", graphheight); + fprintf(psfp, "%f setlinewidth\n", borderthick); + fprintf(psfp, "stroke\n"); + + /* draw y axis legend */ + fprintf(psfp, "gsave\n"); + fprintf(psfp, "HE%d setfont\n", NORMAL_FONT); + fprintf(psfp, "(%s)\n", valueunitstring); + fprintf(psfp, "dup stringwidth pop\n"); + fprintf(psfp, "%f\n", ypage(0.0) + graphheight); + fprintf(psfp, "exch sub\n"); + fprintf(psfp, "%f exch\n", xpage(0.0) - borderspace); + fprintf(psfp, "translate\n"); + fprintf(psfp, "90 rotate\n"); + fprintf(psfp, "0 0 moveto\n"); + fprintf(psfp, "show\n"); + fprintf(psfp, "grestore\n"); + + /* draw y axis scaling */ + increment = max( yrange / (floatish) N_Y_MARKS, 1.0); + increment = Round(increment); + + if (increment >= 1e6) { + unit = MEGABYTE; + } else if (increment >= 1e3) { + unit = KILOBYTE; + } else { + unit = BYTE; + } + + t = graphheight / yrange; + legendlen = StringSize(valueunitstring) + (floatish) YFUDGE; + + for (i = 0.0; i <= yrange; i += increment) { + y = i * t; + + if (y < (graphheight - legendlen)) { + YAxisMark(y, i, unit); + } + } +} + + +/* + * Find a "nice round" value to use on the axis. + */ + +static floatish OneTwoFive PROTO((floatish)); /* forward */ + +static floatish +Round(y) + floatish y; +{ + int i; + + if (y > 10.0) { + for (i = 0; y > 10.0; y /= 10.0, i++) ; + y = OneTwoFive(y); + for ( ; i > 0; y = y * 10.0, i--) ; + + } else if (y < 1.0) { + for (i = 0; y < 1.0; y *= 10.0, i++) ; + y = OneTwoFive(y); + for ( ; i > 0; y = y / 10.0, i--) ; + + } else { + y = OneTwoFive(y); + } + + return (y); +} + + +/* + * OneTwoFive() -- Runciman's 1,2,5 scaling rule. Argument 1.0 <= y <= 10.0. + */ + +static floatish +OneTwoFive(y) + floatish y; +{ + if (y > 4.0) { + return (5.0); + } else if (y > 1.0) { + return (2.0); + } else { + return (1.0); + } +} diff --git a/VEX/head20041019/massif/hp2ps/Axes.h b/VEX/head20041019/massif/hp2ps/Axes.h new file mode 100644 index 0000000000..f9f252f6d2 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Axes.h @@ -0,0 +1,10 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef AXES_H +#define AXES_H + +void Axes PROTO((void)); + +#endif /* AXES_H */ diff --git a/VEX/head20041019/massif/hp2ps/CHANGES b/VEX/head20041019/massif/hp2ps/CHANGES new file mode 100644 index 0000000000..60933d6622 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/CHANGES @@ -0,0 +1,39 @@ +1. + +When generating PostScript to show strings, '(' and ')' may need to be escaped. +These characters are now escaped when the JOB string is shown. + +2. + +Manually deleting samples from a .hp file now does what you would expect. + +3. + +The -t flag for setting the threshold percentage has been scrapped. No one +ever used it. + +4. + +Long JOB strings cause hp2ps to use a big title box. Big and small boxes +can be forced with -b and -s flag. + +5. + +MARKS now print as small triangles which remain below the x axis. + +6. + +There is an updated manual page. + +7. + +-m flag for setting maximum no of bands (default 20, cant be more than 20). +-t flag for setting threshold (between 0% and 5%, default 1%). + +8. + +Axes scaling rounding errors removed. + +9. + +Fixed bug whereby x-axis was assumed to start at zero when placing MARKs. diff --git a/VEX/head20041019/massif/hp2ps/CVS/Entries b/VEX/head20041019/massif/hp2ps/CVS/Entries new file mode 100644 index 0000000000..f9a2e60c21 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/CVS/Entries @@ -0,0 +1,46 @@ +/.cvsignore/1.1/Sun Feb 22 19:34:55 2004// +/AreaBelow.c/1.3/Wed Jun 2 20:43:23 2004// +/AreaBelow.h/1.2/Sun Feb 15 15:38:08 2004// +/AuxFile.c/1.2/Sun Feb 15 15:38:08 2004// +/AuxFile.h/1.2/Sun Feb 15 15:38:08 2004// +/Axes.c/1.2/Sun Feb 15 15:38:08 2004// +/Axes.h/1.2/Sun Feb 15 15:38:08 2004// +/CHANGES/1.1/Sat Feb 14 16:40:02 2004// +/Curves.c/1.3/Wed Jun 2 20:43:23 2004// +/Curves.h/1.2/Sun Feb 15 15:38:08 2004// +/Defines.h/1.2/Sun Feb 15 15:38:08 2004// +/Deviation.c/1.3/Wed Jun 2 20:43:23 2004// +/Deviation.h/1.2/Sun Feb 15 15:38:08 2004// +/Dimensions.c/1.2/Sun Feb 15 15:38:08 2004// +/Dimensions.h/1.2/Sun Feb 15 15:38:08 2004// +/Error.c/1.3/Wed Jun 2 20:43:23 2004// +/Error.h/1.3/Wed Jun 2 20:43:23 2004// +/HpFile.c/1.2/Sun Feb 15 15:38:08 2004// +/HpFile.h/1.2/Sun Feb 15 15:38:08 2004// +/INSTALL/1.1/Sat Feb 14 16:40:02 2004// +/Key.c/1.2/Sun Feb 15 15:38:08 2004// +/Key.h/1.2/Sun Feb 15 15:38:08 2004// +/LICENSE/1.1/Sat Feb 14 16:40:02 2004// +/Main.c/1.2/Sun Feb 15 15:38:08 2004// +/Main.h/1.2/Sun Feb 15 15:38:08 2004// +/Makefile.am/1.5/Wed Sep 1 23:20:49 2004// +/Makefile.old/1.1/Tue Feb 24 23:38:17 2004// +/Marks.c/1.2/Sun Feb 15 15:38:08 2004// +/Marks.h/1.2/Sun Feb 15 15:38:08 2004// +/PsFile.c/1.2/Sun Feb 15 15:38:08 2004// +/PsFile.h/1.2/Sun Feb 15 15:38:08 2004// +/README/1.1/Sat Feb 14 16:40:02 2004// +/Reorder.c/1.2/Sun Feb 15 15:38:08 2004// +/Reorder.h/1.2/Sun Feb 15 15:38:08 2004// +/Scale.c/1.3/Wed Jun 2 20:43:23 2004// +/Scale.h/1.2/Sun Feb 15 15:38:08 2004// +/Shade.c/1.2/Sun Feb 15 15:38:08 2004// +/Shade.h/1.2/Sun Feb 15 15:38:08 2004// +/TopTwenty.c/1.3/Wed Jun 2 20:43:23 2004// +/TopTwenty.h/1.2/Sun Feb 15 15:38:08 2004// +/TraceElement.c/1.3/Wed Jun 2 20:43:24 2004// +/TraceElement.h/1.2/Sun Feb 15 15:38:08 2004// +/Utilities.c/1.4/Wed Jun 2 20:43:24 2004// +/Utilities.h/1.3/Wed Jun 2 20:43:24 2004// +/hp2ps.1/1.1/Sat Feb 14 16:40:02 2004// +D diff --git a/VEX/head20041019/massif/hp2ps/CVS/Repository b/VEX/head20041019/massif/hp2ps/CVS/Repository new file mode 100644 index 0000000000..3db3dee420 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/CVS/Repository @@ -0,0 +1 @@ +valgrind/massif/hp2ps diff --git a/VEX/head20041019/massif/hp2ps/CVS/Root b/VEX/head20041019/massif/hp2ps/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/massif/hp2ps/CVS/Template b/VEX/head20041019/massif/hp2ps/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/massif/hp2ps/Curves.c b/VEX/head20041019/massif/hp2ps/Curves.c new file mode 100644 index 0000000000..5176ecf8fe --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Curves.c @@ -0,0 +1,169 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Dimensions.h" +#include "HpFile.h" +#include "Shade.h" +#include "Utilities.h" + +/* own stuff */ +#include "Curves.h" + +static floatish *g_x; /* x and y values */ +static floatish *g_y; + +static floatish *g_py; /* previous y values */ + +static void Curve PROTO((struct entry *)); /* forward */ +static void ShadeCurve + PROTO((floatish *x, floatish *y, floatish *py, floatish shade)); + +void +Curves() +{ + intish i; + + for (i = 0; i < nidents; i++) { + Curve(identtable[i]); + } +} + +/* + * Draw a curve, and fill the area that is below it and above + * the previous curve. + */ + +static void +Curve(e) + struct entry* e; +{ + struct chunk* ch; + int j; + + for (ch = e->chk; ch; ch = ch->next) { + for (j = 0; j < ch->nd; j++) { + g_y[ ch->d[j].bucket ] += ch->d[j].value; + } + } + + ShadeCurve(g_x, g_y, g_py, ShadeOf(e->name)); +} + + +static void PlotCurveLeftToRight PROTO((floatish *, floatish *)); /* forward */ +static void PlotCurveRightToLeft PROTO((floatish *, floatish *)); /* forward */ + +static void SaveCurve PROTO((floatish *, floatish *)); /* forward */ + +/* + * Map virtual x coord to physical x coord + */ + +floatish +xpage(x) + floatish x; +{ + return (x + graphx0); +} + + + +/* + * Map virtual y coord to physical y coord + */ + +floatish +ypage(y) + floatish y; +{ + return (y + graphy0); +} + + +/* + * Fill the region bounded by two splines, using the given + * shade. + */ + +static void +ShadeCurve(x, y, py, shade) + floatish *x; floatish *y; floatish *py; floatish shade; +{ + fprintf(psfp, "%f %f moveto\n", xpage(x[0]), ypage(py[0])); + PlotCurveLeftToRight(x, py); + + fprintf(psfp, "%f %f lineto\n", xpage(x[nsamples - 1]), + ypage(y[nsamples - 1])); + PlotCurveRightToLeft(x, y); + + fprintf(psfp, "closepath\n"); + + fprintf(psfp, "gsave\n"); + + SetPSColour(shade); + fprintf(psfp, "fill\n"); + + fprintf(psfp, "grestore\n"); + fprintf(psfp, "stroke\n"); + + SaveCurve(y, py); +} + +static void +PlotCurveLeftToRight(x,y) + floatish *x; floatish *y; +{ + intish i; + + for (i = 0; i < nsamples; i++) { + fprintf(psfp, "%f %f lineto\n", xpage(x[i]), ypage(y[i])); + } +} + +static void +PlotCurveRightToLeft(x,y) + floatish *x; floatish *y; +{ + intish i; + + for (i = nsamples - 1; i >= 0; i-- ) { + fprintf(psfp, "%f %f lineto\n", xpage(x[i]), ypage(y[i])); + } +} + +/* + * Save the curve coordinates stored in y[] in py[]. + */ + +static void +SaveCurve(y, py) + floatish *y; floatish* py; +{ + intish i; + + for (i = 0; i < nsamples; i++) { + py[i] = y[i]; + } +} + +extern floatish xrange; + +void +CurvesInit() +{ + intish i; + + g_x = (floatish*) xmalloc(nsamples * sizeof(floatish)); + g_y = (floatish*) xmalloc(nsamples * sizeof(floatish)); + g_py = (floatish*) xmalloc(nsamples * sizeof(floatish)); + + for (i = 0; i < nsamples; i++) { + g_x[i] = ((samplemap[i] - samplemap[0])/ xrange) * graphwidth; + g_y[i] = g_py[i] = 0.0; + } +} diff --git a/VEX/head20041019/massif/hp2ps/Curves.h b/VEX/head20041019/massif/hp2ps/Curves.h new file mode 100644 index 0000000000..7ca720ee32 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Curves.h @@ -0,0 +1,14 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef CURVES_H +#define CURVES_H + +void Curves PROTO((void)); +void CurvesInit PROTO((void)); + +floatish xpage PROTO((floatish)); +floatish ypage PROTO((floatish)); + +#endif /* CURVES_H */ diff --git a/VEX/head20041019/massif/hp2ps/Defines.h b/VEX/head20041019/massif/hp2ps/Defines.h new file mode 100644 index 0000000000..4973555eb5 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Defines.h @@ -0,0 +1,65 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef DEFINES_H +#define DEFINES_H + +/* + * Things that can be altered. + */ + +#define THRESHOLD_PERCENT _thresh_ /* all values below 1% insignificant */ +#define DEFAULT_THRESHOLD 1.0 +extern floatish _thresh_; + +#define TWENTY _twenty_ /* show top 20 bands, grouping excess */ +#define DEFAULT_TWENTY 20 /* this is default and absolute maximum */ +extern int _twenty_; + +#define LARGE_FONT 12 /* Helvetica 12pt */ +#define NORMAL_FONT 10 /* Helvetica 10pt */ + +#define BORDER_HEIGHT 432.0 /* page border box 432pt (6 inches high) */ +#define BORDER_WIDTH 648.0 /* page border box 648pt (9 inches wide) */ +#define BORDER_SPACE 5.0 /* page border space */ +#define BORDER_THICK 0.5 /* page border line thickness 0.5pt */ + + +#define TITLE_HEIGHT 20.0 /* title box is 20pt high */ +#define TITLE_TEXT_FONT LARGE_FONT /* title in large font */ +#define TITLE_TEXT_SPACE 6.0 /* space between title text and box */ + + +#define AXIS_THICK 0.5 /* axis thickness 0.5pt */ +#define AXIS_TEXT_SPACE 6 /* space between axis legends and axis */ +#define AXIS_TEXT_FONT NORMAL_FONT /* axis legends in normal font */ +#define AXIS_Y_TEXT_SPACE 35 /* space for y axis text */ + +#define KEY_BOX_WIDTH 14 /* key boxes are 14pt high */ + +#define SMALL_JOB_STRING_WIDTH 35 /* small title for 35 characters or less */ +#define BIG_JOB_STRING_WIDTH 80 /* big title for everything else */ + +#define GRAPH_X0 (AXIS_Y_TEXT_SPACE + (2 * BORDER_SPACE)) +#define GRAPH_Y0 (AXIS_TEXT_FONT + (2 * BORDER_SPACE)) + + +/* + * Things that should be left well alone. + */ + + + +#define START_X 72 /* start 72pt (1 inch) from left (portrait) */ +#define START_Y 108 /* start 108pt (1.5 inch) from bottom (portrait) */ + +#define NUMBER_LENGTH 32 + +#define N_CHUNK 24 + +#define VERSION "0.25" /* as of 95/03/21 */ + +#define max(x,y) ((x) > (y) ? (x) : (y)) /* not everyone has this */ + +#endif /* DEFINES_H */ diff --git a/VEX/head20041019/massif/hp2ps/Deviation.c b/VEX/head20041019/massif/hp2ps/Deviation.c new file mode 100644 index 0000000000..22a0e9ed4f --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Deviation.c @@ -0,0 +1,143 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Error.h" +#include "HpFile.h" +#include "Utilities.h" + +/* own stuff */ +#include "Deviation.h" + +/* + * Reorder the identifiers in the identifier table so that the + * ones whose data points exhibit the mininal standard deviation + * come first. + */ + +void +Deviation() +{ + intish i; + intish j; + floatish dev; + struct chunk* ch; + int min; + floatish t; + struct entry* e; + floatish *averages; + floatish *deviations; + + averages = (floatish*) xmalloc(nidents * sizeof(floatish)); + deviations = (floatish*) xmalloc(nidents * sizeof(floatish)); + + /* find averages */ + + for (i = 0; i < nidents; i++) { + averages[i] = 0.0; + } + + for (i = 0; i < nidents; i++) { + for (ch = identtable[i]->chk; ch; ch = ch->next) { + for (j = 0; j < ch->nd; j++) { + averages[i] += ch->d[j].value; + } + } + } + + for (i = 0; i < nidents; i++) { + averages[i] /= (floatish) nsamples; + } + + /* calculate standard deviation */ + + for (i = 0; i < nidents; i++) { + deviations[i] = 0.0; + } + + for (i = 0; i < nidents; i++) { + for (ch = identtable[i]->chk; ch; ch = ch->next) { + for (j = 0; j < ch->nd; j++) { + dev = ch->d[j].value - averages[i]; + deviations[i] += dev * dev; + } + } + } + + for (i = 0; i < nidents; i++) { + deviations[i] = (floatish) sqrt ((doublish) (deviations[i] / + (floatish) (nsamples - 1))); + } + + + /* sort on basis of standard deviation */ + + for (i = 0; i < nidents-1; i++) { + min = i; + for (j = i+1; j < nidents; j++) { + if (deviations[ j ] < deviations[min]) { + min = j; + } + } + + t = deviations[min]; + deviations[min] = deviations[i]; + deviations[i] = t; + + e = identtable[min]; + identtable[min] = identtable[i]; + identtable[i] = e; + } + + free(averages); + free(deviations); +} + +void +Identorder(iflag) + int iflag; /* a funny three-way flag ? WDP 95/03 */ +{ + int i; + int j; + int min; + struct entry* e; + + /* sort on basis of ident string */ + if (iflag > 0) { + /* greatest at top i.e. smallest at start */ + + for (i = 0; i < nidents-1; i++) { + min = i; + for (j = i+1; j < nidents; j++) { + if (strcmp(identtable[j]->name, identtable[min]->name) < 0) { + min = j; + } + } + + e = identtable[min]; + identtable[min] = identtable[i]; + identtable[i] = e; + } + } else { + /* smallest at top i.e. greatest at start */ + + for (i = 0; i < nidents-1; i++) { + min = i; + for (j = i+1; j < nidents; j++) { + if (strcmp(identtable[j]->name, identtable[min]->name) > 0) { + min = j; + } + } + + e = identtable[min]; + identtable[min] = identtable[i]; + identtable[i] = e; + } + } +} diff --git a/VEX/head20041019/massif/hp2ps/Deviation.h b/VEX/head20041019/massif/hp2ps/Deviation.h new file mode 100644 index 0000000000..1fe50adbc1 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Deviation.h @@ -0,0 +1,11 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef DEVIATION_H +#define DEVIATION_H + +void Deviation PROTO((void)); +void Identorder PROTO((int)); + +#endif /* DEVIATION_H */ diff --git a/VEX/head20041019/massif/hp2ps/Dimensions.c b/VEX/head20041019/massif/hp2ps/Dimensions.c new file mode 100644 index 0000000000..e3ebe00a74 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Dimensions.c @@ -0,0 +1,207 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include +#include "Main.h" +#include "Defines.h" +#include "HpFile.h" +#include "Scale.h" + +/* own stuff */ +#include "Dimensions.h" + +/* + * Get page and other dimensions before printing. + */ + +floatish borderheight = BORDER_HEIGHT; +floatish borderwidth = BORDER_WIDTH; +floatish borderspace = BORDER_SPACE; +floatish borderthick = BORDER_THICK; + +floatish titlewidth = (BORDER_WIDTH - (2 * BORDER_SPACE)); +floatish titletextspace = TITLE_TEXT_SPACE; +floatish titleheight; + +floatish graphx0 = GRAPH_X0; +floatish graphy0 = GRAPH_Y0; + +floatish graphheight; +floatish graphwidth; + +static floatish KeyWidth PROTO((void)); /* forward */ + +void +Dimensions() +{ + xrange = samplemap[nsamples - 1] - samplemap[0]; + xrange = max(xrange, auxxrange); + if (xrange == 0.0) xrange = 1.0; /* avoid division by 0.0 */ + + yrange = MaxCombinedHeight(); + yrange = max(yrange, auxyrange); + if (yrange == 0.0) yrange = 1.0; /* avoid division by 0.0 */ + + if (!bflag && !sflag) { + bflag = strlen(jobstring) > SMALL_JOB_STRING_WIDTH; + } + + if (bflag) { + titleheight = 2 * TITLE_HEIGHT; + } else { + titleheight = TITLE_HEIGHT; + } + + graphwidth = titlewidth - graphx0 - (TWENTY ? KeyWidth() : 0); + graphheight = borderheight - titleheight - (2 * borderspace) - graphy0; +} + +/* + * Calculate the width of the key. + */ + +static floatish +KeyWidth() +{ + intish i; + floatish c; + + c = 0.0; + + for (i = 0; i < nidents; i++) { + c = max(c, StringSize(identtable[i]->name)); + } + + c += 3.0 * borderspace; + + c += (floatish) KEY_BOX_WIDTH; + + return c; +} + + +/* + * A desperately grim solution. + */ + + +floatish fonttab[] = { + /* 20 (' ') = */ 3.0, + /* 21 ('!') = */ 1.0, + /* 22 ('"') = */ 1.0, + /* 23 ('#') = */ 3.0, + /* 24 ('$') = */ 3.0, + /* 25 ('%') = */ 3.0, + /* 26 ('&') = */ 3.0, + /* 27 (''') = */ 1.0, + /* 28 ('(') = */ 3.0, + /* 29 (')') = */ 3.0, + /* 2a ('*') = */ 2.0, + /* 2b ('+') = */ 3.0, + /* 2c (',') = */ 1.0, + /* 2d ('-') = */ 3.0, + /* 2e ('.') = */ 1.0, + /* 2f ('/') = */ 3.0, + /* 30 ('0') = */ 4.0, + /* 31 ('1') = */ 4.0, + /* 32 ('2') = */ 4.0, + /* 33 ('3') = */ 4.0, + /* 34 ('4') = */ 4.0, + /* 35 ('5') = */ 4.0, + /* 36 ('6') = */ 4.0, + /* 37 ('7') = */ 4.0, + /* 38 ('8') = */ 4.0, + /* 39 ('9') = */ 4.0, + /* 3a (':') = */ 1.0, + /* 3b (';') = */ 1.0, + /* 3c ('<') = */ 3.0, + /* 3d ('=') = */ 3.0, + /* 3e ('>') = */ 3.0, + /* 3f ('?') = */ 2.0, + /* 40 ('@') = */ 3.0, + /* 41 ('A') = */ 5.0, + /* 42 ('B') = */ 5.0, + /* 43 ('C') = */ 5.0, + /* 44 ('D') = */ 5.0, + /* 45 ('E') = */ 5.0, + /* 46 ('F') = */ 5.0, + /* 47 ('G') = */ 5.0, + /* 48 ('H') = */ 5.0, + /* 49 ('I') = */ 1.0, + /* 4a ('J') = */ 5.0, + /* 4b ('K') = */ 5.0, + /* 4c ('L') = */ 5.0, + /* 4d ('M') = */ 5.0, + /* 4e ('N') = */ 5.0, + /* 4f ('O') = */ 5.0, + /* 50 ('P') = */ 5.0, + /* 51 ('Q') = */ 5.0, + /* 52 ('R') = */ 5.0, + /* 53 ('S') = */ 5.0, + /* 54 ('T') = */ 5.0, + /* 55 ('U') = */ 5.0, + /* 56 ('V') = */ 5.0, + /* 57 ('W') = */ 5.0, + /* 58 ('X') = */ 5.0, + /* 59 ('Y') = */ 5.0, + /* 5a ('Z') = */ 5.0, + /* 5b ('[') = */ 2.0, + /* 5c ('\') = */ 3.0, + /* 5d (']') = */ 2.0, + /* 5e ('^') = */ 1.0, + /* 5f ('_') = */ 3.0, + /* 60 ('`') = */ 1.0, + /* 61 ('a') = */ 3.0, + /* 62 ('b') = */ 3.0, + /* 63 ('c') = */ 3.0, + /* 64 ('d') = */ 3.0, + /* 65 ('e') = */ 3.0, + /* 66 ('f') = */ 3.0, + /* 67 ('g') = */ 3.0, + /* 68 ('h') = */ 3.0, + /* 69 ('i') = */ 1.0, + /* 6a ('j') = */ 2.0, + /* 6b ('k') = */ 3.0, + /* 6c ('l') = */ 1.0, + /* 6d ('m') = */ 5.0, + /* 6e ('n') = */ 3.0, + /* 6f ('o') = */ 3.0, + /* 70 ('p') = */ 3.0, + /* 71 ('q') = */ 3.0, + /* 72 ('r') = */ 2.0, + /* 73 ('s') = */ 3.0, + /* 74 ('t') = */ 2.0, + /* 75 ('u') = */ 3.0, + /* 76 ('v') = */ 3.0, + /* 77 ('w') = */ 3.0, + /* 78 ('x') = */ 3.0, + /* 79 ('y') = */ 3.0, + /* 7a ('z') = */ 3.0, + /* 7b ('{') = */ 2.0, + /* 7c ('|') = */ 1.0, + /* 7d ('}') = */ 2.0, + /* 7e ('~') = */ 2.0 +}; + + +/* + * What size is a string (in points)? + */ + +#define FUDGE (2.834646 * 0.6) + +floatish +StringSize(s) + char* s; +{ + floatish r; + + for (r = 0.0; *s; s++) { + r += fonttab[(*s) - 0x20]; + } + + return r * FUDGE; +} diff --git a/VEX/head20041019/massif/hp2ps/Dimensions.h b/VEX/head20041019/massif/hp2ps/Dimensions.h new file mode 100644 index 0000000000..3ce5ab59e9 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Dimensions.h @@ -0,0 +1,26 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef DIMENSIONS_H +#define DIMENSIONS_H + +extern floatish borderheight; +extern floatish borderwidth; +extern floatish borderspace; +extern floatish borderthick; + +extern floatish titleheight; +extern floatish titlewidth; +extern floatish titletextspace; + +extern floatish graphx0; +extern floatish graphy0; + +extern floatish graphheight; +extern floatish graphwidth; + +void Dimensions PROTO((void)); +floatish StringSize PROTO((char *)); + +#endif /* DIMENSIONS_H */ diff --git a/VEX/head20041019/massif/hp2ps/Error.c b/VEX/head20041019/massif/hp2ps/Error.c new file mode 100644 index 0000000000..2ecba3da69 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Error.c @@ -0,0 +1,63 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include +#include "Main.h" +#include "Defines.h" + +/* own stuff */ +#include "Error.h" + +/*VARARGS0*/ +void +Error(const char *fmt, ...) +{ + va_list ap; + fflush(stdout); + fprintf(stderr, "%s: ", programname); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + exit(1); +} + +/*VARARGS0*/ +void +Disaster(const char *fmt, ...) +{ + va_list ap; + fflush(stdout); + fprintf(stderr, "%s: ", programname); + fprintf(stderr, " Disaster! ("); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, ")\n"); + exit(1); +} + +void +Usage(str) + const char *str; +{ + if (str) printf("error: %s\n", str); + printf("usage: %s -b -d -ef -g -i -p -mn -p -s -tf -y [file[.hp]]\n", programname); + printf("where -b use large title box\n"); + printf(" -d sort by standard deviation\n"); + printf(" -ef[in|mm|pt] produce Encapsulated PostScript f units wide (f > 2 inches)\n"); + printf(" -g produce output suitable for GHOSTSCRIPT previever\n"); + printf(" -i[+|-] sort by identifier string (-i+ gives greatest on top) \n"); + printf(" -mn print maximum of n bands (default & max 20)\n"); + printf(" -m0 removes the band limit altogether\n"); + printf(" -p use previous scaling, shading and ordering\n"); + printf(" -s use small title box\n"); + printf(" -tf ignore trace bands which sum below f%% (default 1%%, max 5%%)\n"); + printf(" -y traditional\n"); + printf(" -c colour ouput\n"); + exit(0); +} + diff --git a/VEX/head20041019/massif/hp2ps/Error.h b/VEX/head20041019/massif/hp2ps/Error.h new file mode 100644 index 0000000000..d38518d699 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Error.h @@ -0,0 +1,12 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef ERROR_H +#define ERROR_H + +extern void Error PROTO((const char *, ...)); +extern void Disaster PROTO((const char *, ...)); +extern void Usage PROTO((const char *)); + +#endif /* ERROR_H */ diff --git a/VEX/head20041019/massif/hp2ps/HpFile.c b/VEX/head20041019/massif/hp2ps/HpFile.c new file mode 100644 index 0000000000..26832af9ee --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/HpFile.c @@ -0,0 +1,591 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Error.h" +#include "HpFile.h" +#include "Utilities.h" + +#ifndef atof +double atof PROTO((const char *)); +#endif + +/* own stuff already included */ + +#define N_MARKS 50 /* start size of the mark table */ +#define N_SAMPLES 500 /* start size of the sample table */ + +char *theident; +char *thestring; +int theinteger; +floatish thefloatish; +int g_ch; /* last character read */ +token thetok; /* last token */ +int linenum; /* current line number */ +int endfile; /* true at end of file */ + +static boolish gotjob = 0; /* "JOB" read */ +static boolish gotdate = 0; /* "DATE" read */ +static boolish gotvalueunit = 0; /* "VALUE_UNIT" read */ +static boolish gotsampleunit = 0; /* "SAMPLE_UNIT" read */ +static boolish insample = 0; /* true when in sample */ + +static floatish lastsample; /* the last sample time */ + +static void GetHpLine PROTO((FILE *)); /* forward */ +static void GetHpTok PROTO((FILE *)); /* forward */ + +static struct entry *GetEntry PROTO((char *)); /* forward */ + +static void MakeIdentTable PROTO((void)); /* forward */ + +char *jobstring; +char *datestring; + +char *sampleunitstring; +char *valueunitstring; + +floatish *samplemap; /* sample intervals */ +floatish *markmap; /* sample marks */ + +/* + * An extremely simple parser. The input is organised into lines of + * the form + * + * JOB s -- job identifier string + * DATE s -- date string + * SAMPLE_UNIT s -- sample unit eg "seconds" + * VALUE_UNIT s -- value unit eg "bytes" + * MARK i -- sample mark + * BEGIN_SAMPLE i -- start of ith sample + * identifier i -- there are i identifiers in this sample + * END_SAMPLE i -- end of ith sample + * + */ + +void +GetHpFile(infp) + FILE *infp; +{ + nsamples = 0; + nmarks = 0; + nidents = 0; + + g_ch = ' '; + endfile = 0; + linenum = 1; + lastsample = 0.0; + + GetHpTok(infp); + + while (endfile == 0) { + GetHpLine(infp); + } + + if (!gotjob) { + Error("%s: JOB missing", hpfile); + } + + if (!gotdate) { + Error("%s: DATE missing", hpfile); + } + + if (!gotvalueunit) { + Error("%s: VALUE_UNIT missing", hpfile); + } + + if (!gotsampleunit) { + Error("%s: SAMPLE_UNIT missing", hpfile); + } + + if (nsamples == 0) { + Error("%s: contains no samples", hpfile); + } + + + MakeIdentTable(); + + fclose(hpfp); +} + + +/* + * Read the next line from the input, check the syntax, and perform + * the appropriate action. + */ + +static void +GetHpLine(infp) + FILE* infp; +{ + static intish nmarkmax = 0, nsamplemax = 0; + + switch (thetok) { + case JOB_TOK: + GetHpTok(infp); + if (thetok != STRING_TOK) { + Error("%s, line %d: string must follow JOB", hpfile, linenum); + } + jobstring = thestring; + gotjob = 1; + GetHpTok(infp); + break; + + case DATE_TOK: + GetHpTok(infp); + if (thetok != STRING_TOK) { + Error("%s, line %d: string must follow DATE", hpfile, linenum); + } + datestring = thestring; + gotdate = 1; + GetHpTok(infp); + break; + + case SAMPLE_UNIT_TOK: + GetHpTok(infp); + if (thetok != STRING_TOK) { + Error("%s, line %d: string must follow SAMPLE_UNIT", hpfile, + linenum); + } + sampleunitstring = thestring; + gotsampleunit = 1; + GetHpTok(infp); + break; + + case VALUE_UNIT_TOK: + GetHpTok(infp); + if (thetok != STRING_TOK) { + Error("%s, line %d: string must follow VALUE_UNIT", hpfile, + linenum); + } + valueunitstring = thestring; + gotvalueunit = 1; + GetHpTok(infp); + break; + + case MARK_TOK: + GetHpTok(infp); + if (thetok != FLOAT_TOK) { + Error("%s, line %d, floating point number must follow MARK", + hpfile, linenum); + } + if (insample) { + Error("%s, line %d, MARK occurs within sample", hpfile, linenum); + } + if (nmarks >= nmarkmax) { + if (!markmap) { + nmarkmax = N_MARKS; + markmap = (floatish*) xmalloc(nmarkmax * sizeof(floatish)); + } else { + nmarkmax *= 2; + markmap = (floatish*) xrealloc(markmap, nmarkmax * sizeof(floatish)); + } + } + markmap[ nmarks++ ] = thefloatish; + GetHpTok(infp); + break; + + case BEGIN_SAMPLE_TOK: + insample = 1; + GetHpTok(infp); + if (thetok != FLOAT_TOK) { + Error("%s, line %d, floating point number must follow BEGIN_SAMPLE", hpfile, linenum); + } + if (thefloatish < lastsample) { + Error("%s, line %d, samples out of sequence", hpfile, linenum); + } else { + lastsample = thefloatish; + } + if (nsamples >= nsamplemax) { + if (!samplemap) { + nsamplemax = N_SAMPLES; + samplemap = (floatish*) xmalloc(nsamplemax * sizeof(floatish)); + } else { + nsamplemax *= 2; + samplemap = (floatish*) xrealloc(samplemap, + nsamplemax * sizeof(floatish)); + } + } + samplemap[ nsamples ] = thefloatish; + GetHpTok(infp); + break; + + case END_SAMPLE_TOK: + insample = 0; + GetHpTok(infp); + if (thetok != FLOAT_TOK) { + Error("%s, line %d: floating point number must follow END_SAMPLE", + hpfile, linenum); + } + nsamples++; + GetHpTok(infp); + break; + + case IDENTIFIER_TOK: + GetHpTok(infp); + if (thetok != INTEGER_TOK) { + Error("%s, line %d: integer must follow identifier", hpfile, + linenum); + } + StoreSample(GetEntry(theident), nsamples, (floatish) theinteger); + GetHpTok(infp); + break; + + case EOF_TOK: + endfile = 1; + break; + + default: + Error("%s, line %d: %s unexpected", hpfile, linenum, + TokenToString(thetok)); + break; + } +} + + +char * +TokenToString(t) + token t; +{ + switch (t) { + case EOF_TOK: return "EOF"; + case INTEGER_TOK: return "integer"; + case FLOAT_TOK: return "floating point number"; + case IDENTIFIER_TOK: return "identifier"; + case STRING_TOK: return "string"; + case BEGIN_SAMPLE_TOK: return "BEGIN_SAMPLE"; + case END_SAMPLE_TOK: return "END_SAMPLE"; + case JOB_TOK: return "JOB"; + case DATE_TOK: return "DATE"; + case SAMPLE_UNIT_TOK: return "SAMPLE_UNIT"; + case VALUE_UNIT_TOK: return "VALUE_UNIT"; + case MARK_TOK: return "MARK"; + + case X_RANGE_TOK: return "X_RANGE"; + case Y_RANGE_TOK: return "Y_RANGE"; + case ORDER_TOK: return "ORDER"; + case SHADE_TOK: return "SHADE"; + default: return "(strange token)"; + } +} + +/* + * Read the next token from the input and assign its value + * to the global variable "thetok". In the case of numbers, + * the corresponding value is also assigned to "theinteger" + * or "thefloatish" as appropriate; in the case of identifiers + * it is assigned to "theident". + */ + +static void +GetHpTok(infp) + FILE* infp; +{ + + while (isspace(g_ch)) { /* skip whitespace */ + if (g_ch == '\n') linenum++; + g_ch = getc(infp); + } + + if (g_ch == EOF) { + thetok = EOF_TOK; + return; + } + + if (isdigit(g_ch)) { + thetok = GetNumber(infp); + return; + } else if (g_ch == '\"') { + GetString(infp); + thetok = STRING_TOK; + return; + } else if (IsIdChar(g_ch)) { + ASSERT(! (isdigit(g_ch))); /* g_ch can't be a digit here */ + GetIdent(infp); + if (!isupper(theident[0])) { + thetok = IDENTIFIER_TOK; + } else if (strcmp(theident, "BEGIN_SAMPLE") == 0) { + thetok = BEGIN_SAMPLE_TOK; + } else if (strcmp(theident, "END_SAMPLE") == 0) { + thetok = END_SAMPLE_TOK; + } else if (strcmp(theident, "JOB") == 0) { + thetok = JOB_TOK; + } else if (strcmp(theident, "DATE") == 0) { + thetok = DATE_TOK; + } else if (strcmp(theident, "SAMPLE_UNIT") == 0) { + thetok = SAMPLE_UNIT_TOK; + } else if (strcmp(theident, "VALUE_UNIT") == 0) { + thetok = VALUE_UNIT_TOK; + } else if (strcmp(theident, "MARK") == 0) { + thetok = MARK_TOK; + } else { + thetok = IDENTIFIER_TOK; + } + return; + } else { + Error("%s, line %d: strange character (%c)", hpfile, linenum, g_ch); + } +} + + +/* + * Read a sequence of digits and convert the result to an integer + * or floating point value (assigned to the "theinteger" or + * "thefloatish"). + */ + +static char numberstring[ NUMBER_LENGTH - 1 ]; + +token +GetNumber(infp) + FILE* infp; +{ + int i; + int containsdot; + + ASSERT(isdigit(ch)); /* we must have a digit to start with */ + + containsdot = 0; + + for (i = 0; i < NUMBER_LENGTH && (isdigit(g_ch) || g_ch == '.'); i++) { + numberstring[ i ] = g_ch; + containsdot |= (g_ch == '.'); + g_ch = getc(infp); + } + + ASSERT(i < NUMBER_LENGTH); /* did not overflow */ + + numberstring[ i ] = '\0'; + + if (containsdot) { + thefloatish = (floatish) atof(numberstring); + return FLOAT_TOK; + } else { + theinteger = atoi(numberstring); + return INTEGER_TOK; + } +} + +/* + * Read a sequence of identifier characters and assign the result + * to the string "theident". + */ + +void +GetIdent(infp) + FILE *infp; +{ + unsigned int i; + char idbuffer[5000]; + + for (i = 0; i < (sizeof idbuffer)-1 && IsIdChar(g_ch); i++) { + idbuffer[ i ] = g_ch; + g_ch = getc(infp); + } + + idbuffer[ i ] = '\0'; + + if (theident) + free(theident); + + theident = copystring(idbuffer); +} + + +/* + * Read a sequence of characters that make up a string and + * assign the result to "thestring". + */ + +void +GetString(infp) + FILE *infp; +{ + unsigned int i; + char stringbuffer[5000]; + + ASSERT(ch == '\"'); + + g_ch = getc(infp); /* skip the '\"' that begins the string */ + + for (i = 0; i < (sizeof stringbuffer)-1 && g_ch != '\"'; i++) { + stringbuffer[ i ] = g_ch; + g_ch = getc(infp); + } + + stringbuffer[i] = '\0'; + thestring = copystring(stringbuffer); + + ASSERT(g_ch == '\"'); + + g_ch = getc(infp); /* skip the '\"' that terminates the string */ +} + +boolish +IsIdChar(ch) + int ch; +{ + return (!isspace(ch)); +} + + +/* + * The information associated with each identifier is stored + * in a linked list of chunks. The table below allows the list + * of chunks to be retrieved given an identifier name. + */ + +#define N_HASH 513 + +static struct entry* hashtable[ N_HASH ]; + +static intish +Hash(s) + char *s; +{ + int r; + + for (r = 0; *s; s++) { + r = r + r + r + *s; + } + + if (r < 0) r = -r; + + return r % N_HASH; +} + +/* + * Get space for a new chunk. Initialise it, and return a pointer + * to the new chunk. + */ + +static struct chunk* +MakeChunk() +{ + struct chunk* ch; + struct datapoint* d; + + ch = (struct chunk*) xmalloc( sizeof(struct chunk) ); + + d = (struct datapoint*) xmalloc (sizeof(struct datapoint) * N_CHUNK); + + ch->nd = 0; + ch->d = d; + ch->next = 0; + return ch; +} + + +/* + * Get space for a new entry. Initialise it, and return a pointer + * to the new entry. + */ + +struct entry * +MakeEntry(name) + char *name; +{ + struct entry* e; + + e = (struct entry *) xmalloc(sizeof(struct entry)); + e->chk = MakeChunk(); + e->name = copystring(name); + return e; +} + +/* + * Get the entry associated with "name", creating a new entry if + * necessary. + */ + +static struct entry * +GetEntry(name) + char* name; +{ + intish h; + struct entry* e; + + h = Hash(name); + + for (e = hashtable[ h ]; e; e = e->next) { + if (strcmp(e->name, name) == 0) { + break; + } + } + + if (e) { + return (e); + } else { + nidents++; + e = MakeEntry(name); + e->next = hashtable[ h ]; + hashtable[ h ] = e; + return (e); + } +} + + +/* + * Store information from a sample. + */ + +void +StoreSample(en, bucket, value) + struct entry* en; intish bucket; floatish value; +{ + struct chunk* chk; + + for (chk = en->chk; chk->next != 0; chk = chk->next) + ; + + if (chk->nd < N_CHUNK) { + chk->d[ chk->nd ].bucket = bucket; + chk->d[ chk->nd ].value = value; + chk->nd += 1; + } else { + struct chunk* t; + t = chk->next = MakeChunk(); + t->d[ 0 ].bucket = bucket; + t->d[ 0 ].value = value; + t->nd += 1; + } +} + + +struct entry** identtable; + +/* + * The hash table is useful while reading the input, but it + * becomes a liability thereafter. The code below converts + * it to a more easily processed table. + */ + +static void +MakeIdentTable() +{ + intish i; + intish j; + struct entry* e; + + nidents = 0; + for (i = 0; i < N_HASH; i++) { + for (e = hashtable[ i ]; e; e = e->next) { + nidents++; + } + } + + identtable = (struct entry**) xmalloc(nidents * sizeof(struct entry*)); + j = 0; + + for (i = 0; i < N_HASH; i++) { + for (e = hashtable[ i ]; e; e = e->next, j++) { + identtable[ j ] = e; + } + } +} diff --git a/VEX/head20041019/massif/hp2ps/HpFile.h b/VEX/head20041019/massif/hp2ps/HpFile.h new file mode 100644 index 0000000000..7a23dfeb47 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/HpFile.h @@ -0,0 +1,81 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef HP_FILE_H +#define HP_FILE_H + +typedef enum { + /* These tokens are found in ".hp" files */ + + EOF_TOK, + INTEGER_TOK, + FLOAT_TOK, + IDENTIFIER_TOK, + STRING_TOK, + BEGIN_SAMPLE_TOK, + END_SAMPLE_TOK, + JOB_TOK, + DATE_TOK, + SAMPLE_UNIT_TOK, + VALUE_UNIT_TOK, + MARK_TOK, + + /* These extra ones are found only in ".aux" files */ + + X_RANGE_TOK, + Y_RANGE_TOK, + ORDER_TOK, + SHADE_TOK +} token; + +struct datapoint { + int bucket; + floatish value; +}; + +struct chunk { + struct chunk *next; + short nd; /* 0 .. N_CHUNK - 1 */ + struct datapoint *d; +}; + + +struct entry { + struct entry *next; + struct chunk *chk; + char *name; +}; + +extern char *theident; +extern char *thestring; +extern int theinteger; +extern floatish thefloatish; +extern int g_ch; +extern token thetok; +extern int linenum; +extern int endfile; + +char *TokenToString PROTO((token)); + +extern struct entry** identtable; + +extern floatish *samplemap; +extern floatish *markmap; + +void GetHpFile PROTO((FILE *)); +void StoreSample PROTO((struct entry *, intish, floatish)); +struct entry *MakeEntry PROTO((char *)); + +token GetNumber PROTO((FILE *)); +void GetIdent PROTO((FILE *)); +void GetString PROTO((FILE *)); +boolish IsIdChar PROTO((int)); /* int is a "char" from getc */ + +extern char *jobstring; +extern char *datestring; + +extern char *sampleunitstring; +extern char *valueunitstring; + +#endif /* HP_FILE_H */ diff --git a/VEX/head20041019/massif/hp2ps/INSTALL b/VEX/head20041019/massif/hp2ps/INSTALL new file mode 100644 index 0000000000..92fe7b77dd --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/INSTALL @@ -0,0 +1,17 @@ +NOTE: these are instructions for installing hp2ps separately. If you only want +to use it with the Valgrind tool, Massif, it should be installed automatically. + +The original Makefile, for which the below instructions apply, is kept as +Makefile.old. + +----------------------------------------------------------------------------- +For binary distribution: + + - just copy "hp2ps" into an appropriate directory. + +For source distribution: + + - edit "Makefile" and set the $DSTBIN to the directory you wish to install + hp2ps in + + - do "make install" diff --git a/VEX/head20041019/massif/hp2ps/Key.c b/VEX/head20041019/massif/hp2ps/Key.c new file mode 100644 index 0000000000..d611552b82 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Key.c @@ -0,0 +1,67 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Dimensions.h" +#include "HpFile.h" +#include "Shade.h" + +/* own stuff */ +#include "Key.h" + +static void KeyEntry PROTO((floatish, char *, floatish)); + +void Key() +{ + intish i; + floatish c; + floatish dc; + + for (i = 0; i < nidents; i++) /* count identifiers */ + ; + + c = graphy0; + dc = graphheight / (floatish) (i + 1); + + for (i = 0; i < nidents; i++) { + c += dc; + KeyEntry(c, identtable[i]->name, ShadeOf(identtable[i]->name)); + } +} + + + +static void +KeyEntry(centreline, name, colour) + floatish centreline; char* name; floatish colour; +{ + floatish namebase; + floatish keyboxbase; + floatish kstart; + + namebase = centreline - (floatish) (NORMAL_FONT / 2); + keyboxbase = centreline - ((floatish) KEY_BOX_WIDTH / 2.0); + + kstart = graphx0 + graphwidth; + + fprintf(psfp, "%f %f moveto\n", kstart + borderspace, keyboxbase); + fprintf(psfp, "0 %d rlineto\n", KEY_BOX_WIDTH); + fprintf(psfp, "%d 0 rlineto\n", KEY_BOX_WIDTH); + fprintf(psfp, "0 %d rlineto\n", -KEY_BOX_WIDTH); + fprintf(psfp, "closepath\n"); + + fprintf(psfp, "gsave\n"); + SetPSColour(colour); + fprintf(psfp, "fill\n"); + fprintf(psfp, "grestore\n"); + fprintf(psfp, "stroke\n"); + + fprintf(psfp, "HE%d setfont\n", NORMAL_FONT); + fprintf(psfp, "%f %f moveto\n", kstart + (floatish) KEY_BOX_WIDTH + 2 * borderspace, namebase); + + fprintf(psfp, "(%s) show\n", name); +} diff --git a/VEX/head20041019/massif/hp2ps/Key.h b/VEX/head20041019/massif/hp2ps/Key.h new file mode 100644 index 0000000000..b573ea0813 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Key.h @@ -0,0 +1,10 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef KEY_H +#define KEY_H + +void Key PROTO((void)); + +#endif /* KEY_H */ diff --git a/VEX/head20041019/massif/hp2ps/LICENSE b/VEX/head20041019/massif/hp2ps/LICENSE new file mode 100644 index 0000000000..b5059b71f6 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/LICENSE @@ -0,0 +1,31 @@ +The Glasgow Haskell Compiler License + +Copyright 2002, The University Court of the University of Glasgow. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +- Neither name of the University nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY COURT OF THE UNIVERSITY OF +GLASGOW AND THE CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +UNIVERSITY COURT OF THE UNIVERSITY OF GLASGOW OR THE CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. diff --git a/VEX/head20041019/massif/hp2ps/Main.c b/VEX/head20041019/massif/hp2ps/Main.c new file mode 100644 index 0000000000..86621a92f5 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Main.c @@ -0,0 +1,257 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include +#include "Main.h" +#include "Defines.h" +#include "AuxFile.h" +#include "AreaBelow.h" +#include "Dimensions.h" +#include "HpFile.h" +#include "PsFile.h" +#include "Reorder.h" +#include "Scale.h" +#include "TopTwenty.h" +#include "TraceElement.h" +#include "Deviation.h" +#include "Error.h" +#include "Utilities.h" + +boolish pflag = 0; /* read auxiliary file */ +boolish eflag = 0; /* scaled EPSF */ +boolish dflag = 0; /* sort by standard deviation */ +int iflag = 0; /* sort by identifier (3-way flag) */ +boolish gflag = 0; /* output suitable for previewer */ +boolish yflag = 0; /* ignore marks */ +boolish bflag = 0; /* use a big title box */ +boolish sflag = 0; /* use a small title box */ +int mflag = 0; /* max no. of bands displayed (default 20) */ +boolish tflag = 0; /* ignored threshold specified */ +boolish cflag = 0; /* colour output */ + +boolish filter; /* true when running as a filter */ + +static floatish WidthInPoints PROTO((char *)); /* forward */ +static FILE *Fp PROTO((char *, char **, char *, char *)); /* forward */ + +char *hpfile; +char *psfile; +char *auxfile; + +char *programname; + +static char *pathName; +static char *baseName; /* "basename" is a std C library name (sigh) */ + +FILE* hpfp; +FILE* psfp; +FILE* auxfp; + +floatish xrange = 0.0; +floatish yrange = 0.0; + +floatish auxxrange = 0.0; +floatish auxyrange = 0.0; + +floatish epsfwidth; +floatish areabelow; + +intish nsamples; +intish nmarks; +intish nidents; + +floatish THRESHOLD_PERCENT = DEFAULT_THRESHOLD; +int TWENTY = DEFAULT_TWENTY; + +int main(argc, argv) +int argc; +char* argv[]; +{ + + programname = copystring(Basename(argv[0])); + + argc--, argv++; + while (argc && argv[0][0] == '-') { + while (*++*argv) + switch(**argv) { + case 'p': + pflag++; + break; + case 'e': + eflag++; + epsfwidth = WidthInPoints(*argv + 1); + goto nextarg; + case 'd': + dflag++; + goto nextarg; + case 'i': + switch( *(*argv + 1) ) { + case '-': + iflag = -1; + case '+': + default: + iflag = 1; + } + goto nextarg; + case 'g': + gflag++; + goto nextarg; + case 'y': + yflag++; + goto nextarg; + case 'b': + bflag++; + goto nextarg; + case 's': + sflag++; + goto nextarg; + case 'm': + mflag++; + TWENTY = atoi(*argv + 1); + if (TWENTY > DEFAULT_TWENTY) + Usage(*argv-1); + goto nextarg; + case 't': + tflag++; + THRESHOLD_PERCENT = (floatish) atof(*argv + 1); + if (THRESHOLD_PERCENT < 0 || THRESHOLD_PERCENT > 5) + Usage(*argv-1); + goto nextarg; + case 'c': + cflag++; + goto nextarg; + case '?': + default: + Usage(*argv-1); + } +nextarg: ; + argc--, argv++; + } + + hpfile = "stdin"; + psfile = "stdout"; + + hpfp = stdin; + psfp = stdout; + + filter = argc < 1; + + + + if (!filter) { + pathName = copystring(argv[0]); + DropSuffix(pathName, ".hp"); + baseName = copystring(Basename(pathName)); + + hpfp = Fp(pathName, &hpfile, ".hp", "r"); + psfp = Fp(baseName, &psfile, ".ps", "w"); + + if (pflag) auxfp = Fp(baseName, &auxfile, ".aux", "r"); + } + + GetHpFile(hpfp); + + if (!filter && pflag) GetAuxFile(auxfp); + + + TraceElement(); /* Orders on total, Removes trace elements (tflag) */ + + if (dflag) Deviation(); /* ReOrders on deviation */ + + if (iflag) Identorder(iflag); /* ReOrders on identifier */ + + if (pflag) Reorder(); /* ReOrders on aux file */ + + if (TWENTY) TopTwenty(); /* Selects top twenty (mflag) */ + + Dimensions(); + + areabelow = AreaBelow(); + + Scale(); + + PutPsFile(); + + if (!filter) { + auxfp = Fp(baseName, &auxfile, ".aux", "w"); + PutAuxFile(auxfp); + } + + return(0); +} + + + +typedef enum {POINTS, INCHES, MILLIMETRES} pim; + +static pim Units PROTO((char *)); /* forward */ + +static floatish +WidthInPoints(wstr) + char *wstr; +{ + floatish result; + + result = (floatish) atof(wstr); + + switch (Units(wstr)) { + case INCHES: + result *= 72.0; + break; + case MILLIMETRES: + result *= 2.834646; + break; + case POINTS: + default: ; + } + + if (result <= 144) /* Minimum of 2in wide ! */ + Usage(wstr); + + return result; +} + + +static pim +Units(wstr) + char* wstr; +{ +int i; + + i = strlen(wstr) - 2; + + if (wstr[i] == 'p' && wstr[i+1] == 't') { + return POINTS; + } else if (wstr[i] == 'i' && wstr[i+1] == 'n') { + return INCHES; + } else if (wstr[i] == 'm' && wstr[i+1] == 'm') { + return MILLIMETRES; + } else { + return POINTS; + } +} + +static FILE * +Fp(rootname, filename, suffix, mode) + char* rootname; char** filename; char* suffix; char* mode; +{ + *filename = copystring2(rootname, suffix); + + return(OpenFile(*filename, mode)); +} + +#ifdef DEBUG +void +_stgAssert (filename, linenum) + char *filename; + unsigned int linenum; +{ + fflush(stdout); + fprintf(stderr, "ASSERTION FAILED: file %s, line %u\n", filename, linenum); + fflush(stderr); + abort(); +} +#endif diff --git a/VEX/head20041019/massif/hp2ps/Main.h b/VEX/head20041019/massif/hp2ps/Main.h new file mode 100644 index 0000000000..5fa954eb4d --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Main.h @@ -0,0 +1,82 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef MAIN_H +#define MAIN_H + +//#include "config.h" + +#ifdef __STDC__ +#define PROTO(x) x +#else +#define PROTO(x) () +#endif + +/* our own ASSERT macro (for C) */ +#ifndef DEBUG +#define ASSERT(predicate) /*nothing*/ + +#else +void _ghcAssert PROTO((char *, unsigned int)); + +#define ASSERT(predicate) \ + if (predicate) \ + /*null*/; \ + else \ + _ghcAssert(__FILE__, __LINE__) +#endif + +/* partain: some ubiquitous types: floatish & intish. + Dubious to use float/int, but that is what it used to be... + (WDP 95/03) +*/ +typedef double floatish; +typedef double doublish; /* higher precision, if anything; little used */ +typedef int boolish; + +/* Use "long long" if we have it: the numbers in profiles can easily + * overflow 32 bits after a few seconds execution. + */ +#define HAVE_LONG_LONG 1 // --added by njn, because config.h removed + +#ifdef HAVE_LONG_LONG +typedef long long int intish; +#else +typedef long int intish; +#endif + +extern intish nsamples; +extern intish nmarks; +extern intish nidents; + +extern floatish maxcombinedheight; +extern floatish areabelow; +extern floatish epsfwidth; + +extern floatish xrange; +extern floatish yrange; + +extern floatish auxxrange; +extern floatish auxyrange; + +extern boolish eflag; +extern boolish gflag; +extern boolish yflag; +extern boolish bflag; +extern boolish sflag; +extern int mflag; +extern boolish tflag; +extern boolish cflag; + +extern char *programname; + +extern char *hpfile; +extern char *psfile; +extern char *auxfile; + +extern FILE *hpfp; +extern FILE *psfp; +extern FILE *g_auxfp; + +#endif /* MAIN_H */ diff --git a/VEX/head20041019/massif/hp2ps/Makefile.am b/VEX/head20041019/massif/hp2ps/Makefile.am new file mode 100644 index 0000000000..1f5100623c --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Makefile.am @@ -0,0 +1,58 @@ +include $(top_srcdir)/Makefile.all.am + +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -O -g + +val_PROGRAMS = hp2ps + +EXTRA_DIST = \ + CHANGES \ + INSTALL \ + LICENSE \ + README \ + Makefile.old \ + hp2ps.1 + +hp2ps_SOURCES = \ + AuxFile.c \ + Axes.c \ + AreaBelow.c \ + Curves.c \ + Deviation.c \ + Dimensions.c \ + Error.c \ + HpFile.c \ + Key.c \ + Main.c \ + Marks.c \ + TopTwenty.c \ + TraceElement.c \ + PsFile.c \ + Reorder.c \ + Scale.c \ + Shade.c \ + Utilities.c +hp2ps_LDADD = -lm + +noinst_HEADERS = \ + AreaBelow.h \ + AuxFile.h \ + Axes.h \ + Curves.h \ + Defines.h \ + Deviation.h \ + Dimensions.h \ + Error.h \ + HpFile.h \ + Key.h \ + Main.h \ + Marks.h \ + PsFile.h \ + Reorder.h \ + Scale.h \ + Shade.h \ + TopTwenty.h \ + TraceElement.h \ + Utilities.h + +include $(top_srcdir)/Makefile.tool-inplace.am + diff --git a/VEX/head20041019/massif/hp2ps/Makefile.old b/VEX/head20041019/massif/hp2ps/Makefile.old new file mode 100644 index 0000000000..a625149552 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Makefile.old @@ -0,0 +1,42 @@ +OBJS= \ + AuxFile.o \ + Axes.o \ + AreaBelow.o \ + Curves.o \ + Deviation.o \ + Dimensions.o \ + Error.o \ + HpFile.o \ + Key.o \ + Main.o \ + Marks.o \ + TopTwenty.o \ + TraceElement.o \ + PsFile.o \ + Reorder.o \ + Scale.o \ + Shade.o \ + Utilities.o + +# Please set MATHLIB and BIN appropriately. I don't need MATHLIB on my machine, +# but you may. + +MATHLIB = -lm + +DSTBIN = /n/Numbers/usr/lml/lml-0.997.4hp/sun3/bin + +CC= cc # gcc -Wall +CFLAGS= -g +LDFLAGS= ${STATICFLAG} + +TARGET=hp2ps + +${TARGET}: ${OBJS} + ${CC} -o ${TARGET} ${CCFLAGS} ${LDFLAGS} ${OBJS} ${MATHLIB} + +install: ${TARGET} + mv ${TARGET} ${DSTBIN}/${TARGET} + chmod 555 ${DSTBIN}/${TARGET} + +clean: + rm -f core *.o ${TARGET} diff --git a/VEX/head20041019/massif/hp2ps/Marks.c b/VEX/head20041019/massif/hp2ps/Marks.c new file mode 100644 index 0000000000..80947df99d --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Marks.c @@ -0,0 +1,47 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include "Main.h" +#include "Curves.h" +#include "Dimensions.h" +#include "HpFile.h" + +/* own stuff */ +#include "Marks.h" + +static void Caret PROTO((floatish, floatish, floatish)); + +void +Marks() +{ + intish i; + floatish m; + + for (i = 0; i < nmarks; i++) { + m = ((markmap[i] - samplemap[0]) / xrange) * graphwidth; + Caret(xpage(m), ypage(0.0), 4.0); + } +} + + +/* + * Draw a small white caret at (x,y) with width 2 * d + */ + +static void +Caret(x,y,d) + floatish x; floatish y; floatish d; +{ + fprintf(psfp, "%f %f moveto\n", x - d, y); + fprintf(psfp, "%f %f rlineto\n", d, -d); + fprintf(psfp, "%f %f rlineto\n", d, d); + fprintf(psfp, "closepath\n"); + + fprintf(psfp, "gsave\n"); + fprintf(psfp, "1.0 setgray\n"); + fprintf(psfp, "fill\n"); + fprintf(psfp, "grestore\n"); + fprintf(psfp, "stroke\n"); +} diff --git a/VEX/head20041019/massif/hp2ps/Marks.h b/VEX/head20041019/massif/hp2ps/Marks.h new file mode 100644 index 0000000000..e71f1cdc76 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Marks.h @@ -0,0 +1,10 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef MARKS_H +#define MARKS_H + +void Marks PROTO((void)); + +#endif /* MARKS_H */ diff --git a/VEX/head20041019/massif/hp2ps/PsFile.c b/VEX/head20041019/massif/hp2ps/PsFile.c new file mode 100644 index 0000000000..2b203e4766 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/PsFile.c @@ -0,0 +1,284 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Dimensions.h" +#include "Curves.h" +#include "HpFile.h" +#include "Axes.h" +#include "Key.h" +#include "Marks.h" +#include "Utilities.h" + +/* own stuff */ +#include "PsFile.h" + +static void Prologue PROTO((void)); /* forward */ +static void Variables PROTO((void)); /* forward */ +static void BorderOutlineBox PROTO((void)); /* forward */ +static void BigTitleOutlineBox PROTO((void)); /* forward */ +static void TitleOutlineBox PROTO((void)); /* forward */ +static void BigTitleText PROTO((void)); /* forward */ +static void TitleText PROTO((void)); /* forward */ + +void +PutPsFile() +{ + Prologue(); + Variables(); + BorderOutlineBox(); + + if (bflag) { + BigTitleOutlineBox(); + BigTitleText(); + } else { + TitleOutlineBox(); + TitleText(); + } + + CurvesInit(); + + Axes(); + + if (TWENTY) Key(); + + Curves(); + + if (!yflag) Marks(); + + fprintf(psfp, "showpage\n"); +} + + +static void StandardSpecialComments PROTO((void)); /* forward */ +static void EPSFSpecialComments PROTO((floatish)); /* forward */ +static void Landscape PROTO((void)); /* forward */ +static void Portrait PROTO((void)); /* forward */ +static void Scaling PROTO((floatish)); /* forward */ + +static void +Prologue() +{ + if (eflag) { + floatish epsfscale = epsfwidth / (floatish) borderwidth; + EPSFSpecialComments(epsfscale); + Scaling(epsfscale); + } else { + StandardSpecialComments(); + if (gflag) Portrait(); else Landscape(); + } +} + +extern char *jobstring; +extern char *datestring; + +static void +StandardSpecialComments() +{ + fprintf(psfp, "%%!PS-Adobe-2.0\n"); + fprintf(psfp, "%%%%Title: %s\n", jobstring); + fprintf(psfp, "%%%%Creator: %s (version %s)\n", programname, VERSION); + fprintf(psfp, "%%%%CreationDate: %s\n", datestring); + fprintf(psfp, "%%%%EndComments\n"); +} + +static void +EPSFSpecialComments(epsfscale) + floatish epsfscale; +{ + fprintf(psfp, "%%!PS-Adobe-2.0\n"); + fprintf(psfp, "%%%%Title: %s\n", jobstring); + fprintf(psfp, "%%%%Creator: %s (version %s)\n", programname, VERSION); + fprintf(psfp, "%%%%CreationDate: %s\n", datestring); + fprintf(psfp, "%%%%BoundingBox: 0 0 %d %d\n", + (int) (borderwidth * epsfscale + 0.5), + (int) (borderheight * epsfscale + 0.5) ); + fprintf(psfp, "%%%%EndComments\n"); +} + + + +static void +Landscape() +{ + fprintf(psfp, "-90 rotate\n"); + fprintf(psfp, "%f %f translate\n", -(borderwidth + (floatish) START_Y), + (floatish) START_X); +} + +static void +Portrait() +{ + fprintf(psfp, "%f %f translate\n", (floatish) START_X, (floatish) START_Y); +} + +static void +Scaling(epsfscale) + floatish epsfscale; +{ + fprintf(psfp, "%f %f scale\n", epsfscale, epsfscale); +} + + +static void +Variables() +{ + fprintf(psfp, "/HE%d /Helvetica findfont %d scalefont def\n", + NORMAL_FONT, NORMAL_FONT); + + fprintf(psfp, "/HE%d /Helvetica findfont %d scalefont def\n", + LARGE_FONT, LARGE_FONT); +} + + +static void +BorderOutlineBox() +{ + fprintf(psfp, "newpath\n"); + fprintf(psfp, "0 0 moveto\n"); + fprintf(psfp, "0 %f rlineto\n", borderheight); + fprintf(psfp, "%f 0 rlineto\n", borderwidth); + fprintf(psfp, "0 %f rlineto\n", -borderheight); + fprintf(psfp, "closepath\n"); + fprintf(psfp, "%f setlinewidth\n", borderthick); + fprintf(psfp, "stroke\n"); +} + +static void +BigTitleOutlineBox() +{ + fprintf(psfp, "newpath\n"); + fprintf(psfp, "%f %f moveto\n", borderspace, + borderheight - titleheight - borderspace); + fprintf(psfp, "0 %f rlineto\n", titleheight); + fprintf(psfp, "%f 0 rlineto\n", titlewidth); + fprintf(psfp, "0 %f rlineto\n", -titleheight); + fprintf(psfp, "closepath\n"); + fprintf(psfp, "%f setlinewidth\n", borderthick); + fprintf(psfp, "stroke\n"); + + fprintf(psfp, "%f %f moveto\n", borderspace, + borderheight - titleheight / 2 - borderspace); + fprintf(psfp, "%f 0 rlineto\n", titlewidth); + fprintf(psfp, "stroke\n"); +} + + +static void +TitleOutlineBox() +{ + fprintf(psfp, "newpath\n"); + fprintf(psfp, "%f %f moveto\n", borderspace, + borderheight - titleheight - borderspace); + fprintf(psfp, "0 %f rlineto\n", titleheight); + fprintf(psfp, "%f 0 rlineto\n", titlewidth); + fprintf(psfp, "0 %f rlineto\n", -titleheight); + fprintf(psfp, "closepath\n"); + fprintf(psfp, "%f setlinewidth\n", borderthick); + fprintf(psfp, "stroke\n"); +} + +static void EscapePrint PROTO((char *, int)); /* forward */ + +static void +BigTitleText() +{ + floatish x, y; + + x = borderspace + titletextspace; + y = borderheight - titleheight / 2 - borderspace + titletextspace; + + /* job identifier goes on top at the far left */ + + fprintf(psfp, "HE%d setfont\n", TITLE_TEXT_FONT); + fprintf(psfp, "%f %f moveto\n", x, y); + fputc('(', psfp); + EscapePrint(jobstring, BIG_JOB_STRING_WIDTH); + fprintf(psfp, ") show\n"); + + y = borderheight - titleheight - borderspace + titletextspace; + + /* area below curve gows at the botton, far left */ + + fprintf(psfp, "HE%d setfont\n", TITLE_TEXT_FONT); + fprintf(psfp, "%f %f moveto\n", x, y); + fputc('(', psfp); + CommaPrint(psfp, (intish)areabelow); + fprintf(psfp, " %s x %s)\n", valueunitstring, sampleunitstring); + fprintf(psfp, "show\n"); + + /* date goes at far right */ + + fprintf(psfp, "HE%d setfont\n", TITLE_TEXT_FONT); + fprintf(psfp, "(%s)\n", datestring); + fprintf(psfp, "dup stringwidth pop\n"); + fprintf(psfp, "%f\n", (titlewidth + borderspace) - titletextspace); + fprintf(psfp, "exch sub\n"); + fprintf(psfp, "%f moveto\n", y); + fprintf(psfp, "show\n"); +} + + +static void +TitleText() +{ + floatish x, y; + + x = borderspace + titletextspace; + y = borderheight - titleheight - borderspace + titletextspace; + + /* job identifier goes at far left */ + + fprintf(psfp, "HE%d setfont\n", TITLE_TEXT_FONT); + fprintf(psfp, "%f %f moveto\n", x, y); + fputc('(', psfp); + EscapePrint(jobstring, SMALL_JOB_STRING_WIDTH); + fprintf(psfp, ") show\n"); + + /* area below curve is centered */ + + fprintf(psfp, "HE%d setfont\n", TITLE_TEXT_FONT); + fputc('(', psfp); + CommaPrint(psfp, (intish) areabelow); + fprintf(psfp, " %s x %s)\n", valueunitstring, sampleunitstring); + + fprintf(psfp, "dup stringwidth pop\n"); + fprintf(psfp, "2 div\n"); + fprintf(psfp, "%f\n", titlewidth / 2); + fprintf(psfp, "exch sub\n"); + fprintf(psfp, "%f moveto\n", y); + fprintf(psfp, "show\n"); + + /* date goes at far right */ + + fprintf(psfp, "HE%d setfont\n", TITLE_TEXT_FONT); + fprintf(psfp, "(%s)\n", datestring); + fprintf(psfp, "dup stringwidth pop\n"); + fprintf(psfp, "%f\n", (titlewidth + borderspace) - titletextspace); + fprintf(psfp, "exch sub\n"); + fprintf(psfp, "%f moveto\n", y); + fprintf(psfp, "show\n"); +} + +/* + * Print a string s in width w, escaping characters where necessary. + */ + +static void +EscapePrint(s,w) + char* s; int w; +{ + for ( ; *s && w > 0; s++, w--) { + if (*s == '(') { /* escape required */ + fputc('\\', psfp); + } else if (*s == ')') { + fputc('\\', psfp); + } + + fputc(*s, psfp); + } +} diff --git a/VEX/head20041019/massif/hp2ps/PsFile.h b/VEX/head20041019/massif/hp2ps/PsFile.h new file mode 100644 index 0000000000..01f8cc1927 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/PsFile.h @@ -0,0 +1,10 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef PS_FILE_H +#define PS_FILE_H + +void PutPsFile PROTO((void)); + +#endif /* PS_FILE_H */ diff --git a/VEX/head20041019/massif/hp2ps/README b/VEX/head20041019/massif/hp2ps/README new file mode 100644 index 0000000000..f0f09ce35a --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/README @@ -0,0 +1 @@ +This "hp2ps" program was written by Dave Wakeling at York. diff --git a/VEX/head20041019/massif/hp2ps/Reorder.c b/VEX/head20041019/massif/hp2ps/Reorder.c new file mode 100644 index 0000000000..fc6db6add9 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Reorder.c @@ -0,0 +1,93 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Error.h" +#include "HpFile.h" +#include "Utilities.h" + +/* own stuff */ +#include "Reorder.h" + +static struct order { + char* ident; + int order; +} *ordermap = 0; + +static int ordermapmax = 0; +static int ordermapindex = 0; + + +void +OrderFor(ident, order) + char* ident; + int order; +{ + if (! ordermap) { + ordermapmax = (nidents > TWENTY ? nidents : TWENTY) * 2; + /* Assume nidents read is indication of the No of + idents in the .aux file (*2 for good luck !) */ + ordermap = xmalloc(ordermapmax * sizeof(struct order)); + } + + if (ordermapindex < ordermapmax) { + ordermap[ ordermapindex ].ident = copystring(ident); + ordermap[ ordermapindex ].order = order; + ordermapindex++; + } else { + Disaster("order map overflow"); + } +} + +/* + * Get the order of to be used for "ident" if there is one. + * Otherwise, return 0 which is the minimum ordering value. + */ + +int +OrderOf(ident) + char* ident; +{ + int i; + + for (i = 0; i < ordermapindex; i++) { + if (strcmp(ordermap[i].ident, ident) == 0) { /* got it */ + return(ordermap[i].order); + } + } + + return 0; +} + +/* + * Reorder on the basis of information from ".aux" file. + */ + +void +Reorder() +{ + intish i; + intish j; + int min; + struct entry* e; + int o1, o2; + + for (i = 0; i < nidents-1; i++) { + min = i; + for (j = i+1; j < nidents; j++) { + o1 = OrderOf(identtable[ j ]->name); + o2 = OrderOf(identtable[ min ]->name); + + if (o1 < o2 ) min = j; + } + + e = identtable[ min ]; + identtable[ min ] = identtable[ i ]; + identtable[ i ] = e; + } +} diff --git a/VEX/head20041019/massif/hp2ps/Reorder.h b/VEX/head20041019/massif/hp2ps/Reorder.h new file mode 100644 index 0000000000..81c4ba3404 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Reorder.h @@ -0,0 +1,12 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef REORDER_H +#define REORDER_H + +void Reorder PROTO((void)); +int OrderOf PROTO((char *)); +void OrderFor PROTO((char *, int)); + +#endif /* REORDER_H */ diff --git a/VEX/head20041019/massif/hp2ps/Scale.c b/VEX/head20041019/massif/hp2ps/Scale.c new file mode 100644 index 0000000000..b6b5e3a9ec --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Scale.c @@ -0,0 +1,90 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Dimensions.h" +#include "Error.h" +#include "HpFile.h" +#include "Utilities.h" + +/* own stuff */ +#include "Scale.h" + +/* + * Return the maximum combined height that all the sample + * curves will reach. This (absolute) figure can then be + * used to scale the samples automatically so that they + * fit on the page. + */ + +floatish +MaxCombinedHeight() +{ + intish i; + intish j; + floatish mx; + int bucket; + floatish value; + struct chunk* ch; + floatish *maxima; + + maxima = (floatish*) xmalloc(nsamples * sizeof(floatish)); + for (i = 0; i < nsamples; i++) { + maxima[ i ] = 0.0; + } + + for (i = 0; i < nidents; i++) { + for (ch = identtable[i]->chk; ch; ch = ch->next) { + for (j = 0; j < ch->nd; j++) { + bucket = ch->d[j].bucket; + value = ch->d[j].value; + if (bucket >= nsamples) + Disaster("bucket out of range"); + maxima[ bucket ] += value; + } + } + } + + for (mx = maxima[ 0 ], i = 0; i < nsamples; i++) { + if (maxima[ i ] > mx) mx = maxima[ i ]; + } + + free(maxima); + return mx; +} + + + +/* + * Scale the values from the samples so that they will fit on + * the page. + */ + +extern floatish xrange; +extern floatish yrange; + +void +Scale() +{ + intish i; + intish j; + floatish sf; + struct chunk* ch; + + if (yrange == 0.0) /* no samples */ + return; + + sf = graphheight / yrange; + + for (i = 0; i < nidents; i++) { + for (ch = identtable[i]->chk; ch; ch = ch->next) { + for (j = 0; j < ch->nd; j++) { + ch->d[j].value = ch->d[j].value * sf; + } + } + } +} diff --git a/VEX/head20041019/massif/hp2ps/Scale.h b/VEX/head20041019/massif/hp2ps/Scale.h new file mode 100644 index 0000000000..2a3487ebbb --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Scale.h @@ -0,0 +1,11 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef SCALE_H +#define SCALE_H + +floatish MaxCombinedHeight PROTO((void)); +void Scale PROTO((void)); + +#endif /* SCALE_H */ diff --git a/VEX/head20041019/massif/hp2ps/Shade.c b/VEX/head20041019/massif/hp2ps/Shade.c new file mode 100644 index 0000000000..0c1a3be09d --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Shade.c @@ -0,0 +1,134 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Error.h" +#include "Utilities.h" + +/* own stuff */ +#include "Shade.h" + +static struct shade { + char* ident; + floatish shade; +} *shademap; + +static int shademapmax = 0; +static int shademapindex = 0; + +/* + * Set the shade to be used for "ident" to "shade". + */ + +void +ShadeFor(ident, shade) + char* ident; + floatish shade; +{ + if (! shademap) { + shademapmax = (nidents > TWENTY ? nidents : TWENTY) * 2; + /* Assume nidents read is indication of the No of + idents in the .aux file (*2 for good luck) */ + /* NB *2 is needed as .aux and .hp elements may differ */ + shademap = xmalloc(shademapmax * sizeof(struct shade)); + } + + if (shademapindex < shademapmax) { + shademap[ shademapindex ].ident = copystring(ident); + shademap[ shademapindex ].shade = shade; + shademapindex++; + } else { + Disaster("shade map overflow"); + } +} + +/* + * Get the shade to be used for "ident" if there is one. + * Otherwise, think of a new one. + */ + +static floatish ThinkOfAShade PROTO((void)); /* forward */ + +floatish +ShadeOf(ident) + char* ident; +{ + int i; + floatish shade; + + for (i = 0; i < shademapindex; i++) { + if (strcmp(shademap[i].ident, ident) == 0) { /* got it */ + return(shademap[i].shade); + } + } + + shade = ThinkOfAShade(); + + ShadeFor(ident, shade); + + return shade; +} + + + +#define N_MONO_SHADES 10 + +static floatish m_shades[ N_MONO_SHADES ] = { + 0.00000, 0.20000, 0.60000, 0.30000, 0.90000, + 0.40000, 1.00000, 0.70000, 0.50000, 0.80000 +}; + +#define N_COLOUR_SHADES 27 + +/* HACK: 0.100505 means 100% red, 50% green, 50% blue */ + +static floatish c_shades[ N_COLOUR_SHADES ] = { + 0.000000, 0.000010, 0.001000, 0.001010, 0.100000, + 0.100010, 0.101000, 0.101010, 0.000005, 0.000500, + 0.000510, 0.001005, 0.050000, 0.050010, 0.051000, + 0.051010, 0.100005, 0.100500, 0.100510, 0.101005, + 0.000505, 0.050005, 0.050500, 0.050510, 0.051005, + 0.100505, 0.050505 +}; + +static floatish +ThinkOfAShade() +{ + static int thisshade = -1; + + thisshade++; + return cflag ? + c_shades[ thisshade % N_COLOUR_SHADES ] : + m_shades[ thisshade % N_MONO_SHADES ] ; +} + +static floatish +extract_colour(shade,factor) + floatish shade; + intish factor; +{ + intish i,j; + + i = (int)(shade * factor); + j = i / 100; + return (i - j * 100) / 10.0; +} + +void +SetPSColour(shade) + floatish shade; +{ + if (cflag) { + fprintf(psfp, "%f %f %f setrgbcolor\n", + extract_colour(shade, (intish)100), + extract_colour(shade, (intish)10000), + extract_colour(shade, (intish)1000000)); + } else { + fprintf(psfp, "%f setgray\n", shade); + } +} diff --git a/VEX/head20041019/massif/hp2ps/Shade.h b/VEX/head20041019/massif/hp2ps/Shade.h new file mode 100644 index 0000000000..66a803ea53 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Shade.h @@ -0,0 +1,12 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef SHADE_H +#define SHADE_H + +floatish ShadeOf PROTO((char *)); +void ShadeFor PROTO((char *, floatish)); +void SetPSColour PROTO((floatish)); + +#endif /* SHADE_H */ diff --git a/VEX/head20041019/massif/hp2ps/TopTwenty.c b/VEX/head20041019/massif/hp2ps/TopTwenty.c new file mode 100644 index 0000000000..3c6ccbde0f --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/TopTwenty.c @@ -0,0 +1,76 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Defines.h" +#include "Error.h" +#include "HpFile.h" +#include "Utilities.h" + +/* own stuff */ +#include "TopTwenty.h" + +/* + * We only have room in the key for a maximum of 20 identifiers. + * We therefore choose to keep the top 20 bands --- these will + * be the most important ones, since this pass is performed after + * the threshold and standard deviation passes. If there are more + * than 20 bands, the excess are gathered together as an "OTHER" ] + * band which appears as band 20. + */ + +void +TopTwenty() +{ + intish i; + intish j; + intish compact; + intish bucket; + floatish value; + struct entry* en; + struct chunk* ch; + floatish *other; + + i = nidents; + if (i <= TWENTY) return; /* nothing to do! */ + + other = (floatish*) xmalloc(nsamples * sizeof(floatish)); + /* build a list of samples for "OTHER" */ + + compact = (i - TWENTY) + 1; + + for (i = 0; i < nsamples; i++) { + other[ i ] = 0.0; + } + + for (i = 0; i < compact && i < nidents; i++) { + for (ch = identtable[i]->chk; ch; ch = ch->next) { + for (j = 0; j < ch->nd; j++) { + bucket = ch->d[j].bucket; + value = ch->d[j].value; + if (bucket >= nsamples) + Disaster("bucket out of range"); + other[ bucket ] += value; + } + } + } + + en = MakeEntry("OTHER"); + en->next = 0; + + for (i = 0; i < nsamples; i++) { + StoreSample(en, i, other[i]); + } + + /* slide samples down */ + for (i = compact; i < nidents; i++) { + identtable[i-compact+1] = identtable[i]; + } + + nidents = TWENTY; + identtable[0] = en; + free(other); +} diff --git a/VEX/head20041019/massif/hp2ps/TopTwenty.h b/VEX/head20041019/massif/hp2ps/TopTwenty.h new file mode 100644 index 0000000000..f724a4da7f --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/TopTwenty.h @@ -0,0 +1,10 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef TOP_TWENTY_H +#define TOP_TWENTY_H + +void TopTwenty PROTO((void)); + +#endif /* TOP_TWENTY_H */ diff --git a/VEX/head20041019/massif/hp2ps/TraceElement.c b/VEX/head20041019/massif/hp2ps/TraceElement.c new file mode 100644 index 0000000000..158eb938d0 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/TraceElement.c @@ -0,0 +1,100 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Defines.h" +#include "HpFile.h" +#include "Error.h" +#include "Utilities.h" + +/* own stuff */ +#include "TraceElement.h" + +/* + * Compute the total volume for each identifier, and the grand + * total of these totals. The identifiers whose totals when + * added together amount to less that a threshold percentage + * (default 1%) of the grand total are considered to be ``trace + * elements'' and they are thrown away. + */ + +extern floatish thresholdpercent; + +void TraceElement() +{ + intish i; + intish j; + struct chunk* ch; + floatish grandtotal; + intish min; + floatish t; + floatish p; + struct entry* e; + intish *totals; + + totals = (intish *) xmalloc(nidents * sizeof(intish)); + + /* find totals */ + + for (i = 0; i < nidents; i++) { + totals[ i ] = 0; + } + + for (i = 0; i < nidents; i++) { + for (ch = identtable[i]->chk; ch; ch = ch->next) { + for (j = 0; j < ch->nd; j++) { + totals[ i ] += ch->d[j].value; + } + } + } + + /* sort on the basis of total */ + + for (i = 0; i < nidents-1; i++) { + min = i; + for (j = i+1; j < nidents; j++) { + if (totals[ j ] < totals[ min ]) { + min = j; + } + } + + t = totals[ min ]; + totals[ min ] = totals[ i ]; + totals[ i ] = t; + + e = identtable[ min ]; + identtable[ min ] = identtable[ i ]; + identtable[ i ] = e; + } + + + /* find the grand total (NB: can get *BIG*!) */ + + grandtotal = 0.0; + + for (i = 0; i < nidents; i++) { + grandtotal += (floatish) totals[ i ]; + } + + t = 0.0; /* cumulative percentage */ + + for (i = 0; i < nidents; i++) { + p = (100.0 * (floatish) totals[i]) / grandtotal; + t = t + p; + if (t >= THRESHOLD_PERCENT) { + break; + } + } + + /* identifiers from 0 to i-1 should be removed */ + for (j = 0; i < nidents; i++, j++) { + identtable[j] = identtable[i]; + } + + nidents = j; + + free(totals); +} diff --git a/VEX/head20041019/massif/hp2ps/TraceElement.h b/VEX/head20041019/massif/hp2ps/TraceElement.h new file mode 100644 index 0000000000..b5ee8b53d1 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/TraceElement.h @@ -0,0 +1,10 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef TRACE_ELEMENT_H +#define TRACE_ELEMENT_H + +void TraceElement PROTO((void)); + +#endif /* TRACE_ELEMENT_H */ diff --git a/VEX/head20041019/massif/hp2ps/Utilities.c b/VEX/head20041019/massif/hp2ps/Utilities.c new file mode 100644 index 0000000000..754cc8a01a --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Utilities.c @@ -0,0 +1,136 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#include +#include +#include "Main.h" +#include "Error.h" + +extern void* malloc(); + +char* +Basename(name) + char* name; +{ + char* t; + + t = name; + + while (*name) { + if (*name == '/') { + t = name+1; + } + name++; + } + + return t; +} + +void +DropSuffix(name, suffix) + char* name; char* suffix; +{ + char* t; + + t = (char*) 0; + + while (*name) { + if (*name == '.') { + t = name; + } + name++; + } + + if (t != (char*) 0 && strcmp(t, suffix) == 0) { + *t = '\0'; + } +} + +FILE* +OpenFile(s, mode) + char* s; char* mode; +{ + FILE* r; + + if ((r = fopen(s, mode)) == NULL) { + /*NOTREACHED*/ + Error("cannot open %s", s); + } + + return r; +} + + +#define ONETHOUSAND 1000 + +/* + * Print a positive integer with commas + */ + +void +CommaPrint(fp,n) + FILE* fp; + intish n; +{ + if (n < ONETHOUSAND) { + fprintf(fp, "%d", (int)n); + } else { + CommaPrint(fp, n / ONETHOUSAND); + fprintf(fp, ",%03d", (int)(n % ONETHOUSAND)); + } +} + +void * +xmalloc(n) + size_t n; +{ + void *r; + + r = (void*) malloc(n); + if (!r) { + /*NOTREACHED*/ + Disaster("%s, sorry, out of memory", hpfile); + } + return r; +} + +void * +xrealloc(p, n) + void *p; + size_t n; +{ + void *r; + extern void *realloc(); + + r = realloc(p, n); + if (!r) { + /*NOTREACHED*/ + Disaster("%s, sorry, out of memory", hpfile); + } + return r; +} + +char * +copystring(s) + char *s; +{ + char *r; + + r = (char*) xmalloc(strlen(s)+1); + strcpy(r, s); + return r; +} + +char * +copystring2(s, t) + char *s, *t; +{ + char *r; + + r = (char*) xmalloc(strlen(s)+strlen(t)+1); + strcpy(r, s); + strcat(r, t); + return r; +} + diff --git a/VEX/head20041019/massif/hp2ps/Utilities.h b/VEX/head20041019/massif/hp2ps/Utilities.h new file mode 100644 index 0000000000..674843a65d --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/Utilities.h @@ -0,0 +1,17 @@ +/* This file is part of hp2ps, a graph drawer for memory profiles. + Copyright (C) 2002 The University Court of the University of Glasgow. + This program is governed by the license contained in the file LICENSE. */ + +#ifndef UTILITIES_H +#define UTILITIES_H + +char* Basename PROTO((char *)); +void DropSuffix PROTO((char *, char *)); +FILE* OpenFile PROTO((char *, char *)); +void CommaPrint PROTO((FILE *, intish)); +char *copystring PROTO((char *)); +char *copystring2 PROTO((char *, char *)); +void *xmalloc PROTO((size_t)); +void *xrealloc PROTO((void *, size_t)); + +#endif /* UTILITIES_H */ diff --git a/VEX/head20041019/massif/hp2ps/hp2ps.1 b/VEX/head20041019/massif/hp2ps/hp2ps.1 new file mode 100644 index 0000000000..fd0bca0234 --- /dev/null +++ b/VEX/head20041019/massif/hp2ps/hp2ps.1 @@ -0,0 +1,145 @@ +.\" man page for hp2ps +.ds PS P\s-2OST\s+2S\s-2CRIPT\s+2 +.\" typeset examples in fixed size font as indented paragraph +.de Ex +.sp +.RS +.nf +.ft C +.. +.de Xe +.RE +.sp +.fi +.. +.TH HP2PS 1 "18 April 1992" +.SH NAME +hp2ps \- convert a heap profile to a \*(PS graph +.SH SYNOPSIS +.B hp2ps +[flags] [file][.hp] +.SH DESCRIPTION +The program +.B hp2ps +converts a heap profile stored in +.IR file +into a \*(PS graph, sending the result to +.IR file.ps. +By convention, files to be processed by +.B hp2ps +have a +.I .hp +extension. However, for compatibility with older versions of +.B hp2ps, +this extension can be omitted. If +.IR file +is omitted entirely, then the program behaves as a filter. +.SH OPTIONS +The flags are: +.IP "\fB\-d\fP" +In order to make graphs more readable, +.B hp2ps +sorts the shaded bands for each identifier. The default sort ordering is for +the bands with the largest area to be stacked on top of the smaller ones. +The +.B \-d +option causes rougher bands (those reprsenting series of values with the +largest standard deviations) to be stacked on top of smoother ones. +.IP "\fB\-b\fP" +Normally, +.B hp2ps +puts the title of the graph in a small box at the top of the page. However, +if the JOB string is too long to fit in a small box (more than 35 characters), +then +.B hp2ps +will choose to use a big box instead. The +.B \-b +option forces +.B hp2ps +to use a big box. +.IP "\fB\-e\fP \fIfloat\fP[in|mm|pt]" +Generate encapsulated \*(PS suitable for inclusion in LaTeX documents. +Usually, the \*(PS graph is drawn in landscape mode in an area +9 inches wide by 6 inches high, and +.B hp2ps +arranges for this area to be approximately centered on a sheet of a4 +paper. This format is convenient of studying the graph in detail, but +it is unsuitable for inclusion in LaTeX documents. The +.B \-e +option causes the graph to be drawn in portrait mode, with +.I float +specifying the width in inches, millimetres or points (the default). +The resulting \*(PS file conforms to the +.I "Encapsulated Post Script" +(EPS) convention, and it can be included in a LaTeX document using Rokicki's +dvi-to-\*(PS converter +.B dvips. +.B hp2ps +requires the width to exceed 2 inches. +.IP "\fB\-g\fP" +Create output suitable for the +.B gs +\*(PS previewer (or similar). In this case the graph is printed in portrait +mode without scaling. The output is unsuitable for a laser printer. +.IP "\fB\-p\fP" +Use previous parameters. By default, the \*(PS graph is automatically +scaled both horizontally and vertically so that it fills the page. +However, when preparing a seires of graphs for use in a presentation, +it is often useful to draw a new graph using the same scale, shading and +ordering as a previous one. The +.B \-p +flag causes the graph to be drawn using the parameters determined by +a previous run of +.B hp2ps +on +.IR file. +.IP "\fB\-s\fP" +Use a small box for the title. +.IP "\fB\-y\fP" +Draw the graph in the traditional York style, ignoring marks. +.IP "\fB\-?\fP" +Print out usage information. +.SH "INPUT FORMAT" +The format of a heap profile is best described by example: +.Ex +JOB "a.out -p" +DATE "Fri Apr 17 11:43:45 1992" +SAMPLE_UNIT "seconds" +VALUE_UNIT "bytes" +BEGIN_SAMPLE 0.00 + SYSTEM 24 +END_SAMPLE 0.00 +BEGIN_SAMPLE 1.00 + elim 180 + insert 24 + intersect 12 + disin 60 + main 12 + reduce 20 + SYSTEM 12 +END_SAMPLE 1.00 +MARK 1.50 +MARK 1.75 +MARK 1.80 +BEGIN_SAMPLE 2.00 + elim 192 + insert 24 + intersect 12 + disin 84 + main 12 + SYSTEM 24 +END_SAMPLE 2.00 +BEGIN_SAMPLE 2.82 +END_SAMPLE 2.82 + +.Xe +.SH "SEE ALSO" +dvips(1), latex(1), hbchp (1), lmlchp(1) +.br +C. Runciman and D. Wakeling, +.I +Heap Profiling for Lazy Functional Languages, YCS-172, University of York, 1992 +.SH NOTES +\*(PS is a registered trademark of Adobe Systems Incorporated. +.SH AUTHOR +David Wakeling of the University of York. diff --git a/VEX/head20041019/massif/ms_main.c b/VEX/head20041019/massif/ms_main.c new file mode 100644 index 0000000000..baa6fa70a6 --- /dev/null +++ b/VEX/head20041019/massif/ms_main.c @@ -0,0 +1,1818 @@ + +/*--------------------------------------------------------------------*/ +/*--- Massif: a heap profiling tool. ms_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Massif, a Valgrind tool for profiling memory + usage of programs. + + Copyright (C) 2003-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +// Memory profiler. Produces a graph, gives lots of information about +// allocation contexts, in terms of space.time values (ie. area under the +// graph). Allocation context information is hierarchical, and can thus +// be inspected step-wise to an appropriate depth. See comments on data +// structures below for more info on how things work. + +#include "tool.h" +//#include "vg_profile.c" + +#include "valgrind.h" // For {MALLOC,FREE}LIKE_BLOCK + +/*------------------------------------------------------------*/ +/*--- Overview of operation ---*/ +/*------------------------------------------------------------*/ + +// Heap blocks are tracked, and the amount of space allocated by various +// contexts (ie. lines of code, more or less) is also tracked. +// Periodically, a census is taken, and the amount of space used, at that +// point, by the most significant (highly allocating) contexts is recorded. +// Census start off frequently, but are scaled back as the program goes on, +// so that there are always a good number of them. At the end, overall +// spacetimes for different contexts (of differing levels of precision) is +// calculated, the graph is printed, and the text giving spacetimes for the +// increasingly precise contexts is given. +// +// Measures the following: +// - heap blocks +// - heap admin bytes +// - stack(s) +// - code (code segments loaded at startup, and loaded with mmap) +// - data (data segments loaded at startup, and loaded/created with mmap, +// and brk()d segments) + +/*------------------------------------------------------------*/ +/*--- Main types ---*/ +/*------------------------------------------------------------*/ + +// An XPt represents an "execution point", ie. a code address. Each XPt is +// part of a tree of XPts (an "execution tree", or "XTree"). Each +// top-to-bottom path through an XTree gives an execution context ("XCon"), +// and is equivalent to a traditional Valgrind ExeContext. +// +// The XPt at the top of an XTree (but below "alloc_xpt") is called a +// "top-XPt". The XPts are the bottom of an XTree (leaf nodes) are +// "bottom-XPTs". The number of XCons in an XTree is equal to the number of +// bottom-XPTs in that XTree. +// +// All XCons have the same top-XPt, "alloc_xpt", which represents all +// allocation functions like malloc(). It's a bit of a fake XPt, though, +// and is only used because it makes some of the code simpler. +// +// XTrees are bi-directional. +// +// > parent < Example: if child1() calls parent() and child2() +// / | \ also calls parent(), and parent() calls malloc(), +// | / \ | the XTree will look like this. +// | v v | +// child1 child2 + +typedef struct _XPt XPt; + +struct _XPt { + Addr eip; // code address + + // Bottom-XPts: space for the precise context. + // Other XPts: space of all the descendent bottom-XPts. + // Nb: this value goes up and down as the program executes. + UInt curr_space; + + // An approximate space.time calculation used along the way for selecting + // which contexts to include at each census point. + // !!! top-XPTs only !!! + ULong approx_ST; + + // exact_ST_dbld is an exact space.time calculation done at the end, and + // used in the results. + // Note that it is *doubled*, to avoid rounding errors. + // !!! not used for 'alloc_xpt' !!! + ULong exact_ST_dbld; + + // n_children and max_children are integers; a very big program might + // have more than 65536 allocation points (Konqueror startup has 1800). + XPt* parent; // pointer to parent XPt + UInt n_children; // number of children + UInt max_children; // capacity of children array + XPt** children; // pointers to children XPts +}; + +// Each census snapshots the most significant XTrees, each XTree having a +// top-XPt as its root. The 'curr_space' element for each XPt is recorded +// in the snapshot. The snapshot contains all the XTree's XPts, not in a +// tree structure, but flattened into an array. This flat snapshot is used +// at the end for computing exact_ST_dbld for each XPt. +// +// Graph resolution, x-axis: no point having more than about 200 census +// x-points; you can't see them on the graph. Therefore: +// +// - do a census every 1 ms for first 200 --> 200, all (200 ms) +// - halve (drop half of them) --> 100, every 2nd (200 ms) +// - do a census every 2 ms for next 200 --> 200, every 2nd (400 ms) +// - halve --> 100, every 4th (400 ms) +// - do a census every 4 ms for next 400 --> 200, every 4th (800 ms) +// - etc. +// +// This isn't exactly right, because we actually drop (N/2)-1 when halving, +// but it shows the basic idea. + +#define MAX_N_CENSI 200 // Keep it even, for simplicity + +// Graph resolution, y-axis: hp2ps only draws the 19 biggest (in space-time) +// bands, rest get lumped into OTHERS. I only print the top N +// (cumulative-so-far space-time) at each point. N should be a bit bigger +// than 19 in case the cumulative space-time doesn't fit with the eventual +// space-time computed by hp2ps (but it should be close if the samples are +// evenly spread, since hp2ps does an approximate per-band space-time +// calculation that just sums the totals; ie. it assumes all samples are +// the same distance apart). + +#define MAX_SNAPSHOTS 32 + +typedef + struct { + XPt* xpt; + UInt space; + } + XPtSnapshot; + +// An XTree snapshot is stored as an array of of XPt snapshots. +typedef XPtSnapshot* XTreeSnapshot; + +typedef + struct { + Int ms_time; // Int: must allow -1 + XTreeSnapshot xtree_snapshots[MAX_SNAPSHOTS+1]; // +1 for zero-termination + UInt others_space; + UInt heap_admin_space; + UInt stacks_space; + } + Census; + +// Metadata for heap blocks. Each one contains a pointer to a bottom-XPt, +// which is a foothold into the XCon at which it was allocated. From +// HP_Chunks, XPt 'space' fields are incremented (at allocation) and +// decremented (at deallocation). +// +// Nb: first two fields must match core's VgHashNode. +typedef + struct _HP_Chunk { + struct _HP_Chunk* next; + Addr data; // Ptr to actual block + UInt size; // Size requested + XPt* where; // Where allocated; bottom-XPt + } + HP_Chunk; + +/*------------------------------------------------------------*/ +/*--- Profiling events ---*/ +/*------------------------------------------------------------*/ + +typedef + enum { + VgpGetXPt = VgpFini+1, + VgpGetXPtSearch, + VgpCensus, + VgpCensusHeap, + VgpCensusSnapshot, + VgpCensusTreeSize, + VgpUpdateXCon, + VgpCalcSpacetime2, + VgpPrintHp, + VgpPrintXPts, + } + VgpSkinCC; + +/*------------------------------------------------------------*/ +/*--- Statistics ---*/ +/*------------------------------------------------------------*/ + +// Konqueror startup, to give an idea of the numbers involved with a biggish +// program, with default depth: +// +// depth=3 depth=40 +// - 310,000 allocations +// - 300,000 frees +// - 15,000 XPts 800,000 XPts +// - 1,800 top-XPts + +static UInt n_xpts = 0; +static UInt n_bot_xpts = 0; +static UInt n_allocs = 0; +static UInt n_zero_allocs = 0; +static UInt n_frees = 0; +static UInt n_children_reallocs = 0; +static UInt n_snapshot_frees = 0; + +static UInt n_halvings = 0; +static UInt n_real_censi = 0; +static UInt n_fake_censi = 0; +static UInt n_attempted_censi = 0; + +/*------------------------------------------------------------*/ +/*--- Globals ---*/ +/*------------------------------------------------------------*/ + +#define FILENAME_LEN 256 + +#define SPRINTF(zz_buf, fmt, args...) \ + do { Int len = VG_(sprintf)(zz_buf, fmt, ## args); \ + VG_(write)(fd, (void*)zz_buf, len); \ + } while (0) + +#define BUF_LEN 1024 // general purpose +static Char buf [BUF_LEN]; +static Char buf2[BUF_LEN]; +static Char buf3[BUF_LEN]; + +static UInt sigstacks_space = 0; // Current signal stacks space sum + +static VgHashTable malloc_list = NULL; // HP_Chunks + +static UInt n_heap_blocks = 0; + + +#define MAX_ALLOC_FNS 32 // includes the builtin ones + +// First few filled in, rest should be zeroed. Zero-terminated vector. +static UInt n_alloc_fns = 11; +static Char* alloc_fns[MAX_ALLOC_FNS] = { + "malloc", + "operator new(unsigned)", + "operator new[](unsigned)", + "operator new(unsigned, std::nothrow_t const&)", + "operator new[](unsigned, std::nothrow_t const&)", + "__builtin_new", + "__builtin_vec_new", + "calloc", + "realloc", + "my_malloc", // from vg_libpthread.c + "memalign", +}; + + +/*------------------------------------------------------------*/ +/*--- Command line args ---*/ +/*------------------------------------------------------------*/ + +#define MAX_DEPTH 50 + +typedef + enum { + XText, XHTML, + } + XFormat; + +static Bool clo_heap = True; +static UInt clo_heap_admin = 8; +static Bool clo_stacks = True; +static Bool clo_depth = 3; +static XFormat clo_format = XText; + +Bool SK_(process_cmd_line_option)(Char* arg) +{ + VG_BOOL_CLO("--heap", clo_heap) + else VG_BOOL_CLO("--stacks", clo_stacks) + + else VG_NUM_CLO ("--heap-admin", clo_heap_admin) + else VG_BNUM_CLO("--depth", clo_depth, 1, MAX_DEPTH) + + else if (VG_CLO_STREQN(11, arg, "--alloc-fn=")) { + alloc_fns[n_alloc_fns] = & arg[11]; + n_alloc_fns++; + if (n_alloc_fns >= MAX_ALLOC_FNS) { + VG_(printf)("Too many alloc functions specified, sorry"); + VG_(bad_option)(arg); + } + } + + else if (VG_CLO_STREQ(arg, "--format=text")) + clo_format = XText; + else if (VG_CLO_STREQ(arg, "--format=html")) + clo_format = XHTML; + + else + return VG_(replacement_malloc_process_cmd_line_option)(arg); + + return True; +} + +void SK_(print_usage)(void) +{ + VG_(printf)( +" --heap=no|yes profile heap blocks [yes]\n" +" --heap-admin= average admin bytes per heap block [8]\n" +" --stacks=no|yes profile stack(s) [yes]\n" +" --depth= depth of contexts [3]\n" +" --alloc-fn= specify as an alloc function [empty]\n" +" --format=text|html format of textual output [text]\n" + ); + VG_(replacement_malloc_print_usage)(); +} + +void SK_(print_debug_usage)(void) +{ + VG_(replacement_malloc_print_debug_usage)(); +} + +/*------------------------------------------------------------*/ +/*--- Execution contexts ---*/ +/*------------------------------------------------------------*/ + +// Fake XPt representing all allocation functions like malloc(). Acts as +// parent node to all top-XPts. +static XPt* alloc_xpt; + +// Cheap allocation for blocks that never need to be freed. Saves about 10% +// for Konqueror startup with --depth=40. +static void* perm_malloc(UInt n_bytes) +{ + static Addr hp = 0; // current heap pointer + static Addr hp_lim = 0; // maximum usable byte in current block + + #define SUPERBLOCK_SIZE (1 << 20) // 1 MB + + if (hp + n_bytes > hp_lim) { + hp = (Addr)VG_(get_memory_from_mmap)(SUPERBLOCK_SIZE, "perm_malloc"); + hp_lim = hp + SUPERBLOCK_SIZE - 1; + } + + hp += n_bytes; + + return (void*)(hp - n_bytes); +} + + + +static XPt* new_XPt(Addr eip, XPt* parent, Bool is_bottom) +{ + XPt* xpt = perm_malloc(sizeof(XPt)); + xpt->eip = eip; + + xpt->curr_space = 0; + xpt->approx_ST = 0; + xpt->exact_ST_dbld = 0; + + xpt->parent = parent; + + // Check parent is not a bottom-XPt + sk_assert(parent == NULL || 0 != parent->max_children); + + xpt->n_children = 0; + + // If a bottom-XPt, don't allocate space for children. This can be 50% + // or more, although it tends to drop as --depth increases (eg. 10% for + // konqueror with --depth=20). + if ( is_bottom ) { + xpt->max_children = 0; + xpt->children = NULL; + n_bot_xpts++; + } else { + xpt->max_children = 4; + xpt->children = VG_(malloc)( xpt->max_children * sizeof(XPt*) ); + } + + // Update statistics + n_xpts++; + + return xpt; +} + +static Bool is_alloc_fn(Addr eip) +{ + Int i; + + if ( VG_(get_fnname)(eip, buf, BUF_LEN) ) { + for (i = 0; i < n_alloc_fns; i++) { + if (VG_STREQ(buf, alloc_fns[i])) + return True; + } + } + return False; +} + +// Returns an XCon, from the bottom-XPt. Nb: the XPt returned must be a +// bottom-XPt now and must always remain a bottom-XPt. We go to some effort +// to ensure this in certain cases. See comments below. +static XPt* get_XCon( ThreadId tid, Bool custom_malloc ) +{ + // Static to minimise stack size. +1 for added 0xffffffff %eip. + static Addr eips[MAX_DEPTH + MAX_ALLOC_FNS + 1]; + + XPt* xpt = alloc_xpt; + UInt n_eips, L, A, B, nC; + UInt overestimate; + Bool reached_bottom; + + VGP_PUSHCC(VgpGetXPt); + + // Want at least clo_depth non-alloc-fn entries in the snapshot. + // However, because we have 1 or more (an unknown number, at this point) + // alloc-fns ignored, we overestimate the size needed for the stack + // snapshot. Then, if necessary, we repeatedly increase the size until + // it is enough. + overestimate = 2; + while (True) { + n_eips = VG_(stack_snapshot)( tid, eips, clo_depth + overestimate ); + + // Now we add a dummy "unknown" %eip at the end. This is only used if we + // run out of %eips before hitting clo_depth. It's done to ensure the + // XPt we return is (now and forever) a bottom-XPt. If the returned XPt + // wasn't a bottom-XPt (now or later) it would cause problems later (eg. + // the parent's approx_ST wouldn't be equal [or almost equal] to the + // total of the childrens' approx_STs). + eips[ n_eips++ ] = 0xffffffff; + + // Skip over alloc functions in eips[]. + for (L = 0; is_alloc_fn(eips[L]) && L < n_eips; L++) { } + + // Must be at least one alloc function, unless client used + // MALLOCLIKE_BLOCK + if (!custom_malloc) sk_assert(L > 0); + + // Should be at least one non-alloc function. If not, try again. + if (L == n_eips) { + overestimate += 2; + if (overestimate > MAX_ALLOC_FNS) + VG_(skin_panic)("No stk snapshot big enough to find non-alloc fns"); + } else { + break; + } + } + A = L; + B = n_eips - 1; + reached_bottom = False; + + // By this point, the eips we care about are in eips[A]..eips[B] + + // Now do the search/insertion of the XCon. 'L' is the loop counter, + // being the index into eips[]. + while (True) { + // Look for %eip in xpt's children. + // XXX: linear search, ugh -- about 10% of time for konqueror startup + // XXX: tried cacheing last result, only hit about 4% for konqueror + // Nb: this search hits about 98% of the time for konqueror + VGP_PUSHCC(VgpGetXPtSearch); + + // If we've searched/added deep enough, or run out of EIPs, this is + // the bottom XPt. + if (L - A + 1 == clo_depth || L == B) + reached_bottom = True; + + nC = 0; + while (True) { + if (nC == xpt->n_children) { + // not found, insert new XPt + sk_assert(xpt->max_children != 0); + sk_assert(xpt->n_children <= xpt->max_children); + // Expand 'children' if necessary + if (xpt->n_children == xpt->max_children) { + xpt->max_children *= 2; + xpt->children = VG_(realloc)( xpt->children, + xpt->max_children * sizeof(XPt*) ); + n_children_reallocs++; + } + // Make new XPt for %eip, insert in list + xpt->children[ xpt->n_children++ ] = + new_XPt(eips[L], xpt, reached_bottom); + break; + } + if (eips[L] == xpt->children[nC]->eip) break; // found the %eip + nC++; // keep looking + } + VGP_POPCC(VgpGetXPtSearch); + + // Return found/built bottom-XPt. + if (reached_bottom) { + sk_assert(0 == xpt->children[nC]->n_children); // Must be bottom-XPt + VGP_POPCC(VgpGetXPt); + return xpt->children[nC]; + } + + // Descend to next level in XTree, the newly found/built non-bottom-XPt + xpt = xpt->children[nC]; + L++; + } +} + +// Update 'curr_space' of every XPt in the XCon, by percolating upwards. +static void update_XCon(XPt* xpt, Int space_delta) +{ + VGP_PUSHCC(VgpUpdateXCon); + + sk_assert(True == clo_heap); + sk_assert(0 != space_delta); + sk_assert(NULL != xpt); + sk_assert(0 == xpt->n_children); // must be bottom-XPt + + while (xpt != alloc_xpt) { + if (space_delta < 0) sk_assert(xpt->curr_space >= -space_delta); + xpt->curr_space += space_delta; + xpt = xpt->parent; + } + if (space_delta < 0) sk_assert(alloc_xpt->curr_space >= -space_delta); + alloc_xpt->curr_space += space_delta; + + VGP_POPCC(VgpUpdateXCon); +} + +// Actually want a reverse sort, biggest to smallest +static Int XPt_cmp_approx_ST(void* n1, void* n2) +{ + XPt* xpt1 = *(XPt**)n1; + XPt* xpt2 = *(XPt**)n2; + return (xpt1->approx_ST < xpt2->approx_ST ? 1 : -1); +} + +static Int XPt_cmp_exact_ST_dbld(void* n1, void* n2) +{ + XPt* xpt1 = *(XPt**)n1; + XPt* xpt2 = *(XPt**)n2; + return (xpt1->exact_ST_dbld < xpt2->exact_ST_dbld ? 1 : -1); +} + + +/*------------------------------------------------------------*/ +/*--- A generic Queue ---*/ +/*------------------------------------------------------------*/ + +typedef + struct { + UInt head; // Index of first entry + UInt tail; // Index of final+1 entry, ie. next free slot + UInt max_elems; + void** elems; + } + Queue; + +static Queue* construct_queue(UInt size) +{ + UInt i; + Queue* q = VG_(malloc)(sizeof(Queue)); + q->head = 0; + q->tail = 0; + q->max_elems = size; + q->elems = VG_(malloc)(size * sizeof(void*)); + for (i = 0; i < size; i++) + q->elems[i] = NULL; + + return q; +} + +static void destruct_queue(Queue* q) +{ + VG_(free)(q->elems); + VG_(free)(q); +} + +static void shuffle(Queue* dest_q, void** old_elems) +{ + UInt i, j; + for (i = 0, j = dest_q->head; j < dest_q->tail; i++, j++) + dest_q->elems[i] = old_elems[j]; + dest_q->head = 0; + dest_q->tail = i; + for ( ; i < dest_q->max_elems; i++) + dest_q->elems[i] = NULL; // paranoia +} + +// Shuffles elements down. If not enough slots free, increase size. (We +// don't wait until we've completely run out of space, because there could +// be lots of shuffling just before that point which would be slow.) +static void adjust(Queue* q) +{ + void** old_elems; + + sk_assert(q->tail == q->max_elems); + if (q->head < 10) { + old_elems = q->elems; + q->max_elems *= 2; + q->elems = VG_(malloc)(q->max_elems * sizeof(void*)); + shuffle(q, old_elems); + VG_(free)(old_elems); + } else { + shuffle(q, q->elems); + } +} + +static void enqueue(Queue* q, void* elem) +{ + if (q->tail == q->max_elems) + adjust(q); + q->elems[q->tail++] = elem; +} + +static Bool is_empty_queue(Queue* q) +{ + return (q->head == q->tail); +} + +static void* dequeue(Queue* q) +{ + if (is_empty_queue(q)) + return NULL; // Queue empty + else + return q->elems[q->head++]; +} + +/*------------------------------------------------------------*/ +/*--- malloc() et al replacement wrappers ---*/ +/*------------------------------------------------------------*/ + +static __inline__ +void add_HP_Chunk(HP_Chunk* hc) +{ + n_heap_blocks++; + VG_(HT_add_node) ( malloc_list, (VgHashNode*)hc ); +} + +static __inline__ +HP_Chunk* get_HP_Chunk(void* p, HP_Chunk*** prev_chunks_next_ptr) +{ + return (HP_Chunk*)VG_(HT_get_node) ( malloc_list, (UInt)p, + (VgHashNode***)prev_chunks_next_ptr ); +} + +static __inline__ +void remove_HP_Chunk(HP_Chunk* hc, HP_Chunk** prev_chunks_next_ptr) +{ + sk_assert(n_heap_blocks > 0); + n_heap_blocks--; + *prev_chunks_next_ptr = hc->next; +} + +// Forward declaration +static void hp_census(void); + +static +void* new_block ( void* p, Int size, UInt align, Bool is_zeroed ) +{ + HP_Chunk* hc; + Bool custom_alloc = (NULL == p); + if (size < 0) return NULL; + + VGP_PUSHCC(VgpCliMalloc); + + // Update statistics + n_allocs++; + if (0 == size) n_zero_allocs++; + + // Allocate and zero if necessary + if (!p) { + p = VG_(cli_malloc)( align, size ); + if (!p) { + VGP_POPCC(VgpCliMalloc); + return NULL; + } + if (is_zeroed) VG_(memset)(p, 0, size); + } + + // Make new HP_Chunk node, add to malloclist + hc = VG_(malloc)(sizeof(HP_Chunk)); + hc->size = size; + hc->data = (Addr)p; + hc->where = NULL; // paranoia + if (clo_heap) { + hc->where = get_XCon( VG_(get_current_or_recent_tid)(), custom_alloc ); + if (0 != size) + update_XCon(hc->where, size); + } + add_HP_Chunk( hc ); + + // do a census! + hp_census(); + + VGP_POPCC(VgpCliMalloc); + return p; +} + +static __inline__ +void die_block ( void* p, Bool custom_free ) +{ + HP_Chunk *hc, **remove_handle; + + VGP_PUSHCC(VgpCliMalloc); + + // Update statistics + n_frees++; + + // Remove HP_Chunk from malloclist + hc = get_HP_Chunk( p, &remove_handle ); + if (hc == NULL) + return; // must have been a bogus free(), or p==NULL + sk_assert(hc->data == (Addr)p); + remove_HP_Chunk(hc, remove_handle); + + if (clo_heap && hc->size != 0) + update_XCon(hc->where, -hc->size); + + VG_(free)( hc ); + + // Actually free the heap block, if necessary + if (!custom_free) + VG_(cli_free)( p ); + + // do a census! + hp_census(); + + VGP_POPCC(VgpCliMalloc); +} + + +void* SK_(malloc) ( Int n ) +{ + return new_block( NULL, n, VG_(clo_alignment), /*is_zeroed*/False ); +} + +void* SK_(__builtin_new) ( Int n ) +{ + return new_block( NULL, n, VG_(clo_alignment), /*is_zeroed*/False ); +} + +void* SK_(__builtin_vec_new) ( Int n ) +{ + return new_block( NULL, n, VG_(clo_alignment), /*is_zeroed*/False ); +} + +void* SK_(calloc) ( Int m, Int size ) +{ + return new_block( NULL, m*size, VG_(clo_alignment), /*is_zeroed*/True ); +} + +void *SK_(memalign)( Int align, Int n ) +{ + return new_block( NULL, n, align, False ); +} + +void SK_(free) ( void* p ) +{ + die_block( p, /*custom_free*/False ); +} + +void SK_(__builtin_delete) ( void* p ) +{ + die_block( p, /*custom_free*/False); +} + +void SK_(__builtin_vec_delete) ( void* p ) +{ + die_block( p, /*custom_free*/False ); +} + +void* SK_(realloc) ( void* p_old, Int new_size ) +{ + HP_Chunk* hc; + HP_Chunk** remove_handle; + Int i; + void* p_new; + UInt old_size; + XPt *old_where, *new_where; + + VGP_PUSHCC(VgpCliMalloc); + + // First try and find the block. + hc = get_HP_Chunk ( p_old, &remove_handle ); + if (hc == NULL) { + VGP_POPCC(VgpCliMalloc); + return NULL; // must have been a bogus free() + } + + sk_assert(hc->data == (Addr)p_old); + old_size = hc->size; + + if (new_size <= old_size) { + // new size is smaller or same; block not moved + p_new = p_old; + + } else { + // new size is bigger; make new block, copy shared contents, free old + p_new = VG_(cli_malloc)(VG_(clo_alignment), new_size); + + for (i = 0; i < old_size; i++) + ((UChar*)p_new)[i] = ((UChar*)p_old)[i]; + + VG_(cli_free)(p_old); + } + + old_where = hc->where; + new_where = get_XCon( VG_(get_current_or_recent_tid)(), + /*custom_malloc*/False); + + // Update HP_Chunk + hc->data = (Addr)p_new; + hc->size = new_size; + hc->where = new_where; + + // Update XPt curr_space fields + if (clo_heap) { + if (0 != old_size) update_XCon(old_where, -old_size); + if (0 != new_size) update_XCon(new_where, new_size); + } + + // If block has moved, have to remove and reinsert in the malloclist + // (since the updated 'data' field is the hash lookup key). + if (p_new != p_old) { + remove_HP_Chunk(hc, remove_handle); + add_HP_Chunk(hc); + } + + VGP_POPCC(VgpCliMalloc); + return p_new; +} + + +/*------------------------------------------------------------*/ +/*--- Taking a census ---*/ +/*------------------------------------------------------------*/ + +static Census censi[MAX_N_CENSI]; +static UInt curr_census = 0; + +// Must return False so that all stacks are traversed +static Bool count_stack_size( Addr stack_min, Addr stack_max, void *cp ) +{ + *(UInt *)cp += (stack_max - stack_min); + return False; +} + +static UInt get_xtree_size(XPt* xpt, UInt ix) +{ + UInt i; + + // If no memory allocated at all, nothing interesting to record. + if (alloc_xpt->curr_space == 0) return 0; + + // Ignore sub-XTrees that account for a miniscule fraction of current + // allocated space. + if (xpt->curr_space / (double)alloc_xpt->curr_space > 0.002) { + ix++; + + // Count all (non-zero) descendent XPts + for (i = 0; i < xpt->n_children; i++) + ix = get_xtree_size(xpt->children[i], ix); + } + return ix; +} + +static +UInt do_space_snapshot(XPt xpt[], XTreeSnapshot xtree_snapshot, UInt ix) +{ + UInt i; + + // Structure of this function mirrors that of get_xtree_size(). + + if (alloc_xpt->curr_space == 0) return 0; + + if (xpt->curr_space / (double)alloc_xpt->curr_space > 0.002) { + xtree_snapshot[ix].xpt = xpt; + xtree_snapshot[ix].space = xpt->curr_space; + ix++; + + for (i = 0; i < xpt->n_children; i++) + ix = do_space_snapshot(xpt->children[i], xtree_snapshot, ix); + } + return ix; +} + +static UInt ms_interval; +static UInt do_every_nth_census = 30; + +// Weed out half the censi; we choose those that represent the smallest +// time-spans, because that loses the least information. +// +// Algorithm for N censi: We find the census representing the smallest +// timeframe, and remove it. We repeat this until (N/2)-1 censi are gone. +// (It's (N/2)-1 because we never remove the first and last censi.) +// We have to do this one census at a time, rather than finding the (N/2)-1 +// smallest censi in one hit, because when a census is removed, it's +// neighbours immediately cover greater timespans. So it's N^2, but N only +// equals 200, and this is only done every 100 censi, which is not too often. +static void halve_censi(void) +{ + Int i, jp, j, jn, k; + Census* min_census; + + n_halvings++; + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_UserMsg, "Halving censi..."); + + // Sets j to the index of the first not-yet-removed census at or after i + #define FIND_CENSUS(i, j) \ + for (j = i; -1 == censi[j].ms_time; j++) { } + + for (i = 2; i < MAX_N_CENSI; i += 2) { + // Find the censi representing the smallest timespan. The timespan + // for census n = d(N-1,N)+d(N,N+1), where d(A,B) is the time between + // censi A and B. We don't consider the first and last censi for + // removal. + Int min_span = 0x7fffffff; + Int min_j = 0; + + // Initial triple: (prev, curr, next) == (jp, j, jn) + jp = 0; + FIND_CENSUS(1, j); + FIND_CENSUS(j+1, jn); + while (jn < MAX_N_CENSI) { + Int timespan = censi[jn].ms_time - censi[jp].ms_time; + sk_assert(timespan >= 0); + if (timespan < min_span) { + min_span = timespan; + min_j = j; + } + // Move on to next triple + jp = j; + j = jn; + FIND_CENSUS(jn+1, jn); + } + // We've found the least important census, now remove it + min_census = & censi[ min_j ]; + for (k = 0; NULL != min_census->xtree_snapshots[k]; k++) { + n_snapshot_frees++; + VG_(free)(min_census->xtree_snapshots[k]); + min_census->xtree_snapshots[k] = NULL; + } + min_census->ms_time = -1; + } + + // Slide down the remaining censi over the removed ones. The '<=' is + // because we are removing on (N/2)-1, rather than N/2. + for (i = 0, j = 0; i <= MAX_N_CENSI / 2; i++, j++) { + FIND_CENSUS(j, j); + if (i != j) { + censi[i] = censi[j]; + } + } + curr_census = i; + + // Double intervals + ms_interval *= 2; + do_every_nth_census *= 2; + + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_UserMsg, "...done"); +} + +// Take a census. Census time seems to be insignificant (usually <= 0 ms, +// almost always <= 1ms) so don't have to worry about subtracting it from +// running time in any way. +// +// XXX: NOT TRUE! with bigger depths, konqueror censuses can easily take +// 50ms! +static void hp_census(void) +{ + static UInt ms_prev_census = 0; + static UInt ms_next_census = 0; // zero allows startup census + + Int ms_time, ms_time_since_prev; + Int i, K; + Census* census; + + VGP_PUSHCC(VgpCensus); + + // Only do a census if it's time + ms_time = VG_(read_millisecond_timer)(); + ms_time_since_prev = ms_time - ms_prev_census; + if (ms_time < ms_next_census) { + n_fake_censi++; + VGP_POPCC(VgpCensus); + return; + } + n_real_censi++; + + census = & censi[curr_census]; + + census->ms_time = ms_time; + + // Heap: snapshot the K most significant XTrees ------------------- + if (clo_heap) { + K = ( alloc_xpt->n_children < MAX_SNAPSHOTS + ? alloc_xpt->n_children + : MAX_SNAPSHOTS); // max out + + // Update .approx_ST field (approximatively) for all top-XPts. + // We *do not* do it for any non-top-XPTs. + for (i = 0; i < alloc_xpt->n_children; i++) { + XPt* top_XPt = alloc_xpt->children[i]; + top_XPt->approx_ST += top_XPt->curr_space * ms_time_since_prev; + } + // Sort top-XPts by approx_ST field. + VG_(ssort)(alloc_xpt->children, alloc_xpt->n_children, sizeof(XPt*), + XPt_cmp_approx_ST); + + VGP_PUSHCC(VgpCensusHeap); + + // For each significant top-level XPt, record space info about its + // entire XTree, in a single census entry. + // Nb: the xtree_size count/snapshot buffer allocation, and the actual + // snapshot, take similar amounts of time (measured with the + // millisecond counter). + for (i = 0; i < K; i++) { + UInt xtree_size, xtree_size2; +// VG_(printf)("%7u ", alloc_xpt->children[i]->approx_ST); + // Count how many XPts are in the XTree + VGP_PUSHCC(VgpCensusTreeSize); + xtree_size = get_xtree_size( alloc_xpt->children[i], 0 ); + VGP_POPCC(VgpCensusTreeSize); + + // If no XPts counted (ie. alloc_xpt.curr_space==0 or XTree + // insignificant) then don't take any more snapshots. + if (0 == xtree_size) break; + + // Make array of the appropriate size (+1 for zero termination, + // which calloc() does for us). + census->xtree_snapshots[i] = + VG_(calloc)(xtree_size+1, sizeof(XPtSnapshot)); + if (0 && VG_(clo_verbosity) > 1) + VG_(printf)("calloc: %d (%d B)\n", xtree_size+1, + (xtree_size+1) * sizeof(XPtSnapshot)); + + // Take space-snapshot: copy 'curr_space' for every XPt in the + // XTree into the snapshot array, along with pointers to the XPts. + // (Except for ones with curr_space==0, which wouldn't contribute + // to the final exact_ST_dbld calculation anyway; excluding them + // saves a lot of memory and up to 40% time with big --depth valus. + VGP_PUSHCC(VgpCensusSnapshot); + xtree_size2 = do_space_snapshot(alloc_xpt->children[i], + census->xtree_snapshots[i], 0); + sk_assert(xtree_size == xtree_size2); + VGP_POPCC(VgpCensusSnapshot); + } +// VG_(printf)("\n\n"); + // Zero-terminate 'xtree_snapshot' array + census->xtree_snapshots[i] = NULL; + + VGP_POPCC(VgpCensusHeap); + + //VG_(printf)("printed %d censi\n", K); + + // Lump the rest into a single "others" entry. + census->others_space = 0; + for (i = K; i < alloc_xpt->n_children; i++) { + census->others_space += alloc_xpt->children[i]->curr_space; + } + } + + // Heap admin ------------------------------------------------------- + if (clo_heap_admin > 0) + census->heap_admin_space = clo_heap_admin * n_heap_blocks; + + // Stack(s) --------------------------------------------------------- + if (clo_stacks) { + census->stacks_space = sigstacks_space; + // slightly abusing this function + VG_(first_matching_thread_stack)( count_stack_size, &census->stacks_space ); + i++; + } + + // Finish, update interval if necessary ----------------------------- + curr_census++; + census = NULL; // don't use again now that curr_census changed + + // Halve the entries, if our census table is full + if (MAX_N_CENSI == curr_census) { + halve_censi(); + } + + // Take time for next census from now, rather than when this census + // should have happened. Because, if there's a big gap due to a kernel + // operation, there's no point doing catch-up censi every BB for a while + // -- that would just give N censi at almost the same time. + if (VG_(clo_verbosity) > 1) { + VG_(message)(Vg_UserMsg, "census: %d ms (took %d ms)", ms_time, + VG_(read_millisecond_timer)() - ms_time ); + } + ms_prev_census = ms_time; + ms_next_census = ms_time + ms_interval; + //ms_next_census += ms_interval; + + //VG_(printf)("Next: %d ms\n", ms_next_census); + + VGP_POPCC(VgpCensus); +} + +/*------------------------------------------------------------*/ +/*--- Tracked events ---*/ +/*------------------------------------------------------------*/ + +static void new_mem_stack_signal(Addr a, UInt len) +{ + sigstacks_space += len; +} + +static void die_mem_stack_signal(Addr a, UInt len) +{ + sk_assert(sigstacks_space >= len); + sigstacks_space -= len; +} + +/*------------------------------------------------------------*/ +/*--- Client Requests ---*/ +/*------------------------------------------------------------*/ + +Bool SK_(handle_client_request) ( ThreadId tid, UInt* argv, UInt* ret ) +{ + switch (argv[0]) { + case VG_USERREQ__MALLOCLIKE_BLOCK: { + void* res; + void* p = (void*)argv[1]; + UInt sizeB = argv[2]; + *ret = 0; + res = new_block( p, sizeB, /*align -- ignored*/0, /*is_zeroed*/False ); + sk_assert(res == p); + return True; + } + case VG_USERREQ__FREELIKE_BLOCK: { + void* p = (void*)argv[1]; + *ret = 0; + die_block( p, /*custom_free*/True ); + return True; + } + default: + *ret = 0; + return False; + } +} + +/*------------------------------------------------------------*/ +/*--- Initialisation ---*/ +/*------------------------------------------------------------*/ + +// Current directory at startup. +static Char* base_dir; + +UInt VG_(vg_malloc_redzone_szB) = 0; + +void SK_(pre_clo_init)() +{ + VG_(details_name) ("Massif"); + VG_(details_version) (NULL); + VG_(details_description) ("a space profiler"); + VG_(details_copyright_author)("Copyright (C) 2003, Nicholas Nethercote"); + VG_(details_bug_reports_to) (VG_BUGS_TO); + + // Needs + VG_(needs_libc_freeres)(); + VG_(needs_command_line_options)(); + VG_(needs_client_requests) (); + + // Events to track + VG_(init_new_mem_stack_signal) ( new_mem_stack_signal ); + VG_(init_die_mem_stack_signal) ( die_mem_stack_signal ); + + // Profiling events + VGP_(register_profile_event)(VgpGetXPt, "get-XPt"); + VGP_(register_profile_event)(VgpGetXPtSearch, "get-XPt-search"); + VGP_(register_profile_event)(VgpCensus, "census"); + VGP_(register_profile_event)(VgpCensusHeap, "census-heap"); + VGP_(register_profile_event)(VgpCensusSnapshot, "census-snapshot"); + VGP_(register_profile_event)(VgpCensusTreeSize, "census-treesize"); + VGP_(register_profile_event)(VgpUpdateXCon, "update-XCon"); + VGP_(register_profile_event)(VgpCalcSpacetime2, "calc-exact_ST_dbld"); + VGP_(register_profile_event)(VgpPrintHp, "print-hp"); + VGP_(register_profile_event)(VgpPrintXPts, "print-XPts"); + + // HP_Chunks + malloc_list = VG_(HT_construct)(); + + // Dummy node at top of the context structure. + alloc_xpt = new_XPt(0, NULL, /*is_bottom*/False); + + sk_assert( VG_(getcwd_alloc)(&base_dir) ); +} + +void SK_(post_clo_init)(void) +{ + ms_interval = 1; + + // Do an initial sample for t = 0 + hp_census(); +} + +/*------------------------------------------------------------*/ +/*--- Instrumentation ---*/ +/*------------------------------------------------------------*/ + +UCodeBlock* SK_(instrument)(UCodeBlock* cb_in, Addr orig_addr) +{ + return cb_in; +} + +/*------------------------------------------------------------*/ +/*--- Spacetime recomputation ---*/ +/*------------------------------------------------------------*/ + +// Although we've been calculating space-time along the way, because the +// earlier calculations were done at a finer timescale, the .approx_ST field +// might not agree with what hp2ps sees, because we've thrown away some of +// the information. So recompute it at the scale that hp2ps sees, so we can +// confidently determine which contexts hp2ps will choose for displaying as +// distinct bands. This recomputation only happens to the significant ones +// that get printed in the .hp file, so it's cheap. +// +// The approx_ST calculation: +// ( a[0]*d(0,1) + a[1]*(d(0,1) + d(1,2)) + ... + a[N-1]*d(N-2,N-1) ) / 2 +// where +// a[N] is the space at census N +// d(A,B) is the time interval between censi A and B +// and +// d(A,B) + d(B,C) == d(A,C) +// +// Key point: we can calculate the area for a census without knowing the +// previous or subsequent censi's space; because any over/underestimates +// for this census will be reversed in the next, balancing out. This is +// important, as getting the previous/next census entry for a particular +// AP is a pain with this data structure, but getting the prev/next +// census time is easy. +// +// Each heap calculation gets added to its context's exact_ST_dbld field. +// The ULong* values are all running totals, hence the use of "+=" everywhere. + +// This does the calculations for a single census. +static void calc_exact_ST_dbld2(Census* census, UInt d_t1_t2, + ULong* twice_heap_ST, + ULong* twice_heap_admin_ST, + ULong* twice_stack_ST) +{ + UInt i, j; + XPtSnapshot* xpt_snapshot; + + // Heap -------------------------------------------------------- + if (clo_heap) { + for (i = 0; NULL != census->xtree_snapshots[i]; i++) { + // Compute total heap exact_ST_dbld for the entire XTree using only + // the top-XPt (the first XPt in xtree_snapshot). + *twice_heap_ST += d_t1_t2 * census->xtree_snapshots[i][0].space; + + // Increment exact_ST_dbld for every XPt in xtree_snapshot (inc. + // top one) + for (j = 0; NULL != census->xtree_snapshots[i][j].xpt; j++) { + xpt_snapshot = & census->xtree_snapshots[i][j]; + xpt_snapshot->xpt->exact_ST_dbld += d_t1_t2 * xpt_snapshot->space; + } + } + *twice_heap_ST += d_t1_t2 * census->others_space; + } + + // Heap admin -------------------------------------------------- + if (clo_heap_admin > 0) + *twice_heap_admin_ST += d_t1_t2 * census->heap_admin_space; + + // Stack(s) ---------------------------------------------------- + if (clo_stacks) + *twice_stack_ST += d_t1_t2 * census->stacks_space; +} + +// This does the calculations for all censi. +static void calc_exact_ST_dbld(ULong* heap2, ULong* heap_admin2, ULong* stack2) +{ + UInt i, N = curr_census; + + VGP_PUSHCC(VgpCalcSpacetime2); + + *heap2 = 0; + *heap_admin2 = 0; + *stack2 = 0; + + if (N <= 1) + return; + + calc_exact_ST_dbld2( &censi[0], censi[1].ms_time - censi[0].ms_time, + heap2, heap_admin2, stack2 ); + + for (i = 1; i <= N-2; i++) { + calc_exact_ST_dbld2( & censi[i], censi[i+1].ms_time - censi[i-1].ms_time, + heap2, heap_admin2, stack2 ); + } + + calc_exact_ST_dbld2( & censi[N-1], censi[N-1].ms_time - censi[N-2].ms_time, + heap2, heap_admin2, stack2 ); + // Now get rid of the halves. May lose a 0.5 on each, doesn't matter. + *heap2 /= 2; + *heap_admin2 /= 2; + *stack2 /= 2; + + VGP_POPCC(VgpCalcSpacetime2); +} + +/*------------------------------------------------------------*/ +/*--- Writing the graph file ---*/ +/*------------------------------------------------------------*/ + +static Char* make_filename(Char* dir, Char* suffix) +{ + Char* filename; + + /* Block is big enough for dir name + massif.. */ + filename = VG_(malloc)((VG_(strlen)(dir) + 32)*sizeof(Char)); + VG_(sprintf)(filename, "%s/massif.%d%s", dir, VG_(getpid)(), suffix); + + return filename; +} + +// Make string acceptable to hp2ps (sigh): remove spaces, escape parentheses. +static Char* clean_fnname(Char *d, Char* s) +{ + Char* dorig = d; + while (*s) { + if (' ' == *s) { *d = '%'; } + else if ('(' == *s) { *d++ = '\\'; *d = '('; } + else if (')' == *s) { *d++ = '\\'; *d = ')'; } + else { *d = *s; }; + s++; + d++; + } + *d = '\0'; + return dorig; +} + +static void file_err ( Char* file ) +{ + VG_(message)(Vg_UserMsg, "error: can't open output file `%s'", file ); + VG_(message)(Vg_UserMsg, " ... so profile results will be missing."); +} + +/* Format, by example: + + JOB "a.out -p" + DATE "Fri Apr 17 11:43:45 1992" + SAMPLE_UNIT "seconds" + VALUE_UNIT "bytes" + BEGIN_SAMPLE 0.00 + SYSTEM 24 + END_SAMPLE 0.00 + BEGIN_SAMPLE 1.00 + elim 180 + insert 24 + intersect 12 + disin 60 + main 12 + reduce 20 + SYSTEM 12 + END_SAMPLE 1.00 + MARK 1.50 + MARK 1.75 + MARK 1.80 + BEGIN_SAMPLE 2.00 + elim 192 + insert 24 + intersect 12 + disin 84 + main 12 + SYSTEM 24 + END_SAMPLE 2.00 + BEGIN_SAMPLE 2.82 + END_SAMPLE 2.82 + */ +static void write_hp_file(void) +{ + Int i, j; + Int fd, res; + Char *hp_file, *ps_file, *aux_file; + Char* cmdfmt; + Char* cmdbuf; + Int cmdlen; + + VGP_PUSHCC(VgpPrintHp); + + // Open file + hp_file = make_filename( base_dir, ".hp" ); + ps_file = make_filename( base_dir, ".ps" ); + aux_file = make_filename( base_dir, ".aux" ); + fd = VG_(open)(hp_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY, + VKI_S_IRUSR|VKI_S_IWUSR); + if (fd < 0) { + file_err( hp_file ); + VGP_POPCC(VgpPrintHp); + return; + } + + // File header, including command line + SPRINTF(buf, "JOB \""); + for (i = 0; i < VG_(client_argc); i++) + SPRINTF(buf, "%s ", VG_(client_argv)[i]); + SPRINTF(buf, /*" (%d ms/sample)\"\n"*/ "\"\n" + "DATE \"\"\n" + "SAMPLE_UNIT \"ms\"\n" + "VALUE_UNIT \"bytes\"\n", ms_interval); + + // Censi + for (i = 0; i < curr_census; i++) { + Census* census = & censi[i]; + + // Census start + SPRINTF(buf, "MARK %d.0\n" + "BEGIN_SAMPLE %d.0\n", + census->ms_time, census->ms_time); + + // Heap ----------------------------------------------------------- + if (clo_heap) { + // Print all the significant XPts from that census + for (j = 0; NULL != census->xtree_snapshots[j]; j++) { + // Grab the jth top-XPt + XTreeSnapshot xtree_snapshot = & census->xtree_snapshots[j][0]; + if ( ! VG_(get_fnname)(xtree_snapshot->xpt->eip, buf2, 16)) { + VG_(sprintf)(buf2, "???"); + } + SPRINTF(buf, "x%x:%s %d\n", xtree_snapshot->xpt->eip, + clean_fnname(buf3, buf2), xtree_snapshot->space); + } + + // Remaining heap block alloc points, combined + if (census->others_space > 0) + SPRINTF(buf, "other %d\n", census->others_space); + } + + // Heap admin ----------------------------------------------------- + if (clo_heap_admin > 0 && census->heap_admin_space) + SPRINTF(buf, "heap-admin %d\n", census->heap_admin_space); + + // Stack(s) ------------------------------------------------------- + if (clo_stacks) + SPRINTF(buf, "stack(s) %d\n", census->stacks_space); + + // Census end + SPRINTF(buf, "END_SAMPLE %d.0\n", census->ms_time); + } + + // Close file + sk_assert(fd >= 0); + VG_(close)(fd); + + // Attempt to convert file using hp2ps + cmdfmt = "%s/hp2ps -c -t1 %s"; + cmdlen = VG_(strlen)(VG_(libdir)) + VG_(strlen)(hp_file) + + VG_(strlen)(cmdfmt); + cmdbuf = VG_(malloc)( sizeof(Char) * cmdlen ); + VG_(sprintf)(cmdbuf, cmdfmt, VG_(libdir), hp_file); + res = VG_(system)(cmdbuf); + VG_(free)(cmdbuf); + if (res != 0) { + VG_(message)(Vg_UserMsg, + "Conversion to PostScript failed. Try converting manually."); + } else { + // remove the .hp and .aux file + VG_(unlink)(hp_file); + VG_(unlink)(aux_file); + } + + VG_(free)(hp_file); + VG_(free)(ps_file); + VG_(free)(aux_file); + + VGP_POPCC(VgpPrintHp); +} + +/*------------------------------------------------------------*/ +/*--- Writing the XPt text/HTML file ---*/ +/*------------------------------------------------------------*/ + +static void percentify(Int n, Int pow, Int field_width, char xbuf[]) +{ + int i, len, space; + + VG_(sprintf)(xbuf, "%d.%d%%", n / pow, n % pow); + len = VG_(strlen)(xbuf); + space = field_width - len; + if (space < 0) space = 0; /* Allow for v. small field_width */ + i = len; + + /* Right justify in field */ + for ( ; i >= 0; i--) xbuf[i + space] = xbuf[i]; + for (i = 0; i < space; i++) xbuf[i] = ' '; +} + +// Nb: uses a static buffer, each call trashes the last string returned. +static Char* make_perc(ULong spacetime, ULong total_spacetime) +{ + static Char mbuf[32]; + + UInt p = 10; + sk_assert(0 != total_spacetime); + percentify(spacetime * 100 * p / total_spacetime, p, 5, mbuf); + return mbuf; +} + +// Nb: passed in XPt is a lower-level XPt; %eips are grabbed from +// bottom-to-top of XCon, and then printed in the reverse order. +static UInt pp_XCon(Int fd, XPt* xpt) +{ + Addr rev_eips[clo_depth+1]; + Int i = 0; + Int n = 0; + Bool is_HTML = ( XHTML == clo_format ); + Char* maybe_br = ( is_HTML ? "
" : "" ); + Char* maybe_indent = ( is_HTML ? "  " : "" ); + + sk_assert(NULL != xpt); + + while (True) { + rev_eips[i] = xpt->eip; + n++; + if (alloc_xpt == xpt->parent) break; + i++; + xpt = xpt->parent; + } + + for (i = n-1; i >= 0; i--) { + // -1 means point to calling line + VG_(describe_eip)(rev_eips[i]-1, buf2, BUF_LEN); + SPRINTF(buf, " %s%s%s\n", maybe_indent, buf2, maybe_br); + } + + return n; +} + +// Important point: for HTML, each XPt must be identified uniquely for the +// HTML links to all match up correctly. Using xpt->eip is not +// sufficient, because function pointers mean that you can call more than +// one other function from a single code location. So instead we use the +// address of the xpt struct itself, which is guaranteed to be unique. + +static void pp_all_XPts2(Int fd, Queue* q, ULong heap_spacetime, + ULong total_spacetime) +{ + UInt i; + XPt *xpt, *child; + UInt L = 0; + UInt c1 = 1; + UInt c2 = 0; + ULong sum = 0; + UInt n; + Char *eip_desc, *perc; + Bool is_HTML = ( XHTML == clo_format ); + Char* maybe_br = ( is_HTML ? "
" : "" ); + Char* maybe_p = ( is_HTML ? "

" : "" ); + Char* maybe_ul = ( is_HTML ? "

    " : "" ); + Char* maybe_li = ( is_HTML ? "
  • " : "" ); + Char* maybe_fli = ( is_HTML ? "
  • " : "" ); + Char* maybe_ful = ( is_HTML ? "
" : "" ); + Char* end_hr = ( is_HTML ? "
" : + "=================================" ); + Char* depth = ( is_HTML ? "--depth" : "--depth" ); + + if (total_spacetime == 0) { + SPRINTF(buf, "(No heap memory allocated)\n"); + return; + } + + + SPRINTF(buf, "== %d ===========================%s\n", L, maybe_br); + + while (NULL != (xpt = (XPt*)dequeue(q))) { + // Check that non-top-level XPts have a zero .approx_ST field. + if (xpt->parent != alloc_xpt) sk_assert( 0 == xpt->approx_ST ); + + // Check that the sum of all children .exact_ST_dbld fields equals + // parent's (unless alloc_xpt, when it should == 0). + if (alloc_xpt == xpt) { + sk_assert(0 == xpt->exact_ST_dbld); + } else { + sum = 0; + for (i = 0; i < xpt->n_children; i++) { + sum += xpt->children[i]->exact_ST_dbld; + } + //sk_assert(sum == xpt->exact_ST_dbld); + // It's possible that not all the children were included in the + // exact_ST_dbld calculations. Hopefully almost all of them were, and + // all the important ones. +// sk_assert(sum <= xpt->exact_ST_dbld); +// sk_assert(sum * 1.05 > xpt->exact_ST_dbld ); +// if (sum != xpt->exact_ST_dbld) { +// VG_(printf)("%ld, %ld\n", sum, xpt->exact_ST_dbld); +// } + } + + if (xpt == alloc_xpt) { + SPRINTF(buf, "Heap allocation functions accounted for " + "%s of measured spacetime%s\n", + make_perc(heap_spacetime, total_spacetime), maybe_br); + } else { + // Remember: exact_ST_dbld is space.time *doubled* + perc = make_perc(xpt->exact_ST_dbld / 2, total_spacetime); + if (is_HTML) { + SPRINTF(buf, "" + "Context accounted for " + "%s of measured spacetime
\n", + xpt, xpt, perc); + } else { + SPRINTF(buf, "Context accounted for %s of measured spacetime\n", + perc); + } + n = pp_XCon(fd, xpt); + sk_assert(n == L); + } + + // Sort children by exact_ST_dbld + VG_(ssort)(xpt->children, xpt->n_children, sizeof(XPt*), + XPt_cmp_exact_ST_dbld); + + SPRINTF(buf, "%s\nCalled from:%s\n", maybe_p, maybe_ul); + for (i = 0; i < xpt->n_children; i++) { + child = xpt->children[i]; + + // Stop when <1% of total spacetime + if (child->exact_ST_dbld * 1000 / (total_spacetime * 2) < 5) { + UInt n_insig = xpt->n_children - i; + Char* s = ( n_insig == 1 ? "" : "s" ); + Char* and = ( 0 == i ? "" : "and " ); + Char* other = ( 0 == i ? "" : "other " ); + SPRINTF(buf, " %s%s%d %sinsignificant place%s%s\n\n", + maybe_li, and, n_insig, other, s, maybe_fli); + break; + } + + // Remember: exact_ST_dbld is space.time *doubled* + perc = make_perc(child->exact_ST_dbld / 2, total_spacetime); + eip_desc = VG_(describe_eip)(child->eip-1, buf2, BUF_LEN); + if (is_HTML) { + SPRINTF(buf, "
  • ", child ); + + if (child->n_children > 0) { + SPRINTF(buf, "%s", child, perc); + } else { + SPRINTF(buf, "%s", perc); + } + SPRINTF(buf, ": %s\n", eip_desc); + } else { + SPRINTF(buf, " %6s: %s\n\n", perc, eip_desc); + } + + if (child->n_children > 0) { + enqueue(q, (void*)child); + c2++; + } + } + SPRINTF(buf, "%s%s", maybe_ful, maybe_p); + c1--; + + // Putting markers between levels of the structure: + // c1 tracks how many to go on this level, c2 tracks how many we've + // queued up for the next level while finishing off this level. + // When c1 gets to zero, we've changed levels, so print a marker, + // move c2 into c1, and zero c2. + if (0 == c1) { + L++; + c1 = c2; + c2 = 0; + if (! is_empty_queue(q) ) { // avoid empty one at end + SPRINTF(buf, "== %d ===========================%s\n", L, maybe_br); + } + } else { + SPRINTF(buf, "---------------------------------%s\n", maybe_br); + } + } + SPRINTF(buf, "%s\n\nEnd of information. Rerun with a bigger " + "%s value for more.\n", end_hr, depth); +} + +static void pp_all_XPts(Int fd, XPt* xpt, ULong heap_spacetime, + ULong total_spacetime) +{ + Queue* q = construct_queue(100); + + enqueue(q, xpt); + pp_all_XPts2(fd, q, heap_spacetime, total_spacetime); + destruct_queue(q); +} + +static void +write_text_file(ULong total_ST, ULong heap_ST) +{ + Int fd, i; + Char* text_file; + Char* maybe_p = ( XHTML == clo_format ? "

    " : "" ); + + VGP_PUSHCC(VgpPrintXPts); + + // Open file + text_file = make_filename( base_dir, + ( XText == clo_format ? ".txt" : ".html" ) ); + + fd = VG_(open)(text_file, VKI_O_CREAT|VKI_O_TRUNC|VKI_O_WRONLY, + VKI_S_IRUSR|VKI_S_IWUSR); + if (fd < 0) { + file_err( text_file ); + VGP_POPCC(VgpPrintXPts); + return; + } + + // Header + if (XHTML == clo_format) { + SPRINTF(buf, "\n" + "\n" + "%s\n" + "\n" + "\n", + text_file); + } + + // Command line + SPRINTF(buf, "Command: "); + for (i = 0; i < VG_(client_argc); i++) + SPRINTF(buf, "%s ", VG_(client_argv)[i]); + SPRINTF(buf, "\n%s\n", maybe_p); + + if (clo_heap) + pp_all_XPts(fd, alloc_xpt, heap_ST, total_ST); + + sk_assert(fd >= 0); + VG_(close)(fd); + + VGP_POPCC(VgpPrintXPts); +} + +/*------------------------------------------------------------*/ +/*--- Finalisation ---*/ +/*------------------------------------------------------------*/ + +static void +print_summary(ULong total_ST, ULong heap_ST, ULong heap_admin_ST, + ULong stack_ST) +{ + VG_(message)(Vg_UserMsg, "Total spacetime: %,ld ms.B", total_ST); + + // Heap -------------------------------------------------------------- + if (clo_heap) + VG_(message)(Vg_UserMsg, "heap: %s", + ( 0 == total_ST ? (Char*)"(n/a)" + : make_perc(heap_ST, total_ST) ) ); + + // Heap admin -------------------------------------------------------- + if (clo_heap_admin) + VG_(message)(Vg_UserMsg, "heap admin: %s", + ( 0 == total_ST ? (Char*)"(n/a)" + : make_perc(heap_admin_ST, total_ST) ) ); + + sk_assert( VG_(HT_count_nodes)(malloc_list) == n_heap_blocks ); + + // Stack(s) ---------------------------------------------------------- + if (clo_stacks) { + sk_assert(0 != total_ST); + VG_(message)(Vg_UserMsg, "stack(s): %s", + make_perc(stack_ST, total_ST) ); + } + + if (VG_(clo_verbosity) > 1) { + sk_assert(n_xpts > 0); // always have alloc_xpt + VG_(message)(Vg_DebugMsg, " allocs: %u", n_allocs); + VG_(message)(Vg_DebugMsg, "zeroallocs: %u (%d%%)", n_zero_allocs, + n_zero_allocs * 100 / n_allocs ); + VG_(message)(Vg_DebugMsg, " frees: %u", n_frees); + VG_(message)(Vg_DebugMsg, " XPts: %u (%d B)", n_xpts, + n_xpts*sizeof(XPt)); + VG_(message)(Vg_DebugMsg, " bot-XPts: %u (%d%%)", n_bot_xpts, + n_bot_xpts * 100 / n_xpts); + VG_(message)(Vg_DebugMsg, " top-XPts: %u (%d%%)", alloc_xpt->n_children, + alloc_xpt->n_children * 100 / n_xpts); + VG_(message)(Vg_DebugMsg, "c-reallocs: %u", n_children_reallocs); + VG_(message)(Vg_DebugMsg, "snap-frees: %u", n_snapshot_frees); + VG_(message)(Vg_DebugMsg, "atmp censi: %u", n_attempted_censi); + VG_(message)(Vg_DebugMsg, "fake censi: %u", n_fake_censi); + VG_(message)(Vg_DebugMsg, "real censi: %u", n_real_censi); + VG_(message)(Vg_DebugMsg, " halvings: %u", n_halvings); + } +} + +void SK_(fini)(Int exit_status) +{ + ULong total_ST = 0; + ULong heap_ST = 0; + ULong heap_admin_ST = 0; + ULong stack_ST = 0; + + // Do a final (empty) sample to show program's end + hp_census(); + + // Redo spacetimes of significant contexts to match the .hp file. + calc_exact_ST_dbld(&heap_ST, &heap_admin_ST, &stack_ST); + total_ST = heap_ST + heap_admin_ST + stack_ST; + write_hp_file ( ); + write_text_file( total_ST, heap_ST ); + print_summary ( total_ST, heap_ST, heap_admin_ST, stack_ST ); +} + +VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 0) + +/*--------------------------------------------------------------------*/ +/*--- end ms_main.c ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/VEX/head20041019/massif/tests/.cvsignore b/VEX/head20041019/massif/tests/.cvsignore new file mode 100644 index 0000000000..36e014a1f1 --- /dev/null +++ b/VEX/head20041019/massif/tests/.cvsignore @@ -0,0 +1,11 @@ +Makefile.in +Makefile +massif.*.hp +massif.*.aux +massif.*.ps +massif.*.txt +massif.*.html +*.stdout.diff +*.stderr.diff +*.stdout.out +*.stderr.out diff --git a/VEX/head20041019/massif/tests/CVS/Entries b/VEX/head20041019/massif/tests/CVS/Entries new file mode 100644 index 0000000000..574b572b73 --- /dev/null +++ b/VEX/head20041019/massif/tests/CVS/Entries @@ -0,0 +1,10 @@ +/.cvsignore/1.1/Sun Feb 22 19:34:55 2004// +/Makefile.am/1.2/Sat Jul 10 14:56:27 2004// +/filter_stderr/1.1/Sat Feb 14 16:48:35 2004// +/toobig-allocs.stderr.exp/1.1/Sat Jul 10 14:56:27 2004// +/toobig-allocs.vgtest/1.1/Sat Jul 10 14:56:27 2004// +/true_html.stderr.exp/1.1/Sat Feb 14 16:40:02 2004// +/true_html.vgtest/1.2/Sat Apr 17 17:25:08 2004// +/true_text.stderr.exp/1.1/Sat Feb 14 16:40:02 2004// +/true_text.vgtest/1.2/Sat Apr 17 17:25:08 2004// +D diff --git a/VEX/head20041019/massif/tests/CVS/Repository b/VEX/head20041019/massif/tests/CVS/Repository new file mode 100644 index 0000000000..1b8e77267b --- /dev/null +++ b/VEX/head20041019/massif/tests/CVS/Repository @@ -0,0 +1 @@ +valgrind/massif/tests diff --git a/VEX/head20041019/massif/tests/CVS/Root b/VEX/head20041019/massif/tests/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/massif/tests/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/massif/tests/CVS/Template b/VEX/head20041019/massif/tests/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/massif/tests/Makefile.am b/VEX/head20041019/massif/tests/Makefile.am new file mode 100644 index 0000000000..0616d78024 --- /dev/null +++ b/VEX/head20041019/massif/tests/Makefile.am @@ -0,0 +1,7 @@ +noinst_SCRIPTS = filter_stderr + +EXTRA_DIST = $(noinst_SCRIPTS) \ + toobig-allocs.stderr.exp toobig-allocs.vgtest \ + true_html.stderr.exp true_html.vgtest \ + true_text.stderr.exp true_text.vgtest + diff --git a/VEX/head20041019/massif/tests/filter_stderr b/VEX/head20041019/massif/tests/filter_stderr new file mode 100755 index 0000000000..f285a27206 --- /dev/null +++ b/VEX/head20041019/massif/tests/filter_stderr @@ -0,0 +1,9 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic | + +# Remove numbers from all lines +sed "s/\([a-zA-Z(): ]*\)[ 0-9\.,()+rdw]*\(%\|ms.B\)$/\1/" + diff --git a/VEX/head20041019/massif/tests/toobig-allocs.stderr.exp b/VEX/head20041019/massif/tests/toobig-allocs.stderr.exp new file mode 100644 index 0000000000..550068674b --- /dev/null +++ b/VEX/head20041019/massif/tests/toobig-allocs.stderr.exp @@ -0,0 +1,8 @@ + +Attempting too-big malloc()... +Attempting too-big mmap()... + +Total spacetime: +heap: +heap admin: +stack(s): diff --git a/VEX/head20041019/massif/tests/toobig-allocs.vgtest b/VEX/head20041019/massif/tests/toobig-allocs.vgtest new file mode 100644 index 0000000000..186cf5f901 --- /dev/null +++ b/VEX/head20041019/massif/tests/toobig-allocs.vgtest @@ -0,0 +1 @@ +prog: ../../tests/toobig-allocs diff --git a/VEX/head20041019/massif/tests/true_html.stderr.exp b/VEX/head20041019/massif/tests/true_html.stderr.exp new file mode 100644 index 0000000000..c18e180c98 --- /dev/null +++ b/VEX/head20041019/massif/tests/true_html.stderr.exp @@ -0,0 +1,6 @@ + + +Total spacetime: +heap: +heap admin: +stack(s): diff --git a/VEX/head20041019/massif/tests/true_html.vgtest b/VEX/head20041019/massif/tests/true_html.vgtest new file mode 100644 index 0000000000..7dfc68d823 --- /dev/null +++ b/VEX/head20041019/massif/tests/true_html.vgtest @@ -0,0 +1,3 @@ +prog: ../../tests/true +vgopts: --format=html +delete: massif.*.* diff --git a/VEX/head20041019/massif/tests/true_text.stderr.exp b/VEX/head20041019/massif/tests/true_text.stderr.exp new file mode 100644 index 0000000000..c18e180c98 --- /dev/null +++ b/VEX/head20041019/massif/tests/true_text.stderr.exp @@ -0,0 +1,6 @@ + + +Total spacetime: +heap: +heap admin: +stack(s): diff --git a/VEX/head20041019/massif/tests/true_text.vgtest b/VEX/head20041019/massif/tests/true_text.vgtest new file mode 100644 index 0000000000..79f9796c44 --- /dev/null +++ b/VEX/head20041019/massif/tests/true_text.vgtest @@ -0,0 +1,3 @@ +prog: ../../tests/true +vgopts: --format=text +delete: massif.*.* diff --git a/VEX/head20041019/memcheck/.cvsignore b/VEX/head20041019/memcheck/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/memcheck/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/memcheck/CVS/Entries b/VEX/head20041019/memcheck/CVS/Entries new file mode 100644 index 0000000000..ff0eed04b9 --- /dev/null +++ b/VEX/head20041019/memcheck/CVS/Entries @@ -0,0 +1,18 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:34 2002// +/Makefile.am/1.52/Thu Sep 2 15:37:39 2004// +/mac_leakcheck.c/1.14/Tue Aug 3 13:29:08 2004// +/mac_malloc_wrappers.c/1.13/Sat Jul 10 14:56:27 2004// +/mac_needs.c/1.29/Sat Jul 10 14:56:27 2004// +/mac_replace_strmem.c/1.14/Wed Aug 25 13:33:17 2004// +/mac_shared.h/1.23/Thu Sep 2 08:51:43 2004// +/mc_asm.h/1.1/Thu Sep 2 15:37:39 2004// +/mc_clientreqs.c/1.21/Sun Jun 27 17:37:21 2004// +/mc_errcontext.c/1.22/Sun Jan 4 16:43:22 2004// +/mc_from_ucode.c/1.15/Sun Jan 4 16:43:22 2004// +/mc_helpers.S/1.10/Thu Sep 2 15:37:39 2004// +/mc_include.h/1.21/Thu Sep 2 15:37:39 2004// +/mc_main.c/1.53/Fri Sep 3 13:45:29 2004// +/mc_translate.c/1.43/Tue Aug 3 23:14:00 2004// +/memcheck.h/1.20/Wed Oct 6 12:18:47 2004// +D/docs//// +D/tests//// diff --git a/VEX/head20041019/memcheck/CVS/Repository b/VEX/head20041019/memcheck/CVS/Repository new file mode 100644 index 0000000000..027eadb3e0 --- /dev/null +++ b/VEX/head20041019/memcheck/CVS/Repository @@ -0,0 +1 @@ +valgrind/memcheck diff --git a/VEX/head20041019/memcheck/CVS/Root b/VEX/head20041019/memcheck/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/memcheck/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/memcheck/CVS/Template b/VEX/head20041019/memcheck/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/Makefile.am b/VEX/head20041019/memcheck/Makefile.am new file mode 100644 index 0000000000..86b6d7d5da --- /dev/null +++ b/VEX/head20041019/memcheck/Makefile.am @@ -0,0 +1,37 @@ +include $(top_srcdir)/Makefile.tool.am + +## Build Memcheck at a higher optimisation level +AM_CFLAGS += -O2 + +val_PROGRAMS = vgskin_memcheck.so vgpreload_memcheck.so + +vgpreload_memcheck_so_SOURCES = \ + mac_replace_strmem.c +vgpreload_memcheck_so_LDADD = $(top_builddir)/coregrind/vg_replace_malloc.o +vgpreload_memcheck_so_DEPENDENCIES = $(top_builddir)/coregrind/vg_replace_malloc.o +vgpreload_memcheck_so_LDFLAGS = -shared -Wl,-z,interpose,-z,initfirst + +vgskin_memcheck_so_SOURCES = \ + mac_leakcheck.c \ + mac_malloc_wrappers.c \ + mac_needs.c \ + mc_main.c \ + mc_clientreqs.c \ + mc_errcontext.c \ + mc_from_ucode.c \ + mc_translate.c \ + mc_helpers.S +vgskin_memcheck_so_LDFLAGS = -shared + +mcincludedir = $(includedir)/valgrind + +mcinclude_HEADERS = \ + memcheck.h + +noinst_HEADERS = \ + mac_shared.h \ + mc_asm.h \ + mc_include.h + +mac_replace_strmem.o: CFLAGS += -fno-omit-frame-pointer + diff --git a/VEX/head20041019/memcheck/docs/.cvsignore b/VEX/head20041019/memcheck/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/memcheck/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/memcheck/docs/CVS/Entries b/VEX/head20041019/memcheck/docs/CVS/Entries new file mode 100644 index 0000000000..f1e95dd9aa --- /dev/null +++ b/VEX/head20041019/memcheck/docs/CVS/Entries @@ -0,0 +1,5 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:35 2002// +/Makefile.am/1.3/Wed Aug 25 11:40:06 2004// +/mc_main.html/1.12/Wed Oct 6 12:25:49 2004// +/mc_techdocs.html/1.10/Thu Sep 2 15:50:29 2004// +D diff --git a/VEX/head20041019/memcheck/docs/CVS/Repository b/VEX/head20041019/memcheck/docs/CVS/Repository new file mode 100644 index 0000000000..4005d5f7cd --- /dev/null +++ b/VEX/head20041019/memcheck/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/memcheck/docs diff --git a/VEX/head20041019/memcheck/docs/CVS/Root b/VEX/head20041019/memcheck/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/memcheck/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/memcheck/docs/CVS/Template b/VEX/head20041019/memcheck/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/docs/Makefile.am b/VEX/head20041019/memcheck/docs/Makefile.am new file mode 100644 index 0000000000..8d9e7c80e3 --- /dev/null +++ b/VEX/head20041019/memcheck/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = mc_main.html mc_techdocs.html diff --git a/VEX/head20041019/memcheck/docs/mc_main.html b/VEX/head20041019/memcheck/docs/mc_main.html new file mode 100644 index 0000000000..32b618160c --- /dev/null +++ b/VEX/head20041019/memcheck/docs/mc_main.html @@ -0,0 +1,832 @@ + + + + Memcheck: a heavyweight memory checker + + + +

    Memcheck: a heavyweight memory checker

    + +To use this tool, you must specify --tool=memcheck on the +Valgrind command line. + +

    3.1  Kinds of bugs that memcheck can find

    + +Memcheck is Valgrind-1.0.X's checking mechanism bundled up into a tool. + All reads and writes of memory are checked, and calls to + malloc/new/free/delete are intercepted. As a result, memcheck can + detect the following problems: +
      +
    • Use of uninitialised memory
    • +
    • Reading/writing memory after it has been free'd
    • +
    • Reading/writing off the end of malloc'd blocks
    • +
    • Reading/writing inappropriate areas on the stack
    • +
    • Memory leaks -- where pointers to malloc'd blocks are lost + forever
    • +
    • Mismatched use of malloc/new/new [] vs free/delete/delete []
    • +
    • Overlapping src and dst pointers in + memcpy() and related functions
    • +
    • Some misuses of the POSIX pthreads API
    • +
    +

    + + +

    3.2  Command-line flags specific to memcheck

    + +
      +
    • --leak-check=no [default]
      + --leak-check=yes +

      When enabled, search for memory leaks when the client program + finishes. A memory leak means a malloc'd block, which has not + yet been free'd, but to which no pointer can be found. Such a + block can never be free'd by the program, since no pointer to it + exists. Leak checking is disabled by default because it tends + to generate dozens of error messages.


    • + +

    • --show-reachable=no [default]
      + --show-reachable=yes +

      When disabled, the memory leak detector only shows blocks for + which it cannot find a pointer to at all, or it can only find a + pointer to the middle of. These blocks are prime candidates for + memory leaks. When enabled, the leak detector also reports on + blocks which it could find a pointer to. Your program could, at + least in principle, have freed such blocks before exit. + Contrast this to blocks for which no pointer, or only an + interior pointer could be found: they are more likely to + indicate memory leaks, because you do not actually have a + pointer to the start of the block which you can hand to + free, even if you wanted to.


    • + +

    • --leak-resolution=low [default]
      + --leak-resolution=med
      + --leak-resolution=high +

      When doing leak checking, determines how willing Memcheck is + to consider different backtraces to be the same. When set to + low, the default, only the first two entries need + match. When med, four entries have to match. When + high, all entries need to match. +

      + For hardcore leak debugging, you probably want to use + --leak-resolution=high together with + --num-callers=40 or some such large number. Note + however that this can give an overwhelming amount of + information, which is why the defaults are 4 callers and + low-resolution matching. +

      + Note that the --leak-resolution= setting does not + affect Memcheck's ability to find leaks. It only changes how + the results are presented. +


    • + +

    • --freelist-vol=<number> [default: 1000000] +

      When the client program releases memory using free (in C) or + delete (C++), that memory is not immediately made available for + re-allocation. Instead it is marked inaccessible and placed in + a queue of freed blocks. The purpose is to delay the point at + which freed-up memory comes back into circulation. This + increases the chance that Memcheck will be able to detect + invalid accesses to blocks for some significant period of time + after they have been freed. +

      + This flag specifies the maximum total size, in bytes, of the + blocks in the queue. The default value is one million bytes. + Increasing this increases the total amount of memory used by + Memcheck but may detect invalid uses of freed blocks which would + otherwise go undetected.


    • + +

    • --workaround-gcc296-bugs=no [default]
      + --workaround-gcc296-bugs=yes

      When enabled, + assume that reads and writes some small distance below the stack + pointer %esp are due to bugs in gcc 2.96, and does + not report them. The "small distance" is 256 bytes by default. + Note that gcc 2.96 is the default compiler on some popular Linux + distributions (RedHat 7.X, Mandrake) and so you may well need to + use this flag. Do not use it if you do not have to, as it can + cause real errors to be overlooked. Another option is to use a + gcc/g++ which does not generate accesses below the stack + pointer. 2.95.3 seems to be a good choice in this respect. +

      + Unfortunately (27 Feb 02) it looks like g++ 3.0.4 has a similar + bug, so you may need to issue this flag if you use 3.0.4. A + while later (early Apr 02) this is confirmed as a scheduling bug + in g++-3.0.4. +


    • + +

    • --partial-loads-ok=yes [the default]
      + --partial-loads-ok=no +

      Controls how Memcheck handles word (4-byte) loads from + addresses for which some bytes are addressible and others + are not. When yes (the default), such loads + do not elicit an address error. Instead, the loaded V bytes + corresponding to the illegal addresses indicate undefined, and + those corresponding to legal addresses are loaded from shadow + memory, as usual. +

      + When no, loads from partially + invalid addresses are treated the same as loads from completely + invalid addresses: an illegal-address error is issued, + and the resulting V bytes indicate valid data. +


    • + +

    • --cleanup=no
      + --cleanup=yes [default] +

      This is a flag to help debug valgrind itself. It is of no + use to end-users. When enabled, various improvments are + applied to the post-instrumented intermediate code, aimed at + removing redundant value checks.


    • +

      +

    + + + +

    3.3  Explanation of error messages from Memcheck

    + +Despite considerable sophistication under the hood, Memcheck can only +really detect two kinds of errors, use of illegal addresses, and use +of undefined values. Nevertheless, this is enough to help you +discover all sorts of memory-management nasties in your code. This +section presents a quick summary of what error messages mean. The +precise behaviour of the error-checking machinery is described in +this section. + + +

    3.3.1  Illegal read / Illegal write errors

    +For example: +
    +  Invalid read of size 4
    +     at 0x40F6BBCC: (within /usr/lib/libpng.so.2.1.0.9)
    +     by 0x40F6B804: (within /usr/lib/libpng.so.2.1.0.9)
    +     by 0x40B07FF4: read_png_image__FP8QImageIO (kernel/qpngio.cpp:326)
    +     by 0x40AC751B: QImageIO::read() (kernel/qimage.cpp:3621)
    +     Address 0xBFFFF0E0 is not stack'd, malloc'd or free'd
    +
    + +

    This happens when your program reads or writes memory at a place +which Memcheck reckons it shouldn't. In this example, the program did +a 4-byte read at address 0xBFFFF0E0, somewhere within the +system-supplied library libpng.so.2.1.0.9, which was called from +somewhere else in the same library, called from line 326 of +qpngio.cpp, and so on. + +

    Memcheck tries to establish what the illegal address might relate +to, since that's often useful. So, if it points into a block of +memory which has already been freed, you'll be informed of this, and +also where the block was free'd at. Likewise, if it should turn out +to be just off the end of a malloc'd block, a common result of +off-by-one-errors in array subscripting, you'll be informed of this +fact, and also where the block was malloc'd. + +

    In this example, Memcheck can't identify the address. Actually the +address is on the stack, but, for some reason, this is not a valid +stack address -- it is below the stack pointer, %esp, and that isn't +allowed. In this particular case it's probably caused by gcc +generating invalid code, a known bug in various flavours of gcc. + +

    Note that Memcheck only tells you that your program is about to +access memory at an illegal address. It can't stop the access from +happening. So, if your program makes an access which normally would +result in a segmentation fault, you program will still suffer the same +fate -- but you will get a message from Memcheck immediately prior to +this. In this particular example, reading junk on the stack is +non-fatal, and the program stays alive. + + +

    3.3.2  Use of uninitialised values

    +For example: +
    +  Conditional jump or move depends on uninitialised value(s)
    +     at 0x402DFA94: _IO_vfprintf (_itoa.h:49)
    +     by 0x402E8476: _IO_printf (printf.c:36)
    +     by 0x8048472: main (tests/manuel1.c:8)
    +     by 0x402A6E5E: __libc_start_main (libc-start.c:129)
    +
    + +

    An uninitialised-value use error is reported when your program uses +a value which hasn't been initialised -- in other words, is undefined. +Here, the undefined value is used somewhere inside the printf() +machinery of the C library. This error was reported when running the +following small program: +

    +  int main()
    +  {
    +    int x;
    +    printf ("x = %d\n", x);
    +  }
    +
    + +

    It is important to understand that your program can copy around +junk (uninitialised) data to its heart's content. Memcheck observes +this and keeps track of the data, but does not complain. A complaint +is issued only when your program attempts to make use of uninitialised +data. In this example, x is uninitialised. Memcheck observes the +value being passed to _IO_printf and thence to _IO_vfprintf, but makes +no comment. However, _IO_vfprintf has to examine the value of x so it +can turn it into the corresponding ASCII string, and it is at this +point that Memcheck complains. + +

    Sources of uninitialised data tend to be: +

      +
    • Local variables in procedures which have not been initialised, + as in the example above.
    • + +

    • The contents of malloc'd blocks, before you write something + there. In C++, the new operator is a wrapper round malloc, so + if you create an object with new, its fields will be + uninitialised until you (or the constructor) fill them in, which + is only Right and Proper.
    • +
    + + + +

    3.3.3  Illegal frees

    +For example: +
    +  Invalid free()
    +     at 0x4004FFDF: free (vg_clientmalloc.c:577)
    +     by 0x80484C7: main (tests/doublefree.c:10)
    +     by 0x402A6E5E: __libc_start_main (libc-start.c:129)
    +     by 0x80483B1: (within tests/doublefree)
    +     Address 0x3807F7B4 is 0 bytes inside a block of size 177 free'd
    +     at 0x4004FFDF: free (vg_clientmalloc.c:577)
    +     by 0x80484C7: main (tests/doublefree.c:10)
    +     by 0x402A6E5E: __libc_start_main (libc-start.c:129)
    +     by 0x80483B1: (within tests/doublefree)
    +
    +

    Memcheck keeps track of the blocks allocated by your program with +malloc/new, so it can know exactly whether or not the argument to +free/delete is legitimate or not. Here, this test program has +freed the same block twice. As with the illegal read/write errors, +Memcheck attempts to make sense of the address free'd. If, as +here, the address is one which has previously been freed, you wil +be told that -- making duplicate frees of the same block easy to spot. + + +

    3.3.4  When a block is freed with an inappropriate +deallocation function

    +In the following example, a block allocated with new[] +has wrongly been deallocated with free: +
    +  Mismatched free() / delete / delete []
    +     at 0x40043249: free (vg_clientfuncs.c:171)
    +     by 0x4102BB4E: QGArray::~QGArray(void) (tools/qgarray.cpp:149)
    +     by 0x4C261C41: PptDoc::~PptDoc(void) (include/qmemarray.h:60)
    +     by 0x4C261F0E: PptXml::~PptXml(void) (pptxml.cc:44)
    +     Address 0x4BB292A8 is 0 bytes inside a block of size 64 alloc'd
    +     at 0x4004318C: __builtin_vec_new (vg_clientfuncs.c:152)
    +     by 0x4C21BC15: KLaola::readSBStream(int) const (klaola.cc:314)
    +     by 0x4C21C155: KLaola::stream(KLaola::OLENode const *) (klaola.cc:416)
    +     by 0x4C21788F: OLEFilter::convert(QCString const &) (olefilter.cc:272)
    +
    +The following was told to me be the KDE 3 developers. I didn't know +any of it myself. They also implemented the check itself. +

    +In C++ it's important to deallocate memory in a way compatible with +how it was allocated. The deal is: +

      +
    • If allocated with malloc, calloc, + realloc, valloc or + memalign, you must deallocate with free. +
    • If allocated with new[], you must deallocate with + delete[]. +
    • If allocated with new, you must deallocate with + delete. +
    +The worst thing is that on Linux apparently it doesn't matter if you +do muddle these up, and it all seems to work ok, but the same program +may then crash on a different platform, Solaris for example. So it's +best to fix it properly. According to the KDE folks "it's amazing how +many C++ programmers don't know this". +

    +Pascal Massimino adds the following clarification: +delete[] must be called associated with a +new[] because the compiler stores the size of the array +and the pointer-to-member to the destructor of the array's content +just before the pointer actually returned. This implies a +variable-sized overhead in what's returned by new or +new[]. It rather surprising how compilers [Ed: +runtime-support libraries?] are robust to mismatch in +new/delete +new[]/delete[]. + + +

    3.3.5  Passing system call parameters with inadequate +read/write permissions

    + +Memcheck checks all parameters to system calls. If a system call +needs to read from a buffer provided by your program, Memcheck checks +that the entire buffer is addressible and has valid data, ie, it is +readable. And if the system call needs to write to a user-supplied +buffer, Memcheck checks that the buffer is addressible. After the +system call, Memcheck updates its administrative information to +precisely reflect any changes in memory permissions caused by the +system call. + +

    Here's an example of a system call with an invalid parameter: +

    +  #include <stdlib.h>
    +  #include <unistd.h>
    +  int main( void )
    +  {
    +    char* arr = malloc(10);
    +    (void) write( 1 /* stdout */, arr, 10 );
    +    return 0;
    +  }
    +
    + +

    You get this complaint ... +

    +  Syscall param write(buf) contains uninitialised or unaddressable byte(s)
    +     at 0x4035E072: __libc_write
    +     by 0x402A6E5E: __libc_start_main (libc-start.c:129)
    +     by 0x80483B1: (within tests/badwrite)
    +     by <bogus frame pointer> ???
    +     Address 0x3807E6D0 is 0 bytes inside a block of size 10 alloc'd
    +     at 0x4004FEE6: malloc (ut_clientmalloc.c:539)
    +     by 0x80484A0: main (tests/badwrite.c:6)
    +     by 0x402A6E5E: __libc_start_main (libc-start.c:129)
    +     by 0x80483B1: (within tests/badwrite)
    +
    + +

    ... because the program has tried to write uninitialised junk from +the malloc'd block to the standard output. + +

    3.3.6  Overlapping source and destination blocks

    +The following C library functions copy some data from one memory block +to another (or something similar): memcpy(), +strcpy(), strncpy(), strcat(), +strncat(). The blocks pointed to by their src and +dst pointers aren't allowed to overlap. Memcheck checks +for this. +

    +For example: +

    +==27492== Source and destination overlap in memcpy(0xbffff294, 0xbffff280, 21)
    +==27492==    at 0x40026CDC: memcpy (mc_replace_strmem.c:71)
    +==27492==    by 0x804865A: main (overlap.c:40)
    +==27492==    by 0x40246335: __libc_start_main (../sysdeps/generic/libc-start.c:129)
    +==27492==    by 0x8048470: (within /auto/homes/njn25/grind/head6/memcheck/tests/overlap)
    +==27492== 
    +
    +

    +You don't want the two blocks to overlap because one of them could get +partially trashed by the copying. + + +

    3.4  Writing suppressions files

    + +The basic suppression format was described in this section. +

    +The suppression (2nd) line should have the form: +

    +Memcheck:suppression_type
    +
    +Or, since some of the suppressions are shared with Addrcheck: +
    +Memcheck,Addrcheck:suppression_type
    +
    + +

    +The Memcheck suppression types are as follows: +Value1, +Value2, +Value4, +Value8, +Value16, +meaning an uninitialised-value error when +using a value of 1, 2, 4, 8 or 16 bytes. +Or +Cond (or its old name, Value0), +meaning use of an uninitialised CPU condition code. Or: +Addr1, +Addr2, +Addr4, +Addr8, +Addr16, +meaning an invalid address during a +memory access of 1, 2, 4, 8 or 16 bytes respectively. Or +Param, +meaning an invalid system call parameter error. Or +Free, meaning an invalid or mismatching free. +Overlap, meaning a src/dst +overlap in memcpy() or a similar function. Last but not least, +you can suppress leak reports with Leak. Leak suppression was +added in valgrind-1.9.3, I believe. +

    + +The extra information line: for Param errors, is the name of the offending +system call parameter. +No other error kinds have this extra line. +

    +The first line of the calling context: for Value and Addr errors, it is either +the name of the function in which the error occurred, or, failing that, the +full path of the .so file or executable containing the error location. For +Free errors, is the name of the function doing the freeing (eg, +free, __builtin_vec_delete, etc). For Overlap +errors, is the name of the function with the overlapping arguments (eg. +memcpy(), strcpy(), etc). +

    +Lastly, there's the rest of the calling context. +

    + + +

    3.5  Details of Memcheck's checking machinery

    + +Read this section if you want to know, in detail, exactly what and how +Memcheck is checking. + + +

    3.5.1  Valid-value (V) bits

    + +It is simplest to think of Memcheck implementing a synthetic Intel x86 +CPU which is identical to a real CPU, except for one crucial detail. +Every bit (literally) of data processed, stored and handled by the +real CPU has, in the synthetic CPU, an associated "valid-value" bit, +which says whether or not the accompanying bit has a legitimate value. +In the discussions which follow, this bit is referred to as the V +(valid-value) bit. + +

    Each byte in the system therefore has a 8 V bits which follow +it wherever it goes. For example, when the CPU loads a word-size item +(4 bytes) from memory, it also loads the corresponding 32 V bits from +a bitmap which stores the V bits for the process' entire address +space. If the CPU should later write the whole or some part of that +value to memory at a different address, the relevant V bits will be +stored back in the V-bit bitmap. + +

    In short, each bit in the system has an associated V bit, which +follows it around everywhere, even inside the CPU. Yes, the CPU's +(integer and %eflags) registers have their own V bit +vectors. + +

    Copying values around does not cause Memcheck to check for, or +report on, errors. However, when a value is used in a way which might +conceivably affect the outcome of your program's computation, the +associated V bits are immediately checked. If any of these indicate +that the value is undefined, an error is reported. + +

    Here's an (admittedly nonsensical) example: +

    +  int i, j;
    +  int a[10], b[10];
    +  for (i = 0; i < 10; i++) {
    +    j = a[i];
    +    b[i] = j;
    +  }
    +
    + +

    Memcheck emits no complaints about this, since it merely copies +uninitialised values from a[] into b[], and +doesn't use them in any way. However, if the loop is changed to +

    +  for (i = 0; i < 10; i++) {
    +    j += a[i];
    +  }
    +  if (j == 77) 
    +     printf("hello there\n");
    +
    +then Valgrind will complain, at the if, that the +condition depends on uninitialised values. Note that it +doesn't complain at the j += a[i];, since +at that point the undefinedness is not "observable". It's only +when a decision has to be made as to whether or not to do the +printf -- an observable action of your program -- that +Memcheck complains. + +

    Most low level operations, such as adds, cause Memcheck to +use the V bits for the operands to calculate the V bits for the +result. Even if the result is partially or wholly undefined, +it does not complain. + +

    Checks on definedness only occur in two places: when a value is +used to generate a memory address, and where control flow decision +needs to be made. Also, when a system call is detected, valgrind +checks definedness of parameters as required. + +

    If a check should detect undefinedness, an error message is +issued. The resulting value is subsequently regarded as well-defined. +To do otherwise would give long chains of error messages. In effect, +we say that undefined values are non-infectious. + +

    This sounds overcomplicated. Why not just check all reads from +memory, and complain if an undefined value is loaded into a CPU register? +Well, that doesn't work well, because perfectly legitimate C programs routinely +copy uninitialised values around in memory, and we don't want endless complaints +about that. Here's the canonical example. Consider a struct +like this: +

    +  struct S { int x; char c; };
    +  struct S s1, s2;
    +  s1.x = 42;
    +  s1.c = 'z';
    +  s2 = s1;
    +
    + +

    The question to ask is: how large is struct S, in +bytes? An int is 4 bytes and a char one byte, so perhaps a struct S +occupies 5 bytes? Wrong. All (non-toy) compilers we know of will +round the size of struct S up to a whole number of words, +in this case 8 bytes. Not doing this forces compilers to generate +truly appalling code for subscripting arrays of struct +S's. + +

    So s1 occupies 8 bytes, yet only 5 of them will be initialised. +For the assignment s2 = s1, gcc generates code to copy +all 8 bytes wholesale into s2 without regard for their +meaning. If Memcheck simply checked values as they came out of +memory, it would yelp every time a structure assignment like this +happened. So the more complicated semantics described above is +necessary. This allows gcc to copy s1 into +s2 any way it likes, and a warning will only be emitted +if the uninitialised values are later used. + +

    One final twist to this story. The above scheme allows garbage to +pass through the CPU's integer registers without complaint. It does +this by giving the integer registers V tags, passing these around in +the expected way. This complicated and computationally expensive to +do, but is necessary. Memcheck is more simplistic about +floating-point loads and stores. In particular, V bits for data read +as a result of floating-point loads are checked at the load +instruction. So if your program uses the floating-point registers to +do memory-to-memory copies, you will get complaints about +uninitialised values. Fortunately, I have not yet encountered a +program which (ab)uses the floating-point registers in this way. + + +

    3.5.2  Valid-address (A) bits

    + +Notice that the previous subsection describes how the validity of values +is established and maintained without having to say whether the +program does or does not have the right to access any particular +memory location. We now consider the latter issue. + +

    As described above, every bit in memory or in the CPU has an +associated valid-value (V) bit. In addition, all bytes in memory, but +not in the CPU, have an associated valid-address (A) bit. This +indicates whether or not the program can legitimately read or write +that location. It does not give any indication of the validity or the +data at that location -- that's the job of the V bits -- only whether +or not the location may be accessed. + +

    Every time your program reads or writes memory, Memcheck checks the +A bits associated with the address. If any of them indicate an +invalid address, an error is emitted. Note that the reads and writes +themselves do not change the A bits, only consult them. + +

    So how do the A bits get set/cleared? Like this: + +

      +
    • When the program starts, all the global data areas are marked as + accessible.

    • +

      + +

    • When the program does malloc/new, the A bits for exactly the + area allocated, and not a byte more, are marked as accessible. + Upon freeing the area the A bits are changed to indicate + inaccessibility.

    • +

      + +

    • When the stack pointer register (%esp) moves up or down, A bits + are set. The rule is that the area from %esp up to the base of + the stack is marked as accessible, and below %esp is + inaccessible. (If that sounds illogical, bear in mind that the + stack grows down, not up, on almost all Unix systems, including + GNU/Linux.) Tracking %esp like this has the useful side-effect + that the section of stack used by a function for local variables + etc is automatically marked accessible on function entry and + inaccessible on exit.

    • +

      + +

    • When doing system calls, A bits are changed appropriately. For + example, mmap() magically makes files appear in the process's + address space, so the A bits must be updated if mmap() + succeeds.

    • +

      + +

    • Optionally, your program can tell Valgrind about such changes + explicitly, using the client request mechanism described above. +
    + + + +

    3.5.3  Putting it all together

    +Memcheck's checking machinery can be summarised as follows: + +
      +
    • Each byte in memory has 8 associated V (valid-value) bits, + saying whether or not the byte has a defined value, and a single + A (valid-address) bit, saying whether or not the program + currently has the right to read/write that address.

    • +

      + +

    • When memory is read or written, the relevant A bits are + consulted. If they indicate an invalid address, Valgrind emits + an Invalid read or Invalid write error.

    • +

      + +

    • When memory is read into the CPU's integer registers, the + relevant V bits are fetched from memory and stored in the + simulated CPU. They are not consulted.

    • +

      + +

    • When an integer register is written out to memory, the V bits + for that register are written back to memory too.

    • +

      + +

    • When memory is read into the CPU's floating point registers, the + relevant V bits are read from memory and they are immediately + checked. If any are invalid, an uninitialised value error is + emitted. This precludes using the floating-point registers to + copy possibly-uninitialised memory, but simplifies Valgrind in + that it does not have to track the validity status of the + floating-point registers.

    • +

      + +

    • As a result, when a floating-point register is written to + memory, the associated V bits are set to indicate a valid + value.

    • +

      + +

    • When values in integer CPU registers are used to generate a + memory address, or to determine the outcome of a conditional + branch, the V bits for those values are checked, and an error + emitted if any of them are undefined.

    • +

      + +

    • When values in integer CPU registers are used for any other + purpose, Valgrind computes the V bits for the result, but does + not check them.

    • +

      + +

    • One the V bits for a value in the CPU have been checked, they + are then set to indicate validity. This avoids long chains of + errors.

    • +

      + +

    • When values are loaded from memory, valgrind checks the A bits + for that location and issues an illegal-address warning if + needed. In that case, the V bits loaded are forced to indicate + Valid, despite the location being invalid. +

      + This apparently strange choice reduces the amount of confusing + information presented to the user. It avoids the + unpleasant phenomenon in which memory is read from a place which + is both unaddressible and contains invalid values, and, as a + result, you get not only an invalid-address (read/write) error, + but also a potentially large set of uninitialised-value errors, + one for every time the value is used. +

      + There is a hazy boundary case to do with multi-byte loads from + addresses which are partially valid and partially invalid. See + details of the flag --partial-loads-ok for details. +


    • +
    + +Memcheck intercepts calls to malloc, calloc, realloc, valloc, +memalign, free, new and delete. The behaviour you get is: + +
      + +
    • malloc/new: the returned memory is marked as addressible but not + having valid values. This means you have to write on it before + you can read it.

    • +

      + +

    • calloc: returned memory is marked both addressible and valid, + since calloc() clears the area to zero.

    • +

      + +

    • realloc: if the new size is larger than the old, the new section + is addressible but invalid, as with malloc.

    • +

      + +

    • If the new size is smaller, the dropped-off section is marked as + unaddressible. You may only pass to realloc a pointer + previously issued to you by malloc/calloc/realloc.

    • +

      + +

    • free/delete: you may only pass to free a pointer previously + issued to you by malloc/calloc/realloc, or the value + NULL. Otherwise, Valgrind complains. If the pointer is indeed + valid, Valgrind marks the entire area it points at as + unaddressible, and places the block in the freed-blocks-queue. + The aim is to defer as long as possible reallocation of this + block. Until that happens, all attempts to access it will + elicit an invalid-address error, as you would hope.

    • +
    + + + + + +

    3.6  Memory leak detection

    + +Memcheck keeps track of all memory blocks issued in response to calls +to malloc/calloc/realloc/new. So when the program exits, it knows +which blocks are still outstanding -- have not been returned, in other +words. Ideally, you want your program to have no blocks still in use +at exit. But many programs do. + +

    For each such block, Memcheck scans the entire address space of the +process, looking for pointers to the block. One of three situations +may result: + +

      +
    • A pointer to the start of the block is found. This usually + indicates programming sloppiness; since the block is still + pointed at, the programmer could, at least in principle, free'd + it before program exit.

    • +

      + +

    • A pointer to the interior of the block is found. The pointer + might originally have pointed to the start and have been moved + along, or it might be entirely unrelated. Memcheck deems such a + block as "dubious", that is, possibly leaked, + because it's unclear whether or + not a pointer to it still exists.

    • +

      + +

    • The worst outcome is that no pointer to the block can be found. + The block is classified as "leaked", because the + programmer could not possibly have free'd it at program exit, + since no pointer to it exists. This might be a symptom of + having lost the pointer at some earlier point in the + program.
    • +
    + +Memcheck reports summaries about leaked and dubious blocks. +For each such block, it will also tell you where the block was +allocated. This should help you figure out why the pointer to it has +been lost. In general, you should attempt to ensure your programs do +not have any leaked or dubious blocks at exit. + +

    The precise area of memory in which Memcheck searches for pointers +is: all naturally-aligned 4-byte words for which all A bits indicate +addressibility and all V bits indicated that the stored value is +actually valid. +

    + + + +

    3.7  Client Requests

    + +The following client requests are defined in memcheck.h. They +also work for Addrcheck. See memcheck.h for exact +details of their arguments. + +
      +
    • VALGRIND_MAKE_NOACCESS, + VALGRIND_MAKE_WRITABLE and + VALGRIND_MAKE_READABLE. These mark address + ranges as completely inaccessible, accessible but containing + undefined data, and accessible and containing defined data, + respectively. Subsequent errors may have their faulting + addresses described in terms of these blocks. Returns a + "block handle". Returns zero when not run on Valgrind. +

      +

    • VALGRIND_DISCARD: At some point you may want + Valgrind to stop reporting errors in terms of the blocks + defined by the previous three macros. To do this, the above + macros return a small-integer "block handle". You can pass + this block handle to VALGRIND_DISCARD. After + doing so, Valgrind will no longer be able to relate + addressing errors to the user-defined block associated with + the handle. The permissions settings associated with the + handle remain in place; this just affects how errors are + reported, not whether they are reported. Returns 1 for an + invalid handle and 0 for a valid handle (although passing + invalid handles is harmless). Always returns 0 when not run + on Valgrind. +

      +

    • VALGRIND_CHECK_WRITABLE and + VALGRIND_CHECK_READABLE: check immediately + whether or not the given address range has the relevant + property, and if not, print an error message. Also, for the + convenience of the client, returns zero if the relevant + property holds; otherwise, the returned value is the address + of the first byte for which the property is not true. + Always returns 0 when not run on Valgrind. +

      +

    • VALGRIND_CHECK_DEFINED: a quick and easy way + to find out whether Valgrind thinks a particular variable + (lvalue, to be precise) is addressible and defined. Prints + an error message if not. Returns no value. +

      +

    • VALGRIND_DO_LEAK_CHECK: run the memory leak detector + right now. Returns no value. I guess this could be used to + incrementally check for leaks between arbitrary places in the + program's execution. Warning: not properly tested! +

      +

    • VALGRIND_COUNT_LEAKS: fills in the four arguments with + the number of bytes of memory found by the previous leak check to + be leaked, dubious, reachable and suppressed. Again, useful in + test harness code, after calling VALGRIND_DO_LEAK_CHECK. +

      +

    • VALGRIND_GET_VBITS and + VALGRIND_SET_VBITS: allow you to get and set the V (validity) + bits for an address range. You should probably only set V bits that you + have got with VALGRIND_GET_VBITS. Only for those who really + know what they are doing. +

      +

    + diff --git a/VEX/head20041019/memcheck/docs/mc_techdocs.html b/VEX/head20041019/memcheck/docs/mc_techdocs.html new file mode 100644 index 0000000000..e33a0aa10f --- /dev/null +++ b/VEX/head20041019/memcheck/docs/mc_techdocs.html @@ -0,0 +1,2108 @@ + + + + The design and implementation of Valgrind + + + + +  +

    The design and implementation of Valgrind

    + +
    +Detailed technical notes for hackers, maintainers and the +overly-curious
    +These notes pertain to snapshot 20020306
    +

    +jseward@acm.org
    +http://valgrind.kde.org
    +Copyright © 2000-2004 Julian Seward +

    +Valgrind is licensed under the GNU General Public License, +version 2
    +An open-source tool for finding memory-management problems in +x86 GNU/Linux executables. +

    + +

    + + + + +


    + +

    Introduction

    + +This document contains a detailed, highly-technical description of the +internals of Valgrind. This is not the user manual; if you are an +end-user of Valgrind, you do not want to read this. Conversely, if +you really are a hacker-type and want to know how it works, I assume +that you have read the user manual thoroughly. +

    +You may need to read this document several times, and carefully. Some +important things, I only say once. + + +

    History

    + +Valgrind came into public view in late Feb 2002. However, it has been +under contemplation for a very long time, perhaps seriously for about +five years. Somewhat over two years ago, I started working on the x86 +code generator for the Glasgow Haskell Compiler +(http://www.haskell.org/ghc), gaining familiarity with x86 internals +on the way. I then did Cacheprof (http://www.cacheprof.org), gaining +further x86 experience. Some time around Feb 2000 I started +experimenting with a user-space x86 interpreter for x86-Linux. This +worked, but it was clear that a JIT-based scheme would be necessary to +give reasonable performance for Valgrind. Design work for the JITter +started in earnest in Oct 2000, and by early 2001 I had an x86-to-x86 +dynamic translator which could run quite large programs. This +translator was in a sense pointless, since it did not do any +instrumentation or checking. + +

    +Most of the rest of 2001 was taken up designing and implementing the +instrumentation scheme. The main difficulty, which consumed a lot +of effort, was to design a scheme which did not generate large numbers +of false uninitialised-value warnings. By late 2001 a satisfactory +scheme had been arrived at, and I started to test it on ever-larger +programs, with an eventual eye to making it work well enough so that +it was helpful to folks debugging the upcoming version 3 of KDE. I've +used KDE since before version 1.0, and wanted to Valgrind to be an +indirect contribution to the KDE 3 development effort. At the start of +Feb 02 the kde-core-devel crew started using it, and gave a huge +amount of helpful feedback and patches in the space of three weeks. +Snapshot 20020306 is the result. + +

    +In the best Unix tradition, or perhaps in the spirit of Fred Brooks' +depressing-but-completely-accurate epitaph "build one to throw away; +you will anyway", much of Valgrind is a second or third rendition of +the initial idea. The instrumentation machinery +(vg_translate.c, vg_memory.c) and core CPU +simulation (vg_to_ucode.c, vg_from_ucode.c) +have had three redesigns and rewrites; the register allocator, +low-level memory manager (vg_malloc2.c) and symbol table +reader (vg_symtab2.c) are on the second rewrite. In a +sense, this document serves to record some of the knowledge gained as +a result. + + +

    Design overview

    + +Valgrind is compiled into a Linux shared object, +valgrind.so, and also a dummy one, +valgrinq.so, of which more later. The +valgrind shell script adds valgrind.so to +the LD_PRELOAD list of extra libraries to be +loaded with any dynamically linked library. This is a standard trick, +one which I assume the LD_PRELOAD mechanism was developed +to support. + +

    +valgrind.so +is linked with the -z initfirst flag, which requests that +its initialisation code is run before that of any other object in the +executable image. When this happens, valgrind gains control. The +real CPU becomes "trapped" in valgrind.so and the +translations it generates. The synthetic CPU provided by Valgrind +does, however, return from this initialisation function. So the +normal startup actions, orchestrated by the dynamic linker +ld.so, continue as usual, except on the synthetic CPU, +not the real one. Eventually main is run and returns, +and then the finalisation code of the shared objects is run, +presumably in inverse order to which they were initialised. Remember, +this is still all happening on the simulated CPU. Eventually +valgrind.so's own finalisation code is called. It spots +this event, shuts down the simulated CPU, prints any error summaries +and/or does leak detection, and returns from the initialisation code +on the real CPU. At this point, in effect the real and synthetic CPUs +have merged back into one, Valgrind has lost control of the program, +and the program finally exit()s back to the kernel in the +usual way. + +

    +The normal course of activity, once Valgrind has started up, is as +follows. Valgrind never runs any part of your program (usually +referred to as the "client"), not a single byte of it, directly. +Instead it uses function VG_(translate) to translate +basic blocks (BBs, straight-line sequences of code) into instrumented +translations, and those are run instead. The translations are stored +in the translation cache (TC), vg_tc, with the +translation table (TT), vg_tt supplying the +original-to-translation code address mapping. Auxiliary array +VG_(tt_fast) is used as a direct-map cache for fast +lookups in TT; it usually achieves a hit rate of around 98% and +facilitates an orig-to-trans lookup in 4 x86 insns, which is not bad. + +

    +Function VG_(dispatch) in vg_dispatch.S is +the heart of the JIT dispatcher. Once a translated code address has +been found, it is executed simply by an x86 call +to the translation. At the end of the translation, the next +original code addr is loaded into %eax, and the +translation then does a ret, taking it back to the +dispatch loop, with, interestingly, zero branch mispredictions. +The address requested in %eax is looked up first in +VG_(tt_fast), and, if not found, by calling C helper +VG_(search_transtab). If there is still no translation +available, VG_(dispatch) exits back to the top-level +C dispatcher VG_(toploop), which arranges for +VG_(translate) to make a new translation. All fairly +unsurprising, really. There are various complexities described below. + +

    +The translator, orchestrated by VG_(translate), is +complicated but entirely self-contained. It is described in great +detail in subsequent sections. Translations are stored in TC, with TT +tracking administrative information. The translations are subject to +an approximate LRU-based management scheme. With the current +settings, the TC can hold at most about 15MB of translations, and LRU +passes prune it to about 13.5MB. Given that the +orig-to-translation expansion ratio is about 13:1 to 14:1, this means +TC holds translations for more or less a megabyte of original code, +which generally comes to about 70000 basic blocks for C++ compiled +with optimisation on. Generating new translations is expensive, so it +is worth having a large TC to minimise the (capacity) miss rate. + +

    +The dispatcher, VG_(dispatch), receives hints from +the translations which allow it to cheaply spot all control +transfers corresponding to x86 call and ret +instructions. It has to do this in order to spot some special events: +

      +
    • Calls to VG_(shutdown). This is Valgrind's cue to + exit. NOTE: actually this is done a different way; it should be + cleaned up. +

      +

    • Returns of system call handlers, to the return address + VG_(signalreturn_bogusRA). The signal simulator + needs to know when a signal handler is returning, so we spot + jumps (returns) to this address. +

      +

    • Calls to vg_trap_here. All malloc, + free, etc calls that the client program makes are + eventually routed to a call to vg_trap_here, + and Valgrind does its own special thing with these calls. + In effect this provides a trapdoor, by which Valgrind can + intercept certain calls on the simulated CPU, run the call as it + sees fit itself (on the real CPU), and return the result to + the simulated CPU, quite transparently to the client program. +
    +Valgrind intercepts the client's malloc, +free, etc, +calls, so that it can store additional information. Each block +malloc'd by the client gives rise to a shadow block +in which Valgrind stores the call stack at the time of the +malloc +call. When the client calls free, Valgrind tries to +find the shadow block corresponding to the address passed to +free, and emits an error message if none can be found. +If it is found, the block is placed on the freed blocks queue +vg_freed_list, it is marked as inaccessible, and +its shadow block now records the call stack at the time of the +free call. Keeping free'd blocks in +this queue allows Valgrind to spot all (presumably invalid) accesses +to them. However, once the volume of blocks in the free queue +exceeds VG_(clo_freelist_vol), blocks are finally +removed from the queue. + +

    +Keeping track of A and V bits (note: if you don't know what these are, +you haven't read the user guide carefully enough) for memory is done +in vg_memory.c. This implements a sparse array structure +which covers the entire 4G address space in a way which is reasonably +fast and reasonably space efficient. The 4G address space is divided +up into 64K sections, each covering 64Kb of address space. Given a +32-bit address, the top 16 bits are used to select one of the 65536 +entries in VG_(primary_map). The resulting "secondary" +(SecMap) holds A and V bits for the 64k of address space +chunk corresponding to the lower 16 bits of the address. + + +

    Design decisions

    + +Some design decisions were motivated by the need to make Valgrind +debuggable. Imagine you are writing a CPU simulator. It works fairly +well. However, you run some large program, like Netscape, and after +tens of millions of instructions, it crashes. How can you figure out +where in your simulator the bug is? + +

    +Valgrind's answer is: cheat. Valgrind is designed so that it is +possible to switch back to running the client program on the real +CPU at any point. Using the --stop-after= flag, you can +ask Valgrind to run just some number of basic blocks, and then +run the rest of the way on the real CPU. If you are searching for +a bug in the simulated CPU, you can use this to do a binary search, +which quickly leads you to the specific basic block which is +causing the problem. + +

    +This is all very handy. It does constrain the design in certain +unimportant ways. Firstly, the layout of memory, when viewed from the +client's point of view, must be identical regardless of whether it is +running on the real or simulated CPU. This means that Valgrind can't +do pointer swizzling -- well, no great loss -- and it can't run on +the same stack as the client -- again, no great loss. +Valgrind operates on its own stack, VG_(stack), which +it switches to at startup, temporarily switching back to the client's +stack when doing system calls for the client. + +

    +Valgrind also receives signals on its own stack, +VG_(sigstack), but for different gruesome reasons +discussed below. + +

    +This nice clean switch-back-to-the-real-CPU-whenever-you-like story +is muddied by signals. Problem is that signals arrive at arbitrary +times and tend to slightly perturb the basic block count, with the +result that you can get close to the basic block causing a problem but +can't home in on it exactly. My kludgey hack is to define +SIGNAL_SIMULATION to 1 towards the bottom of +vg_syscall_mem.c, so that signal handlers are run on the +real CPU and don't change the BB counts. + +

    +A second hole in the switch-back-to-real-CPU story is that Valgrind's +way of delivering signals to the client is different from that of the +kernel. Specifically, the layout of the signal delivery frame, and +the mechanism used to detect a sighandler returning, are different. +So you can't expect to make the transition inside a sighandler and +still have things working, but in practice that's not much of a +restriction. + +

    +Valgrind's implementation of malloc, free, +etc, (in vg_clientmalloc.c, not the low-level stuff in +vg_malloc2.c) is somewhat complicated by the need to +handle switching back at arbitrary points. It does work tho. + + + +

    Correctness

    + +There's only one of me, and I have a Real Life (tm) as well as hacking +Valgrind [allegedly :-]. That means I don't have time to waste +chasing endless bugs in Valgrind. My emphasis is therefore on doing +everything as simply as possible, with correctness, stability and +robustness being the number one priority, more important than +performance or functionality. As a result: +
      +
    • The code is absolutely loaded with assertions, and these are + permanently enabled. I have no plan to remove or disable + them later. Over the past couple of months, as valgrind has + become more widely used, they have shown their worth, pulling + up various bugs which would otherwise have appeared as + hard-to-find segmentation faults. +

      + I am of the view that it's acceptable to spend 5% of the total + running time of your valgrindified program doing assertion checks + and other internal sanity checks. +

      +

    • Aside from the assertions, valgrind contains various sets of + internal sanity checks, which get run at varying frequencies + during normal operation. VG_(do_sanity_checks) + runs every 1000 basic blocks, which means 500 to 2000 times/second + for typical machines at present. It checks that Valgrind hasn't + overrun its private stack, and does some simple checks on the + memory permissions maps. Once every 25 calls it does some more + extensive checks on those maps. Etc, etc. +

      + The following components also have sanity check code, which can + be enabled to aid debugging: +

        +
      • The low-level memory-manager + (VG_(mallocSanityCheckArena)). This does a + complete check of all blocks and chains in an arena, which + is very slow. Is not engaged by default. +

        +

      • The symbol table reader(s): various checks to ensure + uniqueness of mappings; see VG_(read_symbols) + for a start. Is permanently engaged. +

        +

      • The A and V bit tracking stuff in vg_memory.c. + This can be compiled with cpp symbol + VG_DEBUG_MEMORY defined, which removes all the + fast, optimised cases, and uses simple-but-slow fallbacks + instead. Not engaged by default. +

        +

      • Ditto VG_DEBUG_LEAKCHECK. +

        +

      • The JITter parses x86 basic blocks into sequences of + UCode instructions. It then sanity checks each one with + VG_(saneUInstr) and sanity checks the sequence + as a whole with VG_(saneUCodeBlock). This stuff + is engaged by default, and has caught some way-obscure bugs + in the simulated CPU machinery in its time. +

        +

      • The system call wrapper does + VG_(first_and_last_secondaries_look_plausible) after + every syscall; this is known to pick up bugs in the syscall + wrappers. Engaged by default. +

        +

      • The main dispatch loop, in VG_(dispatch), checks + that translations do not set %ebp to any value + different from VG_EBP_DISPATCH_CHECKED or + & VG_(baseBlock). In effect this test is free, + and is permanently engaged. +

        +

      • There are a couple of ifdefed-out consistency checks I + inserted whilst debugging the new register allocater, + vg_do_register_allocation. +
      +

      +

    • I try to avoid techniques, algorithms, mechanisms, etc, for which + I can supply neither a convincing argument that they are correct, + nor sanity-check code which might pick up bugs in my + implementation. I don't always succeed in this, but I try. + Basically the idea is: avoid techniques which are, in practice, + unverifiable, in some sense. When doing anything, always have in + mind: "how can I verify that this is correct?" +
    + +

    +Some more specific things are: + +

      +
    • Valgrind runs in the same namespace as the client, at least from + ld.so's point of view, and it therefore absolutely + had better not export any symbol with a name which could clash + with that of the client or any of its libraries. Therefore, all + globally visible symbols exported from valgrind.so + are defined using the VG_ CPP macro. As you'll see + from tool_asm.h, this appends some arbitrary + prefix to the symbol, in order that it be, we hope, globally + unique. Currently the prefix is vgPlain_. For + convenience there are also VGM_, VGP_ + and VGOFF_. All locally defined symbols are declared + static and do not appear in the final shared object. +

      + To check this, I periodically do + nm valgrind.so | grep " T ", + which shows you all the globally exported text symbols. + They should all have an approved prefix, except for those like + malloc, free, etc, which we deliberately + want to shadow and take precedence over the same names exported + from glibc.so, so that valgrind can intercept those + calls easily. Similarly, nm valgrind.so | grep " D " + allows you to find any rogue data-segment symbol names. +

      +

    • Valgrind tries, and almost succeeds, in being completely + independent of all other shared objects, in particular of + glibc.so. For example, we have our own low-level + memory manager in vg_malloc2.c, which is a fairly + standard malloc/free scheme augmented with arenas, and + vg_mylibc.c exports reimplementations of various bits + and pieces you'd normally get from the C library. +

      + Why all the hassle? Because imagine the potential chaos of both + the simulated and real CPUs executing in glibc.so. + It just seems simpler and cleaner to be completely self-contained, + so that only the simulated CPU visits glibc.so. In + practice it's not much hassle anyway. Also, valgrind starts up + before glibc has a chance to initialise itself, and who knows what + difficulties that could lead to. Finally, glibc has definitions + for some types, specifically sigset_t, which conflict + (are different from) the Linux kernel's idea of same. When + Valgrind wants to fiddle around with signal stuff, it wants to + use the kernel's definitions, not glibc's definitions. So it's + simplest just to keep glibc out of the picture entirely. +

      + To find out which glibc symbols are used by Valgrind, reinstate + the link flags -nostdlib -Wl,-no-undefined. This + causes linking to fail, but will tell you what you depend on. + I have mostly, but not entirely, got rid of the glibc + dependencies; what remains is, IMO, fairly harmless. AFAIK the + current dependencies are: memset, + memcmp, stat, system, + sbrk, setjmp and longjmp. + +

      +

    • Similarly, valgrind should not really import any headers other + than the Linux kernel headers, since it knows of no API other than + the kernel interface to talk to. At the moment this is really not + in a good state, and vg_syscall_mem imports, via + vg_unsafe.h, a significant number of C-library + headers so as to know the sizes of various structs passed across + the kernel boundary. This is of course completely bogus, since + there is no guarantee that the C library's definitions of these + structs matches those of the kernel. I have started to sort this + out using vg_kerneliface.h, into which I had intended + to copy all kernel definitions which valgrind could need, but this + has not gotten very far. At the moment it mostly contains + definitions for sigset_t and struct + sigaction, since the kernel's definition for these really + does clash with glibc's. I plan to use a vki_ prefix + on all these types and constants, to denote the fact that they + pertain to Valgrind's Kernel Interface. +

      + Another advantage of having a vg_kerneliface.h file + is that it makes it simpler to interface to a different kernel. + Once can, for example, easily imagine writing a new + vg_kerneliface.h for FreeBSD, or x86 NetBSD. + +

    + +

    Current limitations

    + +Support for weird (non-POSIX) signal stuff is patchy. Does anybody +care? +

    + + + + +


    + +

    The instrumenting JITter

    + +This really is the heart of the matter. We begin with various side +issues. + +

    Run-time storage, and the use of host registers

    + +Valgrind translates client (original) basic blocks into instrumented +basic blocks, which live in the translation cache TC, until either the +client finishes or the translations are ejected from TC to make room +for newer ones. +

    +Since it generates x86 code in memory, Valgrind has complete control +of the use of registers in the translations. Now pay attention. I +shall say this only once, and it is important you understand this. In +what follows I will refer to registers in the host (real) cpu using +their standard names, %eax, %edi, etc. I +refer to registers in the simulated CPU by capitalising them: +%EAX, %EDI, etc. These two sets of +registers usually bear no direct relationship to each other; there is +no fixed mapping between them. This naming scheme is used fairly +consistently in the comments in the sources. +

    +Host registers, once things are up and running, are used as follows: +

      +
    • %esp, the real stack pointer, points + somewhere in Valgrind's private stack area, + VG_(stack) or, transiently, into its signal delivery + stack, VG_(sigstack). +

      +

    • %edi is used as a temporary in code generation; it + is almost always dead, except when used for the Left + value-tag operations. +

      +

    • %eax, %ebx, %ecx, + %edx and %esi are available to + Valgrind's register allocator. They are dead (carry unimportant + values) in between translations, and are live only in + translations. The one exception to this is %eax, + which, as mentioned far above, has a special significance to the + dispatch loop VG_(dispatch): when a translation + returns to the dispatch loop, %eax is expected to + contain the original-code-address of the next translation to run. + The register allocator is so good at minimising spill code that + using five regs and not having to save/restore %edi + actually gives better code than allocating to %edi + as well, but then having to push/pop it around special uses. +

      +

    • %ebp points permanently at + VG_(baseBlock). Valgrind's translations are + position-independent, partly because this is convenient, but also + because translations get moved around in TC as part of the LRUing + activity. All static entities which need to be referred to + from generated code, whether data or helper functions, are stored + starting at VG_(baseBlock) and are therefore reached + by indexing from %ebp. There is but one exception, + which is that by placing the value + VG_EBP_DISPATCH_CHECKED + in %ebp just before a return to the dispatcher, + the dispatcher is informed that the next address to run, + in %eax, requires special treatment. +

      +

    • The real machine's FPU state is pretty much unimportant, for + reasons which will become obvious. Ditto its %eflags + register. +
    + +

    +The state of the simulated CPU is stored in memory, in +VG_(baseBlock), which is a block of 200 words IIRC. +Recall that %ebp points permanently at the start of this +block. Function vg_init_baseBlock decides what the +offsets of various entities in VG_(baseBlock) are to be, +and allocates word offsets for them. The code generator then emits +%ebp relative addresses to get at those things. The +sequence in which entities are allocated has been carefully chosen so +that the 32 most popular entities come first, because this means 8-bit +offsets can be used in the generated code. + +

    +If I was clever, I could make %ebp point 32 words along +VG_(baseBlock), so that I'd have another 32 words of +short-form offsets available, but that's just complicated, and it's +not important -- the first 32 words take 99% (or whatever) of the +traffic. + +

    +Currently, the sequence of stuff in VG_(baseBlock) is as +follows: +

      +
    • 9 words, holding the simulated integer registers, + %EAX .. %EDI, and the simulated flags, + %EFLAGS. +

      +

    • Another 9 words, holding the V bit "shadows" for the above 9 regs. +

      +

    • The addresses of various helper routines called from + generated code: + VG_(helper_value_check4_fail), + VG_(helper_value_check0_fail), + which register V-check failures, + VG_(helperc_STOREV4), + VG_(helperc_STOREV1), + VG_(helperc_LOADV4), + VG_(helperc_LOADV1), + which do stores and loads of V bits to/from the + sparse array which keeps track of V bits in memory, + and + VGM_(handle_esp_assignment), which messes with + memory addressibility resulting from changes in %ESP. +

      +

    • The simulated %EIP. +

      +

    • 24 spill words, for when the register allocator can't make it work + with 5 measly registers. +

      +

    • Addresses of helpers VG_(helperc_STOREV2), + VG_(helperc_LOADV2). These are here because 2-byte + loads and stores are relatively rare, so are placed above the + magic 32-word offset boundary. +

      +

    • For similar reasons, addresses of helper functions + VGM_(fpu_write_check) and + VGM_(fpu_read_check), which handle the A/V maps + testing and changes required by FPU writes/reads. +

      +

    • Some other boring helper addresses: + VG_(helper_value_check2_fail) and + VG_(helper_value_check1_fail). These are probably + never emitted now, and should be removed. +

      +

    • The entire state of the simulated FPU, which I believe to be + 108 bytes long. +

      +

    • Finally, the addresses of various other helper functions in + vg_helpers.S, which deal with rare situations which + are tedious or difficult to generate code in-line for. +
    + +

    +As a general rule, the simulated machine's state lives permanently in +memory at VG_(baseBlock). However, the JITter does some +optimisations which allow the simulated integer registers to be +cached in real registers over multiple simulated instructions within +the same basic block. These are always flushed back into memory at +the end of every basic block, so that the in-memory state is +up-to-date between basic blocks. (This flushing is implied by the +statement above that the real machine's allocatable registers are +dead in between simulated blocks). + + +

    Startup, shutdown, and system calls

    + +Getting into of Valgrind (VG_(startup), called from +valgrind.so's initialisation section), really means +copying the real CPU's state into VG_(baseBlock), and +then installing our own stack pointer, etc, into the real CPU, and +then starting up the JITter. Exiting valgrind involves copying the +simulated state back to the real state. + +

    +Unfortunately, there's a complication at startup time. Problem is +that at the point where we need to take a snapshot of the real CPU's +state, the offsets in VG_(baseBlock) are not set up yet, +because to do so would involve disrupting the real machine's state +significantly. The way round this is to dump the real machine's state +into a temporary, static block of memory, +VG_(m_state_static). We can then set up the +VG_(baseBlock) offsets at our leisure, and copy into it +from VG_(m_state_static) at some convenient later time. +This copying is done by +VG_(copy_m_state_static_to_baseBlock). + +

    +On exit, the inverse transformation is (rather unnecessarily) used: +stuff in VG_(baseBlock) is copied to +VG_(m_state_static), and the assembly stub then copies +from VG_(m_state_static) into the real machine registers. + +

    +Doing system calls on behalf of the client (vg_syscall.S) +is something of a half-way house. We have to make the world look +sufficiently like that which the client would normally have to make +the syscall actually work properly, but we can't afford to lose +control. So the trick is to copy all of the client's state, except +its program counter, into the real CPU, do the system call, and +copy the state back out. Note that the client's state includes its +stack pointer register, so one effect of this partial restoration is +to cause the system call to be run on the client's stack, as it should +be. + +

    +As ever there are complications. We have to save some of our own state +somewhere when restoring the client's state into the CPU, so that we +can keep going sensibly afterwards. In fact the only thing which is +important is our own stack pointer, but for paranoia reasons I save +and restore our own FPU state as well, even though that's probably +pointless. + +

    +The complication on the above complication is, that for horrible +reasons to do with signals, we may have to handle a second client +system call whilst the client is blocked inside some other system +call (unbelievable!). That means there's two sets of places to +dump Valgrind's stack pointer and FPU state across the syscall, +and we decide which to use by consulting +VG_(syscall_depth), which is in turn maintained by +VG_(wrap_syscall). + + + +

    Introduction to UCode

    + +UCode lies at the heart of the x86-to-x86 JITter. The basic premise +is that dealing the the x86 instruction set head-on is just too darn +complicated, so we do the traditional compiler-writer's trick and +translate it into a simpler, easier-to-deal-with form. + +

    +In normal operation, translation proceeds through six stages, +coordinated by VG_(translate): +

      +
    1. Parsing of an x86 basic block into a sequence of UCode + instructions (VG_(disBB)). +

      +

    2. UCode optimisation (vg_improve), with the aim of + caching simulated registers in real registers over multiple + simulated instructions, and removing redundant simulated + %EFLAGS saving/restoring. +

      +

    3. UCode instrumentation (vg_instrument), which adds + value and address checking code. +

      +

    4. Post-instrumentation cleanup (vg_cleanup), removing + redundant value-check computations. +

      +

    5. Register allocation (vg_do_register_allocation), + which, note, is done on UCode. +

      +

    6. Emission of final instrumented x86 code + (VG_(emit_code)). +
    + +

    +Notice how steps 2, 3, 4 and 5 are simple UCode-to-UCode +transformation passes, all on straight-line blocks of UCode (type +UCodeBlock). Steps 2 and 4 are optimisation passes and +can be disabled for debugging purposes, with +--optimise=no and --cleanup=no respectively. + +

    +Valgrind can also run in a no-instrumentation mode, given +--instrument=no. This is useful for debugging the JITter +quickly without having to deal with the complexity of the +instrumentation mechanism too. In this mode, steps 3 and 4 are +omitted. + +

    +These flags combine, so that --instrument=no together with +--optimise=no means only steps 1, 5 and 6 are used. +--single-step=yes causes each x86 instruction to be +treated as a single basic block. The translations are terrible but +this is sometimes instructive. + +

    +The --stop-after=N flag switches back to the real CPU +after N basic blocks. It also re-JITs the final basic +block executed and prints the debugging info resulting, so this +gives you a way to get a quick snapshot of how a basic block looks as +it passes through the six stages mentioned above. If you want to +see full information for every block translated (probably not, but +still ...) find, in VG_(translate), the lines +
    dis = True; +
    dis = debugging_translation; +
    +and comment out the second line. This will spew out debugging +junk faster than you can possibly imagine. + + + +

    UCode operand tags: type Tag

    + +UCode is, more or less, a simple two-address RISC-like code. In +keeping with the x86 AT&T assembly syntax, generally speaking the +first operand is the source operand, and the second is the destination +operand, which is modified when the uinstr is notionally executed. + +

    +UCode instructions have up to three operand fields, each of which has +a corresponding Tag describing it. Possible values for +the tag are: + +

      +
    • NoValue: indicates that the field is not in use. +

      +

    • Lit16: the field contains a 16-bit literal. +

      +

    • Literal: the field denotes a 32-bit literal, whose + value is stored in the lit32 field of the uinstr + itself. Since there is only one lit32 for the whole + uinstr, only one operand field may contain this tag. +

      +

    • SpillNo: the field contains a spill slot number, in + the range 0 to 23 inclusive, denoting one of the spill slots + contained inside VG_(baseBlock). Such tags only + exist after register allocation. +

      +

    • RealReg: the field contains a number in the range 0 + to 7 denoting an integer x86 ("real") register on the host. The + number is the Intel encoding for integer registers. Such tags + only exist after register allocation. +

      +

    • ArchReg: the field contains a number in the range 0 + to 7 denoting an integer x86 register on the simulated CPU. In + reality this means a reference to one of the first 8 words of + VG_(baseBlock). Such tags can exist at any point in + the translation process. +

      +

    • Last, but not least, TempReg. The field contains the + number of one of an infinite set of virtual (integer) + registers. TempRegs are used everywhere throughout + the translation process; you can have as many as you want. The + register allocator maps as many as it can into + RealRegs and turns the rest into + SpillNos, so TempRegs should not exist + after the register allocation phase. +

      + TempRegs are always 32 bits long, even if the data + they hold is logically shorter. In that case the upper unused + bits are required, and, I think, generally assumed, to be zero. + TempRegs holding V bits for quantities shorter than + 32 bits are expected to have ones in the unused places, since a + one denotes "undefined". +

    + + +

    UCode instructions: type UInstr

    + +

    +UCode was carefully designed to make it possible to do register +allocation on UCode and then translate the result into x86 code +without needing any extra registers ... well, that was the original +plan, anyway. Things have gotten a little more complicated since +then. In what follows, UCode instructions are referred to as uinstrs, +to distinguish them from x86 instructions. Uinstrs of course have +uopcodes which are (naturally) different from x86 opcodes. + +

    +A uinstr (type UInstr) contains +various fields, not all of which are used by any one uopcode: +

      +
    • Three 16-bit operand fields, val1, val2 + and val3. +

      +

    • Three tag fields, tag1, tag2 + and tag3. Each of these has a value of type + Tag, + and they describe what the val1, val2 + and val3 fields contain. +

      +

    • A 32-bit literal field. +

      +

    • Two FlagSets, specifying which x86 condition codes are + read and written by the uinstr. +

      +

    • An opcode byte, containing a value of type Opcode. +

      +

    • A size field, indicating the data transfer size (1/2/4/8/10) in + cases where this makes sense, or zero otherwise. +

      +

    • A condition-code field, which, for jumps, holds a + value of type Condcode, indicating the condition + which applies. The encoding is as it is in the x86 insn stream, + except we add a 17th value CondAlways to indicate + an unconditional transfer. +

      +

    • Various 1-bit flags, indicating whether this insn pertains to an + x86 CALL or RET instruction, whether a widening is signed or not, + etc. +
    + +

    +UOpcodes (type Opcode) are divided into two groups: those +necessary merely to express the functionality of the x86 code, and +extra uopcodes needed to express the instrumentation. The former +group contains: +

      +
    • GET and PUT, which move values from the + simulated CPU's integer registers (ArchRegs) into + TempRegs, and back. GETF and + PUTF do the corresponding thing for the simulated + %EFLAGS. There are no corresponding insns for the + FPU register stack, since we don't explicitly simulate its + registers. +

      +

    • LOAD and STORE, which, in RISC-like + fashion, are the only uinstrs able to interact with memory. +

      +

    • MOV and CMOV allow unconditional and + conditional moves of values between TempRegs. +

      +

    • ALU operations. Again in RISC-like fashion, these only operate on + TempRegs (before reg-alloc) or RealRegs + (after reg-alloc). These are: ADD, ADC, + AND, OR, XOR, + SUB, SBB, SHL, + SHR, SAR, ROL, + ROR, RCL, RCR, + NOT, NEG, INC, + DEC, BSWAP, CC2VAL and + WIDEN. WIDEN does signed or unsigned + value widening. CC2VAL is used to convert condition + codes into a value, zero or one. The rest are obvious. +

      + To allow for more efficient code generation, we bend slightly the + restriction at the start of the previous para: for + ADD, ADC, XOR, + SUB and SBB, we allow the first (source) + operand to also be an ArchReg, that is, one of the + simulated machine's registers. Also, many of these ALU ops allow + the source operand to be a literal. See + VG_(saneUInstr) for the final word on the allowable + forms of uinstrs. +

      +

    • LEA1 and LEA2 are not strictly + necessary, but allow faciliate better translations. They + record the fancy x86 addressing modes in a direct way, which + allows those amodes to be emitted back into the final + instruction stream more or less verbatim. +

      +

    • CALLM calls a machine-code helper, one of the methods + whose address is stored at some VG_(baseBlock) + offset. PUSH and POP move values + to/from TempReg to the real (Valgrind's) stack, and + CLEAR removes values from the stack. + CALLM_S and CALLM_E delimit the + boundaries of call setups and clearings, for the benefit of the + instrumentation passes. Getting this right is critical, and so + VG_(saneUCodeBlock) makes various checks on the use + of these uopcodes. +

      + It is important to understand that these uopcodes have nothing to + do with the x86 call, return, + push or pop instructions, and are not + used to implement them. Those guys turn into combinations of + GET, PUT, LOAD, + STORE, ADD, SUB, and + JMP. What these uopcodes support is calling of + helper functions such as VG_(helper_imul_32_64), + which do stuff which is too difficult or tedious to emit inline. +

      +

    • FPU, FPU_R and FPU_W. + Valgrind doesn't attempt to simulate the internal state of the + FPU at all. Consequently it only needs to be able to distinguish + FPU ops which read and write memory from those that don't, and + for those which do, it needs to know the effective address and + data transfer size. This is made easier because the x86 FP + instruction encoding is very regular, basically consisting of + 16 bits for a non-memory FPU insn and 11 (IIRC) bits + an address mode + for a memory FPU insn. So our FPU uinstr carries + the 16 bits in its val1 field. And + FPU_R and FPU_W carry 11 bits in that + field, together with the identity of a TempReg or + (later) RealReg which contains the address. +

      +

    • JIFZ is unique, in that it allows a control-flow + transfer which is not deemed to end a basic block. It causes a + jump to a literal (original) address if the specified argument + is zero. +

      +

    • Finally, INCEIP advances the simulated + %EIP by the specified literal amount. This supports + lazy %EIP updating, as described below. +
    + +

    +Stages 1 and 2 of the 6-stage translation process mentioned above +deal purely with these uopcodes, and no others. They are +sufficient to express pretty much all the x86 32-bit protected-mode +instruction set, at +least everything understood by a pre-MMX original Pentium (P54C). + +

    +Stages 3, 4, 5 and 6 also deal with the following extra +"instrumentation" uopcodes. They are used to express all the +definedness-tracking and -checking machinery which valgrind does. In +later sections we show how to create checking code for each of the +uopcodes above. Note that these instrumentation uopcodes, although +some appearing complicated, have been carefully chosen so that +efficient x86 code can be generated for them. GNU superopt v2.5 did a +great job helping out here. Anyways, the uopcodes are as follows: + +

      +
    • GETV and PUTV are analogues to + GET and PUT above. They are identical + except that they move the V bits for the specified values back and + forth to TempRegs, rather than moving the values + themselves. +

      +

    • Similarly, LOADV and STOREV read and + write V bits from the synthesised shadow memory that Valgrind + maintains. In fact they do more than that, since they also do + address-validity checks, and emit complaints if the read/written + addresses are unaddressible. +

      +

    • TESTV, whose parameters are a TempReg + and a size, tests the V bits in the TempReg, at the + specified operation size (0/1/2/4 byte) and emits an error if any + of them indicate undefinedness. This is the only uopcode capable + of doing such tests. +

      +

    • SETV, whose parameters are also TempReg + and a size, makes the V bits in the TempReg indicated + definedness, at the specified operation size. This is usually + used to generate the correct V bits for a literal value, which is + of course fully defined. +

      +

    • GETVF and PUTVF are analogues to + GETF and PUTF. They move the single V + bit used to model definedness of %EFLAGS between its + home in VG_(baseBlock) and the specified + TempReg. +

      +

    • TAG1 denotes one of a family of unary operations on + TempRegs containing V bits. Similarly, + TAG2 denotes one in a family of binary operations on + V bits. +
    + +

    +These 10 uopcodes are sufficient to express Valgrind's entire +definedness-checking semantics. In fact most of the interesting magic +is done by the TAG1 and TAG2 +suboperations. + +

    +First, however, I need to explain about V-vector operation sizes. +There are 4 sizes: 1, 2 and 4, which operate on groups of 8, 16 and 32 +V bits at a time, supporting the usual 1, 2 and 4 byte x86 operations. +However there is also the mysterious size 0, which really means a +single V bit. Single V bits are used in various circumstances; in +particular, the definedness of %EFLAGS is modelled with a +single V bit. Now might be a good time to also point out that for +V bits, 1 means "undefined" and 0 means "defined". Similarly, for A +bits, 1 means "invalid address" and 0 means "valid address". This +seems counterintuitive (and so it is), but testing against zero on +x86s saves instructions compared to testing against all 1s, because +many ALU operations set the Z flag for free, so to speak. + +

    +With that in mind, the tag ops are: + +

      +
    • (UNARY) Pessimising casts: VgT_PCast40, + VgT_PCast20, VgT_PCast10, + VgT_PCast01, VgT_PCast02 and + VgT_PCast04. A "pessimising cast" takes a V-bit + vector at one size, and creates a new one at another size, + pessimised in the sense that if any of the bits in the source + vector indicate undefinedness, then all the bits in the result + indicate undefinedness. In this case the casts are all to or from + a single V bit, so for example VgT_PCast40 is a + pessimising cast from 32 bits to 1, whereas + VgT_PCast04 simply copies the single source V bit + into all 32 bit positions in the result. Surprisingly, these ops + can all be implemented very efficiently. +

      + There are also the pessimising casts VgT_PCast14, + from 8 bits to 32, VgT_PCast12, from 8 bits to 16, + and VgT_PCast11, from 8 bits to 8. This last one + seems nonsensical, but in fact it isn't a no-op because, as + mentioned above, any undefined (1) bits in the source infect the + entire result. +

      +

    • (UNARY) Propagating undefinedness upwards in a word: + VgT_Left4, VgT_Left2 and + VgT_Left1. These are used to simulate the worst-case + effects of carry propagation in adds and subtracts. They return a + V vector identical to the original, except that if the original + contained any undefined bits, then it and all bits above it are + marked as undefined too. Hence the Left bit in the names. +

      +

    • (UNARY) Signed and unsigned value widening: + VgT_SWiden14, VgT_SWiden24, + VgT_SWiden12, VgT_ZWiden14, + VgT_ZWiden24 and VgT_ZWiden12. These + mimic the definedness effects of standard signed and unsigned + integer widening. Unsigned widening creates zero bits in the new + positions, so VgT_ZWiden* accordingly park mark + those parts of their argument as defined. Signed widening copies + the sign bit into the new positions, so VgT_SWiden* + copies the definedness of the sign bit into the new positions. + Because 1 means undefined and 0 means defined, these operations + can (fascinatingly) be done by the same operations which they + mimic. Go figure. +

      +

    • (BINARY) Undefined-if-either-Undefined, + Defined-if-either-Defined: VgT_UifU4, + VgT_UifU2, VgT_UifU1, + VgT_UifU0, VgT_DifD4, + VgT_DifD2, VgT_DifD1. These do simple + bitwise operations on pairs of V-bit vectors, with + UifU giving undefined if either arg bit is + undefined, and DifD giving defined if either arg bit + is defined. Abstract interpretation junkies, if any make it this + far, may like to think of them as meets and joins (or is it joins + and meets) in the definedness lattices. +

      +

    • (BINARY; one value, one V bits) Generate argument improvement + terms for AND and OR: VgT_ImproveAND4_TQ, + VgT_ImproveAND2_TQ, VgT_ImproveAND1_TQ, + VgT_ImproveOR4_TQ, VgT_ImproveOR2_TQ, + VgT_ImproveOR1_TQ. These help out with AND and OR + operations. AND and OR have the inconvenient property that the + definedness of the result depends on the actual values of the + arguments as well as their definedness. At the bit level: +
      1 AND undefined = undefined, but +
      0 AND undefined = 0, and similarly +
      0 OR undefined = undefined, but +
      1 OR undefined = 1. +
      +

      + It turns out that gcc (quite legitimately) generates code which + relies on this fact, so we have to model it properly in order to + avoid flooding users with spurious value errors. The ultimate + definedness result of AND and OR is calculated using + UifU on the definedness of the arguments, but we + also DifD in some "improvement" terms which + take into account the above phenomena. +

      + ImproveAND takes as its first argument the actual + value of an argument to AND (the T) and the definedness of that + argument (the Q), and returns a V-bit vector which is defined (0) + for bits which have value 0 and are defined; this, when + DifD into the final result causes those bits to be + defined even if the corresponding bit in the other argument is undefined. +

      + The ImproveOR ops do the dual thing for OR + arguments. Note that XOR does not have this property that one + argument can make the other irrelevant, so there is no need for + such complexity for XOR. +

    + +

    +That's all the tag ops. If you stare at this long enough, and then +run Valgrind and stare at the pre- and post-instrumented ucode, it +should be fairly obvious how the instrumentation machinery hangs +together. + +

    +One point, if you do this: in order to make it easy to differentiate +TempRegs carrying values from TempRegs +carrying V bit vectors, Valgrind prints the former as (for example) +t28 and the latter as q28; the fact that +they carry the same number serves to indicate their relationship. +This is purely for the convenience of the human reader; the register +allocator and code generator don't regard them as different. + + +

    Translation into UCode

    + +VG_(disBB) allocates a new UCodeBlock and +then uses disInstr to translate x86 instructions one at a +time into UCode, dumping the result in the UCodeBlock. +This goes on until a control-flow transfer instruction is encountered. + +

    +Despite the large size of vg_to_ucode.c, this translation +is really very simple. Each x86 instruction is translated entirely +independently of its neighbours, merrily allocating new +TempRegs as it goes. The idea is to have a simple +translator -- in reality, no more than a macro-expander -- and the -- +resulting bad UCode translation is cleaned up by the UCode +optimisation phase which follows. To give you an idea of some x86 +instructions and their translations (this is a complete basic block, +as Valgrind sees it): +

    +        0x40435A50:  incl %edx
    +
    +           0: GETL      %EDX, t0
    +           1: INCL      t0  (-wOSZAP)
    +           2: PUTL      t0, %EDX
    +
    +        0x40435A51:  movsbl (%edx),%eax
    +
    +           3: GETL      %EDX, t2
    +           4: LDB       (t2), t2
    +           5: WIDENL_Bs t2
    +           6: PUTL      t2, %EAX
    +
    +        0x40435A54:  testb $0x20, 1(%ecx,%eax,2)
    +
    +           7: GETL      %EAX, t6
    +           8: GETL      %ECX, t8
    +           9: LEA2L     1(t8,t6,2), t4
    +          10: LDB       (t4), t10
    +          11: MOVB      $0x20, t12
    +          12: ANDB      t12, t10  (-wOSZACP)
    +          13: INCEIPo   $9
    +
    +        0x40435A59:  jnz-8 0x40435A50
    +
    +          14: Jnzo      $0x40435A50  (-rOSZACP)
    +          15: JMPo      $0x40435A5B
    +
    + +

    +Notice how the block always ends with an unconditional jump to the +next block. This is a bit unnecessary, but makes many things simpler. + +

    +Most x86 instructions turn into sequences of GET, +PUT, LEA1, LEA2, +LOAD and STORE. Some complicated ones +however rely on calling helper bits of code in +vg_helpers.S. The ucode instructions PUSH, +POP, CALL, CALLM_S and +CALLM_E support this. The calling convention is somewhat +ad-hoc and is not the C calling convention. The helper routines must +save all integer registers, and the flags, that they use. Args are +passed on the stack underneath the return address, as usual, and if +result(s) are to be returned, it (they) are either placed in dummy arg +slots created by the ucode PUSH sequence, or just +overwrite the incoming args. + +

    +In order that the instrumentation mechanism can handle calls to these +helpers, VG_(saneUCodeBlock) enforces the following +restrictions on calls to helpers: + +

      +
    • Each CALL uinstr must be bracketed by a preceding + CALLM_S marker (dummy uinstr) and a trailing + CALLM_E marker. These markers are used by the + instrumentation mechanism later to establish the boundaries of the + PUSH, POP and CLEAR + sequences for the call. +

      +

    • PUSH, POP and CLEAR + may only appear inside sections bracketed by CALLM_S + and CALLM_E, and nowhere else. +

      +

    • In any such bracketed section, no two PUSH insns may + push the same TempReg. Dually, no two two + POPs may pop the same TempReg. +

      +

    • Finally, although this is not checked, args should be removed from + the stack with CLEAR, rather than POPs + into a TempReg which is not subsequently used. This + is because the instrumentation mechanism assumes that all values + POPped from the stack are actually used. +
    + +Some of the translations may appear to have redundant +TempReg-to-TempReg moves. This helps the +next phase, UCode optimisation, to generate better code. + + + +

    UCode optimisation

    + +UCode is then subjected to an improvement pass +(vg_improve()), which blurs the boundaries between the +translations of the original x86 instructions. It's pretty +straightforward. Three transformations are done: + +
      +
    • Redundant GET elimination. Actually, more general + than that -- eliminates redundant fetches of ArchRegs. In our + running example, uinstr 3 GETs %EDX into + t2 despite the fact that, by looking at the previous + uinstr, it is already in t0. The GET is + therefore removed, and t2 renamed to t0. + Assuming t0 is allocated to a host register, it means + the simulated %EDX will exist in a host CPU register + for more than one simulated x86 instruction, which seems to me to + be a highly desirable property. +

      + There is some mucking around to do with subregisters; + %AL vs %AH %AX vs + %EAX etc. I can't remember how it works, but in + general we are very conservative, and these tend to invalidate the + caching. +

      +

    • Redundant PUT elimination. This annuls + PUTs of values back to simulated CPU registers if a + later PUT would overwrite the earlier + PUT value, and there is no intervening reads of the + simulated register (ArchReg). +

      + As before, we are paranoid when faced with subregister references. + Also, PUTs of %ESP are never annulled, + because it is vital the instrumenter always has an up-to-date + %ESP value available, %ESP changes + affect addressibility of the memory around the simulated stack + pointer. +

      + The implication of the above paragraph is that the simulated + machine's registers are only lazily updated once the above two + optimisation phases have run, with the exception of + %ESP. TempRegs go dead at the end of + every basic block, from which is is inferrable that any + TempReg caching a simulated CPU reg is flushed (back + into the relevant VG_(baseBlock) slot) at the end of + every basic block. The further implication is that the simulated + registers are only up-to-date at in between basic blocks, and not + at arbitrary points inside basic blocks. And the consequence of + that is that we can only deliver signals to the client in between + basic blocks. None of this seems any problem in practice. +

      +

    • Finally there is a simple def-use thing for condition codes. If + an earlier uinstr writes the condition codes, and the next uinsn + along which actually cares about the condition codes writes the + same or larger set of them, but does not read any, the earlier + uinsn is marked as not writing any condition codes. This saves + a lot of redundant cond-code saving and restoring. +
    + +The effect of these transformations on our short block is rather +unexciting, and shown below. On longer basic blocks they can +dramatically improve code quality. + +
    +at 3: delete GET, rename t2 to t0 in (4 .. 6)
    +at 7: delete GET, rename t6 to t0 in (8 .. 9)
    +at 1: annul flag write OSZAP due to later OSZACP
    +
    +Improved code:
    +           0: GETL      %EDX, t0
    +           1: INCL      t0
    +           2: PUTL      t0, %EDX
    +           4: LDB       (t0), t0
    +           5: WIDENL_Bs t0
    +           6: PUTL      t0, %EAX
    +           8: GETL      %ECX, t8
    +           9: LEA2L     1(t8,t0,2), t4
    +          10: LDB       (t4), t10
    +          11: MOVB      $0x20, t12
    +          12: ANDB      t12, t10  (-wOSZACP)
    +          13: INCEIPo   $9
    +          14: Jnzo      $0x40435A50  (-rOSZACP)
    +          15: JMPo      $0x40435A5B
    +
    + +

    UCode instrumentation

    + +Once you understand the meaning of the instrumentation uinstrs, +discussed in detail above, the instrumentation scheme is fairly +straightforward. Each uinstr is instrumented in isolation, and the +instrumentation uinstrs are placed before the original uinstr. +Our running example continues below. I have placed a blank line +after every original ucode, to make it easier to see which +instrumentation uinstrs correspond to which originals. + +

    +As mentioned somewhere above, TempRegs carrying values +have names like t28, and each one has a shadow carrying +its V bits, with names like q28. This pairing aids in +reading instrumented ucode. + +

    +One decision about all this is where to have "observation points", +that is, where to check that V bits are valid. I use a minimalistic +scheme, only checking where a failure of validity could cause the +original program to (seg)fault. So the use of values as memory +addresses causes a check, as do conditional jumps (these cause a check +on the definedness of the condition codes). And arguments +PUSHed for helper calls are checked, hence the weird +restrictions on help call preambles described above. + +

    +Another decision is that once a value is tested, it is thereafter +regarded as defined, so that we do not emit multiple undefined-value +errors for the same undefined value. That means that +TESTV uinstrs are always followed by SETV +on the same (shadow) TempRegs. Most of these +SETVs are redundant and are removed by the +post-instrumentation cleanup phase. + +

    +The instrumentation for calling helper functions deserves further +comment. The definedness of results from a helper is modelled using +just one V bit. So, in short, we do pessimising casts of the +definedness of all the args, down to a single bit, and then +UifU these bits together. So this single V bit will say +"undefined" if any part of any arg is undefined. This V bit is then +pessimally cast back up to the result(s) sizes, as needed. If, by +seeing that all the args are got rid of with CLEAR and +none with POP, Valgrind sees that the result of the call +is not actually used, it immediately examines the result V bit with a +TESTV -- SETV pair. If it did not do this, +there would be no observation point to detect that the some of the +args to the helper were undefined. Of course, if the helper's results +are indeed used, we don't do this, since the result usage will +presumably cause the result definedness to be checked at some suitable +future point. + +

    +In general Valgrind tries to track definedness on a bit-for-bit basis, +but as the above para shows, for calls to helpers we throw in the +towel and approximate down to a single bit. This is because it's too +complex and difficult to track bit-level definedness through complex +ops such as integer multiply and divide, and in any case there is no +reasonable code fragments which attempt to (eg) multiply two +partially-defined values and end up with something meaningful, so +there seems little point in modelling multiplies, divides, etc, in +that level of detail. + +

    +Integer loads and stores are instrumented with firstly a test of the +definedness of the address, followed by a LOADV or +STOREV respectively. These turn into calls to +(for example) VG_(helperc_LOADV4). These helpers do two +things: they perform an address-valid check, and they load or store V +bits from/to the relevant address in the (simulated V-bit) memory. + +

    +FPU loads and stores are different. As above the definedness of the +address is first tested. However, the helper routine for FPU loads +(VGM_(fpu_read_check)) emits an error if either the +address is invalid or the referenced area contains undefined values. +It has to do this because we do not simulate the FPU at all, and so +cannot track definedness of values loaded into it from memory, so we +have to check them as soon as they are loaded into the FPU, ie, at +this point. We notionally assume that everything in the FPU is +defined. + +

    +It follows therefore that FPU writes first check the definedness of +the address, then the validity of the address, and finally mark the +written bytes as well-defined. + +

    +If anyone is inspired to extend Valgrind to MMX/SSE insns, I suggest +you use the same trick. It works provided that the FPU/MMX unit is +not used to merely as a conduit to copy partially undefined data from +one place in memory to another. Unfortunately the integer CPU is used +like that (when copying C structs with holes, for example) and this is +the cause of much of the elaborateness of the instrumentation here +described. + +

    +vg_instrument() in vg_translate.c actually +does the instrumentation. There are comments explaining how each +uinstr is handled, so we do not repeat that here. As explained +already, it is bit-accurate, except for calls to helper functions. +Unfortunately the x86 insns bt/bts/btc/btr are done by +helper fns, so bit-level accuracy is lost there. This should be fixed +by doing them inline; it will probably require adding a couple new +uinstrs. Also, left and right rotates through the carry flag (x86 +rcl and rcr) are approximated via a single +V bit; so far this has not caused anyone to complain. The +non-carry rotates, rol and ror, are much +more common and are done exactly. Re-visiting the instrumentation for +AND and OR, they seem rather verbose, and I wonder if it could be done +more concisely now. + +

    +The lowercase o on many of the uopcodes in the running +example indicates that the size field is zero, usually meaning a +single-bit operation. + +

    +Anyroads, the post-instrumented version of our running example looks +like this: + +

    +Instrumented code:
    +           0: GETVL     %EDX, q0
    +           1: GETL      %EDX, t0
    +
    +           2: TAG1o     q0 = Left4 ( q0 )
    +           3: INCL      t0
    +
    +           4: PUTVL     q0, %EDX
    +           5: PUTL      t0, %EDX
    +
    +           6: TESTVL    q0
    +           7: SETVL     q0
    +           8: LOADVB    (t0), q0
    +           9: LDB       (t0), t0
    +
    +          10: TAG1o     q0 = SWiden14 ( q0 )
    +          11: WIDENL_Bs t0
    +
    +          12: PUTVL     q0, %EAX
    +          13: PUTL      t0, %EAX
    +
    +          14: GETVL     %ECX, q8
    +          15: GETL      %ECX, t8
    +
    +          16: MOVL      q0, q4
    +          17: SHLL      $0x1, q4
    +          18: TAG2o     q4 = UifU4 ( q8, q4 )
    +          19: TAG1o     q4 = Left4 ( q4 )
    +          20: LEA2L     1(t8,t0,2), t4
    +
    +          21: TESTVL    q4
    +          22: SETVL     q4
    +          23: LOADVB    (t4), q10
    +          24: LDB       (t4), t10
    +
    +          25: SETVB     q12
    +          26: MOVB      $0x20, t12
    +
    +          27: MOVL      q10, q14
    +          28: TAG2o     q14 = ImproveAND1_TQ ( t10, q14 )
    +          29: TAG2o     q10 = UifU1 ( q12, q10 )
    +          30: TAG2o     q10 = DifD1 ( q14, q10 )
    +          31: MOVL      q12, q14
    +          32: TAG2o     q14 = ImproveAND1_TQ ( t12, q14 )
    +          33: TAG2o     q10 = DifD1 ( q14, q10 )
    +          34: MOVL      q10, q16
    +          35: TAG1o     q16 = PCast10 ( q16 )
    +          36: PUTVFo    q16
    +          37: ANDB      t12, t10  (-wOSZACP)
    +
    +          38: INCEIPo   $9
    +
    +          39: GETVFo    q18
    +          40: TESTVo    q18
    +          41: SETVo     q18
    +          42: Jnzo      $0x40435A50  (-rOSZACP)
    +
    +          43: JMPo      $0x40435A5B
    +
    + + +

    UCode post-instrumentation cleanup

    + +

    +This pass, coordinated by vg_cleanup(), removes redundant +definedness computation created by the simplistic instrumentation +pass. It consists of two passes, +vg_propagate_definedness() followed by +vg_delete_redundant_SETVs. + +

    +vg_propagate_definedness() is a simple +constant-propagation and constant-folding pass. It tries to determine +which TempRegs containing V bits will always indicate +"fully defined", and it propagates this information as far as it can, +and folds out as many operations as possible. For example, the +instrumentation for an ADD of a literal to a variable quantity will be +reduced down so that the definedness of the result is simply the +definedness of the variable quantity, since the literal is by +definition fully defined. + +

    +vg_delete_redundant_SETVs removes SETVs on +shadow TempRegs for which the next action is a write. +I don't think there's anything else worth saying about this; it is +simple. Read the sources for details. + +

    +So the cleaned-up running example looks like this. As above, I have +inserted line breaks after every original (non-instrumentation) uinstr +to aid readability. As with straightforward ucode optimisation, the +results in this block are undramatic because it is so short; longer +blocks benefit more because they have more redundancy which gets +eliminated. + + +

    +at 29: delete UifU1 due to defd arg1
    +at 32: change ImproveAND1_TQ to MOV due to defd arg2
    +at 41: delete SETV
    +at 31: delete MOV
    +at 25: delete SETV
    +at 22: delete SETV
    +at 7: delete SETV
    +
    +           0: GETVL     %EDX, q0
    +           1: GETL      %EDX, t0
    +
    +           2: TAG1o     q0 = Left4 ( q0 )
    +           3: INCL      t0
    +
    +           4: PUTVL     q0, %EDX
    +           5: PUTL      t0, %EDX
    +
    +           6: TESTVL    q0
    +           8: LOADVB    (t0), q0
    +           9: LDB       (t0), t0
    +
    +          10: TAG1o     q0 = SWiden14 ( q0 )
    +          11: WIDENL_Bs t0
    +
    +          12: PUTVL     q0, %EAX
    +          13: PUTL      t0, %EAX
    +
    +          14: GETVL     %ECX, q8
    +          15: GETL      %ECX, t8
    +
    +          16: MOVL      q0, q4
    +          17: SHLL      $0x1, q4
    +          18: TAG2o     q4 = UifU4 ( q8, q4 )
    +          19: TAG1o     q4 = Left4 ( q4 )
    +          20: LEA2L     1(t8,t0,2), t4
    +
    +          21: TESTVL    q4
    +          23: LOADVB    (t4), q10
    +          24: LDB       (t4), t10
    +
    +          26: MOVB      $0x20, t12
    +
    +          27: MOVL      q10, q14
    +          28: TAG2o     q14 = ImproveAND1_TQ ( t10, q14 )
    +          30: TAG2o     q10 = DifD1 ( q14, q10 )
    +          32: MOVL      t12, q14
    +          33: TAG2o     q10 = DifD1 ( q14, q10 )
    +          34: MOVL      q10, q16
    +          35: TAG1o     q16 = PCast10 ( q16 )
    +          36: PUTVFo    q16
    +          37: ANDB      t12, t10  (-wOSZACP)
    +
    +          38: INCEIPo   $9
    +          39: GETVFo    q18
    +          40: TESTVo    q18
    +          42: Jnzo      $0x40435A50  (-rOSZACP)
    +
    +          43: JMPo      $0x40435A5B
    +
    + + +

    Translation from UCode

    + +This is all very simple, even though vg_from_ucode.c +is a big file. Position-independent x86 code is generated into +a dynamically allocated array emitted_code; this is +doubled in size when it overflows. Eventually the array is handed +back to the caller of VG_(translate), who must copy +the result into TC and TT, and free the array. + +

    +This file is structured into four layers of abstraction, which, +thankfully, are glued back together with extensive +__inline__ directives. From the bottom upwards: + +

      +
    • Address-mode emitters, emit_amode_regmem_reg et al. +

      +

    • Emitters for specific x86 instructions. There are quite a lot of + these, with names such as emit_movv_offregmem_reg. + The v suffix is Intel parlance for a 16/32 bit insn; + there are also b suffixes for 8 bit insns. +

      +

    • The next level up are the synth_* functions, which + synthesise possibly a sequence of raw x86 instructions to do some + simple task. Some of these are quite complex because they have to + work around Intel's silly restrictions on subregister naming. See + synth_nonshiftop_reg_reg for example. +

      +

    • Finally, at the top of the heap, we have + emitUInstr(), + which emits code for a single uinstr. +
    + +

    +Some comments: +

      +
    • The hack for FPU instructions becomes apparent here. To do a + FPU ucode instruction, we load the simulated FPU's + state into from its VG_(baseBlock) into the real FPU + using an x86 frstor insn, do the ucode + FPU insn on the real CPU, and write the updated FPU + state back into VG_(baseBlock) using an + fnsave instruction. This is pretty brutal, but is + simple and it works, and even seems tolerably efficient. There is + no attempt to cache the simulated FPU state in the real FPU over + multiple back-to-back ucode FPU instructions. +

      + FPU_R and FPU_W are also done this way, + with the minor complication that we need to patch in some + addressing mode bits so the resulting insn knows the effective + address to use. This is easy because of the regularity of the x86 + FPU instruction encodings. +

      +

    • An analogous trick is done with ucode insns which claim, in their + flags_r and flags_w fields, that they + read or write the simulated %EFLAGS. For such cases + we first copy the simulated %EFLAGS into the real + %eflags, then do the insn, then, if the insn says it + writes the flags, copy back to %EFLAGS. This is a + bit expensive, which is why the ucode optimisation pass goes to + some effort to remove redundant flag-update annotations. +
    + +

    +And so ... that's the end of the documentation for the instrumentating +translator! It's really not that complex, because it's composed as a +sequence of simple(ish) self-contained transformations on +straight-line blocks of code. + + +

    Top-level dispatch loop

    + +Urk. In VG_(toploop). This is basically boring and +unsurprising, not to mention fiddly and fragile. It needs to be +cleaned up. + +

    +The only perhaps surprise is that the whole thing is run +on top of a setjmp-installed exception handler, because, +supposing a translation got a segfault, we have to bail out of the +Valgrind-supplied exception handler VG_(oursignalhandler) +and immediately start running the client's segfault handler, if it has +one. In particular we can't finish the current basic block and then +deliver the signal at some convenient future point, because signals +like SIGILL, SIGSEGV and SIGBUS mean that the faulting insn should not +simply be re-tried. (I'm sure there is a clearer way to explain this). + + +

    Exceptions, creating new translations

    +

    Self-modifying code

    + +

    Lazy updates of the simulated program counter

    + +Simulated %EIP is not updated after every simulated x86 +insn as this was regarded as too expensive. Instead ucode +INCEIP insns move it along as and when necessary. +Currently we don't allow it to fall more than 4 bytes behind reality +(see VG_(disBB) for the way this works). +

    +Note that %EIP is always brought up to date by the inner +dispatch loop in VG_(dispatch), so that if the client +takes a fault we know at least which basic block this happened in. + + +

    The translation cache and translation table

    + +

    Signals

    + +Horrible, horrible. vg_signals.c. +Basically, since we have to intercept all system +calls anyway, we can see when the client tries to install a signal +handler. If it does so, we make a note of what the client asked to +happen, and ask the kernel to route the signal to our own signal +handler, VG_(oursignalhandler). This simply notes the +delivery of signals, and returns. + +

    +Every 1000 basic blocks, we see if more signals have arrived. If so, +VG_(deliver_signals) builds signal delivery frames on the +client's stack, and allows their handlers to be run. Valgrind places +in these signal delivery frames a bogus return address, +VG_(signalreturn_bogusRA), and checks all jumps to see +if any jump to it. If so, this is a sign that a signal handler is +returning, and if so Valgrind removes the relevant signal frame from +the client's stack, restores the from the signal frame the simulated +state before the signal was delivered, and allows the client to run +onwards. We have to do it this way because some signal handlers never +return, they just longjmp(), which nukes the signal +delivery frame. + +

    +The Linux kernel has a different but equally horrible hack for +detecting signal handler returns. Discovering it is left as an +exercise for the reader. + + + +

    Errors, error contexts, error reporting, suppressions

    +

    Client malloc/free

    +

    Low-level memory management

    +

    A and V bitmaps

    +

    Symbol table management

    +

    Dealing with system calls

    +

    Namespace management

    +

    GDB attaching

    +

    Non-dependence on glibc or anything else

    +

    The leak detector

    +

    Performance problems

    +

    Continuous sanity checking

    +

    Tracing, or not tracing, child processes

    +

    Assembly glue for syscalls

    + + +
    + +

    Extensions

    + +Some comments about Stuff To Do. + +

    Bugs

    + +Stephan Kulow and Marc Mutz report problems with kmail in KDE 3 CVS +(RC2 ish) when run on Valgrind. Stephan has it deadlocking; Marc has +it looping at startup. I can't repro either behaviour. Needs +repro-ing and fixing. + + +

    Threads

    + +Doing a good job of thread support strikes me as almost a +research-level problem. The central issues are how to do fast cheap +locking of the VG_(primary_map) structure, whether or not +accesses to the individual secondary maps need locking, what +race-condition issues result, and whether the already-nasty mess that +is the signal simulator needs further hackery. + +

    +I realise that threads are the most-frequently-requested feature, and +I am thinking about it all. If you have guru-level understanding of +fast mutual exclusion mechanisms and race conditions, I would be +interested in hearing from you. + + +

    Verification suite

    + +Directory tests/ contains various ad-hoc tests for +Valgrind. However, there is no systematic verification or regression +suite, that, for example, exercises all the stuff in +vg_memory.c, to ensure that illegal memory accesses and +undefined value uses are detected as they should be. It would be good +to have such a suite. + + +

    Porting to other platforms

    + +It would be great if Valgrind was ported to FreeBSD and x86 NetBSD, +and to x86 OpenBSD, if it's possible (doesn't OpenBSD use a.out-style +executables, not ELF ?) + +

    +The main difficulties, for an x86-ELF platform, seem to be: + +

      +
    • You'd need to rewrite the /proc/self/maps parser + (vg_procselfmaps.c). + Easy. +

      +

    • You'd need to rewrite vg_syscall_mem.c, or, more + specifically, provide one for your OS. This is tedious, but you + can implement syscalls on demand, and the Linux kernel interface + is, for the most part, going to look very similar to the *BSD + interfaces, so it's really a copy-paste-and-modify-on-demand job. + As part of this, you'd need to supply a new + vg_kerneliface.h file. +

      +

    • You'd also need to change the syscall wrappers for Valgrind's + internal use, in vg_mylibc.c. +
    + +All in all, I think a port to x86-ELF *BSDs is not really very +difficult, and in some ways I would like to see it happen, because +that would force a more clear factoring of Valgrind into platform +dependent and independent pieces. Not to mention, *BSD folks also +deserve to use Valgrind just as much as the Linux crew do. + + +

    +


    + +

    Easy stuff which ought to be done

    + +

    MMX instructions

    + +MMX insns should be supported, using the same trick as for FPU insns. +If the MMX registers are not used to copy uninitialised junk from one +place to another in memory, this means we don't have to actually +simulate the internal MMX unit state, so the FPU hack applies. This +should be fairly easy. + + + +

    Fix stabs-info reader

    + +The machinery in vg_symtab2.c which reads "stabs" style +debugging info is pretty weak. It usually correctly translates +simulated program counter values into line numbers and procedure +names, but the file name is often completely wrong. I think the +logic used to parse "stabs" entries is weak. It should be fixed. +The simplest solution, IMO, is to copy either the logic or simply the +code out of GNU binutils which does this; since GDB can clearly get it +right, binutils (or GDB?) must have code to do this somewhere. + + + + + +

    BT/BTC/BTS/BTR

    + +These are x86 instructions which test, complement, set, or reset, a +single bit in a word. At the moment they are both incorrectly +implemented and incorrectly instrumented. + +

    +The incorrect instrumentation is due to use of helper functions. This +means we lose bit-level definedness tracking, which could wind up +giving spurious uninitialised-value use errors. The Right Thing to do +is to invent a couple of new UOpcodes, I think GET_BIT +and SET_BIT, which can be used to implement all 4 x86 +insns, get rid of the helpers, and give bit-accurate instrumentation +rules for the two new UOpcodes. + +

    +I realised the other day that they are mis-implemented too. The x86 +insns take a bit-index and a register or memory location to access. +For registers the bit index clearly can only be in the range zero to +register-width minus 1, and I assumed the same applied to memory +locations too. But evidently not; for memory locations the index can +be arbitrary, and the processor will index arbitrarily into memory as +a result. This too should be fixed. Sigh. Presumably indexing +outside the immediate word is not actually used by any programs yet +tested on Valgrind, for otherwise they (presumably) would simply not +work at all. If you plan to hack on this, first check the Intel docs +to make sure my understanding is really correct. + + + +

    Using PREFETCH instructions

    + +Here's a small but potentially interesting project for performance +junkies. Experiments with valgrind's code generator and optimiser(s) +suggest that reducing the number of instructions executed in the +translations and mem-check helpers gives disappointingly small +performance improvements. Perhaps this is because performance of +Valgrindified code is limited by cache misses. After all, each read +in the original program now gives rise to at least three reads, one +for the VG_(primary_map), one of the resulting +secondary, and the original. Not to mention, the instrumented +translations are 13 to 14 times larger than the originals. All in all +one would expect the memory system to be hammered to hell and then +some. + +

    +So here's an idea. An x86 insn involving a read from memory, after +instrumentation, will turn into ucode of the following form: +

    +    ... calculate effective addr, into ta and qa ...
    +    TESTVL qa             -- is the addr defined?
    +    LOADV (ta), qloaded   -- fetch V bits for the addr
    +    LOAD  (ta), tloaded   -- do the original load
    +
    +At the point where the LOADV is done, we know the actual +address (ta) from which the real LOAD will +be done. We also know that the LOADV will take around +20 x86 insns to do. So it seems plausible that doing a prefetch of +ta just before the LOADV might just avoid a +miss at the LOAD point, and that might be a significant +performance win. + +

    +Prefetch insns are notoriously tempermental, more often than not +making things worse rather than better, so this would require +considerable fiddling around. It's complicated because Intels and +AMDs have different prefetch insns with different semantics, so that +too needs to be taken into account. As a general rule, even placing +the prefetches before the LOADV insn is too near the +LOAD; the ideal distance is apparently circa 200 CPU +cycles. So it might be worth having another analysis/transformation +pass which pushes prefetches as far back as possible, hopefully +immediately after the effective address becomes available. + +

    +Doing too many prefetches is also bad because they soak up bus +bandwidth / cpu resources, so some cleverness in deciding which loads +to prefetch and which to not might be helpful. One can imagine not +prefetching client-stack-relative (%EBP or +%ESP) accesses, since the stack in general tends to show +good locality anyway. + +

    +There's quite a lot of experimentation to do here, but I think it +might make an interesting week's work for someone. + +

    +As of 15-ish March 2002, I've started to experiment with this, using +the AMD prefetch/prefetchw insns. + + + +

    User-defined permission ranges

    + +This is quite a large project -- perhaps a month's hacking for a +capable hacker to do a good job -- but it's potentially very +interesting. The outcome would be that Valgrind could detect a +whole class of bugs which it currently cannot. + +

    +The presentation falls into two pieces. + +

    +Part 1: user-defined address-range permission setting +

    + +Valgrind intercepts the client's malloc, +free, etc calls, watches system calls, and watches the +stack pointer move. This is currently the only way it knows about +which addresses are valid and which not. Sometimes the client program +knows extra information about its memory areas. For example, the +client could at some point know that all elements of an array are +out-of-date. We would like to be able to convey to Valgrind this +information that the array is now addressable-but-uninitialised, so +that Valgrind can then warn if elements are used before they get new +values. + +

    +What I would like are some macros like this: +

    +   VALGRIND_MAKE_NOACCESS(addr, len)
    +   VALGRIND_MAKE_WRITABLE(addr, len)
    +   VALGRIND_MAKE_READABLE(addr, len)
    +
    +and also, to check that memory is addressible/initialised, +
    +   VALGRIND_CHECK_ADDRESSIBLE(addr, len)
    +   VALGRIND_CHECK_INITIALISED(addr, len)
    +
    + +

    +I then include in my sources a header defining these macros, rebuild +my app, run under Valgrind, and get user-defined checks. + +

    +Now here's a neat trick. It's a nuisance to have to re-link the app +with some new library which implements the above macros. So the idea +is to define the macros so that the resulting executable is still +completely stand-alone, and can be run without Valgrind, in which case +the macros do nothing, but when run on Valgrind, the Right Thing +happens. How to do this? The idea is for these macros to turn into a +piece of inline assembly code, which (1) has no effect when run on the +real CPU, (2) is easily spotted by Valgrind's JITter, and (3) no sane +person would ever write, which is important for avoiding false matches +in (2). So here's a suggestion: +

    +   VALGRIND_MAKE_NOACCESS(addr, len)
    +
    +becomes (roughly speaking) +
    +   movl addr, %eax
    +   movl len,  %ebx
    +   movl $1,   %ecx   -- 1 describes the action; MAKE_WRITABLE might be
    +                     -- 2, etc
    +   rorl $13, %ecx
    +   rorl $19, %ecx
    +   rorl $11, %eax
    +   rorl $21, %eax
    +
    +The rotate sequences have no effect, and it's unlikely they would +appear for any other reason, but they define a unique byte-sequence +which the JITter can easily spot. Using the operand constraints +section at the end of a gcc inline-assembly statement, we can tell gcc +that the assembly fragment kills %eax, %ebx, +%ecx and the condition codes, so this fragment is made +harmless when not running on Valgrind, runs quickly when not on +Valgrind, and does not require any other library support. + + +

    +Part 2: using it to detect interference between stack variables +

    + +Currently Valgrind cannot detect errors of the following form: +

    +void fooble ( void )
    +{
    +   int a[10];
    +   int b[10];
    +   a[10] = 99;
    +}
    +
    +Now imagine rewriting this as +
    +void fooble ( void )
    +{
    +   int spacer0;
    +   int a[10];
    +   int spacer1;
    +   int b[10];
    +   int spacer2;
    +   VALGRIND_MAKE_NOACCESS(&spacer0, sizeof(int));
    +   VALGRIND_MAKE_NOACCESS(&spacer1, sizeof(int));
    +   VALGRIND_MAKE_NOACCESS(&spacer2, sizeof(int));
    +   a[10] = 99;
    +}
    +
    +Now the invalid write is certain to hit spacer0 or +spacer1, so Valgrind will spot the error. + +

    +There are two complications. + +

    +The first is that we don't want to annotate sources by hand, so the +Right Thing to do is to write a C/C++ parser, annotator, prettyprinter +which does this automatically, and run it on post-CPP'd C/C++ source. +See http://www.cacheprof.org for an example of a system which +transparently inserts another phase into the gcc/g++ compilation +route. The parser/prettyprinter is probably not as hard as it sounds; +I would write it in Haskell, a powerful functional language well +suited to doing symbolic computation, with which I am intimately +familar. There is already a C parser written in Haskell by someone in +the Haskell community, and that would probably be a good starting +point. + +

    +The second complication is how to get rid of these +NOACCESS records inside Valgrind when the instrumented +function exits; after all, these refer to stack addresses and will +make no sense whatever when some other function happens to re-use the +same stack address range, probably shortly afterwards. I think I +would be inclined to define a special stack-specific macro +

    +   VALGRIND_MAKE_NOACCESS_STACK(addr, len)
    +
    +which causes Valgrind to record the client's %ESP at the +time it is executed. Valgrind will then watch for changes in +%ESP and discard such records as soon as the protected +area is uncovered by an increase in %ESP. I hesitate +with this scheme only because it is potentially expensive, if there +are hundreds of such records, and considering that changes in +%ESP already require expensive messing with stack access +permissions. + +

    +This is probably easier and more robust than for the instrumenter +program to try and spot all exit points for the procedure and place +suitable deallocation annotations there. Plus C++ procedures can +bomb out at any point if they get an exception, so spotting return +points at the source level just won't work at all. + +

    +Although some work, it's all eminently doable, and it would make +Valgrind into an even-more-useful tool. + + +

    + + + diff --git a/VEX/head20041019/memcheck/mac_leakcheck.c b/VEX/head20041019/memcheck/mac_leakcheck.c new file mode 100644 index 0000000000..23c9e45155 --- /dev/null +++ b/VEX/head20041019/memcheck/mac_leakcheck.c @@ -0,0 +1,555 @@ + +/*--------------------------------------------------------------------*/ +/*--- The leak checker, shared between Memcheck and Addrcheck. ---*/ +/*--- mac_leakcheck.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors, and AddrCheck, a lightweight Valgrind tool + for detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mac_shared.h" + +/* Define to debug the memory-leak-detector. */ +/* #define VG_DEBUG_LEAKCHECK */ + +/*------------------------------------------------------------*/ +/*--- Low-level address-space scanning, for the leak ---*/ +/*--- detector. ---*/ +/*------------------------------------------------------------*/ + +static +jmp_buf memscan_jmpbuf; + + +static +void vg_scan_all_valid_memory_sighandler ( Int sigNo ) +{ + __builtin_longjmp(memscan_jmpbuf, 1); +} + + +/* Safely (avoiding SIGSEGV / SIGBUS) scan the entire valid address + space and pass the addresses and values of all addressible, + defined, aligned words to notify_word. This is the basis for the + leak detector. Returns the number of calls made to notify_word. + + Addresses are validated 3 ways. First we enquire whether (addr >> + 16) denotes a 64k chunk in use, by asking is_valid_64k_chunk(). If + so, we decide for ourselves whether each x86-level (4 K) page in + the chunk is safe to inspect. If yes, we enquire with + is_valid_address() whether or not each of the 1024 word-locations + on the page is valid. Only if so are that address and its contents + passed to notify_word. + + This is all to avoid duplication of this machinery between + Memcheck and Addrcheck. +*/ +static +UInt vg_scan_all_valid_memory ( Bool is_valid_64k_chunk ( UInt ), + Bool is_valid_address ( Addr ), + void (*notify_word)( Addr, UInt ) ) +{ + /* All volatile, because some gccs seem paranoid about longjmp(). */ + volatile Bool anyValid; + volatile Addr pageBase, addr; + volatile UInt res, numPages, page, primaryMapNo; + volatile UInt page_first_word, nWordsNotified; + + vki_ksigaction sigbus_saved; + vki_ksigaction sigbus_new; + vki_ksigaction sigsegv_saved; + vki_ksigaction sigsegv_new; + vki_ksigset_t blockmask_saved; + vki_ksigset_t unblockmask_new; + + /* Temporarily install a new sigsegv and sigbus handler, and make + sure SIGBUS, SIGSEGV and SIGTERM are unblocked. (Perhaps the + first two can never be blocked anyway?) */ + + sigbus_new.ksa_handler = vg_scan_all_valid_memory_sighandler; + sigbus_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART; + sigbus_new.ksa_restorer = NULL; + res = VG_(ksigemptyset)( &sigbus_new.ksa_mask ); + sk_assert(res == 0); + + sigsegv_new.ksa_handler = vg_scan_all_valid_memory_sighandler; + sigsegv_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART; + sigsegv_new.ksa_restorer = NULL; + res = VG_(ksigemptyset)( &sigsegv_new.ksa_mask ); + sk_assert(res == 0+0); + + res = VG_(ksigemptyset)( &unblockmask_new ); + res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGBUS ); + res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGSEGV ); + res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGTERM ); + sk_assert(res == 0+0+0); + + res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_new, &sigbus_saved ); + sk_assert(res == 0+0+0+0); + + res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_new, &sigsegv_saved ); + sk_assert(res == 0+0+0+0+0); + + res = VG_(ksigprocmask)( VKI_SIG_UNBLOCK, &unblockmask_new, &blockmask_saved ); + sk_assert(res == 0+0+0+0+0+0); + + /* The signal handlers are installed. Actually do the memory scan. */ + numPages = 1 << (32-VKI_BYTES_PER_PAGE_BITS); + sk_assert(numPages == 1048576); + sk_assert(4096 == (1 << VKI_BYTES_PER_PAGE_BITS)); + + nWordsNotified = 0; + + for (page = 0; page < numPages; page++) { + + /* Base address of this 4k page. */ + pageBase = page << VKI_BYTES_PER_PAGE_BITS; + + /* Skip if this page is in an unused 64k chunk. */ + primaryMapNo = pageBase >> 16; + if (!is_valid_64k_chunk(primaryMapNo)) + continue; + + /* Next, establish whether or not we want to consider any + locations on this page. We need to do so before actually + prodding it, because prodding it when in fact it is not + needed can cause a page fault which under some rare + circumstances can cause the kernel to extend the stack + segment all the way down to here, which is seriously bad. + Hence: */ + anyValid = False; + for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) { + if (is_valid_address(addr)) { + anyValid = True; + break; + } + } + + if (!anyValid) + continue; /* nothing interesting here .. move to the next page */ + + /* Ok, we have to prod cautiously at the page and see if it + explodes or not. */ + if (__builtin_setjmp(memscan_jmpbuf) == 0) { + /* try this ... */ + page_first_word = * (volatile UInt*)pageBase; + /* we get here if we didn't get a fault */ + /* Scan the page */ + for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) { + if (is_valid_address(addr)) { + nWordsNotified++; + notify_word ( addr, *(UInt*)addr ); + } + } + } else { + /* We get here if reading the first word of the page caused a + fault, which in turn caused the signal handler to longjmp. + Ignore this page. */ + if (0) + VG_(printf)( + "vg_scan_all_valid_memory_sighandler: ignoring page at %p\n", + (void*)pageBase + ); + } + } + + /* Restore signal state to whatever it was before. */ + res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_saved, NULL ); + sk_assert(res == 0 +0); + + res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_saved, NULL ); + sk_assert(res == 0 +0 +0); + + res = VG_(ksigprocmask)( VKI_SIG_SETMASK, &blockmask_saved, NULL ); + sk_assert(res == 0 +0 +0 +0); + + return nWordsNotified; +} + +/*------------------------------------------------------------*/ +/*--- Detecting leaked (unreachable) malloc'd blocks. ---*/ +/*------------------------------------------------------------*/ + +/* A block is either + -- Proper-ly reached; a pointer to its start has been found + -- Interior-ly reached; only an interior pointer to it has been found + -- Unreached; so far, no pointers to any part of it have been found. +*/ +typedef + enum { Unreached, Interior, Proper } + Reachedness; + +/* A block record, used for generating err msgs. */ +typedef + struct _LossRecord { + struct _LossRecord* next; + /* Where these lost blocks were allocated. */ + ExeContext* allocated_at; + /* Their reachability. */ + Reachedness loss_mode; + /* Number of blocks and total # bytes involved. */ + UInt total_bytes; + UInt num_blocks; + } + LossRecord; + + +/* Find the i such that ptr points at or inside the block described by + shadows[i]. Return -1 if none found. This assumes that shadows[] + has been sorted on the ->data field. */ + +#ifdef VG_DEBUG_LEAKCHECK +/* Used to sanity-check the fast binary-search mechanism. */ +static +Int find_shadow_for_OLD ( Addr ptr, + MAC_Chunk** shadows, + Int n_shadows ) + +{ + Int i; + Addr a_lo, a_hi; + PROF_EVENT(70); + for (i = 0; i < n_shadows; i++) { + PROF_EVENT(71); + a_lo = shadows[i]->data; + a_hi = ((Addr)shadows[i]->data) + shadows[i]->size - 1; + if (a_lo <= ptr && ptr <= a_hi) + return i; + } + return -1; +} +#endif + + +static +Int find_shadow_for ( Addr ptr, + MAC_Chunk** shadows, + Int n_shadows ) +{ + Addr a_mid_lo, a_mid_hi; + Int lo, mid, hi, retVal; + /* VG_(printf)("find shadow for %p = ", ptr); */ + retVal = -1; + lo = 0; + hi = n_shadows-1; + while (True) { + /* invariant: current unsearched space is from lo to hi, inclusive. */ + if (lo > hi) break; /* not found */ + + mid = (lo + hi) / 2; + a_mid_lo = shadows[mid]->data; + a_mid_hi = shadows[mid]->data + shadows[mid]->size - 1; + + if (ptr < a_mid_lo) { + hi = mid-1; + continue; + } + if (ptr > a_mid_hi) { + lo = mid+1; + continue; + } + sk_assert(ptr >= a_mid_lo && ptr <= a_mid_hi); + retVal = mid; + break; + } + +# ifdef VG_DEBUG_LEAKCHECK + sk_assert(retVal == find_shadow_for_OLD ( ptr, shadows, n_shadows )); +# endif + /* VG_(printf)("%d\n", retVal); */ + return retVal; +} + +/* Globals, for the following callback used by VG_(detect_memory_leaks). */ +static MAC_Chunk** lc_shadows; +static Int lc_n_shadows; +static Reachedness* lc_reachedness; +static Addr lc_min_mallocd_addr; +static Addr lc_max_mallocd_addr; + +static +void vg_detect_memory_leaks_notify_addr ( Addr a, UInt word_at_a ) +{ + Int sh_no; + Addr ptr; + + /* Rule out some known causes of bogus pointers. Mostly these do + not cause much trouble because only a few false pointers can + ever lurk in these places. This mainly stops it reporting that + blocks are still reachable in stupid test programs like this + + int main (void) { char* a = malloc(100); return 0; } + + which people seem inordinately fond of writing, for some reason. + + Note that this is a complete kludge. It would be better to + ignore any addresses corresponding to valgrind.so's .bss and + .data segments, but I cannot think of a reliable way to identify + where the .bss segment has been put. If you can, drop me a + line. + */ + if (!VG_(is_client_addr)(a)) return; + + /* OK, let's get on and do something Useful for a change. */ + + ptr = (Addr)word_at_a; + if (ptr >= lc_min_mallocd_addr && ptr <= lc_max_mallocd_addr) { + /* Might be legitimate; we'll have to investigate further. */ + sh_no = find_shadow_for ( ptr, lc_shadows, lc_n_shadows ); + if (sh_no != -1) { + /* Found a block at/into which ptr points. */ + sk_assert(sh_no >= 0 && sh_no < lc_n_shadows); + sk_assert(ptr < lc_shadows[sh_no]->data + lc_shadows[sh_no]->size); + /* Decide whether Proper-ly or Interior-ly reached. */ + if (ptr == lc_shadows[sh_no]->data) { + if (0) VG_(printf)("pointer at %p to %p\n", a, word_at_a ); + lc_reachedness[sh_no] = Proper; + } else { + if (lc_reachedness[sh_no] == Unreached) + lc_reachedness[sh_no] = Interior; + } + } + } +} + +/* Used for printing leak errors, avoids exposing the LossRecord type (which + comes in as void*, requiring a cast. */ +void MAC_(pp_LeakError)(void* vl, UInt n_this_record, UInt n_total_records) +{ + LossRecord* l = (LossRecord*)vl; + + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, + "%d bytes in %d blocks are %s in loss record %d of %d", + l->total_bytes, l->num_blocks, + l->loss_mode==Unreached ? "definitely lost" + : (l->loss_mode==Interior ? "possibly lost" + : "still reachable"), + n_this_record, n_total_records + ); + VG_(pp_ExeContext)(l->allocated_at); +} + +Int MAC_(bytes_leaked) = 0; +Int MAC_(bytes_dubious) = 0; +Int MAC_(bytes_reachable) = 0; +Int MAC_(bytes_suppressed) = 0; + +static Int lc_compar(void* n1, void* n2) +{ + MAC_Chunk* mc1 = *(MAC_Chunk**)n1; + MAC_Chunk* mc2 = *(MAC_Chunk**)n2; + return (mc1->data < mc2->data ? -1 : 1); +} + +/* Top level entry point to leak detector. Call here, passing in + suitable address-validating functions (see comment at top of + vg_scan_all_valid_memory above). All this is to avoid duplication + of the leak-detection code for Memcheck and Addrcheck. + Also pass in a tool-specific function to extract the .where field + for allocated blocks, an indication of the resolution wanted for + distinguishing different allocation points, and whether or not + reachable blocks should be shown. +*/ +void MAC_(do_detect_memory_leaks) ( + Bool is_valid_64k_chunk ( UInt ), + Bool is_valid_address ( Addr ) +) +{ + Int i; + Int blocks_leaked; + Int blocks_dubious; + Int blocks_reachable; + Int blocks_suppressed; + Int n_lossrecords; + UInt bytes_notified; + Bool is_suppressed; + + LossRecord* errlist; + LossRecord* p; + + /* VG_(HT_to_array) allocates storage for shadows */ + lc_shadows = (MAC_Chunk**)VG_(HT_to_array)( MAC_(malloc_list), + &lc_n_shadows ); + + /* Sort the array. */ + VG_(ssort)((void*)lc_shadows, lc_n_shadows, sizeof(VgHashNode*), lc_compar); + + /* Sanity check; assert that the blocks are now in order */ + for (i = 0; i < lc_n_shadows-1; i++) { + sk_assert( lc_shadows[i]->data <= lc_shadows[i+1]->data); + } + + /* Sanity check -- make sure they don't overlap */ + for (i = 0; i < lc_n_shadows-1; i++) { + sk_assert( lc_shadows[i]->data + lc_shadows[i]->size + < lc_shadows[i+1]->data ); + } + + if (lc_n_shadows == 0) { + sk_assert(lc_shadows == NULL); + if (VG_(clo_verbosity) >= 1) { + VG_(message)(Vg_UserMsg, + "No malloc'd blocks -- no leaks are possible."); + } + return; + } + + if (VG_(clo_verbosity) > 0) + VG_(message)(Vg_UserMsg, + "searching for pointers to %d not-freed blocks.", + lc_n_shadows ); + + lc_min_mallocd_addr = lc_shadows[0]->data; + lc_max_mallocd_addr = lc_shadows[lc_n_shadows-1]->data + + lc_shadows[lc_n_shadows-1]->size - 1; + + lc_reachedness = VG_(malloc)( lc_n_shadows * sizeof(Reachedness) ); + for (i = 0; i < lc_n_shadows; i++) + lc_reachedness[i] = Unreached; + + /* Do the scan of memory. */ + bytes_notified + = VKI_BYTES_PER_WORD + * vg_scan_all_valid_memory ( + is_valid_64k_chunk, + is_valid_address, + &vg_detect_memory_leaks_notify_addr + ); + + if (VG_(clo_verbosity) > 0) + VG_(message)(Vg_UserMsg, "checked %d bytes.", bytes_notified); + + /* Common up the lost blocks so we can print sensible error messages. */ + n_lossrecords = 0; + errlist = NULL; + for (i = 0; i < lc_n_shadows; i++) { + + ExeContext* where = lc_shadows[i]->where; + + for (p = errlist; p != NULL; p = p->next) { + if (p->loss_mode == lc_reachedness[i] + && VG_(eq_ExeContext) ( MAC_(clo_leak_resolution), + p->allocated_at, + where) ) { + break; + } + } + if (p != NULL) { + p->num_blocks ++; + p->total_bytes += lc_shadows[i]->size; + } else { + n_lossrecords ++; + p = VG_(malloc)(sizeof(LossRecord)); + p->loss_mode = lc_reachedness[i]; + p->allocated_at = where; + p->total_bytes = lc_shadows[i]->size; + p->num_blocks = 1; + p->next = errlist; + errlist = p; + } + } + + /* Print out the commoned-up blocks and collect summary stats. */ + blocks_leaked = MAC_(bytes_leaked) = 0; + blocks_dubious = MAC_(bytes_dubious) = 0; + blocks_reachable = MAC_(bytes_reachable) = 0; + blocks_suppressed = MAC_(bytes_suppressed) = 0; + + for (i = 0; i < n_lossrecords; i++) { + Bool print_record; + LossRecord* p_min = NULL; + UInt n_min = 0xFFFFFFFF; + for (p = errlist; p != NULL; p = p->next) { + if (p->num_blocks > 0 && p->total_bytes < n_min) { + n_min = p->total_bytes; + p_min = p; + } + } + sk_assert(p_min != NULL); + + /* Ok to have tst==NULL; it's only used if --gdb-attach=yes, and + we disallow that when --leak-check=yes. + + Prints the error if not suppressed, unless it's reachable (Proper) + and --show-reachable=no */ + + print_record = ( MAC_(clo_show_reachable) || Proper != p_min->loss_mode ); + is_suppressed = + VG_(unique_error) ( VG_(get_current_tid)(), LeakErr, (UInt)i+1, + (Char*)n_lossrecords, (void*) p_min, + p_min->allocated_at, print_record, + /*allow_GDB_attach*/False, /*count_error*/False ); + + if (is_suppressed) { + blocks_suppressed += p_min->num_blocks; + MAC_(bytes_suppressed) += p_min->total_bytes; + + } else if (Unreached == p_min->loss_mode) { + blocks_leaked += p_min->num_blocks; + MAC_(bytes_leaked) += p_min->total_bytes; + + } else if (Interior == p_min->loss_mode) { + blocks_dubious += p_min->num_blocks; + MAC_(bytes_dubious) += p_min->total_bytes; + + } else if (Proper == p_min->loss_mode) { + blocks_reachable += p_min->num_blocks; + MAC_(bytes_reachable) += p_min->total_bytes; + + } else { + VG_(skin_panic)("generic_detect_memory_leaks: unknown loss mode"); + } + p_min->num_blocks = 0; + } + + if (VG_(clo_verbosity) > 0) { + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, "LEAK SUMMARY:"); + VG_(message)(Vg_UserMsg, " definitely lost: %d bytes in %d blocks.", + MAC_(bytes_leaked), blocks_leaked ); + VG_(message)(Vg_UserMsg, " possibly lost: %d bytes in %d blocks.", + MAC_(bytes_dubious), blocks_dubious ); + VG_(message)(Vg_UserMsg, " still reachable: %d bytes in %d blocks.", + MAC_(bytes_reachable), blocks_reachable ); + VG_(message)(Vg_UserMsg, " suppressed: %d bytes in %d blocks.", + MAC_(bytes_suppressed), blocks_suppressed ); + if (!MAC_(clo_show_reachable)) { + VG_(message)(Vg_UserMsg, + "Reachable blocks (those to which a pointer was found) are not shown."); + VG_(message)(Vg_UserMsg, + "To see them, rerun with: --show-reachable=yes"); + } + } + + VG_(free) ( lc_shadows ); + VG_(free) ( lc_reachedness ); +} + +/*--------------------------------------------------------------------*/ +/*--- end mac_leakcheck.c ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/VEX/head20041019/memcheck/mac_malloc_wrappers.c b/VEX/head20041019/memcheck/mac_malloc_wrappers.c new file mode 100644 index 0000000000..8ac92ac9ff --- /dev/null +++ b/VEX/head20041019/memcheck/mac_malloc_wrappers.c @@ -0,0 +1,557 @@ + +/*--------------------------------------------------------------------*/ +/*--- malloc/free wrappers for detecting errors and updating bits. ---*/ +/*--- mac_malloc_wrappers.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors, and AddrCheck, a lightweight Valgrind tool + for detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mac_shared.h" + +/*------------------------------------------------------------*/ +/*--- Defns ---*/ +/*------------------------------------------------------------*/ + +/* Stats ... */ +static UInt cmalloc_n_mallocs = 0; +static UInt cmalloc_n_frees = 0; +static UInt cmalloc_bs_mallocd = 0; + +/* We want a 16B redzone on heap blocks for Addrcheck and Memcheck */ +UInt VG_(vg_malloc_redzone_szB) = 16; + +/* Function pointers for the two tools to track interesting events. */ +void (*MAC_(new_mem_heap)) ( Addr a, UInt len, Bool is_inited ) = NULL; +void (*MAC_(ban_mem_heap)) ( Addr a, UInt len ) = NULL; +void (*MAC_(die_mem_heap)) ( Addr a, UInt len ) = NULL; +void (*MAC_(copy_mem_heap))( Addr from, Addr to, UInt len ) = NULL; + +/* Function pointers for internal sanity checking. */ +Bool (*MAC_(check_noaccess))( Addr a, UInt len, Addr* bad_addr ) = NULL; + + +/*------------------------------------------------------------*/ +/*--- Tracking malloc'd and free'd blocks ---*/ +/*------------------------------------------------------------*/ + +/* Record malloc'd blocks. Nb: Addrcheck and Memcheck construct this + separately in their respective initialisation functions. */ +VgHashTable MAC_(malloc_list) = NULL; + +/* Memory pools. Nb: Addrcheck and Memcheck construct this separately + in their respective initialisation functions. */ +VgHashTable MAC_(mempool_list) = NULL; + +/* Records blocks after freeing. */ +static MAC_Chunk* freed_list_start = NULL; +static MAC_Chunk* freed_list_end = NULL; +static Int freed_list_volume = 0; + +/* Put a shadow chunk on the freed blocks queue, possibly freeing up + some of the oldest blocks in the queue at the same time. */ +static void add_to_freed_queue ( MAC_Chunk* mc ) +{ + MAC_Chunk* sc1; + + /* Put it at the end of the freed list */ + if (freed_list_end == NULL) { + sk_assert(freed_list_start == NULL); + freed_list_end = freed_list_start = mc; + freed_list_volume = mc->size; + } else { + sk_assert(freed_list_end->next == NULL); + freed_list_end->next = mc; + freed_list_end = mc; + freed_list_volume += mc->size; + } + mc->next = NULL; + + /* Release enough of the oldest blocks to bring the free queue + volume below vg_clo_freelist_vol. */ + + while (freed_list_volume > MAC_(clo_freelist_vol)) { + sk_assert(freed_list_start != NULL); + sk_assert(freed_list_end != NULL); + + sc1 = freed_list_start; + freed_list_volume -= sc1->size; + /* VG_(printf)("volume now %d\n", freed_list_volume); */ + sk_assert(freed_list_volume >= 0); + + if (freed_list_start == freed_list_end) { + freed_list_start = freed_list_end = NULL; + } else { + freed_list_start = sc1->next; + } + sc1->next = NULL; /* just paranoia */ + + /* free MAC_Chunk */ + VG_(cli_free) ( (void*)(sc1->data) ); + VG_(free) ( sc1 ); + } +} + +/* Return the first shadow chunk satisfying the predicate p. */ +MAC_Chunk* MAC_(first_matching_freed_MAC_Chunk) ( Bool (*p)(MAC_Chunk*, void*), + void* d ) +{ + MAC_Chunk* mc; + + /* No point looking through freed blocks if we're not keeping + them around for a while... */ + for (mc = freed_list_start; mc != NULL; mc = mc->next) + if (p(mc, d)) + return mc; + + return NULL; +} + +/* Allocate its shadow chunk, put it on the appropriate list. */ +static +void add_MAC_Chunk ( Addr p, UInt size, MAC_AllocKind kind, VgHashTable table) +{ + MAC_Chunk* mc; + + mc = VG_(malloc)(sizeof(MAC_Chunk)); + mc->data = p; + mc->size = size; + mc->allockind = kind; + mc->where = VG_(get_ExeContext)(VG_(get_current_or_recent_tid)()); + + /* Paranoia ... ensure this area is off-limits to the client, so + the mc->data field isn't visible to the leak checker. If memory + management is working correctly, anything pointer returned by + VG_(malloc) should be noaccess as far as the client is + concerned. */ + if (!MAC_(check_noaccess)( (Addr)mc, sizeof(MAC_Chunk), NULL )) { + VG_(skin_panic)("add_MAC_chunk: shadow area is accessible"); + } + + VG_(HT_add_node)( table, (VgHashNode*)mc ); +} + +/*------------------------------------------------------------*/ +/*--- client_malloc(), etc ---*/ +/*------------------------------------------------------------*/ + +/* Allocate memory and note change in memory available */ +__inline__ +void* MAC_(new_block) ( Addr p, UInt size, UInt align, UInt rzB, + Bool is_zeroed, MAC_AllocKind kind, VgHashTable table) +{ + VGP_PUSHCC(VgpCliMalloc); + cmalloc_n_mallocs ++; + cmalloc_bs_mallocd += size; + + // Allocate and zero if necessary + if (p) { + sk_assert(MAC_AllocCustom == kind); + } else { + sk_assert(MAC_AllocCustom != kind); + p = (Addr)VG_(cli_malloc)( align, size ); + if (!p) { + VGP_POPCC(VgpCliMalloc); + return NULL; + } + if (is_zeroed) VG_(memset)((void*)p, 0, size); + } + + add_MAC_Chunk( p, size, kind, table ); + + MAC_(ban_mem_heap)( p-rzB, rzB ); + MAC_(new_mem_heap)( p, size, is_zeroed ); + MAC_(ban_mem_heap)( p+size, rzB ); + + VGP_POPCC(VgpCliMalloc); + + return (void*)p; +} + +void* SK_(malloc) ( Int n ) +{ + if (n < 0) { + VG_(message)(Vg_UserMsg, "Warning: silly arg (%d) to malloc()", n ); + return NULL; + } else { + return MAC_(new_block) ( 0, n, VG_(clo_alignment), + VG_(vg_malloc_redzone_szB), /*is_zeroed*/False, MAC_AllocMalloc, + MAC_(malloc_list)); + } +} + +void* SK_(__builtin_new) ( Int n ) +{ + if (n < 0) { + VG_(message)(Vg_UserMsg, "Warning: silly arg (%d) to __builtin_new()", n); + return NULL; + } else { + return MAC_(new_block) ( 0, n, VG_(clo_alignment), + VG_(vg_malloc_redzone_szB), /*is_zeroed*/False, MAC_AllocNew, + MAC_(malloc_list)); + } +} + +void* SK_(__builtin_vec_new) ( Int n ) +{ + if (n < 0) { + VG_(message)(Vg_UserMsg, + "Warning: silly arg (%d) to __builtin_vec_new()", n ); + return NULL; + } else { + return MAC_(new_block) ( 0, n, VG_(clo_alignment), + VG_(vg_malloc_redzone_szB), /*is_zeroed*/False, MAC_AllocNewVec, + MAC_(malloc_list)); + } +} + +void* SK_(memalign) ( Int align, Int n ) +{ + if (n < 0) { + VG_(message)(Vg_UserMsg, "Warning: silly arg (%d) to memalign()", n); + return NULL; + } else { + return MAC_(new_block) ( 0, n, align, + VG_(vg_malloc_redzone_szB), /*is_zeroed*/False, MAC_AllocMalloc, + MAC_(malloc_list)); + } +} + +void* SK_(calloc) ( Int nmemb, Int size1 ) +{ + if (nmemb < 0 || size1 < 0) { + VG_(message)(Vg_UserMsg, "Warning: silly args (%d,%d) to calloc()", + nmemb, size1 ); + return NULL; + } else { + return MAC_(new_block) ( 0, nmemb*size1, VG_(clo_alignment), + VG_(vg_malloc_redzone_szB), /*is_zeroed*/True, MAC_AllocMalloc, + MAC_(malloc_list)); + } +} + +static +void die_and_free_mem ( MAC_Chunk* mc, + MAC_Chunk** prev_chunks_next_ptr, UInt rzB ) +{ + /* Note: ban redzones again -- just in case user de-banned them + with a client request... */ + MAC_(ban_mem_heap)( mc->data-rzB, rzB ); + MAC_(die_mem_heap)( mc->data, mc->size ); + MAC_(ban_mem_heap)( mc->data+mc->size, rzB ); + + /* Remove mc from the malloclist using prev_chunks_next_ptr to + avoid repeating the hash table lookup. Can't remove until at least + after free and free_mismatch errors are done because they use + describe_addr() which looks for it in malloclist. */ + *prev_chunks_next_ptr = mc->next; + + /* Put it out of harm's way for a while, if not from a client request */ + if (MAC_AllocCustom != mc->allockind) { + /* Record where freed */ + mc->where = VG_(get_ExeContext) ( VG_(get_current_or_recent_tid)() ); + add_to_freed_queue ( mc ); + } else + VG_(free) ( mc ); +} + +__inline__ +void MAC_(handle_free) ( Addr p, UInt rzB, MAC_AllocKind kind ) +{ + MAC_Chunk* mc; + MAC_Chunk** prev_chunks_next_ptr; + ThreadId tid = VG_(get_current_or_recent_tid)(); + + VGP_PUSHCC(VgpCliMalloc); + + cmalloc_n_frees++; + + mc = (MAC_Chunk*)VG_(HT_get_node) ( MAC_(malloc_list), (UInt)p, + (VgHashNode***)&prev_chunks_next_ptr ); + if (mc == NULL) { + MAC_(record_free_error) ( tid, p ); + VGP_POPCC(VgpCliMalloc); + return; + } + + /* check if its a matching free() / delete / delete [] */ + if (kind != mc->allockind) { + MAC_(record_freemismatch_error) ( tid, p ); + } + + die_and_free_mem ( mc, prev_chunks_next_ptr, rzB ); + VGP_POPCC(VgpCliMalloc); +} + +void SK_(free) ( void* p ) +{ + MAC_(handle_free)((Addr)p, VG_(vg_malloc_redzone_szB), MAC_AllocMalloc); +} + +void SK_(__builtin_delete) ( void* p ) +{ + MAC_(handle_free)((Addr)p, VG_(vg_malloc_redzone_szB), MAC_AllocNew); +} + +void SK_(__builtin_vec_delete) ( void* p ) +{ + MAC_(handle_free)((Addr)p, VG_(vg_malloc_redzone_szB), MAC_AllocNewVec); +} + +void* SK_(realloc) ( void* p, Int new_size ) +{ + MAC_Chunk *mc; + MAC_Chunk **prev_chunks_next_ptr; + UInt i; + ThreadId tid = VG_(get_current_or_recent_tid)(); + + VGP_PUSHCC(VgpCliMalloc); + + cmalloc_n_frees ++; + cmalloc_n_mallocs ++; + cmalloc_bs_mallocd += new_size; + + if (new_size < 0) { + VG_(message)(Vg_UserMsg, + "Warning: silly arg (%d) to realloc()", new_size ); + return NULL; + } + + /* First try and find the block. */ + mc = (MAC_Chunk*)VG_(HT_get_node) ( MAC_(malloc_list), (UInt)p, + (VgHashNode***)&prev_chunks_next_ptr ); + + if (mc == NULL) { + MAC_(record_free_error) ( tid, (Addr)p ); + /* Perhaps we should return to the program regardless. */ + VGP_POPCC(VgpCliMalloc); + return NULL; + } + + /* check if its a matching free() / delete / delete [] */ + if (MAC_AllocMalloc != mc->allockind) { + /* can not realloc a range that was allocated with new or new [] */ + MAC_(record_freemismatch_error) ( tid, (Addr)p ); + /* but keep going anyway */ + } + + if (mc->size == new_size) { + /* size unchanged */ + mc->where = VG_(get_ExeContext)(tid); + VGP_POPCC(VgpCliMalloc); + return p; + + } else if (mc->size > new_size) { + /* new size is smaller */ + MAC_(die_mem_heap)( mc->data+new_size, mc->size-new_size ); + mc->size = new_size; + mc->where = VG_(get_ExeContext)(tid); + VGP_POPCC(VgpCliMalloc); + return p; + + } else { + /* new size is bigger */ + Addr p_new; + + /* Get new memory */ + p_new = (Addr)VG_(cli_malloc)(VG_(clo_alignment), new_size); + + /* First half kept and copied, second half new, + red zones as normal */ + MAC_(ban_mem_heap) ( p_new-VG_(vg_malloc_redzone_szB), + VG_(vg_malloc_redzone_szB) ); + MAC_(copy_mem_heap)( (Addr)p, p_new, mc->size ); + MAC_(new_mem_heap) ( p_new+mc->size, new_size-mc->size, /*inited*/False ); + MAC_(ban_mem_heap) ( p_new+new_size, VG_(vg_malloc_redzone_szB) ); + + /* Copy from old to new */ + for (i = 0; i < mc->size; i++) + ((UChar*)p_new)[i] = ((UChar*)p)[i]; + + /* Free old memory */ + die_and_free_mem ( mc, prev_chunks_next_ptr, + VG_(vg_malloc_redzone_szB) ); + + /* this has to be after die_and_free_mem, otherwise the + former succeeds in shorting out the new block, not the + old, in the case when both are on the same list. */ + add_MAC_Chunk ( p_new, new_size, MAC_AllocMalloc, MAC_(malloc_list) ); + + VGP_POPCC(VgpCliMalloc); + return (void*)p_new; + } +} + +/* Memory pool stuff. */ + +void MAC_(create_mempool)(Addr pool, UInt rzB, Bool is_zeroed) +{ + MAC_Mempool* mp; + + mp = VG_(malloc)(sizeof(MAC_Mempool)); + mp->pool = pool; + mp->rzB = rzB; + mp->is_zeroed = is_zeroed; + mp->chunks = VG_(HT_construct)(); + + /* Paranoia ... ensure this area is off-limits to the client, so + the mp->data field isn't visible to the leak checker. If memory + management is working correctly, anything pointer returned by + VG_(malloc) should be noaccess as far as the client is + concerned. */ + if (!MAC_(check_noaccess)( (Addr)mp, sizeof(MAC_Mempool), NULL )) { + VG_(skin_panic)("MAC_(create_mempool): shadow area is accessible"); + } + + VG_(HT_add_node)( MAC_(mempool_list), (VgHashNode*)mp ); + +} + +static void destroy_mempool_nuke_chunk(VgHashNode *node, void *d) +{ + MAC_Chunk *mc = (MAC_Chunk *)node; + MAC_Mempool *mp = (MAC_Mempool *)d; + + /* Note: ban redzones again -- just in case user de-banned them + with a client request... */ + MAC_(ban_mem_heap)(mc->data-mp->rzB, mp->rzB ); + MAC_(die_mem_heap)(mc->data, mc->size ); + MAC_(ban_mem_heap)(mc->data+mc->size, mp->rzB ); +} + +void MAC_(destroy_mempool)(Addr pool) +{ + MAC_Mempool* mp; + MAC_Mempool** prev_next; + + mp = (MAC_Mempool*)VG_(HT_get_node) ( MAC_(mempool_list), + (UInt)pool, + (VgHashNode***)&prev_next ); + + if (mp == NULL) { + ThreadId tid = VG_(get_current_or_recent_tid)(); + + MAC_(record_illegal_mempool_error) ( tid, pool ); + return; + } + + *prev_next = mp->next; + VG_(HT_apply_to_all_nodes)(mp->chunks, destroy_mempool_nuke_chunk, mp); + VG_(HT_destruct)(mp->chunks); + + VG_(free)(mp); +} + +void MAC_(mempool_alloc)(Addr pool, Addr addr, UInt size) +{ + MAC_Mempool* mp; + MAC_Mempool** prev_next; + + mp = (MAC_Mempool*)VG_(HT_get_node) ( MAC_(mempool_list), (UInt)pool, + (VgHashNode***)&prev_next ); + + if (mp == NULL) { + ThreadId tid = VG_(get_current_or_recent_tid)(); + + MAC_(record_illegal_mempool_error) ( tid, pool ); + return; + } + + MAC_(new_block)(addr, size, /*ignored*/0, mp->rzB, mp->is_zeroed, + MAC_AllocCustom, mp->chunks); +} + +void MAC_(mempool_free)(Addr pool, Addr addr) +{ + MAC_Mempool* mp; + MAC_Mempool** prev_pool; + MAC_Chunk* mc; + MAC_Chunk** prev_chunk; + ThreadId tid = VG_(get_current_or_recent_tid)(); + + + mp = (MAC_Mempool*)VG_(HT_get_node)(MAC_(mempool_list), (UInt)pool, + (VgHashNode***)&prev_pool); + + if (mp == NULL) { + MAC_(record_illegal_mempool_error)(tid, pool); + return; + } + + mc = (MAC_Chunk*)VG_(HT_get_node)(mp->chunks, (UInt)addr, + (VgHashNode***)&prev_chunk); + + if (mc == NULL) { + MAC_(record_free_error)(tid, (Addr)addr); + return; + } + + die_and_free_mem(mc, prev_chunk, mp->rzB); +} + +typedef + struct { + UInt nblocks; + UInt nbytes; + } + MallocStats; + +static void malloc_stats_count_chunk(VgHashNode* node, void* d) { + MAC_Chunk* mc = (MAC_Chunk*)node; + MallocStats *ms = (MallocStats *)d; + + ms->nblocks ++; + ms->nbytes += mc->size; +} + +void MAC_(print_malloc_stats) ( void ) +{ + MallocStats ms; + + ms.nblocks = 0; + ms.nbytes = 0; + + /* Mmm... more lexical scoping */ + if (VG_(clo_verbosity) == 0) + return; + + /* Count memory still in use. */ + VG_(HT_apply_to_all_nodes)(MAC_(malloc_list), malloc_stats_count_chunk, &ms); + + VG_(message)(Vg_UserMsg, + "malloc/free: in use at exit: %d bytes in %d blocks.", + ms.nbytes, ms.nblocks); + VG_(message)(Vg_UserMsg, + "malloc/free: %d allocs, %d frees, %u bytes allocated.", + cmalloc_n_mallocs, + cmalloc_n_frees, cmalloc_bs_mallocd); + if (VG_(clo_verbosity) > 1) + VG_(message)(Vg_UserMsg, ""); +} + +/*--------------------------------------------------------------------*/ +/*--- end mac_malloc_wrappers.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/mac_needs.c b/VEX/head20041019/memcheck/mac_needs.c new file mode 100644 index 0000000000..ce09679bb8 --- /dev/null +++ b/VEX/head20041019/memcheck/mac_needs.c @@ -0,0 +1,964 @@ + +/*--------------------------------------------------------------------*/ +/*--- Code that is shared between MemCheck and AddrCheck. ---*/ +/*--- mac_needs.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors, and AddrCheck, a lightweight Valgrind tool + for detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + + +#include "mac_shared.h" + +#include "memcheck.h" /* for VG_USERREQ__* */ + +/*------------------------------------------------------------*/ +/*--- Defns ---*/ +/*------------------------------------------------------------*/ + +/* These many bytes below %ESP are considered addressible if we're + doing the --workaround-gcc296-bugs hack. */ +#define VG_GCC296_BUG_STACK_SLOP 1024 + +/*------------------------------------------------------------*/ +/*--- Command line options ---*/ +/*------------------------------------------------------------*/ + +Bool MAC_(clo_partial_loads_ok) = True; +Int MAC_(clo_freelist_vol) = 1000000; +Bool MAC_(clo_leak_check) = False; +VgRes MAC_(clo_leak_resolution) = Vg_LowRes; +Bool MAC_(clo_show_reachable) = False; +Bool MAC_(clo_workaround_gcc296_bugs) = False; + +Bool MAC_(process_common_cmd_line_option)(Char* arg) +{ + VG_BOOL_CLO("--leak-check", MAC_(clo_leak_check)) + else VG_BOOL_CLO("--partial-loads-ok", MAC_(clo_partial_loads_ok)) + else VG_BOOL_CLO("--show-reachable", MAC_(clo_show_reachable)) + else VG_BOOL_CLO("--workaround-gcc296-bugs",MAC_(clo_workaround_gcc296_bugs)) + + else VG_BNUM_CLO("--freelist-vol", MAC_(clo_freelist_vol), 0, 1000000000) + + else if (VG_CLO_STREQ(arg, "--leak-resolution=low")) + MAC_(clo_leak_resolution) = Vg_LowRes; + else if (VG_CLO_STREQ(arg, "--leak-resolution=med")) + MAC_(clo_leak_resolution) = Vg_MedRes; + else if (VG_CLO_STREQ(arg, "--leak-resolution=high")) + MAC_(clo_leak_resolution) = Vg_HighRes; + + else + return VG_(replacement_malloc_process_cmd_line_option)(arg); + + return True; +} + +void MAC_(print_common_usage)(void) +{ + VG_(printf)( +" --partial-loads-ok=no|yes too hard to explain here; see manual [yes]\n" +" --freelist-vol= volume of freed blocks queue [1000000]\n" +" --leak-check=no|yes search for memory leaks at exit? [no]\n" +" --leak-resolution=low|med|high how much bt merging in leak check [low]\n" +" --show-reachable=no|yes show reachable blocks in leak check? [no]\n" +" --workaround-gcc296-bugs=no|yes self explanatory [no]\n" + ); + VG_(replacement_malloc_print_usage)(); +} + +void MAC_(print_common_debug_usage)(void) +{ + VG_(replacement_malloc_print_debug_usage)(); +} + +/*------------------------------------------------------------*/ +/*--- Comparing and printing errors ---*/ +/*------------------------------------------------------------*/ + +static __inline__ +void clear_AddrInfo ( AddrInfo* ai ) +{ + ai->akind = Unknown; + ai->blksize = 0; + ai->rwoffset = 0; + ai->lastchange = NULL; + ai->stack_tid = VG_INVALID_THREADID; + ai->maybe_gcc = False; +} + +void MAC_(clear_MAC_Error) ( MAC_Error* err_extra ) +{ + err_extra->axskind = ReadAxs; + err_extra->size = 0; + clear_AddrInfo ( &err_extra->addrinfo ); + err_extra->isWrite = False; +} + +__attribute__ ((unused)) +static Bool eq_AddrInfo ( VgRes res, AddrInfo* ai1, AddrInfo* ai2 ) +{ + if (ai1->akind != Undescribed + && ai2->akind != Undescribed + && ai1->akind != ai2->akind) + return False; + if (ai1->akind == Freed || ai1->akind == Mallocd) { + if (ai1->blksize != ai2->blksize) + return False; + if (!VG_(eq_ExeContext)(res, ai1->lastchange, ai2->lastchange)) + return False; + } + return True; +} + +/* Compare error contexts, to detect duplicates. Note that if they + are otherwise the same, the faulting addrs and associated rwoffsets + are allowed to be different. */ + +Bool SK_(eq_SkinError) ( VgRes res, Error* e1, Error* e2 ) +{ + MAC_Error* e1_extra = VG_(get_error_extra)(e1); + MAC_Error* e2_extra = VG_(get_error_extra)(e2); + + /* Guaranteed by calling function */ + sk_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2)); + + switch (VG_(get_error_kind)(e1)) { + case CoreMemErr: { + Char *e1s, *e2s; + if (e1_extra->isWrite != e2_extra->isWrite) return False; + e1s = VG_(get_error_string)(e1); + e2s = VG_(get_error_string)(e2); + if (e1s == e2s) return True; + if (0 == VG_(strcmp)(e1s, e2s)) return True; + return False; + } + + case UserErr: + case ParamErr: + if (e1_extra->isWrite != e2_extra->isWrite) return False; + if (VG_(get_error_kind)(e1) == ParamErr + && 0 != VG_(strcmp)(VG_(get_error_string)(e1), + VG_(get_error_string)(e2))) return False; + return True; + + case FreeErr: + case FreeMismatchErr: + /* JRS 2002-Aug-26: comparing addrs seems overkill and can + cause excessive duplication of errors. Not even AddrErr + below does that. So don't compare either the .addr field + or the .addrinfo fields. */ + /* if (e1->addr != e2->addr) return False; */ + /* if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo)) + return False; + */ + return True; + + case AddrErr: + /* if (e1_extra->axskind != e2_extra->axskind) return False; */ + if (e1_extra->size != e2_extra->size) return False; + /* + if (!eq_AddrInfo(res, &e1_extra->addrinfo, &e2_extra->addrinfo)) + return False; + */ + return True; + + case ValueErr: + if (e1_extra->size != e2_extra->size) return False; + return True; + + case OverlapErr: + return True; + + case LeakErr: + VG_(skin_panic)("Shouldn't get LeakErr in SK_(eq_SkinError),\n" + "since it's handled with VG_(unique_error)()!"); + + case IllegalMempoolErr: + return True; + + default: + VG_(printf)("Error:\n unknown error code %d\n", + VG_(get_error_kind)(e1)); + VG_(skin_panic)("unknown error code in SK_(eq_SkinError)"); + } +} + +void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai ) +{ + switch (ai->akind) { + case Stack: + VG_(message)(Vg_UserMsg, + " Address 0x%x is on thread %d's stack", + a, ai->stack_tid); + break; + case Unknown: + if (ai->maybe_gcc) { + VG_(message)(Vg_UserMsg, + " Address 0x%x is just below %%esp. Possibly a bug in GCC/G++", + a); + VG_(message)(Vg_UserMsg, + " v 2.96 or 3.0.X. To suppress, use: --workaround-gcc296-bugs=yes"); + } else { + VG_(message)(Vg_UserMsg, + " Address 0x%x is not stack'd, malloc'd or (recently) free'd",a); + } + break; + case Freed: case Mallocd: case UserG: case Mempool: { + UInt delta; + UChar* relative; + UChar* kind; + if (ai->akind == Mempool) { + kind = "mempool"; + } else { + kind = "block"; + } + if (ai->rwoffset < 0) { + delta = (UInt)(- ai->rwoffset); + relative = "before"; + } else if (ai->rwoffset >= ai->blksize) { + delta = ai->rwoffset - ai->blksize; + relative = "after"; + } else { + delta = ai->rwoffset; + relative = "inside"; + } + VG_(message)(Vg_UserMsg, + " Address 0x%x is %d bytes %s a %s of size %d %s", + a, delta, relative, kind, + ai->blksize, + ai->akind==Mallocd ? "alloc'd" + : ai->akind==Freed ? "free'd" + : "client-defined"); + VG_(pp_ExeContext)(ai->lastchange); + break; + } + default: + VG_(skin_panic)("MAC_(pp_AddrInfo)"); + } +} + +/* This prints out the message for the error types where Memcheck and + Addrcheck have identical messages */ +void MAC_(pp_shared_SkinError) ( Error* err ) +{ + MAC_Error* err_extra = VG_(get_error_extra)(err); + + switch (VG_(get_error_kind)(err)) { + case FreeErr: + VG_(message)(Vg_UserMsg, "Invalid free() / delete / delete[]"); + /* fall through */ + case FreeMismatchErr: + if (VG_(get_error_kind)(err) == FreeMismatchErr) + VG_(message)(Vg_UserMsg, + "Mismatched free() / delete / delete []"); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); + break; + + case AddrErr: + switch (err_extra->axskind) { + case ReadAxs: + VG_(message)(Vg_UserMsg, "Invalid read of size %d", + err_extra->size ); + break; + case WriteAxs: + VG_(message)(Vg_UserMsg, "Invalid write of size %d", + err_extra->size ); + break; + case ExecAxs: + VG_(message)(Vg_UserMsg, "Jump to the invalid address " + "stated on the next line"); + break; + default: + VG_(skin_panic)("SK_(pp_SkinError)(axskind)"); + } + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); + break; + + case OverlapErr: { + OverlapExtra* ov_extra = (OverlapExtra*)VG_(get_error_extra)(err); + if (ov_extra->len == -1) + VG_(message)(Vg_UserMsg, + "Source and destination overlap in %s(%p, %p)", + VG_(get_error_string)(err), + ov_extra->dst, ov_extra->src); + else + VG_(message)(Vg_UserMsg, + "Source and destination overlap in %s(%p, %p, %d)", + VG_(get_error_string)(err), + ov_extra->dst, ov_extra->src, ov_extra->len); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + break; + } + case LeakErr: { + /* Totally abusing the types of these spare fields... oh well. */ + UInt n_this_record = (UInt)VG_(get_error_address)(err); + UInt n_total_records = (UInt)VG_(get_error_string) (err); + + MAC_(pp_LeakError)(err_extra, n_this_record, n_total_records); + break; + } + + case IllegalMempoolErr: + VG_(message)(Vg_UserMsg, "Illegal memory pool address"); + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); + break; + + default: + VG_(printf)("Error:\n unknown Memcheck/Addrcheck error code %d\n", + VG_(get_error_kind)(err)); + VG_(skin_panic)("unknown error code in MAC_(pp_shared_SkinError)"); + } +} + +/*------------------------------------------------------------*/ +/*--- Recording errors ---*/ +/*------------------------------------------------------------*/ + +/* Additional description function for describe_addr(); used by + MemCheck for user blocks, which Addrcheck doesn't support. */ +Bool (*MAC_(describe_addr_supp)) ( Addr a, AddrInfo* ai ) = NULL; + +/* Callback for searching thread stacks */ +static Bool addr_is_in_bounds(Addr stack_min, Addr stack_max, void *ap) +{ + Addr a = *(Addr *)ap; + + return (stack_min <= a && a <= stack_max); +} + +/* Callback for searching free'd list */ +static Bool addr_is_in_MAC_Chunk(MAC_Chunk* mc, void *ap) +{ + Addr a = *(Addr *)ap; + + return VG_(addr_is_in_block)( a, mc->data, mc->size ); +} + +/* Callback for searching malloc'd lists */ +static Bool addr_is_in_HashNode(VgHashNode* sh_ch, void *ap) +{ + return addr_is_in_MAC_Chunk( (MAC_Chunk*)sh_ch, ap ); +} + +/* Describe an address as best you can, for error messages, + putting the result in ai. */ +static void describe_addr ( Addr a, AddrInfo* ai ) +{ + MAC_Chunk* sc; + ThreadId tid; + + /* Perhaps it's a user-def'd block ? (only check if requested, though) */ + if (NULL != MAC_(describe_addr_supp)) { + if (MAC_(describe_addr_supp)( a, ai )) + return; + } + /* Perhaps it's on a thread's stack? */ + tid = VG_(first_matching_thread_stack)(addr_is_in_bounds, &a); + if (tid != VG_INVALID_THREADID) { + ai->akind = Stack; + ai->stack_tid = tid; + return; + } + /* Search for a recently freed block which might bracket it. */ + sc = MAC_(first_matching_freed_MAC_Chunk)(addr_is_in_MAC_Chunk, &a); + if (NULL != sc) { + ai->akind = Freed; + ai->blksize = sc->size; + ai->rwoffset = (Int)a - (Int)sc->data; + ai->lastchange = sc->where; + return; + } + /* Search for a currently malloc'd block which might bracket it. */ + sc = (MAC_Chunk*)VG_(HT_first_match)(MAC_(malloc_list), addr_is_in_HashNode, &a); + if (NULL != sc) { + ai->akind = Mallocd; + ai->blksize = sc->size; + ai->rwoffset = (Int)(a) - (Int)sc->data; + ai->lastchange = sc->where; + return; + } + /* Clueless ... */ + ai->akind = Unknown; + return; +} + +/* Is this address within some small distance below %ESP? Used only + for the --workaround-gcc296-bugs kludge. */ +static Bool is_just_below_ESP( Addr esp, Addr aa ) +{ + if ((UInt)esp > (UInt)aa + && ((UInt)esp - (UInt)aa) <= VG_GCC296_BUG_STACK_SLOP) + return True; + else + return False; +} + +/* This one called from generated code and non-generated code. */ + +void MAC_(record_address_error) ( ThreadId tid, Addr a, Int size, + Bool isWrite ) +{ + MAC_Error err_extra; + Bool just_below_esp; + + just_below_esp = is_just_below_ESP( VG_(get_stack_pointer)(), a ); + + /* If this is caused by an access immediately below %ESP, and the + user asks nicely, we just ignore it. */ + if (MAC_(clo_workaround_gcc296_bugs) && just_below_esp) + return; + + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.axskind = isWrite ? WriteAxs : ReadAxs; + err_extra.size = size; + err_extra.addrinfo.akind = Undescribed; + err_extra.addrinfo.maybe_gcc = just_below_esp; + VG_(maybe_record_error)( tid, AddrErr, a, /*s*/NULL, &err_extra ); +} + +/* These ones are called from non-generated code */ + +/* This is for memory errors in pthread functions, as opposed to pthread API + errors which are found by the core. */ +void MAC_(record_core_mem_error) ( ThreadId tid, Bool isWrite, Char* msg ) +{ + MAC_Error err_extra; + + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.isWrite = isWrite; + VG_(maybe_record_error)( tid, CoreMemErr, /*addr*/0, msg, &err_extra ); +} + +void MAC_(record_param_error) ( ThreadId tid, Addr a, Bool isWrite, + Char* msg ) +{ + MAC_Error err_extra; + + sk_assert(VG_INVALID_THREADID != tid); + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.addrinfo.akind = Undescribed; + err_extra.isWrite = isWrite; + VG_(maybe_record_error)( tid, ParamErr, a, msg, &err_extra ); +} + +void MAC_(record_jump_error) ( ThreadId tid, Addr a ) +{ + MAC_Error err_extra; + + sk_assert(VG_INVALID_THREADID != tid); + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.axskind = ExecAxs; + err_extra.size = 1; // size only used for suppressions + err_extra.addrinfo.akind = Undescribed; + VG_(maybe_record_error)( tid, AddrErr, a, /*s*/NULL, &err_extra ); +} + +void MAC_(record_free_error) ( ThreadId tid, Addr a ) +{ + MAC_Error err_extra; + + sk_assert(VG_INVALID_THREADID != tid); + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.addrinfo.akind = Undescribed; + VG_(maybe_record_error)( tid, FreeErr, a, /*s*/NULL, &err_extra ); +} + +void MAC_(record_illegal_mempool_error) ( ThreadId tid, Addr a ) +{ + MAC_Error err_extra; + + sk_assert(VG_INVALID_THREADID != tid); + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.addrinfo.akind = Undescribed; + VG_(maybe_record_error)( tid, IllegalMempoolErr, a, /*s*/NULL, &err_extra ); +} + +void MAC_(record_freemismatch_error) ( ThreadId tid, Addr a ) +{ + MAC_Error err_extra; + + sk_assert(VG_INVALID_THREADID != tid); + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.addrinfo.akind = Undescribed; + VG_(maybe_record_error)( tid, FreeMismatchErr, a, /*s*/NULL, &err_extra ); +} + + +// This one not passed a ThreadId, so it grabs it itself. +void MAC_(record_overlap_error) ( Char* function, OverlapExtra* ov_extra ) +{ + VG_(maybe_record_error)( VG_(get_current_or_recent_tid)(), + OverlapErr, /*addr*/0, /*s*/function, ov_extra ); +} + + +/* Updates the copy with address info if necessary (but not for all errors). */ +UInt SK_(update_extra)( Error* err ) +{ + switch (VG_(get_error_kind)(err)) { + case ValueErr: + case CoreMemErr: + case AddrErr: + case ParamErr: + case UserErr: + case FreeErr: + case IllegalMempoolErr: + case FreeMismatchErr: { + MAC_Error* extra = (MAC_Error*)VG_(get_error_extra)(err); + if (extra != NULL && Undescribed == extra->addrinfo.akind) { + describe_addr ( VG_(get_error_address)(err), &(extra->addrinfo) ); + } + return sizeof(MAC_Error); + } + /* Don't need to return the correct size -- LeakErrs are always shown with + VG_(unique_error)() so they're not copied anyway. */ + case LeakErr: return 0; + case OverlapErr: return sizeof(OverlapExtra); + default: VG_(skin_panic)("update_extra: bad errkind"); + } +} + + +/*------------------------------------------------------------*/ +/*--- Suppressions ---*/ +/*------------------------------------------------------------*/ + +Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su ) +{ + SuppKind skind; + + if (VG_STREQ(name, "Param")) skind = ParamSupp; + else if (VG_STREQ(name, "CoreMem")) skind = CoreMemSupp; + else if (VG_STREQ(name, "Addr1")) skind = Addr1Supp; + else if (VG_STREQ(name, "Addr2")) skind = Addr2Supp; + else if (VG_STREQ(name, "Addr4")) skind = Addr4Supp; + else if (VG_STREQ(name, "Addr8")) skind = Addr8Supp; + else if (VG_STREQ(name, "Addr16")) skind = Addr16Supp; + else if (VG_STREQ(name, "Free")) skind = FreeSupp; + else if (VG_STREQ(name, "Leak")) skind = LeakSupp; + else if (VG_STREQ(name, "Overlap")) skind = OverlapSupp; + else if (VG_STREQ(name, "Mempool")) skind = MempoolSupp; + else + return False; + + VG_(set_supp_kind)(su, skind); + return True; +} + +Bool SK_(read_extra_suppression_info) ( Int fd, Char* buf, Int nBuf, Supp *su ) +{ + Bool eof; + + if (VG_(get_supp_kind)(su) == ParamSupp) { + eof = VG_(get_line) ( fd, buf, nBuf ); + if (eof) return False; + VG_(set_supp_string)(su, VG_(strdup)(buf)); + } + return True; +} + +Bool SK_(error_matches_suppression)(Error* err, Supp* su) +{ + Int su_size; + MAC_Error* err_extra = VG_(get_error_extra)(err); + ErrorKind ekind = VG_(get_error_kind )(err); + + switch (VG_(get_supp_kind)(su)) { + case ParamSupp: + return (ekind == ParamErr + && VG_STREQ(VG_(get_error_string)(err), + VG_(get_supp_string)(su))); + + case CoreMemSupp: + return (ekind == CoreMemErr + && VG_STREQ(VG_(get_error_string)(err), + VG_(get_supp_string)(su))); + + case Value0Supp: su_size = 0; goto value_case; + case Value1Supp: su_size = 1; goto value_case; + case Value2Supp: su_size = 2; goto value_case; + case Value4Supp: su_size = 4; goto value_case; + case Value8Supp: su_size = 8; goto value_case; + case Value16Supp:su_size =16; goto value_case; + value_case: + return (ekind == ValueErr && err_extra->size == su_size); + + case Addr1Supp: su_size = 1; goto addr_case; + case Addr2Supp: su_size = 2; goto addr_case; + case Addr4Supp: su_size = 4; goto addr_case; + case Addr8Supp: su_size = 8; goto addr_case; + case Addr16Supp:su_size =16; goto addr_case; + addr_case: + return (ekind == AddrErr && err_extra->size == su_size); + + case FreeSupp: + return (ekind == FreeErr || ekind == FreeMismatchErr); + + case OverlapSupp: + return (ekind = OverlapErr); + + case LeakSupp: + return (ekind == LeakErr); + + case MempoolSupp: + return (ekind == IllegalMempoolErr); + + default: + VG_(printf)("Error:\n" + " unknown suppression type %d\n", + VG_(get_supp_kind)(su)); + VG_(skin_panic)("unknown suppression type in " + "SK_(error_matches_suppression)"); + } +} + +Char* SK_(get_error_name) ( Error* err ) +{ + Char* s; + switch (VG_(get_error_kind)(err)) { + case ParamErr: return "Param"; + case UserErr: return NULL; /* Can't suppress User errors */ + case FreeMismatchErr: return "Free"; + case IllegalMempoolErr: return "Mempool"; + case FreeErr: return "Free"; + case AddrErr: + switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) { + case 1: return "Addr1"; + case 2: return "Addr2"; + case 4: return "Addr4"; + case 8: return "Addr8"; + case 16: return "Addr16"; + default: VG_(skin_panic)("unexpected size for Addr"); + } + + case ValueErr: + switch ( ((MAC_Error*)VG_(get_error_extra)(err))->size ) { + case 0: return "Cond"; + case 1: return "Value1"; + case 2: return "Value2"; + case 4: return "Value4"; + case 8: return "Value8"; + case 16: return "Value16"; + default: VG_(skin_panic)("unexpected size for Value"); + } + case CoreMemErr: return "CoreMem"; + case OverlapErr: return "Overlap"; + case LeakErr: return "Leak"; + default: VG_(skin_panic)("get_error_name: unexpected type"); + } + VG_(printf)(s); +} + +void SK_(print_extra_suppression_info) ( Error* err ) +{ + if (ParamErr == VG_(get_error_kind)(err)) { + VG_(printf)(" %s\n", VG_(get_error_string)(err)); + } +} + +/*------------------------------------------------------------*/ +/*--- Crude profiling machinery. ---*/ +/*------------------------------------------------------------*/ + +/* Event index. If just the name of the fn is given, this means the + number of calls to the fn. Otherwise it is the specified event. + Ones marked 'M' are MemCheck only. Ones marked 'A' are AddrCheck only. + The rest are shared. + + 10 alloc_secondary_map + + 20 get_abit +M 21 get_vbyte + 22 set_abit +M 23 set_vbyte + 24 get_abits4_ALIGNED +M 25 get_vbytes4_ALIGNED + + 30 set_address_range_perms + 31 set_address_range_perms(lower byte loop) + 32 set_address_range_perms(quadword loop) + 33 set_address_range_perms(upper byte loop) + + 35 make_noaccess + 36 make_writable + 37 make_readable +A 38 make_accessible + + 40 copy_address_range_state + 41 copy_address_range_state(byte loop) + 42 check_writable + 43 check_writable(byte loop) + 44 check_readable + 45 check_readable(byte loop) + 46 check_readable_asciiz + 47 check_readable_asciiz(byte loop) +A 48 check_accessible +A 49 check_accessible(byte loop) + + 50 make_noaccess_aligned + 51 make_writable_aligned + +M 60 helperc_LOADV4 +M 61 helperc_STOREV4 +M 62 helperc_LOADV2 +M 63 helperc_STOREV2 +M 64 helperc_LOADV1 +M 65 helperc_STOREV1 + +A 66 helperc_ACCESS4 +A 67 helperc_ACCESS2 +A 68 helperc_ACCESS1 + +M 70 rim_rd_V4_SLOWLY +M 71 rim_wr_V4_SLOWLY +M 72 rim_rd_V2_SLOWLY +M 73 rim_wr_V2_SLOWLY +M 74 rim_rd_V1_SLOWLY +M 75 rim_wr_V1_SLOWLY + +A 76 ACCESS4_SLOWLY +A 77 ACCESS2_SLOWLY +A 78 ACCESS1_SLOWLY + + 80 fpu_read + 81 fpu_read aligned 4 + 82 fpu_read aligned 8 + 83 fpu_read 2 + 84 fpu_read 10/28/108/512 + +M 85 fpu_write +M 86 fpu_write aligned 4 +M 87 fpu_write aligned 8 +M 88 fpu_write 2 +M 89 fpu_write 10/28/108/512 + + 90 fpu_access + 91 fpu_access aligned 4 + 92 fpu_access aligned 8 + 93 fpu_access 2 + 94 fpu_access 10/28/108/512 + + 100 fpu_access_check_SLOWLY + 101 fpu_access_check_SLOWLY(byte loop) + + 110 new_mem_stack_4 + 111 new_mem_stack_8 + 112 new_mem_stack_12 + 113 new_mem_stack_16 + 114 new_mem_stack_32 + 115 new_mem_stack + + 120 die_mem_stack_4 + 121 die_mem_stack_8 + 122 die_mem_stack_12 + 123 die_mem_stack_16 + 124 die_mem_stack_32 + 125 die_mem_stack +*/ + +#ifdef MAC_PROFILE_MEMORY + +UInt MAC_(event_ctr)[N_PROF_EVENTS]; + +static void init_prof_mem ( void ) +{ + Int i; + for (i = 0; i < N_PROF_EVENTS; i++) + MAC_(event_ctr)[i] = 0; +} + +static void done_prof_mem ( void ) +{ + Int i; + for (i = 0; i < N_PROF_EVENTS; i++) { + if ((i % 10) == 0) + VG_(printf)("\n"); + if (MAC_(event_ctr)[i] > 0) + VG_(printf)( "prof mem event %2d: %d\n", i, MAC_(event_ctr)[i] ); + } + VG_(printf)("\n"); +} + +#else + +static void init_prof_mem ( void ) { } +static void done_prof_mem ( void ) { } + +#endif + +/*------------------------------------------------------------*/ +/*--- Common initialisation + finalisation ---*/ +/*------------------------------------------------------------*/ + +void MAC_(common_pre_clo_init)(void) +{ + MAC_(malloc_list) = VG_(HT_construct)(); + MAC_(mempool_list) = VG_(HT_construct)(); + init_prof_mem(); +} + +void MAC_(common_fini)(void (*leak_check)(void)) +{ + MAC_(print_malloc_stats)(); + + if (VG_(clo_verbosity) == 1) { + if (!MAC_(clo_leak_check)) + VG_(message)(Vg_UserMsg, + "For a detailed leak analysis, rerun with: --leak-check=yes"); + + VG_(message)(Vg_UserMsg, + "For counts of detected errors, rerun with: -v"); + } + if (MAC_(clo_leak_check)) leak_check(); + + done_prof_mem(); +} + +/*------------------------------------------------------------*/ +/*--- Common client request handling ---*/ +/*------------------------------------------------------------*/ + +Bool MAC_(handle_common_client_requests)(ThreadId tid, UInt* arg, UInt* ret ) +{ + Char* err = + "The client requests VALGRIND_MALLOCLIKE_BLOCK and\n" + " VALGRIND_FREELIKE_BLOCK have moved. Please recompile your\n" + " program to incorporate the updates in the Valgrind header files.\n" + " You shouldn't need to change the text of your program at all.\n" + " Everything should then work as before. Sorry for the bother.\n"; + + // Not using 'tid' here because MAC_(new_block)() and MAC_(handle_free)() + // grab it themselves. But what they grab should match 'tid', check + // this. + sk_assert(tid == VG_(get_current_or_recent_tid)()); + + switch (arg[0]) { + case VG_USERREQ__COUNT_LEAKS: { /* count leaked bytes */ + UInt** argp = (UInt**)arg; + // MAC_(bytes_leaked) et al were set by the last leak check (or zero + // if no prior leak checks performed). + *argp[1] = MAC_(bytes_leaked); + *argp[2] = MAC_(bytes_dubious); + *argp[3] = MAC_(bytes_reachable); + *argp[4] = MAC_(bytes_suppressed); + *ret = 0; + return True; + } + case VG_USERREQ__MALLOCLIKE_BLOCK__OLD_DO_NOT_USE: + case VG_USERREQ__FREELIKE_BLOCK__OLD_DO_NOT_USE: + VG_(skin_panic)(err); + + case VG_USERREQ__MALLOCLIKE_BLOCK: { + Addr p = (Addr)arg[1]; + UInt sizeB = arg[2]; + UInt rzB = arg[3]; + Bool is_zeroed = (Bool)arg[4]; + + MAC_(new_block) ( p, sizeB, /*ignored*/0, rzB, is_zeroed, + MAC_AllocCustom, MAC_(malloc_list) ); + return True; + } + case VG_USERREQ__FREELIKE_BLOCK: { + Addr p = (Addr)arg[1]; + UInt rzB = arg[2]; + + MAC_(handle_free) ( p, rzB, MAC_AllocCustom ); + return True; + } + + case _VG_USERREQ__MEMCHECK_GET_RECORD_OVERLAP: + *ret = (Addr)MAC_(record_overlap_error); + return True; + + case VG_USERREQ__CREATE_MEMPOOL: { + Addr pool = (Addr)arg[1]; + UInt rzB = arg[2]; + Bool is_zeroed = (Bool)arg[3]; + + MAC_(create_mempool) ( pool, rzB, is_zeroed ); + return True; + } + + case VG_USERREQ__DESTROY_MEMPOOL: { + Addr pool = (Addr)arg[1]; + + MAC_(destroy_mempool) ( pool ); + return True; + } + + case VG_USERREQ__MEMPOOL_ALLOC: { + Addr pool = (Addr)arg[1]; + Addr addr = (Addr)arg[2]; + UInt size = arg[3]; + + MAC_(mempool_alloc) ( pool, addr, size ); + return True; + } + + case VG_USERREQ__MEMPOOL_FREE: { + Addr pool = (Addr)arg[1]; + Addr addr = (Addr)arg[2]; + + MAC_(mempool_free) ( pool, addr ); + return True; + } + + default: + return False; + } +} + +/*------------------------------------------------------------*/ +/*--- Syscall wrappers ---*/ +/*------------------------------------------------------------*/ + +void* SK_(pre_syscall) ( ThreadId tid, UInt syscallno, Bool isBlocking ) +{ + Int sane = SK_(cheap_sanity_check)(); + return (void*)sane; +} + +void SK_(post_syscall) ( ThreadId tid, UInt syscallno, + void* pre_result, Int res, Bool isBlocking ) +{ + Int sane_before_call = (Int)pre_result; + Bool sane_after_call = SK_(cheap_sanity_check)(); + + if ((Int)sane_before_call && (!sane_after_call)) { + VG_(message)(Vg_DebugMsg, "post-syscall: "); + VG_(message)(Vg_DebugMsg, + "probable sanity check failure for syscall number %d\n", + syscallno ); + VG_(skin_panic)("aborting due to the above ... bye!"); + } +} + +/*--------------------------------------------------------------------*/ +/*--- end mac_needs.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/mac_replace_strmem.c b/VEX/head20041019/memcheck/mac_replace_strmem.c new file mode 100644 index 0000000000..5e8ecd3347 --- /dev/null +++ b/VEX/head20041019/memcheck/mac_replace_strmem.c @@ -0,0 +1,358 @@ + +/*--------------------------------------------------------------------*/ +/*--- Replacements for strcpy(), memcpy() et al, which run on the ---*/ +/*--- simulated CPU. ---*/ +/*--- mac_replace_strmem.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mc_include.h" +#include "memcheck.h" +#include "valgrind.h" + +static Addr record_overlap_error; + +static int init_done; + +/* Startup hook - called as init section */ +static void init(void) __attribute__((constructor)); +static void init(void) +{ + if (init_done) + return; + + VALGRIND_MAGIC_SEQUENCE(record_overlap_error, 0, + _VG_USERREQ__MEMCHECK_GET_RECORD_OVERLAP, + 0, 0, 0, 0); + init_done = 1; +} + +/* --------------------------------------------------------------------- + The normal versions of these functions are hyper-optimised, which fools + Memcheck and cause spurious value warnings. So we replace them with + simpler versions. THEY RUN ON SIMD CPU! + ------------------------------------------------------------------ */ + +/* Figure out if [dst .. dst+dstlen-1] overlaps with + [src .. src+srclen-1]. + We assume that the address ranges do not wrap around + (which is safe since on Linux addresses >= 0xC0000000 + are not accessible and the program will segfault in this + circumstance, presumably). +*/ +static __inline__ +Bool is_overlap ( void* dst, const void* src, UInt dstlen, UInt srclen ) +{ + Addr loS, hiS, loD, hiD; + + if (dstlen == 0 || srclen == 0) + return False; + + loS = (Addr)src; + loD = (Addr)dst; + hiS = loS + srclen - 1; + hiD = loD + dstlen - 1; + + /* So figure out if [loS .. hiS] overlaps with [loD .. hiD]. */ + if (loS < loD) { + return !(hiS < loD); + } + else if (loD < loS) { + return !(hiD < loS); + } + else { + /* They start at same place. Since we know neither of them has + zero length, they must overlap. */ + return True; + } +} + + +static __inline__ +void complain2 ( Char* s, char* dst, const char* src ) +{ + OverlapExtra extra = { + .src = (Addr)src, .dst = (Addr)dst, .len = -1, + }; + init(); + VALGRIND_NON_SIMD_CALL2( record_overlap_error, s, &extra ); +} + +static __inline__ +void complain3 ( Char* s, void* dst, const void* src, int n ) +{ + /* Must wrap it up here, because we cannot pass 4 args to core */ + OverlapExtra extra = { + .src = (Addr)src, .dst = (Addr)dst, .len = n, + }; + init(); + VALGRIND_NON_SIMD_CALL2( record_overlap_error, s, &extra ); +} + +char* strrchr ( const char* s, int c ) +{ + UChar ch = (UChar)((UInt)c); + UChar* p = (UChar*)s; + UChar* last = NULL; + while (True) { + if (*p == ch) last = p; + if (*p == 0) return last; + p++; + } +} + +char* strchr ( const char* s, int c ) +{ + UChar ch = (UChar)((UInt)c); + UChar* p = (UChar*)s; + while (True) { + if (*p == ch) return p; + if (*p == 0) return NULL; + p++; + } +} + +char* strcat ( char* dst, const char* src ) +{ + const Char* src_orig = src; + Char* dst_orig = dst; + while (*dst) dst++; + while (*src) *dst++ = *src++; + *dst = 0; + + /* This is a bit redundant, I think; any overlap and the strcat will + go forever... or until a seg fault occurs. */ + if (is_overlap(dst_orig, + src_orig, + (Addr)dst-(Addr)dst_orig+1, + (Addr)src-(Addr)src_orig+1)) + complain2("strcat", dst_orig, src_orig); + + return dst_orig; +} + +char* strncat ( char* dst, const char* src, unsigned int n ) +{ + const Char* src_orig = src; + Char* dst_orig = dst; + UInt m = 0; + + while (*dst) dst++; + while (m < n && *src) { m++; *dst++ = *src++; } /* concat <= n chars */ + *dst = 0; /* always add null */ + + /* This checks for overlap after copying, unavoidable without + pre-counting lengths... should be ok */ + if (is_overlap(dst_orig, + src_orig, + (Addr)dst-(Addr)dst_orig+1, + (Addr)src-(Addr)src_orig+1)) + complain3("strncat", dst_orig, src_orig, n); + + return dst_orig; +} + +unsigned int strnlen ( const char* str, unsigned int n ) +{ + UInt i = 0; + while (i < n && str[i] != 0) i++; + return i; +} + +unsigned int strlen ( const char* str ) +{ + UInt i = 0; + while (str[i] != 0) i++; + return i; +} + +char* strcpy ( char* dst, const char* src ) +{ + const Char* src_orig = src; + Char* dst_orig = dst; + + while (*src) *dst++ = *src++; + *dst = 0; + + /* This checks for overlap after copying, unavoidable without + pre-counting length... should be ok */ + if (is_overlap(dst_orig, + src_orig, + (Addr)dst-(Addr)dst_orig+1, + (Addr)src-(Addr)src_orig+1)) + complain2("strcpy", dst_orig, src_orig); + + return dst_orig; +} + +char* strncpy ( char* dst, const char* src, unsigned int n ) +{ + const Char* src_orig = src; + Char* dst_orig = dst; + UInt m = 0; + + while (m < n && *src) { m++; *dst++ = *src++; } + /* Check for overlap after copying; all n bytes of dst are relevant, + but only m+1 bytes of src if terminator was found */ + if (is_overlap(dst_orig, src_orig, n, (m < n) ? m+1 : n)) + complain3("strncpy", dst, src, n); + while (m++ < n) *dst++ = 0; /* must pad remainder with nulls */ + + return dst_orig; +} + +int strncmp ( const char* s1, const char* s2, unsigned int nmax ) +{ + unsigned int n = 0; + while (True) { + if (n >= nmax) return 0; + if (*s1 == 0 && *s2 == 0) return 0; + if (*s1 == 0) return -1; + if (*s2 == 0) return 1; + + if (*(unsigned char*)s1 < *(unsigned char*)s2) return -1; + if (*(unsigned char*)s1 > *(unsigned char*)s2) return 1; + + s1++; s2++; n++; + } +} + +int strcmp ( const char* s1, const char* s2 ) +{ + register unsigned char c1; + register unsigned char c2; + while (True) { + c1 = *(unsigned char *)s1; + c2 = *(unsigned char *)s2; + if (c1 != c2) break; + if (c1 == 0) break; + s1++; s2++; + } + if ((unsigned char)c1 < (unsigned char)c2) return -1; + if ((unsigned char)c1 > (unsigned char)c2) return 1; + return 0; +} + +void* memchr(const void *s, int c, unsigned int n) +{ + unsigned int i; + UChar c0 = (UChar)c; + UChar* p = (UChar*)s; + for (i = 0; i < n; i++) + if (p[i] == c0) return (void*)(&p[i]); + return NULL; +} + +void* memcpy( void *dst, const void *src, unsigned int len ) +{ + register char *d; + register char *s; + + if (len == 0) + return dst; + + if (is_overlap(dst, src, len, len)) + complain3("memcpy", dst, src, len); + + if ( dst > src ) { + d = (char *)dst + len - 1; + s = (char *)src + len - 1; + while ( len >= 4 ) { + *d-- = *s--; + *d-- = *s--; + *d-- = *s--; + *d-- = *s--; + len -= 4; + } + while ( len-- ) { + *d-- = *s--; + } + } else if ( dst < src ) { + d = (char *)dst; + s = (char *)src; + while ( len >= 4 ) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + len -= 4; + } + while ( len-- ) { + *d++ = *s++; + } + } + return dst; +} + +int memcmp ( const void *s1V, const void *s2V, unsigned int n ) +{ + int res; + unsigned char a0; + unsigned char b0; + unsigned char* s1 = (unsigned char*)s1V; + unsigned char* s2 = (unsigned char*)s2V; + + while (n != 0) { + a0 = s1[0]; + b0 = s2[0]; + s1 += 1; + s2 += 1; + res = ((int)a0) - ((int)b0); + if (res != 0) + return res; + n -= 1; + } + return 0; +} + + +/* Copy SRC to DEST, returning the address of the terminating '\0' in + DEST. (minor variant of strcpy) */ + +char* stpcpy ( char* dst, const char* src ) +{ + const Char* src_orig = src; + Char* dst_orig = dst; + + while (*src) *dst++ = *src++; + *dst = 0; + + /* This checks for overlap after copying, unavoidable without + pre-counting length... should be ok */ + if (is_overlap(dst_orig, + src_orig, + (Addr)dst-(Addr)dst_orig+1, + (Addr)src-(Addr)src_orig+1)) + complain2("stpcpy", dst_orig, src_orig); + + return dst; +} + + +/*--------------------------------------------------------------------*/ +/*--- end mac_replace_strmem.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/mac_shared.h b/VEX/head20041019/memcheck/mac_shared.h new file mode 100644 index 0000000000..4a5014c98b --- /dev/null +++ b/VEX/head20041019/memcheck/mac_shared.h @@ -0,0 +1,539 @@ + +/*--------------------------------------------------------------------*/ +/*--- Declarations shared between MemCheck and AddrCheck. ---*/ +/*--- mac_shared.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors, and AddrCheck, a lightweight Valgrind tool + for detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* Note: This header contains the declarations shared between + Addrcheck and Memcheck, and is #included by both. */ + +#ifndef __MAC_SHARED_H +#define __MAC_SHARED_H + +#include "tool.h" + +#define MAC_(str) VGAPPEND(vgMAC_,str) + +/*------------------------------------------------------------*/ +/*--- Errors and suppressions ---*/ +/*------------------------------------------------------------*/ + +/* The classification of a faulting address. */ +typedef + enum { + Undescribed, /* as-yet unclassified */ + Stack, + Unknown, /* classification yielded nothing useful */ + Freed, Mallocd, + UserG, /* in a user-defined block; Addrcheck & Memcheck only */ + Mempool, /* in a mempool; Addrcheck & Memcheck only */ + } + AddrKind; + +/* Records info about a faulting address. */ +typedef + struct { // Used by: + AddrKind akind; // ALL + Int blksize; // Freed, Mallocd + Int rwoffset; // Freed, Mallocd + ExeContext* lastchange; // Freed, Mallocd + ThreadId stack_tid; // Stack + Bool maybe_gcc; // True if just below %esp -- could be a gcc bug. + } + AddrInfo; + +typedef + enum { + ParamSupp, // Bad syscall params + CoreMemSupp, // Memory errors in core (pthread ops, signal handling) + + // Use of invalid values of given size (MemCheck only) + Value0Supp, Value1Supp, Value2Supp, Value4Supp, Value8Supp, Value16Supp, + + // Invalid read/write attempt at given size + Addr1Supp, Addr2Supp, Addr4Supp, Addr8Supp, Addr16Supp, + + FreeSupp, // Invalid or mismatching free + OverlapSupp, // Overlapping blocks in memcpy(), strcpy(), etc + LeakSupp, // Something to be suppressed in a leak check. + MempoolSupp, // Memory pool suppression. + } + MAC_SuppKind; + +/* What kind of error it is. */ +typedef + enum { ValueErr, /* Memcheck only */ + CoreMemErr, + AddrErr, + ParamErr, UserErr, /* behaves like an anonymous ParamErr */ + FreeErr, FreeMismatchErr, + OverlapErr, + LeakErr, + IllegalMempoolErr, + } + MAC_ErrorKind; + +/* What kind of memory access is involved in the error? */ +typedef + enum { ReadAxs, WriteAxs, ExecAxs } + AxsKind; + +/* Extra context for memory errors */ +typedef + struct { // Used by: + AxsKind axskind; // AddrErr + Int size; // AddrErr, ValueErr + AddrInfo addrinfo; // {Addr,Free,FreeMismatch,Param,User}Err + Bool isWrite; // ParamErr, UserErr, CoreMemErr + } + MAC_Error; + +/* Extra info for overlap errors */ +typedef + struct { + Addr src; + Addr dst; + Int len; // -1 if unused + } + OverlapExtra; + +/* For malloc()/new/new[] vs. free()/delete/delete[] mismatch checking. */ +typedef + enum { + MAC_AllocMalloc = 0, + MAC_AllocNew = 1, + MAC_AllocNewVec = 2, + MAC_AllocCustom = 3 + } + MAC_AllocKind; + +/* Nb: first two fields must match core's VgHashNode. */ +typedef + struct _MAC_Chunk { + struct _MAC_Chunk* next; + Addr data; // ptr to actual block + UInt size : 30; // size requested + MAC_AllocKind allockind : 2; // which wrapper did the allocation + ExeContext* where; // where it was allocated + } + MAC_Chunk; + +/* Memory pool. Nb: first two fields must match core's VgHashNode. */ +typedef + struct _MAC_Mempool { + struct _MAC_Mempool* next; + Addr pool; // pool identifier + UInt rzB; // pool red-zone size + Bool is_zeroed; // allocations from this pool are zeroed + VgHashTable chunks; // chunks associated with this pool + } + MAC_Mempool; + + +/*------------------------------------------------------------*/ +/*--- Profiling of tools and memory events ---*/ +/*------------------------------------------------------------*/ + +typedef + enum { + VgpCheckMem = VgpFini+1, + VgpSetMem, + VgpESPAdj + } + VgpToolCC; + +/* Define to collect detailed performance info. */ +/* #define MAC_PROFILE_MEMORY */ + +#ifdef MAC_PROFILE_MEMORY +# define N_PROF_EVENTS 150 + +extern UInt MAC_(event_ctr)[N_PROF_EVENTS]; + +# define PROF_EVENT(ev) \ + do { sk_assert((ev) >= 0 && (ev) < N_PROF_EVENTS); \ + MAC_(event_ctr)[ev]++; \ + } while (False); + +#else + +# define PROF_EVENT(ev) /* */ + +#endif /* MAC_PROFILE_MEMORY */ + +/*------------------------------------------------------------*/ +/*--- V and A bits ---*/ +/*------------------------------------------------------------*/ + +#define IS_DISTINGUISHED_SM(smap) \ + ((smap) == &distinguished_secondary_map) + +#define ENSURE_MAPPABLE(addr,caller) \ + do { \ + if (IS_DISTINGUISHED_SM(primary_map[(addr) >> 16])) { \ + primary_map[(addr) >> 16] = alloc_secondary_map(caller); \ + /* VG_(printf)("new 2map because of %p\n", addr); */ \ + } \ + } while(0) + +#define BITARR_SET(aaa_p,iii_p) \ + do { \ + UInt iii = (UInt)iii_p; \ + UChar* aaa = (UChar*)aaa_p; \ + aaa[iii >> 3] |= (1 << (iii & 7)); \ + } while (0) + +#define BITARR_CLEAR(aaa_p,iii_p) \ + do { \ + UInt iii = (UInt)iii_p; \ + UChar* aaa = (UChar*)aaa_p; \ + aaa[iii >> 3] &= ~(1 << (iii & 7)); \ + } while (0) + +#define BITARR_TEST(aaa_p,iii_p) \ + (0 != (((UChar*)aaa_p)[ ((UInt)iii_p) >> 3 ] \ + & (1 << (((UInt)iii_p) & 7)))) \ + + +#define VGM_BIT_VALID 0 +#define VGM_BIT_INVALID 1 + +#define VGM_NIBBLE_VALID 0 +#define VGM_NIBBLE_INVALID 0xF + +#define VGM_BYTE_VALID 0 +#define VGM_BYTE_INVALID 0xFF + +#define VGM_WORD_VALID 0 +#define VGM_WORD_INVALID 0xFFFFFFFF + +#define VGM_EFLAGS_VALID 0xFFFFFFFE +#define VGM_EFLAGS_INVALID 0xFFFFFFFF /* not used */ + +/*------------------------------------------------------------*/ +/*--- Command line options + defaults ---*/ +/*------------------------------------------------------------*/ + +/* Memcheck defines a couple more. */ + +/* Allow loads from partially-valid addresses? default: YES */ +extern Bool MAC_(clo_partial_loads_ok); + +/* Max volume of the freed blocks queue. */ +extern Int MAC_(clo_freelist_vol); + +/* Do leak check at exit? default: NO */ +extern Bool MAC_(clo_leak_check); + +/* How closely should we compare ExeContexts in leak records? default: 2 */ +extern VgRes MAC_(clo_leak_resolution); + +/* In leak check, show reachable-but-not-freed blocks? default: NO */ +extern Bool MAC_(clo_show_reachable); + +/* Assume accesses immediately below %esp are due to gcc-2.96 bugs. + * default: NO*/ +extern Bool MAC_(clo_workaround_gcc296_bugs); + +extern Bool MAC_(process_common_cmd_line_option) ( Char* arg ); +extern void MAC_(print_common_usage) ( void ); +extern void MAC_(print_common_debug_usage) ( void ); + + +/*------------------------------------------------------------*/ +/*--- Variables ---*/ +/*------------------------------------------------------------*/ + +/* For tracking malloc'd blocks */ +extern VgHashTable MAC_(malloc_list); + +/* For tracking memory pools. */ +extern VgHashTable MAC_(mempool_list); + +/* Function pointers for the two tools to track interesting events. */ +extern void (*MAC_(new_mem_heap)) ( Addr a, UInt len, Bool is_inited ); +extern void (*MAC_(ban_mem_heap)) ( Addr a, UInt len ); +extern void (*MAC_(die_mem_heap)) ( Addr a, UInt len ); +extern void (*MAC_(copy_mem_heap))( Addr from, Addr to, UInt len ); + +/* Function pointers for internal sanity checking. */ +extern Bool (*MAC_(check_noaccess))( Addr a, UInt len, Addr* bad_addr ); + +/* Used in describe_addr() */ +extern Bool (*MAC_(describe_addr_supp)) ( Addr a, AddrInfo* ai ); + +/* For VALGRIND_COUNT_LEAKS client request */ +extern Int MAC_(bytes_leaked); +extern Int MAC_(bytes_dubious); +extern Int MAC_(bytes_reachable); +extern Int MAC_(bytes_suppressed); + +/*------------------------------------------------------------*/ +/*--- Functions ---*/ +/*------------------------------------------------------------*/ + +extern void MAC_(pp_AddrInfo) ( Addr a, AddrInfo* ai ); + +extern void MAC_(clear_MAC_Error) ( MAC_Error* err_extra ); + +extern Bool MAC_(shared_recognised_suppression) ( Char* name, Supp* su ); + +extern void* MAC_(new_block) ( Addr p, UInt size, UInt align, UInt rzB, + Bool is_zeroed, MAC_AllocKind kind, + VgHashTable table); +extern void MAC_(handle_free) ( Addr p, UInt rzB, MAC_AllocKind kind ); + +extern void MAC_(create_mempool)(Addr pool, UInt rzB, Bool is_zeroed); +extern void MAC_(destroy_mempool)(Addr pool); +extern void MAC_(mempool_alloc)(Addr pool, Addr addr, UInt size); +extern void MAC_(mempool_free)(Addr pool, Addr addr); + +extern void MAC_(record_address_error) ( ThreadId tid, Addr a, + Int size, Bool isWrite ); +extern void MAC_(record_core_mem_error) ( ThreadId tid, Bool isWrite, + Char* s ); +extern void MAC_(record_param_error) ( ThreadId tid, Addr a, + Bool isWriteLack, Char* msg ); +extern void MAC_(record_jump_error) ( ThreadId tid, Addr a ); +extern void MAC_(record_free_error) ( ThreadId tid, Addr a ); +extern void MAC_(record_freemismatch_error)( ThreadId tid, Addr a ); +extern void MAC_(record_overlap_error) ( Char* function, OverlapExtra* oe ); +extern void MAC_(record_illegal_mempool_error) ( ThreadId tid, Addr pool ); + +extern void MAC_(pp_shared_SkinError) ( Error* err); + +extern MAC_Chunk* MAC_(first_matching_freed_MAC_Chunk)( Bool (*p)(MAC_Chunk*, void*), void* d ); + +extern void MAC_(common_pre_clo_init) ( void ); +extern void MAC_(common_fini) ( void (*leak_check)(void) ); + +extern Bool MAC_(handle_common_client_requests) ( ThreadId tid, + UInt* arg_block, UInt* ret ); + +extern void MAC_(print_malloc_stats) ( void ); + +/* For leak checking */ +extern void MAC_(pp_LeakError)(void* vl, UInt n_this_record, + UInt n_total_records); + +extern void MAC_(do_detect_memory_leaks) ( + Bool is_valid_64k_chunk ( UInt ), + Bool is_valid_address ( Addr ) + ); + +extern REGPARM(1) void MAC_(new_mem_stack_4) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(die_mem_stack_4) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(new_mem_stack_8) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(die_mem_stack_8) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(new_mem_stack_12) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(die_mem_stack_12) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(new_mem_stack_16) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(die_mem_stack_16) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(new_mem_stack_32) ( Addr old_ESP ); +extern REGPARM(1) void MAC_(die_mem_stack_32) ( Addr old_ESP ); +extern void MAC_(die_mem_stack) ( Addr a, UInt len); +extern void MAC_(new_mem_stack) ( Addr a, UInt len); + + +/*------------------------------------------------------------*/ +/*--- Stack pointer adjustment ---*/ +/*------------------------------------------------------------*/ + +/* Some noble preprocessor abuse, to enable Memcheck and Addrcheck to + share this code, but call different functions. + + Note that this code is executed very frequently and must be highly + optimised, which is why I resort to the preprocessor to achieve the + factoring, rather than eg. using function pointers. +*/ + +#define ESP_UPDATE_HANDLERS(ALIGNED4_NEW, ALIGNED4_DIE, \ + ALIGNED8_NEW, ALIGNED8_DIE, \ + UNALIGNED_NEW, UNALIGNED_DIE) \ + \ +void REGPARM(1) MAC_(new_mem_stack_4)(Addr new_ESP) \ +{ \ + PROF_EVENT(110); \ + if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_NEW ( new_ESP ); \ + } else { \ + UNALIGNED_NEW ( new_ESP, 4 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(die_mem_stack_4)(Addr new_ESP) \ +{ \ + PROF_EVENT(120); \ + if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_DIE ( new_ESP-4 ); \ + } else { \ + UNALIGNED_DIE ( new_ESP-4, 4 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(new_mem_stack_8)(Addr new_ESP) \ +{ \ + PROF_EVENT(111); \ + if (IS_ALIGNED8_ADDR(new_ESP)) { \ + ALIGNED8_NEW ( new_ESP ); \ + } else if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_NEW ( new_ESP ); \ + ALIGNED4_NEW ( new_ESP+4 ); \ + } else { \ + UNALIGNED_NEW ( new_ESP, 8 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(die_mem_stack_8)(Addr new_ESP) \ +{ \ + PROF_EVENT(121); \ + if (IS_ALIGNED8_ADDR(new_ESP)) { \ + ALIGNED8_DIE ( new_ESP-8 ); \ + } else if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_DIE ( new_ESP-8 ); \ + ALIGNED4_DIE ( new_ESP-4 ); \ + } else { \ + UNALIGNED_DIE ( new_ESP-8, 8 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(new_mem_stack_12)(Addr new_ESP) \ +{ \ + PROF_EVENT(112); \ + if (IS_ALIGNED8_ADDR(new_ESP)) { \ + ALIGNED8_NEW ( new_ESP ); \ + ALIGNED4_NEW ( new_ESP+8 ); \ + } else if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_NEW ( new_ESP ); \ + ALIGNED8_NEW ( new_ESP+4 ); \ + } else { \ + UNALIGNED_NEW ( new_ESP, 12 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(die_mem_stack_12)(Addr new_ESP) \ +{ \ + PROF_EVENT(122); \ + /* Note the -12 in the test */ \ + if (IS_ALIGNED8_ADDR(new_ESP-12)) { \ + ALIGNED8_DIE ( new_ESP-12 ); \ + ALIGNED4_DIE ( new_ESP-4 ); \ + } else if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_DIE ( new_ESP-12 ); \ + ALIGNED8_DIE ( new_ESP-8 ); \ + } else { \ + UNALIGNED_DIE ( new_ESP-12, 12 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(new_mem_stack_16)(Addr new_ESP) \ +{ \ + PROF_EVENT(113); \ + if (IS_ALIGNED8_ADDR(new_ESP)) { \ + ALIGNED8_NEW ( new_ESP ); \ + ALIGNED8_NEW ( new_ESP+8 ); \ + } else if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_NEW ( new_ESP ); \ + ALIGNED8_NEW ( new_ESP+4 ); \ + ALIGNED4_NEW ( new_ESP+12 ); \ + } else { \ + UNALIGNED_NEW ( new_ESP, 16 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(die_mem_stack_16)(Addr new_ESP) \ +{ \ + PROF_EVENT(123); \ + if (IS_ALIGNED8_ADDR(new_ESP)) { \ + ALIGNED8_DIE ( new_ESP-16 ); \ + ALIGNED8_DIE ( new_ESP-8 ); \ + } else if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_DIE ( new_ESP-16 ); \ + ALIGNED8_DIE ( new_ESP-12 ); \ + ALIGNED4_DIE ( new_ESP-4 ); \ + } else { \ + UNALIGNED_DIE ( new_ESP-16, 16 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(new_mem_stack_32)(Addr new_ESP) \ +{ \ + PROF_EVENT(114); \ + if (IS_ALIGNED8_ADDR(new_ESP)) { \ + ALIGNED8_NEW ( new_ESP ); \ + ALIGNED8_NEW ( new_ESP+8 ); \ + ALIGNED8_NEW ( new_ESP+16 ); \ + ALIGNED8_NEW ( new_ESP+24 ); \ + } else if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_NEW ( new_ESP ); \ + ALIGNED8_NEW ( new_ESP+4 ); \ + ALIGNED8_NEW ( new_ESP+12 ); \ + ALIGNED8_NEW ( new_ESP+20 ); \ + ALIGNED4_NEW ( new_ESP+28 ); \ + } else { \ + UNALIGNED_NEW ( new_ESP, 32 ); \ + } \ +} \ + \ +void REGPARM(1) MAC_(die_mem_stack_32)(Addr new_ESP) \ +{ \ + PROF_EVENT(124); \ + if (IS_ALIGNED8_ADDR(new_ESP)) { \ + ALIGNED8_DIE ( new_ESP-32 ); \ + ALIGNED8_DIE ( new_ESP-24 ); \ + ALIGNED8_DIE ( new_ESP-16 ); \ + ALIGNED8_DIE ( new_ESP- 8 ); \ + } else if (IS_ALIGNED4_ADDR(new_ESP)) { \ + ALIGNED4_DIE ( new_ESP-32 ); \ + ALIGNED8_DIE ( new_ESP-28 ); \ + ALIGNED8_DIE ( new_ESP-20 ); \ + ALIGNED8_DIE ( new_ESP-12 ); \ + ALIGNED4_DIE ( new_ESP-4 ); \ + } else { \ + UNALIGNED_DIE ( new_ESP-32, 32 ); \ + } \ +} \ + \ +void MAC_(new_mem_stack) ( Addr a, UInt len ) \ +{ \ + PROF_EVENT(115); \ + UNALIGNED_NEW ( a, len ); \ +} \ + \ +void MAC_(die_mem_stack) ( Addr a, UInt len ) \ +{ \ + PROF_EVENT(125); \ + UNALIGNED_DIE ( a, len ); \ +} + +#endif /* __MAC_SHARED_H */ + +/*--------------------------------------------------------------------*/ +/*--- end mac_shared.h ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/mc_asm.h b/VEX/head20041019/memcheck/mc_asm.h new file mode 100644 index 0000000000..cf4a736f24 --- /dev/null +++ b/VEX/head20041019/memcheck/mc_asm.h @@ -0,0 +1,43 @@ + +/*--------------------------------------------------------------------*/ +/*--- mc_asm.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#ifndef __MC_ASM_H +#define __MC_ASM_H + +#include "tool_asm.h" + +#define MC_(str) VGAPPEND(vgMemCheck_,str) + +#endif /* __MC_ASM_H */ + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/VEX/head20041019/memcheck/mc_clientreqs.c b/VEX/head20041019/memcheck/mc_clientreqs.c new file mode 100644 index 0000000000..65b5f650fc --- /dev/null +++ b/VEX/head20041019/memcheck/mc_clientreqs.c @@ -0,0 +1,288 @@ + +/*--------------------------------------------------------------------*/ +/*--- For when the client advises Valgrind about memory ---*/ +/*--- permissions. ---*/ +/*--- mc_clientreqs.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mc_include.h" + +#include "memcheck.h" /* for VG_USERREQ__* */ + + +/*------------------------------------------------------------*/ +/*--- General client block management. ---*/ +/*------------------------------------------------------------*/ + +/* This is managed as an expanding array of client block descriptors. + Indices of live descriptors are issued to the client, so it can ask + to free them later. Therefore we cannot slide live entries down + over dead ones. Instead we must use free/inuse flags and scan for + an empty slot at allocation time. This in turn means allocation is + relatively expensive, so we hope this does not happen too often. +*/ + +typedef + enum { CG_NotInUse, CG_NoAccess, CG_Writable, CG_Readable } + CGenBlockKind; + +typedef + struct { + Addr start; + UInt size; + ExeContext* where; + CGenBlockKind kind; + } + CGenBlock; + +/* This subsystem is self-initialising. */ +static UInt vg_cgb_size = 0; +static UInt vg_cgb_used = 0; +static CGenBlock* vg_cgbs = NULL; + +/* Stats for this subsystem. */ +static UInt vg_cgb_used_MAX = 0; /* Max in use. */ +static UInt vg_cgb_allocs = 0; /* Number of allocs. */ +static UInt vg_cgb_discards = 0; /* Number of discards. */ +static UInt vg_cgb_search = 0; /* Number of searches. */ + + +static +Int vg_alloc_client_block ( void ) +{ + UInt i, sz_new; + CGenBlock* cgbs_new; + + vg_cgb_allocs++; + + for (i = 0; i < vg_cgb_used; i++) { + vg_cgb_search++; + if (vg_cgbs[i].kind == CG_NotInUse) + return i; + } + + /* Not found. Try to allocate one at the end. */ + if (vg_cgb_used < vg_cgb_size) { + vg_cgb_used++; + return vg_cgb_used-1; + } + + /* Ok, we have to allocate a new one. */ + sk_assert(vg_cgb_used == vg_cgb_size); + sz_new = (vg_cgbs == NULL) ? 10 : (2 * vg_cgb_size); + + cgbs_new = VG_(malloc)( sz_new * sizeof(CGenBlock) ); + for (i = 0; i < vg_cgb_used; i++) + cgbs_new[i] = vg_cgbs[i]; + + if (vg_cgbs != NULL) + VG_(free)( vg_cgbs ); + vg_cgbs = cgbs_new; + + vg_cgb_size = sz_new; + vg_cgb_used++; + if (vg_cgb_used > vg_cgb_used_MAX) + vg_cgb_used_MAX = vg_cgb_used; + return vg_cgb_used-1; +} + + +/*------------------------------------------------------------*/ +/*--- Externally visible functions. ---*/ +/*------------------------------------------------------------*/ + +void MC_(show_client_block_stats) ( void ) +{ + VG_(message)(Vg_DebugMsg, + "general CBs: %d allocs, %d discards, %d maxinuse, %d search", + vg_cgb_allocs, vg_cgb_discards, vg_cgb_used_MAX, vg_cgb_search + ); +} + +static Bool find_addr(VgHashNode* sh_ch, void* ap) +{ + MAC_Chunk *m = (MAC_Chunk*)sh_ch; + Addr a = *(Addr*)ap; + + return VG_(addr_is_in_block)(a, m->data, m->size); +} + +Bool MC_(client_perm_maybe_describe)( Addr a, AddrInfo* ai ) +{ + UInt i; + /* VG_(printf)("try to identify %d\n", a); */ + + /* Perhaps it's a general block ? */ + for (i = 0; i < vg_cgb_used; i++) { + if (vg_cgbs[i].kind == CG_NotInUse) + continue; + if (VG_(addr_is_in_block)(a, vg_cgbs[i].start, vg_cgbs[i].size)) { + MAC_Mempool **d, *mp; + + /* OK - maybe it's a mempool, too? */ + mp = (MAC_Mempool*)VG_(HT_get_node)(MAC_(mempool_list), + (UInt)vg_cgbs[i].start, + (VgHashNode***)&d); + if(mp != NULL) { + if(mp->chunks != NULL) { + MAC_Chunk *mc; + + mc = (MAC_Chunk*)VG_(HT_first_match)(mp->chunks, find_addr, &a); + if(mc != NULL) { + ai->akind = UserG; + ai->blksize = mc->size; + ai->rwoffset = (Int)(a) - (Int)mc->data; + ai->lastchange = mc->where; + return True; + } + } + ai->akind = Mempool; + ai->blksize = vg_cgbs[i].size; + ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start); + ai->lastchange = vg_cgbs[i].where; + return True; + } + ai->akind = UserG; + ai->blksize = vg_cgbs[i].size; + ai->rwoffset = (Int)(a) - (Int)(vg_cgbs[i].start); + ai->lastchange = vg_cgbs[i].where; + return True; + } + } + return False; +} + +Bool SK_(handle_client_request) ( ThreadId tid, UInt* arg, UInt* ret ) +{ + Int i; + Bool ok; + Addr bad_addr; + + if (!VG_IS_SKIN_USERREQ('M','C',arg[0]) + && VG_USERREQ__MALLOCLIKE_BLOCK != arg[0] + && VG_USERREQ__FREELIKE_BLOCK != arg[0] + && VG_USERREQ__CREATE_MEMPOOL != arg[0] + && VG_USERREQ__DESTROY_MEMPOOL != arg[0] + && VG_USERREQ__MEMPOOL_ALLOC != arg[0] + && VG_USERREQ__MEMPOOL_FREE != arg[0]) + return False; + + switch (arg[0]) { + case VG_USERREQ__CHECK_WRITABLE: /* check writable */ + ok = MC_(check_writable) ( arg[1], arg[2], &bad_addr ); + if (!ok) + MC_(record_user_error) ( tid, bad_addr, True ); + *ret = ok ? (UInt)NULL : bad_addr; + break; + + case VG_USERREQ__CHECK_READABLE: /* check readable */ + ok = MC_(check_readable) ( arg[1], arg[2], &bad_addr ); + if (!ok) + MC_(record_user_error) ( tid, bad_addr, False ); + *ret = ok ? (UInt)NULL : bad_addr; + break; + + case VG_USERREQ__DO_LEAK_CHECK: + MC_(detect_memory_leaks)(); + *ret = 0; /* return value is meaningless */ + break; + + case VG_USERREQ__MAKE_NOACCESS: /* make no access */ + i = vg_alloc_client_block(); + /* VG_(printf)("allocated %d %p\n", i, vg_cgbs); */ + vg_cgbs[i].kind = CG_NoAccess; + vg_cgbs[i].start = arg[1]; + vg_cgbs[i].size = arg[2]; + vg_cgbs[i].where = VG_(get_ExeContext) ( tid ); + MC_(make_noaccess) ( arg[1], arg[2] ); + *ret = i; + break; + + case VG_USERREQ__MAKE_WRITABLE: /* make writable */ + i = vg_alloc_client_block(); + vg_cgbs[i].kind = CG_Writable; + vg_cgbs[i].start = arg[1]; + vg_cgbs[i].size = arg[2]; + vg_cgbs[i].where = VG_(get_ExeContext) ( tid ); + MC_(make_writable) ( arg[1], arg[2] ); + *ret = i; + break; + + case VG_USERREQ__MAKE_READABLE: /* make readable */ + i = vg_alloc_client_block(); + vg_cgbs[i].kind = CG_Readable; + vg_cgbs[i].start = arg[1]; + vg_cgbs[i].size = arg[2]; + vg_cgbs[i].where = VG_(get_ExeContext) ( tid ); + MC_(make_readable) ( arg[1], arg[2] ); + *ret = i; + break; + + case VG_USERREQ__DISCARD: /* discard */ + if (vg_cgbs == NULL + || arg[2] >= vg_cgb_used || vg_cgbs[arg[2]].kind == CG_NotInUse) + return 1; + sk_assert(arg[2] >= 0 && arg[2] < vg_cgb_used); + vg_cgbs[arg[2]].kind = CG_NotInUse; + vg_cgb_discards++; + *ret = 0; + break; + + case VG_USERREQ__GET_VBITS: + /* Returns: 1 == OK, 2 == alignment error, 3 == addressing + error. */ + /* VG_(printf)("get_vbits %p %p %d\n", arg[1], arg[2], arg[3] ); */ + *ret = MC_(get_or_set_vbits_for_client) + ( tid, arg[1], arg[2], arg[3], False /* get them */ ); + break; + + case VG_USERREQ__SET_VBITS: + /* Returns: 1 == OK, 2 == alignment error, 3 == addressing + error. */ + /* VG_(printf)("set_vbits %p %p %d\n", arg[1], arg[2], arg[3] ); */ + *ret = MC_(get_or_set_vbits_for_client) + ( tid, arg[1], arg[2], arg[3], True /* set them */ ); + break; + + default: + if (MAC_(handle_common_client_requests)(tid, arg, ret )) { + return True; + } else { + VG_(message)(Vg_UserMsg, + "Warning: unknown memcheck client request code %d", + arg[0]); + return False; + } + } + return True; +} + + +/*--------------------------------------------------------------------*/ +/*--- end mc_clientreqs.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/mc_errcontext.c b/VEX/head20041019/memcheck/mc_errcontext.c new file mode 100644 index 0000000000..70e20577bf --- /dev/null +++ b/VEX/head20041019/memcheck/mc_errcontext.c @@ -0,0 +1,159 @@ + +/*--------------------------------------------------------------------*/ +/*--- Management of memory error messages. ---*/ +/*--- mc_errcontext.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mc_include.h" + +/*------------------------------------------------------------*/ +/*--- Printing errors ---*/ +/*------------------------------------------------------------*/ + +void SK_(pp_SkinError) ( Error* err ) +{ + MAC_Error* err_extra = VG_(get_error_extra)(err); + + switch (VG_(get_error_kind)(err)) { + case CoreMemErr: + if (err_extra->isWrite) { + VG_(message)(Vg_UserMsg, + "%s contains unaddressable byte(s)", VG_(get_error_string)(err)); + } else { + VG_(message)(Vg_UserMsg, + "%s contains uninitialised or unaddressable byte(s)", + VG_(get_error_string)(err)); + } + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + break; + + case ValueErr: + if (err_extra->size == 0) { + VG_(message)( + Vg_UserMsg, + "Conditional jump or move depends on uninitialised value(s)"); + } else { + VG_(message)(Vg_UserMsg, + "Use of uninitialised value of size %d", + err_extra->size); + } + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + break; + + case ParamErr: + if (err_extra->isWrite) { + VG_(message)(Vg_UserMsg, + "Syscall param %s contains unaddressable byte(s)", + VG_(get_error_string)(err)); + } else { + VG_(message)(Vg_UserMsg, + "Syscall param %s contains uninitialised or " + "unaddressable byte(s)", + VG_(get_error_string)(err)); + } + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); + break; + + case UserErr: + if (err_extra->isWrite) { + VG_(message)(Vg_UserMsg, + "Unaddressable byte(s) found during client check request"); + } else { + VG_(message)(Vg_UserMsg, + "Uninitialised or " + "unaddressable byte(s) found during client check request"); + } + VG_(pp_ExeContext)( VG_(get_error_where)(err) ); + MAC_(pp_AddrInfo)(VG_(get_error_address)(err), &err_extra->addrinfo); + break; + + default: + MAC_(pp_shared_SkinError)(err); + break; + } +} + +/*------------------------------------------------------------*/ +/*--- Recording errors ---*/ +/*------------------------------------------------------------*/ + +/* Creates a copy of the `extra' part, updates the copy with address info if + necessary, and returns the copy. */ +/* This one called from generated code and non-generated code. */ +void MC_(record_value_error) ( ThreadId tid, Int size ) +{ + MAC_Error err_extra; + + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.size = size; + VG_(maybe_record_error)( tid, ValueErr, /*addr*/0, /*s*/NULL, &err_extra ); +} + +/* This called from non-generated code */ + +void MC_(record_user_error) ( ThreadId tid, Addr a, Bool isWrite ) +{ + MAC_Error err_extra; + + sk_assert(VG_INVALID_THREADID != tid); + MAC_(clear_MAC_Error)( &err_extra ); + err_extra.addrinfo.akind = Undescribed; + err_extra.isWrite = isWrite; + VG_(maybe_record_error)( tid, UserErr, a, /*s*/NULL, &err_extra ); +} + +/*------------------------------------------------------------*/ +/*--- Suppressions ---*/ +/*------------------------------------------------------------*/ + +Bool SK_(recognised_suppression) ( Char* name, Supp* su ) +{ + SuppKind skind; + + if (MAC_(shared_recognised_suppression)(name, su)) + return True; + + /* Extra suppressions not used by Addrcheck */ + else if (VG_STREQ(name, "Cond")) skind = Value0Supp; + else if (VG_STREQ(name, "Value0")) skind = Value0Supp;/* backwards compat */ + else if (VG_STREQ(name, "Value1")) skind = Value1Supp; + else if (VG_STREQ(name, "Value2")) skind = Value2Supp; + else if (VG_STREQ(name, "Value4")) skind = Value4Supp; + else if (VG_STREQ(name, "Value8")) skind = Value8Supp; + else if (VG_STREQ(name, "Value16")) skind = Value16Supp; + else + return False; + + VG_(set_supp_kind)(su, skind); + return True; +} + +/*--------------------------------------------------------------------*/ +/*--- end mc_errcontext.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/mc_from_ucode.c b/VEX/head20041019/memcheck/mc_from_ucode.c new file mode 100644 index 0000000000..a36fcbc21e --- /dev/null +++ b/VEX/head20041019/memcheck/mc_from_ucode.c @@ -0,0 +1,661 @@ + +/*--------------------------------------------------------------------*/ +/*--- Generate code for tool-specific UInstrs. ---*/ +/*--- mc_from_ucode.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mc_include.h" + +/*------------------------------------------------------------*/ +/*--- Renamings of frequently-used global functions. ---*/ +/*------------------------------------------------------------*/ + +#define dis VG_(print_codegen) + +/*------------------------------------------------------------*/ +/*--- Instruction emission -- turning final uinstrs back ---*/ +/*--- into x86 code. ---*/ +/*------------------------------------------------------------*/ + +/* See the corresponding comment at the top of vg_from_ucode.c to find out + * how all this works */ + +/*----------------------------------------------------*/ +/*--- v-size (4, or 2 with OSO) insn emitters ---*/ +/*----------------------------------------------------*/ + +static void emit_testv_lit_reg ( Int sz, UInt lit, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsOSZACP); + if (sz == 2) { + VG_(emitB) ( 0x66 ); + } else { + sk_assert(sz == 4); + } + VG_(emitB) ( 0xF7 ); /* Grp3 Ev */ + VG_(emit_amode_ereg_greg) ( reg, 0 /* Grp3 subopcode for TEST */ ); + if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit ); + if (dis) + VG_(printf)("\n\t\ttest%c $0x%x, %s\n", nameISize(sz), + lit, nameIReg(sz,reg)); +} + +static void emit_testv_lit_offregmem ( Int sz, UInt lit, Int off, Int reg ) +{ + VG_(new_emit)(False, FlagsEmpty, FlagsOSZACP); + if (sz == 2) { + VG_(emitB) ( 0x66 ); + } else { + sk_assert(sz == 4); + } + VG_(emitB) ( 0xF7 ); /* Grp3 Ev */ + VG_(emit_amode_offregmem_reg) ( off, reg, 0 /* Grp3 subopcode for TEST */ ); + if (sz == 2) VG_(emitW) ( lit ); else VG_(emitL) ( lit ); + if (dis) + VG_(printf)("\n\t\ttest%c $%d, 0x%x(%s)\n", + nameISize(sz), lit, off, nameIReg(4,reg) ); +} + +/*----------------------------------------------------*/ +/*--- Instruction synthesisers ---*/ +/*----------------------------------------------------*/ + +/* Synthesise a minimal test (and which discards result) of reg32 + against lit. It's always safe do simply + emit_testv_lit_reg ( 4, lit, reg32 ) + but we try to do better when possible. +*/ +static void synth_minimal_test_lit_reg ( UInt lit, Int reg32 ) +{ + if ((lit & 0xFFFFFF00) == 0 && reg32 < 4) { + /* We can get away with a byte insn. */ + VG_(emit_testb_lit_reg) ( False, lit, reg32 ); + } + else + if ((lit & 0xFFFF0000) == 0) { + /* Literal fits in 16 bits; do a word insn. */ + emit_testv_lit_reg ( 2, lit, reg32 ); + } + else { + /* Totally general ... */ + emit_testv_lit_reg ( 4, lit, reg32 ); + } +} + +/*----------------------------------------------------*/ +/*--- Top level of the uinstr -> x86 translation. ---*/ +/*----------------------------------------------------*/ + +static void synth_LOADV ( Int sz, Int a_reg, Int tv_reg, + RRegSet regs_live_before, + RRegSet regs_live_after ) +{ + Addr helper; + UInt argv[] = { a_reg }; + UInt tagv[] = { RealReg }; + + switch (sz) { + case 4: helper = (Addr) & MC_(helperc_LOADV4); break; + case 2: helper = (Addr) & MC_(helperc_LOADV2); break; + case 1: helper = (Addr) & MC_(helperc_LOADV1); break; + default: VG_(skin_panic)("synth_LOADV"); + } + VG_(synth_ccall) ( helper, 1, 1, argv, tagv, tv_reg, + regs_live_before, regs_live_after ); +} + + +static void synth_STOREV ( Int sz, Int tv_tag, Int tv_val, Int a_reg, + RRegSet regs_live_before, + RRegSet regs_live_after ) +{ + Addr helper; + UInt argv[] = { a_reg, tv_val }; + Tag tagv[] = { RealReg, tv_tag }; + + sk_assert(tv_tag == RealReg || tv_tag == Literal); + switch (sz) { + case 4: helper = (Addr) MC_(helperc_STOREV4); break; + case 2: helper = (Addr) MC_(helperc_STOREV2); break; + case 1: helper = (Addr) MC_(helperc_STOREV1); break; + default: VG_(skin_panic)("synth_STOREV"); + } + VG_(synth_ccall) ( helper, 2, 2, argv, tagv, INVALID_REALREG, + regs_live_before, regs_live_after ); +} + + +static void synth_SETV ( Int sz, Int reg ) +{ + UInt val; + switch (sz) { + case 4: val = 0x00000000; break; + case 2: val = 0xFFFF0000; break; + case 1: val = 0xFFFFFF00; break; + case 0: val = 0xFFFFFFFE; break; + default: VG_(skin_panic)("synth_SETV"); + } + VG_(emit_movv_lit_reg) ( 4, val, reg ); +} + + +static void synth_TESTV ( Int sz, Int tag, Int val ) +{ + Int tgt; /* jump target */ + + /* Important note. Note that that the calls to + MC_(helper_value_check[0124]_fail) must be compact helpers due to + the codegen scheme used below. Since there are a shortage of + compact helper slots, and since the size==1 case is never + actually used, we assert against it. */ + sk_assert(sz == 0 || sz == 2 || sz == 4); + + VG_(init_target)(&tgt); + + sk_assert(tag == ArchReg || tag == RealReg); + if (tag == ArchReg) { + switch (sz) { + case 4: + emit_testv_lit_offregmem ( + 4, 0xFFFFFFFF, VG_(shadow_reg_offset)(val), R_EBP ); + break; + case 2: + emit_testv_lit_offregmem ( + 4, 0x0000FFFF, VG_(shadow_reg_offset)(val), R_EBP ); + break; + case 1: + if (val < 4) { + emit_testv_lit_offregmem ( + 4, 0x000000FF, VG_(shadow_reg_offset)(val), R_EBP ); + } else { + emit_testv_lit_offregmem ( + 4, 0x0000FF00, VG_(shadow_reg_offset)(val-4), R_EBP ); + } + break; + case 0: + /* should never happen */ + default: + VG_(skin_panic)("synth_TESTV(ArchReg)"); + } + } else { + switch (sz) { + case 4: + /* Works, but holds the entire 32-bit literal, hence + generating a 6-byte insn. We want to know if any bits + in the reg are set, but since this is for the full reg, + we might as well compare it against zero, which can be + done with a shorter insn. */ + /* synth_minimal_test_lit_reg ( 0xFFFFFFFF, val ); */ + VG_(emit_cmpl_zero_reg) ( False, val ); + break; + case 2: + synth_minimal_test_lit_reg ( 0x0000FFFF, val ); + break; + case 1: + synth_minimal_test_lit_reg ( 0x000000FF, val ); + break; + case 0: + synth_minimal_test_lit_reg ( 0x00000001, val ); + break; + default: + VG_(skin_panic)("synth_TESTV(RealReg)"); + } + } + + /* predict taken because we assume failures are rare */ + VG_(emit_jcondshort_target) ( False, CondZ, &tgt, JP_TAKEN ); + + VG_(synth_call) ( + False, + ( sz==4 + ? VG_(helper_offset)((Addr) & MC_(helper_value_check4_fail)) + : ( sz==2 + ? VG_(helper_offset)((Addr) & MC_(helper_value_check2_fail)) + : ( sz==1 + ? VG_(helper_offset)((Addr) & MC_(helper_value_check1_fail)) + : VG_(helper_offset)((Addr) & MC_(helper_value_check0_fail))))), + False, FlagsEmpty, FlagsOSZACP /* helpers don't preserve flags */ + ); + VG_(target_forward)(&tgt); +} + + +static void synth_GETV ( Int sz, Int arch, Int reg ) +{ + /* VG_(printf)("synth_GETV %d of Arch %s\n", sz, nameIReg(sz, arch)); */ + switch (sz) { + case 4: + VG_(emit_movv_offregmem_reg) ( 4, VG_(shadow_reg_offset)(arch), + R_EBP, reg ); + break; + case 2: + VG_(emit_movzwl_offregmem_reg) ( False, VG_(shadow_reg_offset)(arch), + R_EBP, reg ); + VG_(emit_nonshiftopv_lit_reg) ( False, 4, OR, 0xFFFF0000, reg ); + break; + case 1: + if (arch < 4) { + VG_(emit_movzbl_offregmem_reg) ( False, VG_(shadow_reg_offset)(arch), + R_EBP, reg ); + } else { + VG_(emit_movzbl_offregmem_reg) ( False, VG_(shadow_reg_offset)(arch-4)+1, + R_EBP, reg ); + } + VG_(emit_nonshiftopv_lit_reg) ( False, 4, OR, 0xFFFFFF00, reg ); + break; + default: + VG_(skin_panic)("synth_GETV"); + } +} + + +static void synth_PUTV ( Int sz, Int srcTag, UInt lit_or_reg, Int arch ) +{ + if (srcTag == Literal) { + /* PUTV with a Literal is only ever used to set the corresponding + ArchReg to `all valid'. Should really be a kind of SETV. */ + UInt lit = lit_or_reg; + switch (sz) { + case 4: + sk_assert(lit == 0x00000000); + VG_(emit_movv_lit_offregmem) ( 4, 0x00000000, + VG_(shadow_reg_offset)(arch), R_EBP ); + break; + case 2: + sk_assert(lit == 0xFFFF0000); + VG_(emit_movv_lit_offregmem) ( 2, 0x0000, + VG_(shadow_reg_offset)(arch), R_EBP ); + break; + case 1: + sk_assert(lit == 0xFFFFFF00); + if (arch < 4) { + VG_(emit_movb_lit_offregmem) ( 0x00, + VG_(shadow_reg_offset)(arch), R_EBP ); + } else { + VG_(emit_movb_lit_offregmem) ( 0x00, + VG_(shadow_reg_offset)(arch-4)+1, + R_EBP ); + } + break; + default: + VG_(skin_panic)("synth_PUTV(lit)"); + } + + } else { + + UInt reg; + sk_assert(srcTag == RealReg); + + if (sz == 1 && lit_or_reg >= 4) { + VG_(emit_swapl_reg_EAX) ( lit_or_reg ); + reg = R_EAX; + } else { + reg = lit_or_reg; + } + + if (sz == 1) sk_assert(reg < 4); + + switch (sz) { + case 4: + VG_(emit_movv_reg_offregmem) ( 4, reg, + VG_(shadow_reg_offset)(arch), R_EBP ); + break; + case 2: + VG_(emit_movv_reg_offregmem) ( 2, reg, + VG_(shadow_reg_offset)(arch), R_EBP ); + break; + case 1: + if (arch < 4) { + VG_(emit_movb_reg_offregmem) ( reg, + VG_(shadow_reg_offset)(arch), R_EBP ); + } else { + VG_(emit_movb_reg_offregmem) ( reg, + VG_(shadow_reg_offset)(arch-4)+1, R_EBP ); + } + break; + default: + VG_(skin_panic)("synth_PUTV(reg)"); + } + + if (sz == 1 && lit_or_reg >= 4) { + VG_(emit_swapl_reg_EAX) ( lit_or_reg ); + } + } +} + + +static void synth_GETVF ( Int reg ) +{ + VG_(emit_movv_offregmem_reg) ( 4, VG_(shadow_flags_offset)(), R_EBP, reg ); + /* paranoia only; should be unnecessary ... */ + /* VG_(emit_nonshiftopv_lit_reg) ( 4, OR, 0xFFFFFFFE, reg ); */ +} + + +static void synth_PUTVF ( UInt reg ) +{ + VG_(emit_movv_reg_offregmem) ( 4, reg, VG_(shadow_flags_offset)(), R_EBP ); +} + + +static void synth_TAG1_op ( TagOp op, Int reg, RRegSet regs_live_after ) +{ + switch (op) { + + /* Scheme is + neg %reg -- CF = %reg==0 ? 0 : 1 + sbbl %reg, %reg -- %reg = -CF + or 0xFFFFFFFE, %reg -- invalidate all bits except lowest + */ + case Tag_PCast40: + VG_(emit_unaryopv_reg)(False, 4, NEG, reg); + VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFFFE, reg); + break; + case Tag_PCast20: + VG_(emit_unaryopv_reg)(False, 2, NEG, reg); + VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFFFE, reg); + break; + case Tag_PCast10: + if (reg >= 4) { + VG_(emit_swapl_reg_EAX)(reg); + VG_(emit_unaryopb_reg)(False, NEG, R_EAX); + VG_(emit_swapl_reg_EAX)(reg); + } else { + VG_(emit_unaryopb_reg)(False, NEG, reg); + } + VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFFFE, reg); + break; + + /* Scheme is + andl $1, %reg -- %reg is 0 or 1 + negl %reg -- %reg is 0 or 0xFFFFFFFF + and possibly an OR to invalidate unused bits. + */ + case Tag_PCast04: + VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x00000001, reg); + VG_(emit_unaryopv_reg)(False, 4, NEG, reg); + break; + case Tag_PCast02: + VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x00000001, reg); + VG_(emit_unaryopv_reg)(False, 4, NEG, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, reg); + break; + case Tag_PCast01: + VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x00000001, reg); + VG_(emit_unaryopv_reg)(False, 4, NEG, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFF00, reg); + break; + + /* Scheme is + shl $24, %reg -- make irrelevant bits disappear + negl %reg -- CF = %reg==0 ? 0 : 1 + sbbl %reg, %reg -- %reg = -CF + and possibly an OR to invalidate unused bits. + */ + case Tag_PCast14: + VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); + VG_(emit_unaryopv_reg)(False, 4, NEG, reg); + VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); + break; + case Tag_PCast12: + VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); + VG_(emit_unaryopv_reg)(False, 4, NEG, reg); + VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, reg); + break; + case Tag_PCast11: + VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); + VG_(emit_unaryopv_reg)(False, 4, NEG, reg); + VG_(emit_nonshiftopv_reg_reg)(False, 4, SBB, reg, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFF00, reg); + break; + + /* We use any non-live reg (except %reg) as a temporary, + or push/pop %ebp if none available: + (%dead_reg = any dead reg, else choose anything other than %reg) + (pushl %dead_reg if live) + movl %reg, %dead_reg + negl %dead_reg + orl %dead_reg, %reg + (popl %dead_reg if live) + This sequence turns out to be correct regardless of the + operation width. + */ + case Tag_Left4: + case Tag_Left2: + case Tag_Left1: { + Bool push = True; + UInt dead_reg = R_ESP; + Int i, reg_of_i; + + for (i = 0; i < VG_MAX_REALREGS; i++) { + if (! IS_RREG_LIVE(i, regs_live_after)) { + reg_of_i = VG_(rank_to_realreg)(i); + if (reg != reg_of_i) { + dead_reg = reg_of_i; + push = False; + break; + } + } + } + + if (push) { + dead_reg = (reg != R_EAX) ? R_EAX : R_EBX; + VG_(emit_pushv_reg)(4, dead_reg); + } + + VG_(emit_movv_reg_reg)(4, reg, dead_reg); + VG_(emit_unaryopv_reg)(False, 4, NEG, dead_reg); + VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, dead_reg, reg); + + if (push) + VG_(emit_popv_reg)(4, dead_reg); + break; + } + + /* These are all fairly obvious; do the op and then, if + necessary, invalidate unused bits. */ + case Tag_SWiden14: + VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); + VG_(emit_shiftopv_lit_reg)(False, 4, SAR, 24, reg); + break; + case Tag_SWiden24: + VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 16, reg); + VG_(emit_shiftopv_lit_reg)(False, 4, SAR, 16, reg); + break; + case Tag_SWiden12: + VG_(emit_shiftopv_lit_reg)(False, 4, SHL, 24, reg); + VG_(emit_shiftopv_lit_reg)(False, 4, SAR, 24, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, reg); + break; + case Tag_ZWiden14: + VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x000000FF, reg); + break; + case Tag_ZWiden24: + VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x0000FFFF, reg); + break; + case Tag_ZWiden12: + VG_(emit_nonshiftopv_lit_reg)(False, 4, AND, 0x000000FF, reg); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, reg); + break; + + default: + VG_(skin_panic)("synth_TAG1_op"); + } +} + + +static void synth_TAG2_op ( TagOp op, Int regs, Int regd ) +{ + switch (op) { + + /* UifU is implemented by OR, since 1 means Undefined. */ + case Tag_UifU4: + case Tag_UifU2: + case Tag_UifU1: + case Tag_UifU0: + VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, regs, regd); + break; + + /* DifD is implemented by AND, since 0 means Defined. */ + case Tag_DifD4: + case Tag_DifD2: + case Tag_DifD1: + VG_(emit_nonshiftopv_reg_reg)(False, 4, AND, regs, regd); + break; + + /* ImproveAND(value, tags) = value OR tags. + Defined (0) value 0s give defined (0); all other -> undefined (1). + value is in regs; tags is in regd. + Be paranoid and invalidate unused bits; I don't know whether + or not this is actually necessary. */ + case Tag_ImproveAND4_TQ: + VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, regs, regd); + break; + case Tag_ImproveAND2_TQ: + VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, regs, regd); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, regd); + break; + case Tag_ImproveAND1_TQ: + VG_(emit_nonshiftopv_reg_reg)(False, 4, OR, regs, regd); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFF00, regd); + break; + + /* ImproveOR(value, tags) = (not value) OR tags. + Defined (0) value 1s give defined (0); all other -> undefined (1). + value is in regs; tags is in regd. + To avoid trashing value, this is implemented (re de Morgan) as + not (value AND (not tags)) + Be paranoid and invalidate unused bits; I don't know whether + or not this is actually necessary. */ + case Tag_ImproveOR4_TQ: + VG_(emit_unaryopv_reg)(False, 4, NOT, regd); + VG_(emit_nonshiftopv_reg_reg)(False, 4, AND, regs, regd); + VG_(emit_unaryopv_reg)(False, 4, NOT, regd); + break; + case Tag_ImproveOR2_TQ: + VG_(emit_unaryopv_reg)(False, 4, NOT, regd); + VG_(emit_nonshiftopv_reg_reg)(False, 4, AND, regs, regd); + VG_(emit_unaryopv_reg)(False, 4, NOT, regd); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFF0000, regd); + break; + case Tag_ImproveOR1_TQ: + VG_(emit_unaryopv_reg)(False, 4, NOT, regd); + VG_(emit_nonshiftopv_reg_reg)(False, 4, AND, regs, regd); + VG_(emit_unaryopv_reg)(False, 4, NOT, regd); + VG_(emit_nonshiftopv_lit_reg)(False, 4, OR, 0xFFFFFF00, regd); + break; + + default: + VG_(skin_panic)("synth_TAG2_op"); + } +} + +/*----------------------------------------------------*/ +/*--- Generate code for a single UInstr. ---*/ +/*----------------------------------------------------*/ + +void SK_(emit_XUInstr) ( UInstr* u, RRegSet regs_live_before ) +{ + switch (u->opcode) { + + case SETV: + sk_assert(u->tag1 == RealReg); + synth_SETV ( u->size, u->val1 ); + break; + + case STOREV: + sk_assert(u->tag1 == RealReg || u->tag1 == Literal); + sk_assert(u->tag2 == RealReg); + synth_STOREV ( u->size, u->tag1, + u->tag1==Literal ? u->lit32 : u->val1, + u->val2, + regs_live_before, u->regs_live_after ); + break; + + case LOADV: + sk_assert(u->tag1 == RealReg); + sk_assert(u->tag2 == RealReg); + if (0) + VG_(emit_AMD_prefetch_reg) ( u->val1 ); + synth_LOADV ( u->size, u->val1, u->val2, + regs_live_before, u->regs_live_after ); + break; + + case TESTV: + sk_assert(u->tag1 == RealReg || u->tag1 == ArchReg); + synth_TESTV(u->size, u->tag1, u->val1); + break; + + case GETV: + sk_assert(u->tag1 == ArchReg); + sk_assert(u->tag2 == RealReg); + synth_GETV(u->size, u->val1, u->val2); + break; + + case GETVF: + sk_assert(u->tag1 == RealReg); + sk_assert(u->size == 0); + synth_GETVF(u->val1); + break; + + case PUTV: + sk_assert(u->tag1 == RealReg || u->tag1 == Literal); + sk_assert(u->tag2 == ArchReg); + synth_PUTV(u->size, u->tag1, + u->tag1==Literal ? u->lit32 : u->val1, + u->val2 ); + break; + + case PUTVF: + sk_assert(u->tag1 == RealReg); + sk_assert(u->size == 0); + synth_PUTVF(u->val1); + break; + + case TAG1: + synth_TAG1_op ( u->val3, u->val1, u->regs_live_after ); + break; + + case TAG2: + synth_TAG2_op ( u->val3, u->val1, u->val2 ); + break; + + default: + VG_(printf)("emit_XUInstr: unhandled extension insn:\n"); + VG_(pp_UInstr)(0,u); + VG_(skin_panic)("emit_XUInstr: unhandled extension opcode"); + } +} + +/*--------------------------------------------------------------------*/ +/*--- end mc_from_ucode.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/mc_helpers.S b/VEX/head20041019/memcheck/mc_helpers.S new file mode 100644 index 0000000000..c56642de93 --- /dev/null +++ b/VEX/head20041019/memcheck/mc_helpers.S @@ -0,0 +1,67 @@ + +##--------------------------------------------------------------------## +##--- Support routines for the memory checker. ---## +##--- mc_helpers.S ---## +##--------------------------------------------------------------------## + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mc_asm.h" + +.global MC_(helper_value_check0_fail) +MC_(helper_value_check0_fail): + pushal + call MC_(helperc_value_check0_fail) + popal + ret + +.global MC_(helper_value_check1_fail) +MC_(helper_value_check1_fail): + pushal + call MC_(helperc_value_check1_fail) + popal + ret + +.global MC_(helper_value_check2_fail) +MC_(helper_value_check2_fail): + pushal + call MC_(helperc_value_check2_fail) + popal + ret + +.global MC_(helper_value_check4_fail) +MC_(helper_value_check4_fail): + pushal + call MC_(helperc_value_check4_fail) + popal + ret + +/* Let the linker know we don't need an executable stack */ +.section .note.GNU-stack,"",@progbits + +##--------------------------------------------------------------------## +##--- end mc_helpers.S ---## +##--------------------------------------------------------------------## diff --git a/VEX/head20041019/memcheck/mc_include.h b/VEX/head20041019/memcheck/mc_include.h new file mode 100644 index 0000000000..948cef8ab6 --- /dev/null +++ b/VEX/head20041019/memcheck/mc_include.h @@ -0,0 +1,169 @@ + +/*--------------------------------------------------------------------*/ +/*--- A header file for all parts of the MemCheck tool. ---*/ +/*--- mc_include.h ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +/* Note: this header should contain declarations that are for use by + Memcheck only -- declarations shared with Addrcheck go in mac_shared.h. +*/ + +#ifndef __MC_INCLUDE_H +#define __MC_INCLUDE_H + +#include "mac_shared.h" +#include "mc_asm.h" + +/*------------------------------------------------------------*/ +/*--- Types ---*/ +/*------------------------------------------------------------*/ + +/* UCode extension for efficient memory checking operations */ +typedef + enum { + /* uinstrs which are not needed for mere translation of x86 code, + only for instrumentation of it. */ + LOADV = DUMMY_FINAL_UOPCODE + 1, + STOREV, + GETV, + PUTV, + TESTV, + SETV, + /* Get/set the v-bit (and it is only one bit) for the simulated + %eflags register. */ + GETVF, + PUTVF, + + /* Do a unary or binary tag op. Only for post-instrumented + code. For TAG1, first and only arg is a TempReg, and is both + arg and result reg. For TAG2, first arg is src, second is + dst, in the normal way; both are TempRegs. In both cases, + 3rd arg is a RiCHelper with a Lit16 tag. This indicates + which tag op to do. */ + TAG1, + TAG2 + } + MemCheckOpcode; + + +/* Lists the names of value-tag operations used in instrumented + code. These are the third argument to TAG1 and TAG2 uinsns. */ +typedef + enum { + /* Unary. */ + Tag_PCast40, Tag_PCast20, Tag_PCast10, + Tag_PCast01, Tag_PCast02, Tag_PCast04, + + Tag_PCast14, Tag_PCast12, Tag_PCast11, + + Tag_Left4, Tag_Left2, Tag_Left1, + + Tag_SWiden14, Tag_SWiden24, Tag_SWiden12, + Tag_ZWiden14, Tag_ZWiden24, Tag_ZWiden12, + + /* Binary; 1st is rd; 2nd is rd+wr */ + Tag_UifU4, Tag_UifU2, Tag_UifU1, Tag_UifU0, + Tag_DifD4, Tag_DifD2, Tag_DifD1, + + Tag_ImproveAND4_TQ, Tag_ImproveAND2_TQ, Tag_ImproveAND1_TQ, + Tag_ImproveOR4_TQ, Tag_ImproveOR2_TQ, Tag_ImproveOR1_TQ, + Tag_DebugFn + } + TagOp; + + +/*------------------------------------------------------------*/ +/*--- Command line options ---*/ +/*------------------------------------------------------------*/ + +/* DEBUG: clean up instrumented code? default: YES */ +extern Bool MC_(clo_cleanup); + +/* When instrumenting, omit some checks if tell-tale literals for + inlined strlen() are visible in the basic block. default: YES */ +extern Bool MC_(clo_avoid_strlen_errors); + + +/*------------------------------------------------------------*/ +/*--- Functions ---*/ +/*------------------------------------------------------------*/ + +/* Functions defined in mc_helpers.S */ +extern void MC_(helper_value_check4_fail) ( void ); +extern void MC_(helper_value_check2_fail) ( void ); +extern void MC_(helper_value_check1_fail) ( void ); +extern void MC_(helper_value_check0_fail) ( void ); + + +/* Functions defined in mc_main.c */ +extern REGPARM(2) void MC_(helperc_STOREV4) ( Addr, UInt ); +extern REGPARM(2) void MC_(helperc_STOREV2) ( Addr, UInt ); +extern REGPARM(2) void MC_(helperc_STOREV1) ( Addr, UInt ); + +extern REGPARM(1) UInt MC_(helperc_LOADV1) ( Addr ); +extern REGPARM(1) UInt MC_(helperc_LOADV2) ( Addr ); +extern REGPARM(1) UInt MC_(helperc_LOADV4) ( Addr ); + +extern REGPARM(2) void MC_(fpu_write_check) ( Addr addr, Int size ); +extern REGPARM(2) void MC_(fpu_read_check) ( Addr addr, Int size ); + + +/* For client requests */ +extern void MC_(make_noaccess) ( Addr a, UInt len ); +extern void MC_(make_readable) ( Addr a, UInt len ); +extern void MC_(make_writable) ( Addr a, UInt len ); + +extern Bool MC_(check_writable) ( Addr a, UInt len, Addr* bad_addr ); +extern Bool MC_(check_readable) ( Addr a, UInt len, Addr* bad_addr ); + +extern void MC_(detect_memory_leaks) ( void ); + +extern Int MC_(get_or_set_vbits_for_client) ( + ThreadId tid, + Addr dataV, + Addr vbitsV, + UInt size, + Bool setting /* True <=> set vbits, False <=> get vbits */ + ); + +/* Functions defined in mc_clientreqs.c */ +extern Bool MC_(client_perm_maybe_describe)( Addr a, AddrInfo* ai ); +extern void MC_(show_client_block_stats) ( void ); + + +/* Functions defined in mc_errcontext.c */ +extern void MC_(record_value_error) ( ThreadId tid, Int size ); +extern void MC_(record_user_error) ( ThreadId tid, Addr a, Bool isWrite ); + + +#endif + +/*--------------------------------------------------------------------*/ +/*--- end mc_include.h ---*/ +/*--------------------------------------------------------------------*/ + diff --git a/VEX/head20041019/memcheck/mc_main.c b/VEX/head20041019/memcheck/mc_main.c new file mode 100644 index 0000000000..a8369cfadf --- /dev/null +++ b/VEX/head20041019/memcheck/mc_main.c @@ -0,0 +1,1741 @@ + +/*--------------------------------------------------------------------*/ +/*--- MemCheck: Maintain bitmaps of memory, tracking the ---*/ +/*--- accessibility (A) and validity (V) status of each byte. ---*/ +/*--- mc_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mc_include.h" +#include "memcheck.h" /* for client requests */ +//#include "vg_profile.c" + +/* Define to debug the mem audit system. */ +/* #define VG_DEBUG_MEMORY */ + +#define DEBUG(fmt, args...) //VG_(printf)(fmt, ## args) + +/*------------------------------------------------------------*/ +/*--- Low-level support for memory checking. ---*/ +/*------------------------------------------------------------*/ + +/* All reads and writes are checked against a memory map, which + records the state of all memory in the process. The memory map is + organised like this: + + The top 16 bits of an address are used to index into a top-level + map table, containing 65536 entries. Each entry is a pointer to a + second-level map, which records the accesibililty and validity + permissions for the 65536 bytes indexed by the lower 16 bits of the + address. Each byte is represented by nine bits, one indicating + accessibility, the other eight validity. So each second-level map + contains 73728 bytes. This two-level arrangement conveniently + divides the 4G address space into 64k lumps, each size 64k bytes. + + All entries in the primary (top-level) map must point to a valid + secondary (second-level) map. Since most of the 4G of address + space will not be in use -- ie, not mapped at all -- there is a + distinguished secondary map, which indicates `not addressible and + not valid' writeable for all bytes. Entries in the primary map for + which the entire 64k is not in use at all point at this + distinguished map. + + [...] lots of stuff deleted due to out of date-ness + + As a final optimisation, the alignment and address checks for + 4-byte loads and stores are combined in a neat way. The primary + map is extended to have 262144 entries (2^18), rather than 2^16. + The top 3/4 of these entries are permanently set to the + distinguished secondary map. For a 4-byte load/store, the + top-level map is indexed not with (addr >> 16) but instead f(addr), + where + + f( XXXX XXXX XXXX XXXX ____ ____ ____ __YZ ) + = ____ ____ ____ __YZ XXXX XXXX XXXX XXXX or + = ____ ____ ____ __ZY XXXX XXXX XXXX XXXX + + ie the lowest two bits are placed above the 16 high address bits. + If either of these two bits are nonzero, the address is misaligned; + this will select a secondary map from the upper 3/4 of the primary + map. Because this is always the distinguished secondary map, a + (bogus) address check failure will result. The failure handling + code can then figure out whether this is a genuine addr check + failure or whether it is a possibly-legitimate access at a + misaligned address. +*/ + + +/*------------------------------------------------------------*/ +/*--- Function declarations. ---*/ +/*------------------------------------------------------------*/ + +static UInt mc_rd_V4_SLOWLY ( Addr a ); +static UInt mc_rd_V2_SLOWLY ( Addr a ); +static UInt mc_rd_V1_SLOWLY ( Addr a ); +static void mc_wr_V4_SLOWLY ( Addr a, UInt vbytes ); +static void mc_wr_V2_SLOWLY ( Addr a, UInt vbytes ); +static void mc_wr_V1_SLOWLY ( Addr a, UInt vbytes ); +static void mc_fpu_read_check_SLOWLY ( Addr addr, Int size ); +static void mc_fpu_write_check_SLOWLY ( Addr addr, Int size ); + +/*------------------------------------------------------------*/ +/*--- Data defns. ---*/ +/*------------------------------------------------------------*/ + +typedef + struct { + UChar abits[8192]; + UChar vbyte[65536]; + } + SecMap; + +static SecMap* primary_map[ /*65536*/ 262144 ]; +static SecMap distinguished_secondary_map; + +static void init_shadow_memory ( void ) +{ + Int i; + + for (i = 0; i < 8192; i++) /* Invalid address */ + distinguished_secondary_map.abits[i] = VGM_BYTE_INVALID; + for (i = 0; i < 65536; i++) /* Invalid Value */ + distinguished_secondary_map.vbyte[i] = VGM_BYTE_INVALID; + + /* These entries gradually get overwritten as the used address + space expands. */ + for (i = 0; i < 65536; i++) + primary_map[i] = &distinguished_secondary_map; + + /* These ones should never change; it's a bug in Valgrind if they do. */ + for (i = 65536; i < 262144; i++) + primary_map[i] = &distinguished_secondary_map; +} + +/*------------------------------------------------------------*/ +/*--- Basic bitmap management, reading and writing. ---*/ +/*------------------------------------------------------------*/ + +/* Allocate and initialise a secondary map. */ + +static SecMap* alloc_secondary_map ( __attribute__ ((unused)) + Char* caller ) +{ + SecMap* map; + UInt i; + PROF_EVENT(10); + + /* Mark all bytes as invalid access and invalid value. */ + map = (SecMap *)VG_(shadow_alloc)(sizeof(SecMap)); + + for (i = 0; i < 8192; i++) + map->abits[i] = VGM_BYTE_INVALID; /* Invalid address */ + for (i = 0; i < 65536; i++) + map->vbyte[i] = VGM_BYTE_INVALID; /* Invalid Value */ + + /* VG_(printf)("ALLOC_2MAP(%s)\n", caller ); */ + return map; +} + + +/* Basic reading/writing of the bitmaps, for byte-sized accesses. */ + +static __inline__ UChar get_abit ( Addr a ) +{ + SecMap* sm = primary_map[a >> 16]; + UInt sm_off = a & 0xFFFF; + PROF_EVENT(20); +# if 0 + if (IS_DISTINGUISHED_SM(sm)) + VG_(message)(Vg_DebugMsg, + "accessed distinguished 2ndary (A)map! 0x%x\n", a); +# endif + return BITARR_TEST(sm->abits, sm_off) + ? VGM_BIT_INVALID : VGM_BIT_VALID; +} + +static __inline__ UChar get_vbyte ( Addr a ) +{ + SecMap* sm = primary_map[a >> 16]; + UInt sm_off = a & 0xFFFF; + PROF_EVENT(21); +# if 0 + if (IS_DISTINGUISHED_SM(sm)) + VG_(message)(Vg_DebugMsg, + "accessed distinguished 2ndary (V)map! 0x%x\n", a); +# endif + return sm->vbyte[sm_off]; +} + +static /* __inline__ */ void set_abit ( Addr a, UChar abit ) +{ + SecMap* sm; + UInt sm_off; + PROF_EVENT(22); + ENSURE_MAPPABLE(a, "set_abit"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + if (abit) + BITARR_SET(sm->abits, sm_off); + else + BITARR_CLEAR(sm->abits, sm_off); +} + +static __inline__ void set_vbyte ( Addr a, UChar vbyte ) +{ + SecMap* sm; + UInt sm_off; + PROF_EVENT(23); + ENSURE_MAPPABLE(a, "set_vbyte"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + sm->vbyte[sm_off] = vbyte; +} + + +/* Reading/writing of the bitmaps, for aligned word-sized accesses. */ + +static __inline__ UChar get_abits4_ALIGNED ( Addr a ) +{ + SecMap* sm; + UInt sm_off; + UChar abits8; + PROF_EVENT(24); +# ifdef VG_DEBUG_MEMORY + sk_assert(IS_ALIGNED4_ADDR(a)); +# endif + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + abits8 = sm->abits[sm_off >> 3]; + abits8 >>= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */ + abits8 &= 0x0F; + return abits8; +} + +static UInt __inline__ get_vbytes4_ALIGNED ( Addr a ) +{ + SecMap* sm = primary_map[a >> 16]; + UInt sm_off = a & 0xFFFF; + PROF_EVENT(25); +# ifdef VG_DEBUG_MEMORY + sk_assert(IS_ALIGNED4_ADDR(a)); +# endif + return ((UInt*)(sm->vbyte))[sm_off >> 2]; +} + + +static void __inline__ set_vbytes4_ALIGNED ( Addr a, UInt vbytes ) +{ + SecMap* sm; + UInt sm_off; + ENSURE_MAPPABLE(a, "set_vbytes4_ALIGNED"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + PROF_EVENT(23); +# ifdef VG_DEBUG_MEMORY + sk_assert(IS_ALIGNED4_ADDR(a)); +# endif + ((UInt*)(sm->vbyte))[sm_off >> 2] = vbytes; +} + + +/*------------------------------------------------------------*/ +/*--- Setting permissions over address ranges. ---*/ +/*------------------------------------------------------------*/ + +static void set_address_range_perms ( Addr a, UInt len, + UInt example_a_bit, + UInt example_v_bit ) +{ + UChar vbyte, abyte8; + UInt vword4, sm_off; + SecMap* sm; + + PROF_EVENT(30); + + if (len == 0) + return; + + if (VG_(clo_verbosity) > 0) { + if (len > 100 * 1000 * 1000) { + VG_(message)(Vg_UserMsg, + "Warning: set address range perms: " + "large range %u, a %d, v %d", + len, example_a_bit, example_v_bit ); + } + } + + VGP_PUSHCC(VgpSetMem); + + /* Requests to change permissions of huge address ranges may + indicate bugs in our machinery. 30,000,000 is arbitrary, but so + far all legitimate requests have fallen beneath that size. */ + /* 4 Mar 02: this is just stupid; get rid of it. */ + /* sk_assert(len < 30000000); */ + + /* Check the permissions make sense. */ + sk_assert(example_a_bit == VGM_BIT_VALID + || example_a_bit == VGM_BIT_INVALID); + sk_assert(example_v_bit == VGM_BIT_VALID + || example_v_bit == VGM_BIT_INVALID); + if (example_a_bit == VGM_BIT_INVALID) + sk_assert(example_v_bit == VGM_BIT_INVALID); + + /* The validity bits to write. */ + vbyte = example_v_bit==VGM_BIT_VALID + ? VGM_BYTE_VALID : VGM_BYTE_INVALID; + + /* In order that we can charge through the address space at 8 + bytes/main-loop iteration, make up some perms. */ + abyte8 = (example_a_bit << 7) + | (example_a_bit << 6) + | (example_a_bit << 5) + | (example_a_bit << 4) + | (example_a_bit << 3) + | (example_a_bit << 2) + | (example_a_bit << 1) + | (example_a_bit << 0); + vword4 = (vbyte << 24) | (vbyte << 16) | (vbyte << 8) | vbyte; + +# ifdef VG_DEBUG_MEMORY + /* Do it ... */ + while (True) { + PROF_EVENT(31); + if (len == 0) break; + set_abit ( a, example_a_bit ); + set_vbyte ( a, vbyte ); + a++; + len--; + } + +# else + /* Slowly do parts preceding 8-byte alignment. */ + while (True) { + PROF_EVENT(31); + if (len == 0) break; + if ((a % 8) == 0) break; + set_abit ( a, example_a_bit ); + set_vbyte ( a, vbyte ); + a++; + len--; + } + + if (len == 0) { + VGP_POPCC(VgpSetMem); + return; + } + sk_assert((a % 8) == 0 && len > 0); + + /* Once aligned, go fast. */ + while (True) { + PROF_EVENT(32); + if (len < 8) break; + ENSURE_MAPPABLE(a, "set_address_range_perms(fast)"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + sm->abits[sm_off >> 3] = abyte8; + ((UInt*)(sm->vbyte))[(sm_off >> 2) + 0] = vword4; + ((UInt*)(sm->vbyte))[(sm_off >> 2) + 1] = vword4; + a += 8; + len -= 8; + } + + if (len == 0) { + VGP_POPCC(VgpSetMem); + return; + } + sk_assert((a % 8) == 0 && len > 0 && len < 8); + + /* Finish the upper fragment. */ + while (True) { + PROF_EVENT(33); + if (len == 0) break; + set_abit ( a, example_a_bit ); + set_vbyte ( a, vbyte ); + a++; + len--; + } +# endif + + /* Check that zero page and highest page have not been written to + -- this could happen with buggy syscall wrappers. Today + (2001-04-26) had precisely such a problem with __NR_setitimer. */ + sk_assert(SK_(cheap_sanity_check)()); + VGP_POPCC(VgpSetMem); +} + +/* Set permissions for address ranges ... */ + +void MC_(make_noaccess) ( Addr a, UInt len ) +{ + PROF_EVENT(35); + DEBUG("MC_(make_noaccess)(%p, %x)\n", a, len); + set_address_range_perms ( a, len, VGM_BIT_INVALID, VGM_BIT_INVALID ); +} + +void MC_(make_writable) ( Addr a, UInt len ) +{ + PROF_EVENT(36); + DEBUG("MC_(make_writable)(%p, %x)\n", a, len); + set_address_range_perms ( a, len, VGM_BIT_VALID, VGM_BIT_INVALID ); +} + +void MC_(make_readable) ( Addr a, UInt len ) +{ + PROF_EVENT(37); + DEBUG("MC_(make_readable)(%p, 0x%x)\n", a, len); + set_address_range_perms ( a, len, VGM_BIT_VALID, VGM_BIT_VALID ); +} + +static __inline__ +void make_aligned_word_writable(Addr a) +{ + SecMap* sm; + UInt sm_off; + UChar mask; + + VGP_PUSHCC(VgpESPAdj); + ENSURE_MAPPABLE(a, "make_aligned_word_writable"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + ((UInt*)(sm->vbyte))[sm_off >> 2] = VGM_WORD_INVALID; + mask = 0x0F; + mask <<= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */ + /* mask now contains 1s where we wish to make address bits invalid (0s). */ + sm->abits[sm_off >> 3] &= ~mask; + VGP_POPCC(VgpESPAdj); +} + +static __inline__ +void make_aligned_word_noaccess(Addr a) +{ + SecMap* sm; + UInt sm_off; + UChar mask; + + VGP_PUSHCC(VgpESPAdj); + ENSURE_MAPPABLE(a, "make_aligned_word_noaccess"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + ((UInt*)(sm->vbyte))[sm_off >> 2] = VGM_WORD_INVALID; + mask = 0x0F; + mask <<= (a & 4 /* 100b */); /* a & 4 is either 0 or 4 */ + /* mask now contains 1s where we wish to make address bits invalid (1s). */ + sm->abits[sm_off >> 3] |= mask; + VGP_POPCC(VgpESPAdj); +} + +/* Nb: by "aligned" here we mean 8-byte aligned */ +static __inline__ +void make_aligned_doubleword_writable(Addr a) +{ + SecMap* sm; + UInt sm_off; + + VGP_PUSHCC(VgpESPAdj); + ENSURE_MAPPABLE(a, "make_aligned_doubleword_writable"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + sm->abits[sm_off >> 3] = VGM_BYTE_VALID; + ((UInt*)(sm->vbyte))[(sm_off >> 2) + 0] = VGM_WORD_INVALID; + ((UInt*)(sm->vbyte))[(sm_off >> 2) + 1] = VGM_WORD_INVALID; + VGP_POPCC(VgpESPAdj); +} + +static __inline__ +void make_aligned_doubleword_noaccess(Addr a) +{ + SecMap* sm; + UInt sm_off; + + VGP_PUSHCC(VgpESPAdj); + ENSURE_MAPPABLE(a, "make_aligned_doubleword_noaccess"); + sm = primary_map[a >> 16]; + sm_off = a & 0xFFFF; + sm->abits[sm_off >> 3] = VGM_BYTE_INVALID; + ((UInt*)(sm->vbyte))[(sm_off >> 2) + 0] = VGM_WORD_INVALID; + ((UInt*)(sm->vbyte))[(sm_off >> 2) + 1] = VGM_WORD_INVALID; + VGP_POPCC(VgpESPAdj); +} + +/* The %esp update handling functions */ +ESP_UPDATE_HANDLERS ( make_aligned_word_writable, + make_aligned_word_noaccess, + make_aligned_doubleword_writable, + make_aligned_doubleword_noaccess, + MC_(make_writable), + MC_(make_noaccess) + ); + +/* Block-copy permissions (needed for implementing realloc()). */ +static void mc_copy_address_range_state ( Addr src, Addr dst, UInt len ) +{ + UInt i; + + DEBUG("mc_copy_address_range_state\n"); + + PROF_EVENT(40); + for (i = 0; i < len; i++) { + UChar abit = get_abit ( src+i ); + UChar vbyte = get_vbyte ( src+i ); + PROF_EVENT(41); + set_abit ( dst+i, abit ); + set_vbyte ( dst+i, vbyte ); + } +} + + +/* Check permissions for address range. If inadequate permissions + exist, *bad_addr is set to the offending address, so the caller can + know what it is. */ + +/* Returns True if [a .. a+len) is not addressible. Otherwise, + returns False, and if bad_addr is non-NULL, sets *bad_addr to + indicate the lowest failing address. Functions below are + similar. */ +Bool MC_(check_noaccess) ( Addr a, UInt len, Addr* bad_addr ) +{ + UInt i; + UChar abit; + PROF_EVENT(42); + for (i = 0; i < len; i++) { + PROF_EVENT(43); + abit = get_abit(a); + if (abit == VGM_BIT_VALID) { + if (bad_addr != NULL) *bad_addr = a; + return False; + } + a++; + } + return True; +} + +Bool MC_(check_writable) ( Addr a, UInt len, Addr* bad_addr ) +{ + UInt i; + UChar abit; + PROF_EVENT(42); + for (i = 0; i < len; i++) { + PROF_EVENT(43); + abit = get_abit(a); + if (abit == VGM_BIT_INVALID) { + if (bad_addr != NULL) *bad_addr = a; + return False; + } + a++; + } + return True; +} + +Bool MC_(check_readable) ( Addr a, UInt len, Addr* bad_addr ) +{ + UInt i; + UChar abit; + UChar vbyte; + + PROF_EVENT(44); + DEBUG("MC_(check_readable)\n"); + for (i = 0; i < len; i++) { + abit = get_abit(a); + vbyte = get_vbyte(a); + PROF_EVENT(45); + if (abit != VGM_BIT_VALID || vbyte != VGM_BYTE_VALID) { + if (bad_addr != NULL) *bad_addr = a; + return False; + } + a++; + } + return True; +} + + +/* Check a zero-terminated ascii string. Tricky -- don't want to + examine the actual bytes, to find the end, until we're sure it is + safe to do so. */ + +static Bool mc_check_readable_asciiz ( Addr a, Addr* bad_addr ) +{ + UChar abit; + UChar vbyte; + PROF_EVENT(46); + DEBUG("mc_check_readable_asciiz\n"); + while (True) { + PROF_EVENT(47); + abit = get_abit(a); + vbyte = get_vbyte(a); + if (abit != VGM_BIT_VALID || vbyte != VGM_BYTE_VALID) { + if (bad_addr != NULL) *bad_addr = a; + return False; + } + /* Ok, a is safe to read. */ + if (* ((UChar*)a) == 0) return True; + a++; + } +} + + +/*------------------------------------------------------------*/ +/*--- Memory event handlers ---*/ +/*------------------------------------------------------------*/ + +static +void mc_check_is_writable ( CorePart part, ThreadId tid, Char* s, + Addr base, UInt size ) +{ + Bool ok; + Addr bad_addr; + + VGP_PUSHCC(VgpCheckMem); + + /* VG_(message)(Vg_DebugMsg,"check is writable: %x .. %x", + base,base+size-1); */ + ok = MC_(check_writable) ( base, size, &bad_addr ); + if (!ok) { + switch (part) { + case Vg_CoreSysCall: + MAC_(record_param_error) ( tid, bad_addr, /*isWrite =*/True, s ); + break; + + case Vg_CorePThread: + case Vg_CoreSignal: + MAC_(record_core_mem_error)( tid, /*isWrite=*/True, s ); + break; + + default: + VG_(skin_panic)("mc_check_is_writable: unexpected CorePart"); + } + } + + VGP_POPCC(VgpCheckMem); +} + +static +void mc_check_is_readable ( CorePart part, ThreadId tid, Char* s, + Addr base, UInt size ) +{ + Bool ok; + Addr bad_addr; + + VGP_PUSHCC(VgpCheckMem); + + /* VG_(message)(Vg_DebugMsg,"check is readable: %x .. %x", + base,base+size-1); */ + ok = MC_(check_readable) ( base, size, &bad_addr ); + if (!ok) { + switch (part) { + case Vg_CoreSysCall: + MAC_(record_param_error) ( tid, bad_addr, /*isWrite =*/False, s ); + break; + + case Vg_CorePThread: + MAC_(record_core_mem_error)( tid, /*isWrite=*/False, s ); + break; + + /* If we're being asked to jump to a silly address, record an error + message before potentially crashing the entire system. */ + case Vg_CoreTranslate: + MAC_(record_jump_error)( tid, bad_addr ); + break; + + default: + VG_(skin_panic)("mc_check_is_readable: unexpected CorePart"); + } + } + VGP_POPCC(VgpCheckMem); +} + +static +void mc_check_is_readable_asciiz ( CorePart part, ThreadId tid, + Char* s, Addr str ) +{ + Bool ok = True; + Addr bad_addr; + /* VG_(message)(Vg_DebugMsg,"check is readable asciiz: 0x%x",str); */ + + VGP_PUSHCC(VgpCheckMem); + + sk_assert(part == Vg_CoreSysCall); + ok = mc_check_readable_asciiz ( (Addr)str, &bad_addr ); + if (!ok) { + MAC_(record_param_error) ( tid, bad_addr, /*is_writable =*/False, s ); + } + + VGP_POPCC(VgpCheckMem); +} + + +static +void mc_new_mem_startup( Addr a, UInt len, Bool rr, Bool ww, Bool xx ) +{ + /* Ignore the permissions, just make it readable. Seems to work... */ + DEBUG("mc_new_mem_startup(%p, %u, rr=%u, ww=%u, xx=%u)\n", a,len,rr,ww,xx); + MC_(make_readable)(a, len); +} + +static +void mc_new_mem_heap ( Addr a, UInt len, Bool is_inited ) +{ + if (is_inited) { + MC_(make_readable)(a, len); + } else { + MC_(make_writable)(a, len); + } +} + +static +void mc_set_perms (Addr a, UInt len, Bool rr, Bool ww, Bool xx) +{ + DEBUG("mc_set_perms(%p, %u, rr=%u ww=%u, xx=%u)\n", a, len, rr, ww, xx); + if (rr) MC_(make_readable)(a, len); + else if (ww) MC_(make_writable)(a, len); + else MC_(make_noaccess)(a, len); +} + + +/*------------------------------------------------------------*/ +/*--- Register event handlers ---*/ +/*------------------------------------------------------------*/ + +static void mc_post_regs_write_init ( void ) +{ + UInt i; + for (i = FIRST_ARCH_REG; i <= LAST_ARCH_REG; i++) + VG_(set_shadow_archreg)( i, VGM_WORD_VALID ); + VG_(set_shadow_eflags)( VGM_EFLAGS_VALID ); +} + +static void mc_post_reg_write(ThreadId tid, UInt reg) +{ + VG_(set_thread_shadow_archreg)( tid, reg, VGM_WORD_VALID ); +} + +static void mc_post_reg_write_clientcall(ThreadId tid, UInt reg, Addr f ) +{ + VG_(set_thread_shadow_archreg)( tid, reg, VGM_WORD_VALID ); +} + + +/*------------------------------------------------------------*/ +/*--- Functions called directly from generated code. ---*/ +/*------------------------------------------------------------*/ + +static __inline__ UInt rotateRight16 ( UInt x ) +{ + /* Amazingly, gcc turns this into a single rotate insn. */ + return (x >> 16) | (x << 16); +} + + +static __inline__ UInt shiftRight16 ( UInt x ) +{ + return x >> 16; +} + + +/* Read/write 1/2/4 sized V bytes, and emit an address error if + needed. */ + +/* VG_(helperc_{LD,ST}V{1,2,4}) handle the common case fast. + Under all other circumstances, it defers to the relevant _SLOWLY + function, which can handle all situations. +*/ +REGPARM(1) +UInt MC_(helperc_LOADV4) ( Addr a ) +{ +# ifdef VG_DEBUG_MEMORY + return mc_rd_V4_SLOWLY(a); +# else + UInt sec_no = rotateRight16(a) & 0x3FFFF; + SecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + UChar abits = sm->abits[a_off]; + abits >>= (a & 4); + abits &= 15; + PROF_EVENT(60); + if (abits == VGM_NIBBLE_VALID) { + /* Handle common case quickly: a is suitably aligned, is mapped, + and is addressible. */ + UInt v_off = a & 0xFFFF; + return ((UInt*)(sm->vbyte))[ v_off >> 2 ]; + } else { + /* Slow but general case. */ + return mc_rd_V4_SLOWLY(a); + } +# endif +} + +REGPARM(2) +void MC_(helperc_STOREV4) ( Addr a, UInt vbytes ) +{ +# ifdef VG_DEBUG_MEMORY + mc_wr_V4_SLOWLY(a, vbytes); +# else + UInt sec_no = rotateRight16(a) & 0x3FFFF; + SecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + UChar abits = sm->abits[a_off]; + abits >>= (a & 4); + abits &= 15; + PROF_EVENT(61); + if (abits == VGM_NIBBLE_VALID) { + /* Handle common case quickly: a is suitably aligned, is mapped, + and is addressible. */ + UInt v_off = a & 0xFFFF; + ((UInt*)(sm->vbyte))[ v_off >> 2 ] = vbytes; + } else { + /* Slow but general case. */ + mc_wr_V4_SLOWLY(a, vbytes); + } +# endif +} + +REGPARM(1) +UInt MC_(helperc_LOADV2) ( Addr a ) +{ +# ifdef VG_DEBUG_MEMORY + return mc_rd_V2_SLOWLY(a); +# else + UInt sec_no = rotateRight16(a) & 0x1FFFF; + SecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + PROF_EVENT(62); + if (sm->abits[a_off] == VGM_BYTE_VALID) { + /* Handle common case quickly. */ + UInt v_off = a & 0xFFFF; + return 0xFFFF0000 + | + (UInt)( ((UShort*)(sm->vbyte))[ v_off >> 1 ] ); + } else { + /* Slow but general case. */ + return mc_rd_V2_SLOWLY(a); + } +# endif +} + +REGPARM(2) +void MC_(helperc_STOREV2) ( Addr a, UInt vbytes ) +{ +# ifdef VG_DEBUG_MEMORY + mc_wr_V2_SLOWLY(a, vbytes); +# else + UInt sec_no = rotateRight16(a) & 0x1FFFF; + SecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + PROF_EVENT(63); + if (sm->abits[a_off] == VGM_BYTE_VALID) { + /* Handle common case quickly. */ + UInt v_off = a & 0xFFFF; + ((UShort*)(sm->vbyte))[ v_off >> 1 ] = vbytes & 0x0000FFFF; + } else { + /* Slow but general case. */ + mc_wr_V2_SLOWLY(a, vbytes); + } +# endif +} + +REGPARM(1) +UInt MC_(helperc_LOADV1) ( Addr a ) +{ +# ifdef VG_DEBUG_MEMORY + return mc_rd_V1_SLOWLY(a); +# else + UInt sec_no = shiftRight16(a); + SecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + PROF_EVENT(64); + if (sm->abits[a_off] == VGM_BYTE_VALID) { + /* Handle common case quickly. */ + UInt v_off = a & 0xFFFF; + return 0xFFFFFF00 + | + (UInt)( ((UChar*)(sm->vbyte))[ v_off ] ); + } else { + /* Slow but general case. */ + return mc_rd_V1_SLOWLY(a); + } +# endif +} + +REGPARM(2) +void MC_(helperc_STOREV1) ( Addr a, UInt vbytes ) +{ +# ifdef VG_DEBUG_MEMORY + mc_wr_V1_SLOWLY(a, vbytes); +# else + UInt sec_no = shiftRight16(a); + SecMap* sm = primary_map[sec_no]; + UInt a_off = (a & 0xFFFF) >> 3; + PROF_EVENT(65); + if (sm->abits[a_off] == VGM_BYTE_VALID) { + /* Handle common case quickly. */ + UInt v_off = a & 0xFFFF; + ((UChar*)(sm->vbyte))[ v_off ] = vbytes & 0x000000FF; + } else { + /* Slow but general case. */ + mc_wr_V1_SLOWLY(a, vbytes); + } +# endif +} + + +/*------------------------------------------------------------*/ +/*--- Fallback functions to handle cases that the above ---*/ +/*--- VG_(helperc_{LD,ST}V{1,2,4}) can't manage. ---*/ +/*------------------------------------------------------------*/ + +static UInt mc_rd_V4_SLOWLY ( Addr a ) +{ + Bool a0ok, a1ok, a2ok, a3ok; + UInt vb0, vb1, vb2, vb3; + + PROF_EVENT(70); + + /* First establish independently the addressibility of the 4 bytes + involved. */ + a0ok = get_abit(a+0) == VGM_BIT_VALID; + a1ok = get_abit(a+1) == VGM_BIT_VALID; + a2ok = get_abit(a+2) == VGM_BIT_VALID; + a3ok = get_abit(a+3) == VGM_BIT_VALID; + + /* Also get the validity bytes for the address. */ + vb0 = (UInt)get_vbyte(a+0); + vb1 = (UInt)get_vbyte(a+1); + vb2 = (UInt)get_vbyte(a+2); + vb3 = (UInt)get_vbyte(a+3); + + /* Now distinguish 3 cases */ + + /* Case 1: the address is completely valid, so: + - no addressing error + - return V bytes as read from memory + */ + if (a0ok && a1ok && a2ok && a3ok) { + UInt vw = VGM_WORD_INVALID; + vw <<= 8; vw |= vb3; + vw <<= 8; vw |= vb2; + vw <<= 8; vw |= vb1; + vw <<= 8; vw |= vb0; + return vw; + } + + /* Case 2: the address is completely invalid. + - emit addressing error + - return V word indicating validity. + This sounds strange, but if we make loads from invalid addresses + give invalid data, we also risk producing a number of confusing + undefined-value errors later, which confuses the fact that the + error arose in the first place from an invalid address. + */ + /* VG_(printf)("%p (%d %d %d %d)\n", a, a0ok, a1ok, a2ok, a3ok); */ + if (!MAC_(clo_partial_loads_ok) + || ((a & 3) != 0) + || (!a0ok && !a1ok && !a2ok && !a3ok)) { + MAC_(record_address_error)( VG_(get_current_tid)(), a, 4, False ); + return (VGM_BYTE_VALID << 24) | (VGM_BYTE_VALID << 16) + | (VGM_BYTE_VALID << 8) | VGM_BYTE_VALID; + } + + /* Case 3: the address is partially valid. + - no addressing error + - returned V word is invalid where the address is invalid, + and contains V bytes from memory otherwise. + Case 3 is only allowed if MC_(clo_partial_loads_ok) is True + (which is the default), and the address is 4-aligned. + If not, Case 2 will have applied. + */ + sk_assert(MAC_(clo_partial_loads_ok)); + { + UInt vw = VGM_WORD_INVALID; + vw <<= 8; vw |= (a3ok ? vb3 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a2ok ? vb2 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a1ok ? vb1 : VGM_BYTE_INVALID); + vw <<= 8; vw |= (a0ok ? vb0 : VGM_BYTE_INVALID); + return vw; + } +} + +static void mc_wr_V4_SLOWLY ( Addr a, UInt vbytes ) +{ + /* Check the address for validity. */ + Bool aerr = False; + PROF_EVENT(71); + + if (get_abit(a+0) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+1) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+2) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+3) != VGM_BIT_VALID) aerr = True; + + /* Store the V bytes, remembering to do it little-endian-ly. */ + set_vbyte( a+0, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+1, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+2, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+3, vbytes & 0x000000FF ); + + /* If an address error has happened, report it. */ + if (aerr) + MAC_(record_address_error)( VG_(get_current_tid)(), a, 4, True ); +} + +static UInt mc_rd_V2_SLOWLY ( Addr a ) +{ + /* Check the address for validity. */ + UInt vw = VGM_WORD_INVALID; + Bool aerr = False; + PROF_EVENT(72); + + if (get_abit(a+0) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+1) != VGM_BIT_VALID) aerr = True; + + /* Fetch the V bytes, remembering to do it little-endian-ly. */ + vw <<= 8; vw |= (UInt)get_vbyte(a+1); + vw <<= 8; vw |= (UInt)get_vbyte(a+0); + + /* If an address error has happened, report it. */ + if (aerr) { + MAC_(record_address_error)( VG_(get_current_tid)(), a, 2, False ); + vw = (VGM_BYTE_INVALID << 24) | (VGM_BYTE_INVALID << 16) + | (VGM_BYTE_VALID << 8) | (VGM_BYTE_VALID); + } + return vw; +} + +static void mc_wr_V2_SLOWLY ( Addr a, UInt vbytes ) +{ + /* Check the address for validity. */ + Bool aerr = False; + PROF_EVENT(73); + + if (get_abit(a+0) != VGM_BIT_VALID) aerr = True; + if (get_abit(a+1) != VGM_BIT_VALID) aerr = True; + + /* Store the V bytes, remembering to do it little-endian-ly. */ + set_vbyte( a+0, vbytes & 0x000000FF ); vbytes >>= 8; + set_vbyte( a+1, vbytes & 0x000000FF ); + + /* If an address error has happened, report it. */ + if (aerr) + MAC_(record_address_error)( VG_(get_current_tid)(), a, 2, True ); +} + +static UInt mc_rd_V1_SLOWLY ( Addr a ) +{ + /* Check the address for validity. */ + UInt vw = VGM_WORD_INVALID; + Bool aerr = False; + PROF_EVENT(74); + + if (get_abit(a+0) != VGM_BIT_VALID) aerr = True; + + /* Fetch the V byte. */ + vw <<= 8; vw |= (UInt)get_vbyte(a+0); + + /* If an address error has happened, report it. */ + if (aerr) { + MAC_(record_address_error)( VG_(get_current_tid)(), a, 1, False ); + vw = (VGM_BYTE_INVALID << 24) | (VGM_BYTE_INVALID << 16) + | (VGM_BYTE_INVALID << 8) | (VGM_BYTE_VALID); + } + return vw; +} + +static void mc_wr_V1_SLOWLY ( Addr a, UInt vbytes ) +{ + /* Check the address for validity. */ + Bool aerr = False; + PROF_EVENT(75); + if (get_abit(a+0) != VGM_BIT_VALID) aerr = True; + + /* Store the V bytes, remembering to do it little-endian-ly. */ + set_vbyte( a+0, vbytes & 0x000000FF ); + + /* If an address error has happened, report it. */ + if (aerr) + MAC_(record_address_error)( VG_(get_current_tid)(), a, 1, True ); +} + + +/* --------------------------------------------------------------------- + Called from generated code, or from the assembly helpers. + Handlers for value check failures. + ------------------------------------------------------------------ */ + +void MC_(helperc_value_check0_fail) ( void ) +{ + MC_(record_value_error) ( VG_(get_current_tid)(), 0 ); +} + +void MC_(helperc_value_check1_fail) ( void ) +{ + MC_(record_value_error) ( VG_(get_current_tid)(), 1 ); +} + +void MC_(helperc_value_check2_fail) ( void ) +{ + MC_(record_value_error) ( VG_(get_current_tid)(), 2 ); +} + +void MC_(helperc_value_check4_fail) ( void ) +{ + MC_(record_value_error) ( VG_(get_current_tid)(), 4 ); +} + + +/* --------------------------------------------------------------------- + FPU load and store checks, called from generated code. + ------------------------------------------------------------------ */ + +REGPARM(2) +void MC_(fpu_read_check) ( Addr addr, Int size ) +{ + /* Ensure the read area is both addressible and valid (ie, + readable). If there's an address error, don't report a value + error too; but if there isn't an address error, check for a + value error. + + Try to be reasonably fast on the common case; wimp out and defer + to mc_fpu_read_check_SLOWLY for everything else. */ + + SecMap* sm; + UInt sm_off, v_off, a_off; + Addr addr4; + + PROF_EVENT(80); + +# ifdef VG_DEBUG_MEMORY + mc_fpu_read_check_SLOWLY ( addr, size ); +# else + + if (size == 4) { + if (!IS_ALIGNED4_ADDR(addr)) goto slow4; + PROF_EVENT(81); + /* Properly aligned. */ + sm = primary_map[addr >> 16]; + sm_off = addr & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow4; + /* Properly aligned and addressible. */ + v_off = addr & 0xFFFF; + if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID) + goto slow4; + /* Properly aligned, addressible and with valid data. */ + return; + slow4: + mc_fpu_read_check_SLOWLY ( addr, 4 ); + return; + } + + if (size == 8) { + if (!IS_ALIGNED4_ADDR(addr)) goto slow8; + PROF_EVENT(82); + /* Properly aligned. Do it in two halves. */ + addr4 = addr + 4; + /* First half. */ + sm = primary_map[addr >> 16]; + sm_off = addr & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8; + /* First half properly aligned and addressible. */ + v_off = addr & 0xFFFF; + if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID) + goto slow8; + /* Second half. */ + sm = primary_map[addr4 >> 16]; + sm_off = addr4 & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8; + /* Second half properly aligned and addressible. */ + v_off = addr4 & 0xFFFF; + if (((UInt*)(sm->vbyte))[ v_off >> 2 ] != VGM_WORD_VALID) + goto slow8; + /* Both halves properly aligned, addressible and with valid + data. */ + return; + slow8: + mc_fpu_read_check_SLOWLY ( addr, 8 ); + return; + } + + /* Can't be bothered to huff'n'puff to make these (allegedly) rare + cases go quickly. */ + if (size == 2) { + PROF_EVENT(83); + mc_fpu_read_check_SLOWLY ( addr, 2 ); + return; + } + + if (size == 16 /*SSE*/ + || size == 10 || size == 28 || size == 108 || size == 512) { + PROF_EVENT(84); + mc_fpu_read_check_SLOWLY ( addr, size ); + return; + } + + VG_(printf)("size is %d\n", size); + VG_(skin_panic)("MC_(fpu_read_check): unhandled size"); +# endif +} + + +REGPARM(2) +void MC_(fpu_write_check) ( Addr addr, Int size ) +{ + /* Ensure the written area is addressible, and moan if otherwise. + If it is addressible, make it valid, otherwise invalid. + */ + + SecMap* sm; + UInt sm_off, v_off, a_off; + Addr addr4; + + PROF_EVENT(85); + +# ifdef VG_DEBUG_MEMORY + mc_fpu_write_check_SLOWLY ( addr, size ); +# else + + if (size == 4) { + if (!IS_ALIGNED4_ADDR(addr)) goto slow4; + PROF_EVENT(86); + /* Properly aligned. */ + sm = primary_map[addr >> 16]; + sm_off = addr & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow4; + /* Properly aligned and addressible. Make valid. */ + v_off = addr & 0xFFFF; + ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID; + return; + slow4: + mc_fpu_write_check_SLOWLY ( addr, 4 ); + return; + } + + if (size == 8) { + if (!IS_ALIGNED4_ADDR(addr)) goto slow8; + PROF_EVENT(87); + /* Properly aligned. Do it in two halves. */ + addr4 = addr + 4; + /* First half. */ + sm = primary_map[addr >> 16]; + sm_off = addr & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8; + /* First half properly aligned and addressible. Make valid. */ + v_off = addr & 0xFFFF; + ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID; + /* Second half. */ + sm = primary_map[addr4 >> 16]; + sm_off = addr4 & 0xFFFF; + a_off = sm_off >> 3; + if (sm->abits[a_off] != VGM_BYTE_VALID) goto slow8; + /* Second half properly aligned and addressible. */ + v_off = addr4 & 0xFFFF; + ((UInt*)(sm->vbyte))[ v_off >> 2 ] = VGM_WORD_VALID; + /* Properly aligned, addressible and with valid data. */ + return; + slow8: + mc_fpu_write_check_SLOWLY ( addr, 8 ); + return; + } + + /* Can't be bothered to huff'n'puff to make these (allegedly) rare + cases go quickly. */ + if (size == 2) { + PROF_EVENT(88); + mc_fpu_write_check_SLOWLY ( addr, 2 ); + return; + } + + if (size == 16 /*SSE*/ + || size == 10 || size == 28 || size == 108 || size == 512) { + PROF_EVENT(89); + mc_fpu_write_check_SLOWLY ( addr, size ); + return; + } + + VG_(printf)("size is %d\n", size); + VG_(skin_panic)("MC_(fpu_write_check): unhandled size"); +# endif +} + + +/* --------------------------------------------------------------------- + Slow, general cases for FPU load and store checks. + ------------------------------------------------------------------ */ + +/* Generic version. Test for both addr and value errors, but if + there's an addr error, don't report a value error even if it + exists. */ + +void mc_fpu_read_check_SLOWLY ( Addr addr, Int size ) +{ + Int i; + Bool aerr = False; + Bool verr = False; + PROF_EVENT(90); + for (i = 0; i < size; i++) { + PROF_EVENT(91); + if (get_abit(addr+i) != VGM_BIT_VALID) + aerr = True; + if (get_vbyte(addr+i) != VGM_BYTE_VALID) + verr = True; + } + + if (aerr) { + MAC_(record_address_error)( VG_(get_current_tid)(), addr, size, False ); + } else { + if (verr) + MC_(record_value_error)( VG_(get_current_tid)(), size ); + } +} + + +/* Generic version. Test for addr errors. Valid addresses are + given valid values, and invalid addresses invalid values. */ + +void mc_fpu_write_check_SLOWLY ( Addr addr, Int size ) +{ + Int i; + Addr a_here; + Bool a_ok; + Bool aerr = False; + PROF_EVENT(92); + for (i = 0; i < size; i++) { + PROF_EVENT(93); + a_here = addr+i; + a_ok = get_abit(a_here) == VGM_BIT_VALID; + if (a_ok) { + set_vbyte(a_here, VGM_BYTE_VALID); + } else { + set_vbyte(a_here, VGM_BYTE_INVALID); + aerr = True; + } + } + if (aerr) { + MAC_(record_address_error)( VG_(get_current_tid)(), addr, size, True ); + } +} + + +/*------------------------------------------------------------*/ +/*--- Metadata get/set functions, for client requests. ---*/ +/*------------------------------------------------------------*/ + +/* Copy Vbits for src into vbits. Returns: 1 == OK, 2 == alignment + error, 3 == addressing error. */ +Int MC_(get_or_set_vbits_for_client) ( + ThreadId tid, + Addr dataV, + Addr vbitsV, + UInt size, + Bool setting /* True <=> set vbits, False <=> get vbits */ +) +{ + Bool addressibleD = True; + Bool addressibleV = True; + UInt* data = (UInt*)dataV; + UInt* vbits = (UInt*)vbitsV; + UInt szW = size / 4; /* sigh */ + UInt i; + UInt* dataP = NULL; /* bogus init to keep gcc happy */ + UInt* vbitsP = NULL; /* ditto */ + + /* Check alignment of args. */ + if (!(IS_ALIGNED4_ADDR(data) && IS_ALIGNED4_ADDR(vbits))) + return 2; + if ((size & 3) != 0) + return 2; + + /* Check that arrays are addressible. */ + for (i = 0; i < szW; i++) { + dataP = &data[i]; + vbitsP = &vbits[i]; + if (get_abits4_ALIGNED((Addr)dataP) != VGM_NIBBLE_VALID) { + addressibleD = False; + break; + } + if (get_abits4_ALIGNED((Addr)vbitsP) != VGM_NIBBLE_VALID) { + addressibleV = False; + break; + } + } + if (!addressibleD) { + MAC_(record_address_error)( tid, (Addr)dataP, 4, + setting ? True : False ); + return 3; + } + if (!addressibleV) { + MAC_(record_address_error)( tid, (Addr)vbitsP, 4, + setting ? False : True ); + return 3; + } + + /* Do the copy */ + if (setting) { + /* setting */ + for (i = 0; i < szW; i++) { + if (get_vbytes4_ALIGNED( (Addr)&vbits[i] ) != VGM_WORD_VALID) + MC_(record_value_error)(tid, 4); + set_vbytes4_ALIGNED( (Addr)&data[i], vbits[i] ); + } + } else { + /* getting */ + for (i = 0; i < szW; i++) { + vbits[i] = get_vbytes4_ALIGNED( (Addr)&data[i] ); + set_vbytes4_ALIGNED( (Addr)&vbits[i], VGM_WORD_VALID ); + } + } + + return 1; +} + + +/*------------------------------------------------------------*/ +/*--- Detecting leaked (unreachable) malloc'd blocks. ---*/ +/*------------------------------------------------------------*/ + +/* For the memory leak detector, say whether an entire 64k chunk of + address space is possibly in use, or not. If in doubt return + True. +*/ +static +Bool mc_is_valid_64k_chunk ( UInt chunk_number ) +{ + sk_assert(chunk_number >= 0 && chunk_number < 65536); + if (IS_DISTINGUISHED_SM(primary_map[chunk_number])) { + /* Definitely not in use. */ + return False; + } else { + return True; + } +} + + +/* For the memory leak detector, say whether or not a given word + address is to be regarded as valid. */ +static +Bool mc_is_valid_address ( Addr a ) +{ + UInt vbytes; + UChar abits; + sk_assert(IS_ALIGNED4_ADDR(a)); + abits = get_abits4_ALIGNED(a); + vbytes = get_vbytes4_ALIGNED(a); + if (abits == VGM_NIBBLE_VALID && vbytes == VGM_WORD_VALID) { + return True; + } else { + return False; + } +} + + +/* Leak detector for this tool. We don't actually do anything, merely + run the generic leak detector with suitable parameters for this + tool. */ +void MC_(detect_memory_leaks) ( void ) +{ + MAC_(do_detect_memory_leaks) ( mc_is_valid_64k_chunk, mc_is_valid_address ); +} + + +/* --------------------------------------------------------------------- + Sanity check machinery (permanently engaged). + ------------------------------------------------------------------ */ + +Bool SK_(cheap_sanity_check) ( void ) +{ + /* nothing useful we can rapidly check */ + return True; +} + +Bool SK_(expensive_sanity_check) ( void ) +{ + Int i; + + /* Make sure nobody changed the distinguished secondary. */ + for (i = 0; i < 8192; i++) + if (distinguished_secondary_map.abits[i] != VGM_BYTE_INVALID) + return False; + + for (i = 0; i < 65536; i++) + if (distinguished_secondary_map.vbyte[i] != VGM_BYTE_INVALID) + return False; + + /* Make sure that the upper 3/4 of the primary map hasn't + been messed with. */ + for (i = 65536; i < 262144; i++) + if (primary_map[i] != & distinguished_secondary_map) + return False; + + return True; +} + +/* --------------------------------------------------------------------- + Debugging machinery (turn on to debug). Something of a mess. + ------------------------------------------------------------------ */ + +#if 0 +/* Print the value tags on the 8 integer registers & flag reg. */ + +static void uint_to_bits ( UInt x, Char* str ) +{ + Int i; + Int w = 0; + /* str must point to a space of at least 36 bytes. */ + for (i = 31; i >= 0; i--) { + str[w++] = (x & ( ((UInt)1) << i)) ? '1' : '0'; + if (i == 24 || i == 16 || i == 8) + str[w++] = ' '; + } + str[w++] = 0; + sk_assert(w == 36); +} + +/* Caution! Not vthread-safe; looks in VG_(baseBlock), not the thread + state table. */ + +static void vg_show_reg_tags ( void ) +{ + Char buf1[36]; + Char buf2[36]; + UInt z_eax, z_ebx, z_ecx, z_edx, + z_esi, z_edi, z_ebp, z_esp, z_eflags; + + z_eax = VG_(baseBlock)[VGOFF_(sh_eax)]; + z_ebx = VG_(baseBlock)[VGOFF_(sh_ebx)]; + z_ecx = VG_(baseBlock)[VGOFF_(sh_ecx)]; + z_edx = VG_(baseBlock)[VGOFF_(sh_edx)]; + z_esi = VG_(baseBlock)[VGOFF_(sh_esi)]; + z_edi = VG_(baseBlock)[VGOFF_(sh_edi)]; + z_ebp = VG_(baseBlock)[VGOFF_(sh_ebp)]; + z_esp = VG_(baseBlock)[VGOFF_(sh_esp)]; + z_eflags = VG_(baseBlock)[VGOFF_(sh_eflags)]; + + uint_to_bits(z_eflags, buf1); + VG_(message)(Vg_DebugMsg, "efl %s\n", buf1); + + uint_to_bits(z_eax, buf1); + uint_to_bits(z_ebx, buf2); + VG_(message)(Vg_DebugMsg, "eax %s ebx %s\n", buf1, buf2); + + uint_to_bits(z_ecx, buf1); + uint_to_bits(z_edx, buf2); + VG_(message)(Vg_DebugMsg, "ecx %s edx %s\n", buf1, buf2); + + uint_to_bits(z_esi, buf1); + uint_to_bits(z_edi, buf2); + VG_(message)(Vg_DebugMsg, "esi %s edi %s\n", buf1, buf2); + + uint_to_bits(z_ebp, buf1); + uint_to_bits(z_esp, buf2); + VG_(message)(Vg_DebugMsg, "ebp %s esp %s\n", buf1, buf2); +} + + +/* For debugging only. Scan the address space and touch all allegedly + addressible words. Useful for establishing where Valgrind's idea of + addressibility has diverged from what the kernel believes. */ + +static +void zzzmemscan_notify_word ( Addr a, UInt w ) +{ +} + +void zzzmemscan ( void ) +{ + Int n_notifies + = VG_(scan_all_valid_memory)( zzzmemscan_notify_word ); + VG_(printf)("zzzmemscan: n_bytes = %d\n", 4 * n_notifies ); +} +#endif + + + + +#if 0 +static Int zzz = 0; + +void show_bb ( Addr eip_next ) +{ + VG_(printf)("[%4d] ", zzz); + vg_show_reg_tags( &VG_(m_shadow ); + VG_(translate) ( eip_next, NULL, NULL, NULL ); +} +#endif /* 0 */ + + +/*------------------------------------------------------------*/ +/*--- Command line args ---*/ +/*------------------------------------------------------------*/ + +Bool MC_(clo_avoid_strlen_errors) = True; +Bool MC_(clo_cleanup) = True; + +Bool SK_(process_cmd_line_option)(Char* arg) +{ + VG_BOOL_CLO("--avoid-strlen-errors", MC_(clo_avoid_strlen_errors)) + else VG_BOOL_CLO("--cleanup", MC_(clo_cleanup)) + else + return MAC_(process_common_cmd_line_option)(arg); + + return True; +} + +void SK_(print_usage)(void) +{ + MAC_(print_common_usage)(); + VG_(printf)( +" --avoid-strlen-errors=no|yes suppress errs from inlined strlen [yes]\n" + ); +} + +void SK_(print_debug_usage)(void) +{ + MAC_(print_common_debug_usage)(); + VG_(printf)( +" --cleanup=no|yes improve after instrumentation? [yes]\n" + ); +} + + +/*------------------------------------------------------------*/ +/*--- Setup ---*/ +/*------------------------------------------------------------*/ + +void SK_(pre_clo_init)(void) +{ + VG_(details_name) ("Memcheck"); + VG_(details_version) (NULL); + VG_(details_description) ("a memory error detector"); + VG_(details_copyright_author)( + "Copyright (C) 2002-2004, and GNU GPL'd, by Julian Seward et al."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + VG_(details_avg_translation_sizeB) ( 228 ); + + VG_(needs_core_errors) (); + VG_(needs_skin_errors) (); + VG_(needs_libc_freeres) (); + VG_(needs_shadow_regs) (); + VG_(needs_command_line_options)(); + VG_(needs_client_requests) (); + VG_(needs_extended_UCode) (); + VG_(needs_syscall_wrapper) (); + VG_(needs_sanity_checks) (); + VG_(needs_shadow_memory) (); + + MAC_( new_mem_heap) = & mc_new_mem_heap; + MAC_( ban_mem_heap) = & MC_(make_noaccess); + MAC_(copy_mem_heap) = & mc_copy_address_range_state; + MAC_( die_mem_heap) = & MC_(make_noaccess); + MAC_(check_noaccess) = & MC_(check_noaccess); + + VG_(init_new_mem_startup) ( & mc_new_mem_startup ); + VG_(init_new_mem_stack_signal) ( & MC_(make_writable) ); + VG_(init_new_mem_brk) ( & MC_(make_writable) ); + VG_(init_new_mem_mmap) ( & mc_set_perms ); + + VG_(init_copy_mem_remap) ( & mc_copy_address_range_state ); + VG_(init_change_mem_mprotect) ( & mc_set_perms ); + + VG_(init_die_mem_stack_signal) ( & MC_(make_noaccess) ); + VG_(init_die_mem_brk) ( & MC_(make_noaccess) ); + VG_(init_die_mem_munmap) ( & MC_(make_noaccess) ); + + VG_(init_new_mem_stack_4) ( & MAC_(new_mem_stack_4) ); + VG_(init_new_mem_stack_8) ( & MAC_(new_mem_stack_8) ); + VG_(init_new_mem_stack_12) ( & MAC_(new_mem_stack_12) ); + VG_(init_new_mem_stack_16) ( & MAC_(new_mem_stack_16) ); + VG_(init_new_mem_stack_32) ( & MAC_(new_mem_stack_32) ); + VG_(init_new_mem_stack) ( & MAC_(new_mem_stack) ); + + VG_(init_die_mem_stack_4) ( & MAC_(die_mem_stack_4) ); + VG_(init_die_mem_stack_8) ( & MAC_(die_mem_stack_8) ); + VG_(init_die_mem_stack_12) ( & MAC_(die_mem_stack_12) ); + VG_(init_die_mem_stack_16) ( & MAC_(die_mem_stack_16) ); + VG_(init_die_mem_stack_32) ( & MAC_(die_mem_stack_32) ); + VG_(init_die_mem_stack) ( & MAC_(die_mem_stack) ); + + VG_(init_ban_mem_stack) ( & MC_(make_noaccess) ); + + VG_(init_pre_mem_read) ( & mc_check_is_readable ); + VG_(init_pre_mem_read_asciiz) ( & mc_check_is_readable_asciiz ); + VG_(init_pre_mem_write) ( & mc_check_is_writable ); + VG_(init_post_mem_write) ( & MC_(make_readable) ); + + VG_(init_post_regs_write_init) ( & mc_post_regs_write_init ); + VG_(init_post_reg_write_syscall_return) ( & mc_post_reg_write ); + VG_(init_post_reg_write_deliver_signal) ( & mc_post_reg_write ); + VG_(init_post_reg_write_pthread_return) ( & mc_post_reg_write ); + VG_(init_post_reg_write_clientreq_return) ( & mc_post_reg_write ); + VG_(init_post_reg_write_clientcall_return) ( & mc_post_reg_write_clientcall ); + + /* Three compact slots taken up by stack memory helpers */ + VG_(register_compact_helper)((Addr) & MC_(helper_value_check4_fail)); + VG_(register_compact_helper)((Addr) & MC_(helper_value_check0_fail)); + VG_(register_compact_helper)((Addr) & MC_(helper_value_check2_fail)); + VG_(register_compact_helper)((Addr) & MC_(helperc_STOREV4)); + VG_(register_compact_helper)((Addr) & MC_(helperc_LOADV4)); + + /* These two made non-compact because 2-byte transactions are rare. */ + VG_(register_noncompact_helper)((Addr) & MC_(helperc_STOREV2)); + VG_(register_noncompact_helper)((Addr) & MC_(helperc_STOREV1)); + VG_(register_noncompact_helper)((Addr) & MC_(helperc_LOADV2)); + VG_(register_noncompact_helper)((Addr) & MC_(helperc_LOADV1)); + VG_(register_noncompact_helper)((Addr) & MC_(fpu_write_check)); + VG_(register_noncompact_helper)((Addr) & MC_(fpu_read_check)); + VG_(register_noncompact_helper)((Addr) & MC_(helper_value_check1_fail)); + + VGP_(register_profile_event) ( VgpSetMem, "set-mem-perms" ); + VGP_(register_profile_event) ( VgpCheckMem, "check-mem-perms" ); + VGP_(register_profile_event) ( VgpESPAdj, "adjust-ESP" ); + + /* Additional block description for VG_(describe_addr)() */ + MAC_(describe_addr_supp) = MC_(client_perm_maybe_describe); + + init_shadow_memory(); + MAC_(common_pre_clo_init)(); +} + +void SK_(post_clo_init) ( void ) +{ +} + +void SK_(fini) ( Int exitcode ) +{ + MAC_(common_fini)( MC_(detect_memory_leaks) ); + + if (0) { + VG_(message)(Vg_DebugMsg, + "------ Valgrind's client block stats follow ---------------" ); + MC_(show_client_block_stats)(); + } +} + +VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 9./8) + +/*--------------------------------------------------------------------*/ +/*--- end mc_main.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/mc_translate.c b/VEX/head20041019/memcheck/mc_translate.c new file mode 100644 index 0000000000..af01e6d4fb --- /dev/null +++ b/VEX/head20041019/memcheck/mc_translate.c @@ -0,0 +1,1659 @@ + +/*--------------------------------------------------------------------*/ +/*--- Instrument UCode to perform memory checking operations. ---*/ +/*--- mc_translate.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward + jseward@acm.org + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "mc_include.h" + +/* --------------------------------------------------------------------- + Template functions for extending UCode + ------------------------------------------------------------------ */ + +/* Compare this with the restrictions on core instructions in + vg_translate.c:is_sane_UInstr(). Everything general said there + applies here too. +*/ +Bool SK_(sane_XUInstr)(Bool beforeRA, Bool beforeLiveness, UInstr* u) +{ +// SSS: duplicating these macros really sucks +# define LIT0 (u->lit32 == 0) +# define LIT1 (!(LIT0)) +# define LITm (u->tag1 == Literal ? True : LIT0 ) +# define SZ0 (u->size == 0) +# define SZi (u->size == 4 || u->size == 2 || u->size == 1) +# define SZj (u->size == 4 || u->size == 2 || u->size == 1 || u->size == 0) +# define CC0 (u->flags_r == FlagsEmpty && u->flags_w == FlagsEmpty) +# define TR1 (beforeRA ? (u->tag1 == TempReg) : (u->tag1 == RealReg)) +# define TR2 (beforeRA ? (u->tag2 == TempReg) : (u->tag2 == RealReg)) +# define A1 (u->tag1 == ArchReg) +# define A2 (u->tag2 == ArchReg) +# define L1 (u->tag1 == Literal && u->val1 == 0) +# define Ls1 (u->tag1 == Lit16) +# define Ls3 (u->tag3 == Lit16) +# define TRL1 (TR1 || L1) +# define N2 (u->tag2 == NoValue) +# define N3 (u->tag3 == NoValue) +# define COND0 (u->cond == 0) +# define EXTRA4b0 (u->extra4b == 0) +# define SG_WD0 (u->signed_widen == 0) +# define JMPKIND0 (u->jmpkind == 0) +# define CCALL0 (u->argc==0 && u->regparms_n==0 && u->has_ret_val==0 && \ + ( beforeLiveness \ + ? u->regs_live_after == ALL_RREGS_LIVE \ + : True )) +# define XOTHER (COND0 && EXTRA4b0 && SG_WD0 && JMPKIND0 && CCALL0) + + Int n_lits = 0; + if (u->tag1 == Literal) n_lits++; + if (u->tag2 == Literal) n_lits++; + if (u->tag3 == Literal) n_lits++; + if (n_lits > 1) + return False; + + /* Fields not checked: val1, val2, val3 */ + + switch (u->opcode) { + + /* Fields checked: lit32 size flags_r/w tag1 tag2 tag3 (rest) */ + case LOADV: return LIT0 && SZi && CC0 && TR1 && TR2 && N3 && XOTHER; + case STOREV: return LITm && SZi && CC0 && TRL1 && TR2 && N3 && XOTHER; + case GETV: return LIT0 && SZi && CC0 && A1 && TR2 && N3 && XOTHER; + case PUTV: return LITm && SZi && CC0 && TRL1 && A2 && N3 && XOTHER; + case GETVF: + case PUTVF: return LIT0 && SZ0 && CC0 && TR1 && N2 && N3 && XOTHER; + case TESTV: + case SETV: return LIT0 && SZj && CC0 && TR1 && N2 && N3 && XOTHER; + case TAG1: return LIT0 && SZ0 && CC0 && TR1 && N2 && Ls3 && XOTHER; + case TAG2: return LIT0 && SZ0 && CC0 && TR1 && TR2 && Ls3 && XOTHER; + default: + VG_(printf)("unhandled opcode: %u\n", u->opcode); + VG_(skin_panic)("SK_(sane_XUInstr): unhandled opcode"); + } +# undef LIT0 +# undef LIT1 +# undef LITm +# undef SZ0 +# undef SZi +# undef SZj +# undef CC0 +# undef TR1 +# undef TR2 +# undef A1 +# undef A2 +# undef L1 +# undef Ls1 +# undef Ls3 +# undef TRL1 +# undef N2 +# undef N3 +# undef COND0 +# undef EXTRA4b0 +# undef JMPKIND0 +# undef CCALL0 +# undef XOTHER +} + +static Char* nameOfTagOp ( TagOp h ) +{ + switch (h) { + case Tag_PCast40: return "PCast40"; + case Tag_PCast20: return "PCast20"; + case Tag_PCast10: return "PCast10"; + case Tag_PCast01: return "PCast01"; + case Tag_PCast02: return "PCast02"; + case Tag_PCast04: return "PCast04"; + case Tag_PCast14: return "PCast14"; + case Tag_PCast12: return "PCast12"; + case Tag_PCast11: return "PCast11"; + case Tag_Left4: return "Left4"; + case Tag_Left2: return "Left2"; + case Tag_Left1: return "Left1"; + case Tag_SWiden14: return "SWiden14"; + case Tag_SWiden24: return "SWiden24"; + case Tag_SWiden12: return "SWiden12"; + case Tag_ZWiden14: return "ZWiden14"; + case Tag_ZWiden24: return "ZWiden24"; + case Tag_ZWiden12: return "ZWiden12"; + case Tag_UifU4: return "UifU4"; + case Tag_UifU2: return "UifU2"; + case Tag_UifU1: return "UifU1"; + case Tag_UifU0: return "UifU0"; + case Tag_DifD4: return "DifD4"; + case Tag_DifD2: return "DifD2"; + case Tag_DifD1: return "DifD1"; + case Tag_ImproveAND4_TQ: return "ImproveAND4_TQ"; + case Tag_ImproveAND2_TQ: return "ImproveAND2_TQ"; + case Tag_ImproveAND1_TQ: return "ImproveAND1_TQ"; + case Tag_ImproveOR4_TQ: return "ImproveOR4_TQ"; + case Tag_ImproveOR2_TQ: return "ImproveOR2_TQ"; + case Tag_ImproveOR1_TQ: return "ImproveOR1_TQ"; + case Tag_DebugFn: return "DebugFn"; + default: VG_(skin_panic)("vg_nameOfTagOp"); + } +} + + +Char* SK_(name_XUOpcode)(Opcode opc) +{ + switch (opc) { + case GETVF: return "GETVF"; + case PUTVF: return "PUTVF"; + case TAG1: return "TAG1"; + case TAG2: return "TAG2"; + case LOADV: return "LOADV"; + case STOREV: return "STOREV"; + case GETV: return "GETV"; + case PUTV: return "PUTV"; + case TESTV: return "TESTV"; + case SETV: return "SETV"; + default: + VG_(printf)("unhandled opcode: %u\n", opc); + VG_(skin_panic)("SK_(name_XUOpcode): unhandled case"); + } +} + +/* --------------------------------------------------------------------- + Debugging stuff. + ------------------------------------------------------------------ */ + +void SK_(pp_XUInstr)(UInstr* u) +{ + switch (u->opcode) { + case TAG1: + VG_(pp_UOperand)(u, 1, 4, False); + VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 )); + VG_(pp_UOperand)(u, 1, 4, False); + VG_(printf)(" )"); + break; + + case TAG2: + VG_(pp_UOperand)(u, 2, 4, False); + VG_(printf)(" = %s ( ", nameOfTagOp( u->val3 )); + VG_(pp_UOperand)(u, 1, 4, False); + VG_(printf)(", "); + VG_(pp_UOperand)(u, 2, 4, False); + VG_(printf)(" )"); + break; + + case STOREV: case LOADV: + VG_(pp_UOperand)(u, 1, u->size, u->opcode==LOADV); + VG_(printf)(", "); + VG_(pp_UOperand)(u, 2, u->size, u->opcode==STOREV); + break; + + case PUTVF: case GETVF: + VG_(pp_UOperand)(u, 1, 0, False); + break; + + case GETV: case PUTV: + VG_(pp_UOperand)(u, 1, u->opcode==PUTV ? 4 : u->size, False); + VG_(printf)(", "); + VG_(pp_UOperand)(u, 2, u->opcode==GETV ? 4 : u->size, False); + break; + + case TESTV: case SETV: + VG_(pp_UOperand)(u, 1, u->size, False); + break; + + default: + VG_(printf)("unhandled opcode: %u\n", u->opcode); + VG_(skin_panic)("SK_(pp_XUInstr): unhandled opcode"); + } + +} + +Int SK_(get_Xreg_usage)(UInstr* u, Tag tag, Int* regs, Bool* isWrites) +{ +# define RD(ono) VG_UINSTR_READS_REG(ono, regs, isWrites) +# define WR(ono) VG_UINSTR_WRITES_REG(ono, regs, isWrites) + + Int n = 0; + switch (u->opcode) { + case TAG1: RD(1); WR(1); break; + case TAG2: RD(1); RD(2); WR(2); break; + case LOADV: RD(1); WR(2); break; + case STOREV: RD(1); RD(2); break; + case GETV: WR(2); break; + case PUTV: RD(1); break; + case TESTV: RD(1); break; + case SETV: WR(1); break; + case PUTVF: RD(1); break; + case GETVF: WR(1); break; + + default: + VG_(printf)("unhandled opcode: %u\n", u->opcode); + VG_(skin_panic)("SK_(get_Xreg_usage): unhandled opcode"); + } + return n; + +# undef RD +# undef WR +} + +/*------------------------------------------------------------*/ +/*--- New instrumentation machinery. ---*/ +/*------------------------------------------------------------*/ + +static +TagOp get_Tag_ImproveOR_TQ ( Int sz ) +{ + switch (sz) { + case 4: return Tag_ImproveOR4_TQ; + case 2: return Tag_ImproveOR2_TQ; + case 1: return Tag_ImproveOR1_TQ; + default: VG_(skin_panic)("get_Tag_ImproveOR_TQ"); + } +} + + +static +TagOp get_Tag_ImproveAND_TQ ( Int sz ) +{ + switch (sz) { + case 4: return Tag_ImproveAND4_TQ; + case 2: return Tag_ImproveAND2_TQ; + case 1: return Tag_ImproveAND1_TQ; + default: VG_(skin_panic)("get_Tag_ImproveAND_TQ"); + } +} + + +static +TagOp get_Tag_Left ( Int sz ) +{ + switch (sz) { + case 4: return Tag_Left4; + case 2: return Tag_Left2; + case 1: return Tag_Left1; + default: VG_(skin_panic)("get_Tag_Left"); + } +} + + +static +TagOp get_Tag_UifU ( Int sz ) +{ + switch (sz) { + case 4: return Tag_UifU4; + case 2: return Tag_UifU2; + case 1: return Tag_UifU1; + case 0: return Tag_UifU0; + default: VG_(skin_panic)("get_Tag_UifU"); + } +} + + +static +TagOp get_Tag_DifD ( Int sz ) +{ + switch (sz) { + case 4: return Tag_DifD4; + case 2: return Tag_DifD2; + case 1: return Tag_DifD1; + default: VG_(skin_panic)("get_Tag_DifD"); + } +} + + +static +TagOp get_Tag_PCast ( Int szs, Int szd ) +{ + if (szs == 4 && szd == 0) return Tag_PCast40; + if (szs == 2 && szd == 0) return Tag_PCast20; + if (szs == 1 && szd == 0) return Tag_PCast10; + if (szs == 0 && szd == 1) return Tag_PCast01; + if (szs == 0 && szd == 2) return Tag_PCast02; + if (szs == 0 && szd == 4) return Tag_PCast04; + if (szs == 1 && szd == 4) return Tag_PCast14; + if (szs == 1 && szd == 2) return Tag_PCast12; + if (szs == 1 && szd == 1) return Tag_PCast11; + VG_(printf)("get_Tag_PCast(%d,%d)\n", szs, szd); + VG_(skin_panic)("get_Tag_PCast"); +} + + +static +TagOp get_Tag_Widen ( Bool syned, Int szs, Int szd ) +{ + if (szs == 1 && szd == 2 && syned) return Tag_SWiden12; + if (szs == 1 && szd == 2 && !syned) return Tag_ZWiden12; + + if (szs == 1 && szd == 4 && syned) return Tag_SWiden14; + if (szs == 1 && szd == 4 && !syned) return Tag_ZWiden14; + + if (szs == 2 && szd == 4 && syned) return Tag_SWiden24; + if (szs == 2 && szd == 4 && !syned) return Tag_ZWiden24; + + VG_(printf)("get_Tag_Widen(%d,%d,%d)\n", (Int)syned, szs, szd); + VG_(skin_panic)("get_Tag_Widen"); +} + +/* Pessimally cast the spec'd shadow from one size to another. */ +static +void create_PCast ( UCodeBlock* cb, Int szs, Int szd, Int tempreg ) +{ + if (szs == 0 && szd == 0) + return; + uInstr3(cb, TAG1, 0, TempReg, tempreg, + NoValue, 0, + Lit16, get_Tag_PCast(szs,szd)); +} + + +/* Create a signed or unsigned widen of the spec'd shadow from one + size to another. The only allowed size transitions are 1->2, 1->4 + and 2->4. */ +static +void create_Widen ( UCodeBlock* cb, Bool signed_widen, + Int szs, Int szd, Int tempreg ) +{ + if (szs == szd) return; + uInstr3(cb, TAG1, 0, TempReg, tempreg, + NoValue, 0, + Lit16, get_Tag_Widen(signed_widen,szs,szd)); +} + + +/* Get the condition codes into a new shadow, at the given size. */ +static +Int create_GETVF ( UCodeBlock* cb, Int sz ) +{ + Int tt = newShadow(cb); + uInstr1(cb, GETVF, 0, TempReg, tt); + create_PCast(cb, 0, sz, tt); + return tt; +} + + +/* Save the condition codes from the spec'd shadow. */ +static +void create_PUTVF ( UCodeBlock* cb, Int sz, Int tempreg ) +{ + if (sz == 0) { + uInstr1(cb, PUTVF, 0, TempReg, tempreg); + } else { + Int tt = newShadow(cb); + uInstr2(cb, MOV, 4, TempReg, tempreg, TempReg, tt); + create_PCast(cb, sz, 0, tt); + uInstr1(cb, PUTVF, 0, TempReg, tt); + } +} + + +/* Do Left on the spec'd shadow. */ +static +void create_Left ( UCodeBlock* cb, Int sz, Int tempreg ) +{ + uInstr3(cb, TAG1, 0, + TempReg, tempreg, + NoValue, 0, + Lit16, get_Tag_Left(sz)); +} + + +/* Do UifU on ts and td, putting the result in td. */ +static +void create_UifU ( UCodeBlock* cb, Int sz, Int ts, Int td ) +{ + uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td, + Lit16, get_Tag_UifU(sz)); +} + + +/* Do DifD on ts and td, putting the result in td. */ +static +void create_DifD ( UCodeBlock* cb, Int sz, Int ts, Int td ) +{ + uInstr3(cb, TAG2, 0, TempReg, ts, TempReg, td, + Lit16, get_Tag_DifD(sz)); +} + + +/* Do HelpAND on value tval and tag tqqq, putting the result in + tqqq. */ +static +void create_ImproveAND_TQ ( UCodeBlock* cb, Int sz, Int tval, Int tqqq ) +{ + uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq, + Lit16, get_Tag_ImproveAND_TQ(sz)); +} + + +/* Do HelpOR on value tval and tag tqqq, putting the result in + tqqq. */ +static +void create_ImproveOR_TQ ( UCodeBlock* cb, Int sz, Int tval, Int tqqq ) +{ + uInstr3(cb, TAG2, 0, TempReg, tval, TempReg, tqqq, + Lit16, get_Tag_ImproveOR_TQ(sz)); +} + + +/* Get the shadow for an operand described by (tag, val). Emit code + to do this and return the identity of the shadow holding the + result. The result tag is always copied into a new shadow, so it + can be modified without trashing the original.*/ +static +Int /* TempReg */ getOperandShadow ( UCodeBlock* cb, + Int sz, Int tag, Int val ) +{ + Int sh; + sh = newShadow(cb); + if (tag == TempReg) { + uInstr2(cb, MOV, 4, TempReg, SHADOW(val), TempReg, sh); + return sh; + } + if (tag == Literal) { + uInstr1(cb, SETV, sz, TempReg, sh); + return sh; + } + if (tag == ArchReg) { + uInstr2(cb, GETV, sz, ArchReg, val, TempReg, sh); + return sh; + } + VG_(skin_panic)("getOperandShadow"); +} + +/* Create and return an instrumented version of cb_in. Free cb_in + before returning. */ +static UCodeBlock* memcheck_instrument ( UCodeBlock* cb_in ) +{ + UCodeBlock* cb; + Int i, j; + UInstr* u_in; + Int qs, qd, qt, qtt; + Bool bogusLiterals; + + cb = VG_(setup_UCodeBlock)(cb_in); + + /* Scan the block to look for bogus literals. These are magic + numbers which particularly appear in hand-optimised / inlined + implementations of strlen() et al which cause so much trouble + (spurious reports of uninit-var uses). Purpose of this horrible + hack is to disable some checks any such literals are present in + this basic block. */ + bogusLiterals = False; + + if (MC_(clo_avoid_strlen_errors)) { + for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { + u_in = VG_(get_instr)(cb_in, i); + switch (u_in->opcode) { + case ADD: case SUB: case MOV: + if (u_in->size == 4 && u_in->tag1 == Literal) + goto literal; + break; + case LEA1: + sk_assert(u_in->size == 4); + goto literal; + default: + break; + } + continue; + literal: + if (u_in->lit32 == 0xFEFEFEFF || + u_in->lit32 == 0x80808080 || + u_in->lit32 == 0x00008080) { + bogusLiterals = True; + break; + } + } + } + + for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { + u_in = VG_(get_instr)(cb_in, i); + qs = qd = qt = qtt = INVALID_TEMPREG; + + switch (u_in->opcode) { + + case LOCK: + case NOP: + break; + + case INCEIP: + VG_(copy_UInstr)(cb, u_in); + break; + + /* The segment registers do not have their definedness + tracked. We therefore make fake shadows on GETSEG and + test them on PUTSEG. This will catch writing garbage to a + segment register; therefore we can assume it to be defined + when read (GETSEGd). Since the first arg of USESEG is + fetched by GETSEG, we can assume it to be defined, and so + the definedness of the result is simply the definedness of + the second (virtual_address) arg of USESEG. The upshot of + all this is that instrumentation of USESEG is a no-op! */ + + case PUTSEG: + sk_assert(u_in->tag1 == TempReg); + uInstr1(cb, TESTV, 2, TempReg, SHADOW(u_in->val1)); + uInstr1(cb, SETV, 2, TempReg, SHADOW(u_in->val1)); + VG_(copy_UInstr)(cb, u_in); + break; + + case GETSEG: + sk_assert(u_in->tag2 == TempReg); + uInstr1(cb, SETV, 2, TempReg, SHADOW(u_in->val2)); + VG_(copy_UInstr)(cb, u_in); + break; + + case USESEG: + VG_(copy_UInstr)(cb, u_in); + break; + + /* Loads and stores. Test the V bits for the address. 24 + Mar 02: since the address is A-checked anyway, there's not + really much point in doing the V-check too, unless you + think that you might use addresses which are undefined but + still addressible. Hence the optionalisation of the V + check. 15 Dec 02: optionalisation removed, since it no + longer makes much sense given we also have an addrcheck + tool. + + The LOADV/STOREV does an addressibility check for the + address. */ + + case LOAD: + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1)); + uInstr2(cb, LOADV, u_in->size, + TempReg, u_in->val1, + TempReg, SHADOW(u_in->val2)); + VG_(copy_UInstr)(cb, u_in); + break; + + case STORE: + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); + uInstr2(cb, STOREV, u_in->size, + TempReg, SHADOW(u_in->val1), + TempReg, u_in->val2); + VG_(copy_UInstr)(cb, u_in); + break; + + /* Moving stuff around. Make the V bits follow accordingly, + but don't do anything else. */ + + case GET: + uInstr2(cb, GETV, u_in->size, + ArchReg, u_in->val1, + TempReg, SHADOW(u_in->val2)); + VG_(copy_UInstr)(cb, u_in); + break; + + case PUT: + uInstr2(cb, PUTV, u_in->size, + TempReg, SHADOW(u_in->val1), + ArchReg, u_in->val2); + VG_(copy_UInstr)(cb, u_in); + break; + + case GETF: + /* This is not the smartest way to do it, but should work. */ + qd = create_GETVF(cb, u_in->size); + uInstr2(cb, MOV, 4, TempReg, qd, TempReg, SHADOW(u_in->val1)); + VG_(copy_UInstr)(cb, u_in); + break; + + case PUTF: + create_PUTVF(cb, u_in->size, SHADOW(u_in->val1)); + VG_(copy_UInstr)(cb, u_in); + break; + + case MOV: + switch (u_in->tag1) { + case TempReg: + uInstr2(cb, MOV, 4, + TempReg, SHADOW(u_in->val1), + TempReg, SHADOW(u_in->val2)); + break; + case Literal: + uInstr1(cb, SETV, u_in->size, + TempReg, SHADOW(u_in->val2)); + break; + default: + VG_(skin_panic)("memcheck_instrument: MOV"); + } + VG_(copy_UInstr)(cb, u_in); + break; + + /* Special case of add, where one of the operands is a literal. + lea1(t) = t + some literal. + Therefore: lea1#(qa) = left(qa) + */ + case LEA1: + sk_assert(u_in->size == 4 && !VG_(any_flag_use)(u_in)); + qs = SHADOW(u_in->val1); + qd = SHADOW(u_in->val2); + uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qd); + create_Left(cb, u_in->size, qd); + VG_(copy_UInstr)(cb, u_in); + break; + + /* Another form of add. + lea2(ts,tt,shift) = ts + (tt << shift); shift is a literal + and is 0,1,2 or 3. + lea2#(qs,qt) = left(qs `UifU` (qt << shift)). + Note, subtly, that the shift puts zeroes at the bottom of qt, + meaning Valid, since the corresponding shift of tt puts + zeroes at the bottom of tb. + */ + case LEA2: { + Int shift; + sk_assert(u_in->size == 4 && !VG_(any_flag_use)(u_in)); + switch (u_in->extra4b) { + case 1: shift = 0; break; + case 2: shift = 1; break; + case 4: shift = 2; break; + case 8: shift = 3; break; + default: VG_(skin_panic)( "memcheck_instrument(LEA2)" ); + } + qs = SHADOW(u_in->val1); + qt = SHADOW(u_in->val2); + qd = SHADOW(u_in->val3); + uInstr2(cb, MOV, 4, TempReg, qt, TempReg, qd); + if (shift > 0) { + uInstr2(cb, SHL, 4, Literal, 0, TempReg, qd); + uLiteral(cb, shift); + } + create_UifU(cb, 4, qs, qd); + create_Left(cb, u_in->size, qd); + VG_(copy_UInstr)(cb, u_in); + break; + } + + /* inc#/dec#(qd) = q `UifU` left(qd) = left(qd) */ + case INC: case DEC: + qd = SHADOW(u_in->val1); + create_Left(cb, u_in->size, qd); + if (u_in->flags_w != FlagsEmpty) + create_PUTVF(cb, u_in->size, qd); + VG_(copy_UInstr)(cb, u_in); + break; + + /* This is a HACK (approximation :-) */ + /* rcl#/rcr#(qs,qd) + = let q0 = pcast-sz-0(qd) `UifU` pcast-sz-0(qs) `UifU` eflags# + eflags# = q0 + qd =pcast-0-sz(q0) + Ie, cast everything down to a single bit, then back up. + This assumes that any bad bits infect the whole word and + the eflags. + */ + case RCL: case RCR: + sk_assert(u_in->flags_r != FlagsEmpty); + /* The following assertion looks like it makes sense, but is + actually wrong. Consider this: + rcll %eax + imull %eax, %eax + The rcll writes O and C but so does the imull, so the O and C + write of the rcll is annulled by the prior improvement pass. + Noticed by Kevin Ryde + */ + /* sk_assert(u_in->flags_w != FlagsEmpty); */ + qs = getOperandShadow(cb, 1, u_in->tag1, u_in->val1); + /* We can safely modify qs; cast it to 0-size. */ + create_PCast(cb, 1, 0, qs); + qd = SHADOW(u_in->val2); + create_PCast(cb, u_in->size, 0, qd); + /* qs is cast-to-0(shift count#), and qd is cast-to-0(value#). */ + create_UifU(cb, 0, qs, qd); + /* qs is now free; reuse it for the flag definedness. */ + qs = create_GETVF(cb, 0); + create_UifU(cb, 0, qs, qd); + create_PUTVF(cb, 0, qd); + create_PCast(cb, 0, u_in->size, qd); + VG_(copy_UInstr)(cb, u_in); + break; + + /* for OP in shl shr sar rol ror + (qs is shift count#, qd is value to be OP#d) + OP(ts,td) + OP#(qs,qd) + = pcast-1-sz(qs) `UifU` OP(ts,qd) + So we apply OP to the tag bits too, and then UifU with + the shift count# to take account of the possibility of it + being undefined. + + A bit subtle: + ROL/ROR rearrange the tag bits as per the value bits. + SHL/SHR shifts zeroes into the value, and corresponding + zeroes indicating Definedness into the tag. + SAR copies the top bit of the value downwards, and therefore + SAR also copies the definedness of the top bit too. + So in all five cases, we just apply the same op to the tag + bits as is applied to the value bits. Neat! + */ + case SHL: + case SHR: case SAR: + case ROL: case ROR: { + Int t_amount = INVALID_TEMPREG; + sk_assert(u_in->tag1 == TempReg || u_in->tag1 == Literal); + sk_assert(u_in->tag2 == TempReg); + qd = SHADOW(u_in->val2); + + /* Make qs hold shift-count# and make + t_amount be a TempReg holding the shift count. */ + if (u_in->tag1 == Literal) { + t_amount = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_amount); + uLiteral(cb, u_in->lit32); + qs = SHADOW(t_amount); + uInstr1(cb, SETV, 1, TempReg, qs); + } else { + t_amount = u_in->val1; + qs = SHADOW(u_in->val1); + } + + uInstr2(cb, u_in->opcode, + u_in->size, + TempReg, t_amount, + TempReg, qd); + qt = newShadow(cb); + uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qt); + create_PCast(cb, 1, u_in->size, qt); + create_UifU(cb, u_in->size, qt, qd); + VG_(copy_UInstr)(cb, u_in); + break; + } + + /* One simple tag operation. */ + case WIDEN: + sk_assert(u_in->tag1 == TempReg); + create_Widen(cb, u_in->signed_widen, u_in->extra4b, u_in->size, + SHADOW(u_in->val1)); + VG_(copy_UInstr)(cb, u_in); + break; + + /* not#(x) = x (since bitwise independent) */ + case NOT: + sk_assert(u_in->tag1 == TempReg); + VG_(copy_UInstr)(cb, u_in); + break; + + /* neg#(x) = left(x) (derivable from case for SUB) */ + case NEG: + sk_assert(u_in->tag1 == TempReg); + create_Left(cb, u_in->size, SHADOW(u_in->val1)); + VG_(copy_UInstr)(cb, u_in); + break; + + /* bswap#(x) = bswap(x) */ + case BSWAP: + sk_assert(u_in->tag1 == TempReg); + sk_assert(u_in->size == 4); + qd = SHADOW(u_in->val1); + uInstr1(cb, BSWAP, 4, TempReg, qd); + VG_(copy_UInstr)(cb, u_in); + break; + + /* cc2val#(qd) = pcast-0-to-size(eflags#) */ + case CC2VAL: + sk_assert(u_in->tag1 == TempReg); + sk_assert(u_in->flags_r != FlagsEmpty); + qt = create_GETVF(cb, u_in->size); + uInstr2(cb, MOV, 4, TempReg, qt, TempReg, SHADOW(u_in->val1)); + VG_(copy_UInstr)(cb, u_in); + break; + + /* cmov#(qs,qd) = cmov(qs,qd) + That is, do the cmov of tags using the same flags as for + the data (obviously). However, first do a test on the + validity of the flags. + */ + case CMOV: + sk_assert(u_in->size == 4); + sk_assert(u_in->tag1 == TempReg); + sk_assert(u_in->tag2 == TempReg); + sk_assert(u_in->flags_r != FlagsEmpty); + sk_assert(u_in->flags_w == FlagsEmpty); + qs = SHADOW(u_in->val1); + qd = SHADOW(u_in->val2); + qt = create_GETVF(cb, 0); + uInstr1(cb, TESTV, 0, TempReg, qt); + /* qt should never be referred to again. Nevertheless + ... */ + uInstr1(cb, SETV, 0, TempReg, qt); + + uInstr2(cb, CMOV, 4, TempReg, qs, TempReg, qd); + uCond(cb, u_in->cond); + uFlagsRWU(cb, u_in->flags_r, u_in->flags_w, FlagsEmpty); + + VG_(copy_UInstr)(cb, u_in); + break; + + /* add#/sub#(qs,qd) + = qs `UifU` qd `UifU` left(qs) `UifU` left(qd) + = left(qs) `UifU` left(qd) + = left(qs `UifU` qd) + adc#/sbb#(qs,qd) + = left(qs `UifU` qd) `UifU` pcast(eflags#) + Second arg (dest) is TempReg. + First arg (src) is Literal or TempReg or ArchReg. + */ + case ADD: case SUB: + case ADC: case SBB: + case MUL: + qd = SHADOW(u_in->val2); + qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1); + create_UifU(cb, u_in->size, qs, qd); + create_Left(cb, u_in->size, qd); + if (u_in->opcode == ADC || u_in->opcode == SBB) { + sk_assert(u_in->flags_r != FlagsEmpty); + qt = create_GETVF(cb, u_in->size); + create_UifU(cb, u_in->size, qt, qd); + } + if (u_in->flags_w != FlagsEmpty) { + create_PUTVF(cb, u_in->size, qd); + } + VG_(copy_UInstr)(cb, u_in); + break; + + /* xor#(qs,qd) = qs `UifU` qd */ + case XOR: + qd = SHADOW(u_in->val2); + qs = getOperandShadow(cb, u_in->size, u_in->tag1, u_in->val1); + create_UifU(cb, u_in->size, qs, qd); + if (u_in->flags_w != FlagsEmpty) { + create_PUTVF(cb, u_in->size, qd); + } + VG_(copy_UInstr)(cb, u_in); + break; + + /* and#/or#(qs,qd) + = (qs `UifU` qd) `DifD` improve(vs,qs) + `DifD` improve(vd,qd) + where improve is the relevant one of + Improve{AND,OR}_TQ + Use the following steps, with qt as a temp: + qt = improve(vd,qd) + qd = qs `UifU` qd + qd = qt `DifD` qd + qt = improve(vs,qs) + qd = qt `DifD` qd + */ + case AND: case OR: + sk_assert(u_in->tag1 == TempReg); + sk_assert(u_in->tag2 == TempReg); + qd = SHADOW(u_in->val2); + qs = SHADOW(u_in->val1); + qt = newShadow(cb); + + /* qt = improve(vd,qd) */ + uInstr2(cb, MOV, 4, TempReg, qd, TempReg, qt); + if (u_in->opcode == AND) + create_ImproveAND_TQ(cb, u_in->size, u_in->val2, qt); + else + create_ImproveOR_TQ(cb, u_in->size, u_in->val2, qt); + /* qd = qs `UifU` qd */ + create_UifU(cb, u_in->size, qs, qd); + /* qd = qt `DifD` qd */ + create_DifD(cb, u_in->size, qt, qd); + /* qt = improve(vs,qs) */ + uInstr2(cb, MOV, 4, TempReg, qs, TempReg, qt); + if (u_in->opcode == AND) + create_ImproveAND_TQ(cb, u_in->size, u_in->val1, qt); + else + create_ImproveOR_TQ(cb, u_in->size, u_in->val1, qt); + /* qd = qt `DifD` qd */ + create_DifD(cb, u_in->size, qt, qd); + /* So, finally qd is the result tag. */ + if (u_in->flags_w != FlagsEmpty) { + create_PUTVF(cb, u_in->size, qd); + } + VG_(copy_UInstr)(cb, u_in); + break; + + /* Machinery to do with supporting CALLM. Copy the start and + end markers only to make the result easier to read + (debug); they generate no code and have no effect. + */ + case CALLM_S: case CALLM_E: + VG_(copy_UInstr)(cb, u_in); + break; + + /* Copy PUSH and POP verbatim. Arg/result absval + calculations are done when the associated CALL is + processed. CLEAR has no effect on absval calculations but + needs to be copied. + */ + case PUSH: case POP: case CLEAR: + VG_(copy_UInstr)(cb, u_in); + break; + + /* In short: + callm#(a1# ... an#) = (a1# `UifU` ... `UifU` an#) + We have to decide on a size to do the computation at, + although the choice doesn't affect correctness. We will + do a pcast to the final size anyway, so the only important + factor is to choose a size which minimises the total + number of casts needed. Valgrind: just use size 0, + regardless. It may not be very good for performance + but does simplify matters, mainly by reducing the number + of different pessimising casts which have to be implemented. + */ + case CALLM: { + UInstr* uu; + Bool res_used; + + /* Now generate the code. Get the final result absval + into qt. */ + qt = newShadow(cb); + qtt = newShadow(cb); + uInstr1(cb, SETV, 0, TempReg, qt); + for (j = i-1; VG_(get_instr)(cb_in, j)->opcode != CALLM_S; j--) { + uu = VG_(get_instr)(cb_in, j); + if (uu->opcode != PUSH) continue; + /* cast via a temporary */ + uInstr2(cb, MOV, 4, TempReg, SHADOW(uu->val1), + TempReg, qtt); + create_PCast(cb, uu->size, 0, qtt); + create_UifU(cb, 0, qtt, qt); + } + /* Remembering also that flags read count as inputs. */ + if (u_in->flags_r != FlagsEmpty) { + qtt = create_GETVF(cb, 0); + create_UifU(cb, 0, qtt, qt); + } + + /* qt now holds the result tag. If any results from the + call are used, either by fetching with POP or + implicitly by writing the flags, we copy the result + absval to the relevant location. If not used, the call + must have been for its side effects, so we test qt here + and now. Note that this assumes that all values + removed by POP continue to be live. So dead args + *must* be removed with CLEAR, not by POPping them into + a dummy tempreg. + */ + res_used = False; + for (j = i+1; VG_(get_instr)(cb_in, j)->opcode != CALLM_E; j++) { + uu = VG_(get_instr)(cb_in, j); + if (uu->opcode != POP) continue; + /* Cast via a temp. */ + uInstr2(cb, MOV, 4, TempReg, qt, TempReg, qtt); + create_PCast(cb, 0, uu->size, qtt); + uInstr2(cb, MOV, 4, TempReg, qtt, + TempReg, SHADOW(uu->val1)); + res_used = True; + } + if (u_in->flags_w != FlagsEmpty) { + create_PUTVF(cb, 0, qt); + res_used = True; + } + if (!res_used) { + uInstr1(cb, TESTV, 0, TempReg, qt); + /* qt should never be referred to again. Nevertheless + ... */ + uInstr1(cb, SETV, 0, TempReg, qt); + } + VG_(copy_UInstr)(cb, u_in); + break; + } + /* Whew ... */ + + case JMP: + if (u_in->tag1 == TempReg) { + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1)); + } else { + sk_assert(u_in->tag1 == Literal); + } + if (u_in->cond != CondAlways) { + sk_assert(u_in->flags_r != FlagsEmpty); + qt = create_GETVF(cb, 0); + if (/* HACK */ bogusLiterals) { + if (0) + VG_(printf)("ignore TESTV due to bogus literal\n"); + } else { + uInstr1(cb, TESTV, 0, TempReg, qt); + } + /* qt should never be referred to again. Nevertheless + ... */ + uInstr1(cb, SETV, 0, TempReg, qt); + } + VG_(copy_UInstr)(cb, u_in); + break; + + case JIFZ: + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1)); + VG_(copy_UInstr)(cb, u_in); + break; + + /* Emit a check on the address used. The value loaded into the + FPU is checked by the call to fpu_{read/write}_check(). */ + case MMX2_MemRd: case MMX2_MemWr: + case FPU_R: case FPU_W: { + Int t_size = INVALID_TEMPREG; + Bool is_load; + + if (u_in->opcode == MMX2_MemRd || u_in->opcode == MMX2_MemWr) + sk_assert(u_in->size == 4 || u_in->size == 8); + + is_load = u_in->opcode==FPU_R || u_in->opcode==MMX2_MemRd; + sk_assert(u_in->tag2 == TempReg); + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); + + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, u_in->size); + uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, t_size); + uCCall(cb, is_load ? (Addr) & MC_(fpu_read_check) + : (Addr) & MC_(fpu_write_check), + 2, 2, False); + + VG_(copy_UInstr)(cb, u_in); + break; + } + + case MMX2a1_MemRd: { + Int t_size = INVALID_TEMPREG; + + sk_assert(u_in->size == 8); + + sk_assert(u_in->tag3 == TempReg); + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val3)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val3)); + + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, u_in->size); + uInstr2(cb, CCALL, 0, TempReg, u_in->val3, TempReg, t_size); + uCCall(cb, (Addr) & MC_(fpu_read_check), 2, 2, False); + + VG_(copy_UInstr)(cb, u_in); + break; + } + + /* SSE ins referencing scalar integer registers */ + case SSE2g_RegWr: + case SSE2g1_RegWr: + case SSE2e1_RegRd: + case SSE3g_RegWr: + case SSE3e_RegRd: + case SSE3e_RegWr: + case SSE3g1_RegWr: + case SSE3e1_RegRd: + sk_assert(u_in->tag3 == TempReg); + + if (u_in->opcode == SSE2e1_RegRd || u_in->opcode == SSE3e1_RegRd) { + sk_assert(u_in->size == 2); + } else { + sk_assert(u_in->size == 4); + } + + /* Is it a read ? Better check the V bits right now. */ + if ( u_in->opcode == SSE2e1_RegRd + || u_in->opcode == SSE3e_RegRd + || u_in->opcode == SSE3e1_RegRd ) + uInstr1(cb, TESTV, u_in->size, + TempReg, SHADOW(u_in->val3)); + + /* And for both read and write, set the register to be + defined. */ + uInstr1(cb, SETV, u_in->size, + TempReg, SHADOW(u_in->val3)); + + VG_(copy_UInstr)(cb, u_in); + break; + + /* ... and the same deal for SSE insns referencing memory */ + case SSE3a_MemRd: + case SSE3a_MemWr: + case SSE2a_MemWr: + case SSE2a_MemRd: + case SSE3a1_MemRd: + case SSE2a1_MemRd: { + Bool is_load; + Int t_size; + + sk_assert(u_in->size == 4 || u_in->size == 8 + || u_in->size == 16 || u_in->size == 512); + + t_size = INVALID_TEMPREG; + is_load = u_in->opcode==SSE2a_MemRd + || u_in->opcode==SSE3a_MemRd + || u_in->opcode==SSE2a1_MemRd + || u_in->opcode==SSE3a1_MemRd; + + sk_assert(u_in->tag3 == TempReg); + + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val3)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val3)); + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, u_in->size); + uInstr2(cb, CCALL, 0, TempReg, u_in->val3, TempReg, t_size); + uCCall(cb, is_load ? (Addr) & MC_(fpu_read_check) + : (Addr) & MC_(fpu_write_check), + 2, 2, False); + + VG_(copy_UInstr)(cb, u_in); + break; + } + + case SSE3ag_MemRd_RegWr: + { + Int t_size; + + sk_assert(u_in->size == 4 || u_in->size == 8); + sk_assert(u_in->tag1 == TempReg); + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val1)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val1)); + t_size = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, t_size); + uLiteral(cb, u_in->size); + uInstr2(cb, CCALL, 0, TempReg, u_in->val1, TempReg, t_size); + uCCall(cb, (Addr) MC_(fpu_read_check), 2, 2, False ); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); + VG_(copy_UInstr)(cb, u_in); + break; + } + + /* For MMX and SSE insns not referencing memory, just + make sure the eflags are defined if the instruction + read them, and make them defined it it writes them. */ + case SSE5: case SSE4: case SSE3: + case MMX1: case MMX2: case MMX3: + case FPU: + if (u_in->flags_r != FlagsEmpty) { + qt = create_GETVF(cb, 0); + uInstr1(cb, TESTV, 0, TempReg, qt); + /* qt should never be referred to again. Nevertheless + ... */ + uInstr1(cb, SETV, 0, TempReg, qt); + } + if (u_in->flags_w != FlagsEmpty) { + qd = newTemp(cb); + uInstr2(cb, MOV, 4, Literal, 0, TempReg, qd); + uLiteral(cb, 0); + create_PUTVF(cb, 0, qd); + } + VG_(copy_UInstr)(cb, u_in); + break; + + /* Since we don't track definedness of values inside the + MMX state, we'd better check that the (int) reg being + read here is defined. */ + case MMX2_ERegRd: + sk_assert(u_in->tag2 == TempReg); + sk_assert(u_in->size == 4); + uInstr1(cb, TESTV, 4, TempReg, SHADOW(u_in->val2)); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); + VG_(copy_UInstr)(cb, u_in); + break; + + /* The MMX register is assumed to be fully defined, so + that's what this register becomes. */ + case MMX2_ERegWr: + sk_assert(u_in->tag2 == TempReg); + sk_assert(u_in->size == 4); + uInstr1(cb, SETV, 4, TempReg, SHADOW(u_in->val2)); + VG_(copy_UInstr)(cb, u_in); + break; + + default: + VG_(pp_UInstr)(0, u_in); + VG_(skin_panic)( "memcheck_instrument: unhandled case"); + + } /* end of switch (u_in->opcode) */ + + } /* end of for loop */ + + VG_(free_UCodeBlock)(cb_in); + return cb; +} + +/*------------------------------------------------------------*/ +/*--- Clean up mem check instrumentation. ---*/ +/*------------------------------------------------------------*/ + +#define dis VG_(print_codegen) + + +#define VGC_IS_SHADOW(tempreg) ((tempreg % 2) == 1) +#define VGC_UNDEF ((UChar)100) +#define VGC_VALUE ((UChar)101) + +#define NOP_no_msg(uu) \ + do { VG_(new_NOP)(uu); } while (False) + +#define NOP_tag1_op(uu) \ + do { VG_(new_NOP)(uu); \ + if (dis) \ + VG_(printf)(" at %2d: delete %s due to defd arg\n", \ + i, nameOfTagOp(u->val3)); \ + } while (False) + +#define SETV_tag1_op(uu,newsz) \ + do { uu->opcode = SETV; \ + uu->size = newsz; \ + uu->tag2 = uu->tag3 = NoValue; \ + if (dis) \ + VG_(printf)(" at %2d: convert %s to SETV%d " \ + "due to defd arg\n", \ + i, nameOfTagOp(u->val3), newsz); \ + } while (False) + + + +/* Run backwards and delete SETVs on shadow temps for which the next + action is a write. Needs an env saying whether or not the next + action is a write. The supplied UCodeBlock is destructively + modified. +*/ +static void vg_delete_redundant_SETVs ( UCodeBlock* cb ) +{ + Int i, j, k; + Int n_temps = VG_(get_num_temps)(cb); + Bool* next_is_write; + UInstr* u; + Int tempUse[VG_MAX_REGS_USED]; + Bool isWrites[VG_MAX_REGS_USED]; + + if (n_temps == 0) return; + + next_is_write = VG_(malloc)(n_temps * sizeof(Bool)); + + for (i = 0; i < n_temps; i++) next_is_write[i] = True; + + for (i = VG_(get_num_instrs)(cb) - 1; i >= 0; i--) { + u = VG_(get_instr)(cb, i); + + /* Occasionally there will be GETVs, TAG1s and TAG2s calculating + values which are never used. These first three cases get rid + of them. */ + + if (u->opcode == GETV && VGC_IS_SHADOW(u->val2) + && next_is_write[u->val2]) { + sk_assert(u->val2 < n_temps); + VG_(new_NOP)(u); + if (dis) + VG_(printf)(" at %2d: delete GETV\n", i); + } else + + if (u->opcode == TAG1 && VGC_IS_SHADOW(u->val1) + && next_is_write[u->val1]) { + sk_assert(u->val1 < n_temps); + VG_(new_NOP)(u); + if (dis) + VG_(printf)(" at %2d: delete TAG1\n", i); + } else + + if (u->opcode == TAG2 && VGC_IS_SHADOW(u->val2) + && next_is_write[u->val2]) { + sk_assert(u->val2 < n_temps); + VG_(new_NOP)(u); + if (dis) + VG_(printf)(" at %2d: delete TAG2\n", i); + } else + + /* The bulk of the cleanup work of this function is done by + the code from here downwards. */ + + if (u->opcode == MOV && VGC_IS_SHADOW(u->val2) + && next_is_write[u->val2]) { + /* This MOV is pointless because the target is dead at this + point. Delete it. */ + VG_(new_NOP)(u); + if (dis) + VG_(printf)(" at %2d: delete MOV\n", i); + } else + + if (u->opcode == SETV) { + if (u->tag1 == TempReg) { + sk_assert(VGC_IS_SHADOW(u->val1)); + if (next_is_write[u->val1]) { + /* This write is pointless, so annul it. */ + VG_(new_NOP)(u); + if (dis) + VG_(printf)(" at %2d: delete SETV\n", i); + } else { + /* This write has a purpose; don't annul it, but do + notice that we did it. */ + next_is_write[u->val1] = True; + } + + } + + } else { + /* Find out what this insn does to the temps. */ + k = VG_(get_reg_usage)(u, TempReg, &tempUse[0], &isWrites[0]); + sk_assert(0 <= k && k <= VG_MAX_REGS_USED); + for (j = k-1; j >= 0; j--) { + next_is_write[ tempUse[j] ] = isWrites[j]; + } + } + } +} + + +/* Run forwards, propagating and using the is-completely-defined + property. This removes a lot of redundant tag-munging code. + Unfortunately it requires intimate knowledge of how each uinstr and + tagop modifies its arguments. This duplicates knowledge of uinstr + tempreg uses embodied in VG_(get_reg_usage)(), which is unfortunate. + The supplied UCodeBlock* is modified in-place. + + For each value temp, def[] should hold VGC_VALUE. + + For each shadow temp, def[] may hold 4,2,1 or 0 iff that shadow is + definitely known to be fully defined at that size. In all other + circumstances a shadow's def[] entry is VGC_UNDEF, meaning possibly + undefined. In cases of doubt, VGC_UNDEF is always safe. +*/ +static void vg_propagate_definedness ( UCodeBlock* cb ) +{ + Int i, j, k, t; + Int n_temps = VG_(get_num_temps)(cb); + UChar* def; + UInstr* u; + Int tempUse[VG_MAX_REGS_USED]; + Bool isWrites[VG_MAX_REGS_USED]; + + if (n_temps == 0) return; + + def = VG_(malloc)(n_temps * sizeof(UChar)); + + for (i = 0; i < n_temps; i++) + def[i] = VGC_IS_SHADOW(i) ? VGC_UNDEF : VGC_VALUE; + + /* Run forwards, detecting and using the all-defined property. */ + + for (i = 0; i < VG_(get_num_instrs)(cb); i++) { + u = VG_(get_instr)(cb, i); + switch (u->opcode) { + + /* Tag-handling uinstrs. */ + + /* Deal with these quickly. */ + case NOP: + case LOCK: + case INCEIP: + break; + + /* Make a tag defined. */ + case SETV: + sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); + def[u->val1] = u->size; + break; + + /* Check definedness of a tag. */ + case TESTV: + sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); + if (def[u->val1] <= 4) { + sk_assert(def[u->val1] == u->size); + NOP_no_msg(u); + if (dis) + VG_(printf)(" at %2d: delete TESTV on defd arg\n", i); + } + break; + + /* Applies to both values and tags. Propagate Definedness + property through copies. Note that this isn't optional; + we *have* to do this to keep def[] correct. */ + case MOV: + sk_assert(u->tag2 == TempReg); + if (u->tag1 == TempReg) { + if (VGC_IS_SHADOW(u->val1)) { + sk_assert(VGC_IS_SHADOW(u->val2)); + def[u->val2] = def[u->val1]; + } + } + break; + + case PUTV: + sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); + if (def[u->val1] <= 4) { + sk_assert(def[u->val1] == u->size); + u->tag1 = Literal; + u->val1 = 0; + switch (u->size) { + case 4: u->lit32 = 0x00000000; break; + case 2: u->lit32 = 0xFFFF0000; break; + case 1: u->lit32 = 0xFFFFFF00; break; + default: VG_(skin_panic)("vg_cleanup(PUTV)"); + } + if (dis) + VG_(printf)( + " at %2d: propagate definedness into PUTV\n", i); + } + break; + + case STOREV: + sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); + if (def[u->val1] <= 4) { + sk_assert(def[u->val1] == u->size); + u->tag1 = Literal; + u->val1 = 0; + switch (u->size) { + case 4: u->lit32 = 0x00000000; break; + case 2: u->lit32 = 0xFFFF0000; break; + case 1: u->lit32 = 0xFFFFFF00; break; + default: VG_(skin_panic)("vg_cleanup(STOREV)"); + } + if (dis) + VG_(printf)( + " at %2d: propagate definedness into STandV\n", i); + } + break; + + /* Nothing interesting we can do with this, I think. */ + case PUTVF: + break; + + /* Tag handling operations. */ + case TAG2: + sk_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2)); + sk_assert(u->tag3 == Lit16); + /* Ultra-paranoid "type" checking. */ + switch (u->val3) { + case Tag_ImproveAND4_TQ: case Tag_ImproveAND2_TQ: + case Tag_ImproveAND1_TQ: case Tag_ImproveOR4_TQ: + case Tag_ImproveOR2_TQ: case Tag_ImproveOR1_TQ: + sk_assert(u->tag1 == TempReg && !VGC_IS_SHADOW(u->val1)); + break; + default: + sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); + break; + } + switch (u->val3) { + Int sz; + case Tag_UifU4: + sz = 4; goto do_UifU; + case Tag_UifU2: + sz = 2; goto do_UifU; + case Tag_UifU1: + sz = 1; goto do_UifU; + case Tag_UifU0: + sz = 0; goto do_UifU; + do_UifU: + sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); + sk_assert(u->tag2 == TempReg && VGC_IS_SHADOW(u->val2)); + if (def[u->val1] <= 4) { + /* UifU. The first arg is defined, so result is + simply second arg. Delete this operation. */ + sk_assert(def[u->val1] == sz); + NOP_no_msg(u); + if (dis) + VG_(printf)( + " at %2d: delete UifU%d due to defd arg1\n", + i, sz); + } + else + if (def[u->val2] <= 4) { + /* UifU. The second arg is defined, so result is + simply first arg. Copy to second. */ + sk_assert(def[u->val2] == sz); + u->opcode = MOV; + u->size = 4; + u->tag3 = NoValue; + def[u->val2] = def[u->val1]; + if (dis) + VG_(printf)( + " at %2d: change UifU%d to MOV due to defd" + " arg2\n", + i, sz); + } + break; + case Tag_ImproveAND4_TQ: + sz = 4; goto do_ImproveAND; + case Tag_ImproveAND1_TQ: + sz = 1; goto do_ImproveAND; + do_ImproveAND: + /* Implements Q = T AND Q. So if Q is entirely defined, + ie all 0s, we get MOV T, Q. */ + if (def[u->val2] <= 4) { + sk_assert(def[u->val2] == sz); + u->size = 4; /* Regardless of sz */ + u->opcode = MOV; + u->tag3 = NoValue; + def[u->val2] = VGC_UNDEF; + if (dis) + VG_(printf)( + " at %2d: change ImproveAND%d_TQ to MOV due " + "to defd arg2\n", + i, sz); + } + break; + default: + goto unhandled; + } + break; + + case TAG1: + sk_assert(u->tag1 == TempReg && VGC_IS_SHADOW(u->val1)); + if (def[u->val1] > 4) break; + /* We now know that the arg to the op is entirely defined. + If the op changes the size of the arg, we must replace + it with a SETV at the new size. If it doesn't change + the size, we can delete it completely. */ + switch (u->val3) { + /* Maintain the same size ... */ + case Tag_Left4: + sk_assert(def[u->val1] == 4); + NOP_tag1_op(u); + break; + case Tag_PCast11: + sk_assert(def[u->val1] == 1); + NOP_tag1_op(u); + break; + /* Change size ... */ + case Tag_PCast40: + sk_assert(def[u->val1] == 4); + SETV_tag1_op(u,0); + def[u->val1] = 0; + break; + case Tag_PCast14: + sk_assert(def[u->val1] == 1); + SETV_tag1_op(u,4); + def[u->val1] = 4; + break; + case Tag_PCast12: + sk_assert(def[u->val1] == 1); + SETV_tag1_op(u,2); + def[u->val1] = 2; + break; + case Tag_PCast10: + sk_assert(def[u->val1] == 1); + SETV_tag1_op(u,0); + def[u->val1] = 0; + break; + case Tag_PCast02: + sk_assert(def[u->val1] == 0); + SETV_tag1_op(u,2); + def[u->val1] = 2; + break; + default: + goto unhandled; + } + if (dis) + VG_(printf)( + " at %2d: delete TAG1 %s due to defd arg\n", + i, nameOfTagOp(u->val3)); + break; + + default: + unhandled: + /* We don't know how to handle this uinstr. Be safe, and + set to VGC_VALUE or VGC_UNDEF all temps written by it. */ + k = VG_(get_reg_usage)(u, TempReg, &tempUse[0], &isWrites[0]); + sk_assert(0 <= k && k <= VG_MAX_REGS_USED); + for (j = 0; j < k; j++) { + t = tempUse[j]; + sk_assert(t >= 0 && t < n_temps); + if (!isWrites[j]) { + /* t is read; ignore it. */ + if (0&& VGC_IS_SHADOW(t) && def[t] <= 4) + VG_(printf)("ignoring def %d at %s %s\n", + def[t], + VG_(name_UOpcode)(True, u->opcode), + (u->opcode == TAG1 || u->opcode == TAG2) + ? nameOfTagOp(u->val3) + : (Char*)""); + } else { + /* t is written; better nullify it. */ + def[t] = VGC_IS_SHADOW(t) ? VGC_UNDEF : VGC_VALUE; + } + } + } + } +} + + +/* Top level post-MemCheck-instrumentation cleanup function. */ +static void vg_cleanup ( UCodeBlock* cb ) +{ + vg_propagate_definedness ( cb ); + vg_delete_redundant_SETVs ( cb ); +} + + +/* Caller will print out final instrumented code if necessary; we + print out intermediate instrumented code here if necessary. */ +UCodeBlock* SK_(instrument) ( UCodeBlock* cb, Addr not_used ) +{ + cb = memcheck_instrument ( cb ); + if (MC_(clo_cleanup)) { + if (dis) { + VG_(pp_UCodeBlock) ( cb, "Unimproved instrumented UCode:" ); + VG_(printf)("Instrumentation improvements:\n"); + } + vg_cleanup(cb); + if (dis) VG_(printf)("\n"); + } + return cb; +} + +#undef dis + +/*--------------------------------------------------------------------*/ +/*--- end mc_translate.c ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/memcheck/memcheck.h b/VEX/head20041019/memcheck/memcheck.h new file mode 100644 index 0000000000..4b2458d235 --- /dev/null +++ b/VEX/head20041019/memcheck/memcheck.h @@ -0,0 +1,259 @@ + +/* + ---------------------------------------------------------------- + + Notice that the following BSD-style license applies to this one + file (memcheck.h) only. The entire rest of Valgrind is licensed + under the terms of the GNU General Public License, version 2. See + the COPYING file in the source distribution for details. + + ---------------------------------------------------------------- + + This file is part of MemCheck, a heavyweight Valgrind tool for + detecting memory errors. + + Copyright (C) 2000-2004 Julian Seward. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + + 4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + ---------------------------------------------------------------- + + Notice that the above BSD-style license applies to this one file + (memcheck.h) only. The entire rest of Valgrind is licensed under + the terms of the GNU General Public License, version 2. See the + COPYING file in the source distribution for details. + + ---------------------------------------------------------------- +*/ + + +#ifndef __MEMCHECK_H +#define __MEMCHECK_H + + +/* This file is for inclusion into client (your!) code. + + You can use these macros to manipulate and query memory permissions + inside your own programs. + + See comment near the top of valgrind.h on how to use them. +*/ + +#include "valgrind.h" + +typedef + enum { + VG_USERREQ__MAKE_NOACCESS = VG_USERREQ_SKIN_BASE('M','C'), + VG_USERREQ__MAKE_WRITABLE, + VG_USERREQ__MAKE_READABLE, + VG_USERREQ__DISCARD, + VG_USERREQ__CHECK_WRITABLE, + VG_USERREQ__CHECK_READABLE, + VG_USERREQ__DO_LEAK_CHECK, + VG_USERREQ__COUNT_LEAKS, + + /* These two have been moved into core, because they are useful for + any tool that tracks heap blocks. Hence the suffix. But they're + still here for backwards compatibility, although Valgrind will + abort with an explanatory message if you use them. */ + VG_USERREQ__MALLOCLIKE_BLOCK__OLD_DO_NOT_USE, + VG_USERREQ__FREELIKE_BLOCK__OLD_DO_NOT_USE, + + VG_USERREQ__GET_VBITS, + VG_USERREQ__SET_VBITS, + + /* This is just for memcheck's internal use - don't use it */ + _VG_USERREQ__MEMCHECK_GET_RECORD_OVERLAP = VG_USERREQ_SKIN_BASE('M','C')+256 + } Vg_MemCheckClientRequest; + + + +/* Client-code macros to manipulate the state of memory. */ + +/* Mark memory at _qzz_addr as unaddressible and undefined for + _qzz_len bytes. Returns an int handle pertaining to the block + descriptions Valgrind will use in subsequent error messages. */ +#define VALGRIND_MAKE_NOACCESS(_qzz_addr,_qzz_len) \ + (__extension__({unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */, \ + VG_USERREQ__MAKE_NOACCESS, \ + _qzz_addr, _qzz_len, 0, 0); \ + _qzz_res; \ + })) + +/* Similarly, mark memory at _qzz_addr as addressible but undefined + for _qzz_len bytes. */ +#define VALGRIND_MAKE_WRITABLE(_qzz_addr,_qzz_len) \ + (__extension__({unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */, \ + VG_USERREQ__MAKE_WRITABLE, \ + _qzz_addr, _qzz_len, 0, 0); \ + _qzz_res; \ + })) + +/* Similarly, mark memory at _qzz_addr as addressible and defined + for _qzz_len bytes. */ +#define VALGRIND_MAKE_READABLE(_qzz_addr,_qzz_len) \ + (__extension__({unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */, \ + VG_USERREQ__MAKE_READABLE, \ + _qzz_addr, _qzz_len, 0, 0); \ + _qzz_res; \ + })) + +/* Discard a block-description-handle obtained from the above three + macros. After this, Valgrind will no longer be able to relate + addressing errors to the user-defined block associated with the + handle. The permissions settings associated with the handle remain + in place. Returns 1 for an invalid handle, 0 for a valid + handle. */ +#define VALGRIND_DISCARD(_qzz_blkindex) \ + (__extension__ ({unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0 /* default return */, \ + VG_USERREQ__DISCARD, \ + 0, _qzz_blkindex, 0, 0); \ + _qzz_res; \ + })) + + +/* Client-code macros to check the state of memory. */ + +/* Check that memory at _qzz_addr is addressible for _qzz_len bytes. + If suitable addressibility is not established, Valgrind prints an + error message and returns the address of the first offending byte. + Otherwise it returns zero. */ +#define VALGRIND_CHECK_WRITABLE(_qzz_addr,_qzz_len) \ + (__extension__({unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__CHECK_WRITABLE, \ + _qzz_addr, _qzz_len, 0, 0); \ + _qzz_res; \ + })) + +/* Check that memory at _qzz_addr is addressible and defined for + _qzz_len bytes. If suitable addressibility and definedness are not + established, Valgrind prints an error message and returns the + address of the first offending byte. Otherwise it returns zero. */ +#define VALGRIND_CHECK_READABLE(_qzz_addr,_qzz_len) \ + (__extension__({unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__CHECK_READABLE, \ + _qzz_addr, _qzz_len, 0, 0); \ + _qzz_res; \ + })) + +/* Use this macro to force the definedness and addressibility of a + value to be checked. If suitable addressibility and definedness + are not established, Valgrind prints an error message and returns + the address of the first offending byte. Otherwise it returns + zero. */ +#define VALGRIND_CHECK_DEFINED(__lvalue) \ + VALGRIND_CHECK_READABLE( \ + (volatile unsigned char *)&(__lvalue), \ + (unsigned int)(sizeof (__lvalue))) + +/* Do a memory leak check mid-execution. */ +#define VALGRIND_DO_LEAK_CHECK \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__DO_LEAK_CHECK, \ + 0, 0, 0, 0); \ + } + +/* Return number of leaked, dubious, reachable and suppressed bytes found by + all previous leak checks. They must be lvalues. */ +#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__COUNT_LEAKS, \ + &leaked, &dubious, &reachable, &suppressed);\ + } + + +/* These two have been moved to valgrind.h; still here so that a warning can + be printed out for any programs using the old ones. */ +#define VALGRIND_MALLOCLIKE_BLOCK__OLD_DO_NOT_USE(addr, sizeB, rzB, is_zeroed)\ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__MALLOCLIKE_BLOCK, \ + addr, sizeB, rzB, is_zeroed); \ + } +#define VALGRIND_FREELIKE_BLOCK__OLD_DO_NOT_USE(addr, rzB) \ + {unsigned int _qzz_res; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__FREELIKE_BLOCK, \ + addr, rzB, 0, 0); \ + } + + +/* Get in zzvbits the validity data for the zznbytes starting at + zzsrc. Return values: + 0 if not running on valgrind + 1 success + 2 if zzsrc/zzvbits arrays are not aligned 0 % 4, or + zznbytes is not 0 % 4. + 3 if any parts of zzsrc/zzvbits are not addressible. + The metadata is not copied in cases 0, 2 or 3 so it should be + impossible to segfault your system by using this call. +*/ +#define VALGRIND_GET_VBITS(zzsrc,zzvbits,zznbytes) \ + (__extension__({unsigned int _qzz_res; \ + char* czzsrc = (char*)zzsrc; \ + char* czzvbits = (char*)zzvbits; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__GET_VBITS, \ + czzsrc, czzvbits, zznbytes,0 ); \ + _qzz_res; \ + })) + +/* Apply the validity data in zzvbits to the zznbytes starting at + zzdst. Return values: + 0 if not running on valgrind + 1 success + 2 if zzdst/zzvbits arrays are not aligned 0 % 4, or + zznbytes is not 0 % 4. + 3 if any parts of zzdst/zzvbits are not addressible. + The metadata is not copied in cases 0, 2 or 3 so it should be + impossible to segfault your system by using this call. +*/ +#define VALGRIND_SET_VBITS(zzdst,zzvbits,zznbytes) \ + (__extension__({unsigned int _qzz_res; \ + char* czzdst = (char*)zzdst; \ + char* czzvbits = (char*)zzvbits; \ + VALGRIND_MAGIC_SEQUENCE(_qzz_res, 0, \ + VG_USERREQ__SET_VBITS, \ + czzdst, czzvbits, zznbytes,0 ); \ + _qzz_res; \ + })) + +#endif + diff --git a/VEX/head20041019/memcheck/tests/.cvsignore b/VEX/head20041019/memcheck/tests/.cvsignore new file mode 100644 index 0000000000..27a7a5b83c --- /dev/null +++ b/VEX/head20041019/memcheck/tests/.cvsignore @@ -0,0 +1,68 @@ +Makefile.in +Makefile +badaddrvalue +badfree +badjump +badjump2 +badloop +buflen_check +clientperm +clientstackperm +custom_alloc +dir +doublefree +error_counts +errs1 +execve +execve2 +exitprog +filter_leak_check_size +filter_stderr +fpeflags +fprw +fwrite +hello +inits +inline +malloc1 +malloc2 +malloc3 +manuel1 +manuel2 +manuel3 +memalign_test +memalign2 +memcmptest +mempool +mismatches +mmaptest +nanoleak +new_override +null_socket +overlap +pushfpopf +realloc1 +realloc2 +sigaltstack +signal2 +supp1 +supp2 +suppfree +trivialleak +tronical +vgtest_ume +weirdioctl +*.stdout.diff +*.stderr.diff* +*.stdout.out +*.stderr.out +badrw +brk +brk2 +metadata +new_nothrow +realloc3 +threadederrno +vgcore.pid* +writev +zeropage diff --git a/VEX/head20041019/memcheck/tests/CVS/Entries b/VEX/head20041019/memcheck/tests/CVS/Entries new file mode 100644 index 0000000000..0423d51619 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/CVS/Entries @@ -0,0 +1,230 @@ +/.cvsignore/1.18/Sat Oct 16 11:02:33 2004// +/Makefile.am/1.46/Mon Oct 18 11:52:17 2004// +/badaddrvalue.c/1.2/Mon Sep 23 09:36:25 2002// +/badaddrvalue.stderr.exp/1.8/Thu Nov 13 17:53:43 2003// +/badaddrvalue.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/badaddrvalue.vgtest/1.3/Sun Jul 6 23:24:18 2003// +/badfree-2trace.stderr.exp/1.6/Tue Apr 13 08:36:35 2004// +/badfree-2trace.vgtest/1.3/Sun Jul 6 23:43:01 2003// +/badfree.c/1.2/Mon Sep 23 09:36:25 2002// +/badfree.stderr.exp/1.10/Tue Apr 13 08:36:35 2004// +/badfree.vgtest/1.3/Sun Jul 6 23:43:01 2003// +/badjump.c/1.2/Mon Sep 23 09:36:25 2002// +/badjump.stderr.exp/1.9/Tue Apr 13 08:36:35 2004// +/badjump.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/badjump2.c/1.1/Wed Oct 13 09:47:24 2004// +/badjump2.stderr.exp/1.1/Wed Oct 13 09:47:24 2004// +/badjump2.vgtest/1.1/Wed Oct 13 09:47:24 2004// +/badloop.c/1.2/Mon Sep 23 09:36:25 2002// +/badloop.stderr.exp/1.6/Thu Nov 13 17:35:04 2003// +/badloop.vgtest/1.3/Sun Jul 6 23:43:01 2003// +/badrw.c/1.2/Wed Jan 7 08:47:03 2004// +/badrw.stderr.exp/1.4/Wed Jan 7 08:47:03 2004// +/badrw.vgtest/1.1/Fri Sep 5 23:29:33 2003// +/brk.c/1.1/Thu Sep 4 20:57:51 2003// +/brk.stderr.exp/1.1/Thu Sep 4 20:57:51 2003// +/brk.vgtest/1.1/Thu Sep 4 20:57:51 2003// +/brk2.c/1.1/Wed Aug 25 13:43:44 2004// +/brk2.stderr.exp/1.1/Wed Aug 25 13:43:44 2004// +/brk2.vgtest/1.1/Wed Aug 25 13:43:44 2004// +/buflen_check.c/1.2/Mon Sep 23 09:36:25 2002// +/buflen_check.stderr.exp/1.9/Tue Apr 13 19:08:34 2004// +/buflen_check.vgtest/1.3/Sun Jul 6 23:43:01 2003// +/clientperm.c/1.1/Fri Oct 4 14:16:35 2002// +/clientperm.stderr.exp/1.5/Thu Nov 13 17:35:04 2003// +/clientperm.stdout.exp/1.1/Fri Oct 4 14:16:35 2002// +/clientperm.vgtest/1.2/Sun Jul 6 23:43:01 2003// +/custom_alloc.c/1.2/Sat Jan 3 14:18:02 2004// +/custom_alloc.stderr.exp/1.5/Tue Apr 13 08:36:35 2004// +/custom_alloc.vgtest/1.2/Sun Jul 6 23:43:01 2003// +/doublefree.c/1.2/Mon Sep 23 09:36:25 2002// +/doublefree.stderr.exp/1.9/Thu Nov 13 17:53:43 2003// +/doublefree.vgtest/1.3/Mon Jul 7 00:23:23 2003// +/error_counts.c/1.4/Tue Jul 22 22:03:58 2003// +/error_counts.stderr.exp/1.1/Mon Apr 21 13:24:40 2003// +/error_counts.stdout.exp/1.1/Mon Apr 21 13:24:40 2003// +/error_counts.vgtest/1.2/Mon Jun 21 12:42:35 2004// +/errs1.c/1.2/Mon Sep 23 09:36:25 2002// +/errs1.stderr.exp/1.5/Thu Nov 13 17:53:43 2003// +/errs1.vgtest/1.3/Sun Jul 13 22:35:55 2003// +/execve.c/1.1/Thu Feb 12 14:34:14 2004// +/execve.stderr.exp/1.2/Tue Apr 13 08:36:35 2004// +/execve.vgtest/1.1/Thu Feb 12 14:34:14 2004// +/execve2.c/1.1/Mon Aug 9 12:21:57 2004// +/execve2.stderr.exp/1.1/Mon Aug 9 12:21:57 2004// +/execve2.vgtest/1.1/Mon Aug 9 12:21:57 2004// +/exitprog.c/1.3/Mon Jul 7 00:32:44 2003// +/exitprog.stderr.exp/1.10/Thu Nov 13 17:53:43 2003// +/exitprog.vgtest/1.3/Mon Jul 7 00:23:23 2003// +/filter_allocs/1.3/Sat Jan 3 14:18:02 2004// +/filter_leak_check_size/1.4/Sat Jan 3 14:18:02 2004// +/filter_pushfpopf/1.2/Mon May 5 16:18:51 2003// +/filter_stderr/1.11/Tue Mar 9 09:59:26 2004// +/filter_stderr_backtrace/1.1/Thu Apr 24 00:40:38 2003// +/filter_tronical/1.2/Mon May 5 16:18:51 2003// +/fpeflags.c/1.2/Thu Oct 14 09:48:55 2004// +/fpeflags.stderr.exp/1.1/Sun Mar 28 11:26:29 2004// +/fpeflags.vgtest/1.1/Sun Mar 28 11:26:29 2004// +/fprw.c/1.2/Mon Sep 23 09:36:25 2002// +/fprw.stderr.exp/1.10/Tue Apr 13 08:36:35 2004// +/fprw.vgtest/1.4/Tue Dec 16 02:05:15 2003// +/fwrite.c/1.2/Mon Sep 23 09:36:25 2002// +/fwrite.stderr.exp/1.9/Mon Dec 15 09:00:21 2003// +/fwrite.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/fwrite.vgtest/1.3/Mon Jul 7 00:23:23 2003// +/hello.c/1.1/Thu Oct 14 08:38:06 2004// +/inits.c/1.2/Mon Sep 23 09:36:25 2002// +/inits.stderr.exp/1.6/Thu Nov 13 17:35:04 2003// +/inits.vgtest/1.3/Mon Jul 7 00:23:23 2003// +/inline.c/1.2/Mon Sep 23 09:36:25 2002// +/inline.stderr.exp/1.9/Thu Nov 13 17:53:43 2003// +/inline.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/inline.vgtest/1.3/Mon Jul 7 00:23:23 2003// +/insn_basic.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_basic.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_basic.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_cmov.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_fpu.stderr.exp/1.1/Sat Mar 27 18:02:37 2004// +/insn_fpu.stdout.exp/1.4/Wed Mar 31 22:47:52 2004// +/insn_fpu.vgtest/1.1/Sat Mar 27 18:02:37 2004// +/insn_mmx.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmx.stdout.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmx.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_mmxext.stderr.exp/1.2/Tue Mar 9 08:50:02 2004// +/insn_mmxext.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_mmxext.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_sse.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse2.stderr.exp/1.1/Tue Mar 9 01:44:11 2004// +/insn_sse2.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_sse2.vgtest/1.1/Tue Mar 9 01:44:11 2004// +/malloc1.c/1.2/Mon Sep 23 09:36:25 2002// +/malloc1.stderr.exp/1.9/Thu Nov 13 17:53:43 2003// +/malloc1.vgtest/1.3/Mon Jul 7 00:23:23 2003// +/malloc2.c/1.2/Mon Sep 23 09:36:25 2002// +/malloc2.stderr.exp/1.9/Thu Nov 13 17:53:43 2003// +/malloc2.vgtest/1.3/Mon Jul 7 00:23:23 2003// +/malloc3.c/1.1/Tue Apr 15 13:03:20 2003// +/malloc3.stderr.exp/1.2/Mon Jul 7 00:23:23 2003// +/malloc3.stdout.exp/1.1/Tue Apr 15 13:03:20 2003// +/malloc3.vgtest/1.2/Mon Jul 7 00:23:23 2003// +/manuel1.c/1.2/Mon Sep 23 09:36:25 2002// +/manuel1.stderr.exp/1.6/Thu Nov 13 17:35:04 2003// +/manuel1.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/manuel1.vgtest/1.3/Mon Jul 7 00:43:34 2003// +/manuel2.c/1.3/Sat Jan 3 14:18:02 2004// +/manuel2.stderr.exp/1.6/Thu Nov 13 17:35:04 2003// +/manuel2.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/manuel2.vgtest/1.3/Mon Jul 7 00:43:34 2003// +/manuel3.c/1.3/Sat Jan 3 14:18:02 2004// +/manuel3.stderr.exp/1.6/Thu Nov 13 17:35:04 2003// +/manuel3.vgtest/1.3/Mon Jul 7 00:43:34 2003// +/memalign2.c/1.2/Fri Sep 3 14:04:40 2004// +/memalign2.stderr.exp/1.1/Wed Aug 11 09:40:52 2004// +/memalign2.vgtest/1.1/Wed Aug 11 09:40:52 2004// +/memalign_test.c/1.2/Mon Sep 23 09:36:25 2002// +/memalign_test.stderr.exp/1.9/Thu Nov 13 17:53:43 2003// +/memalign_test.vgtest/1.3/Mon Jul 7 00:43:34 2003// +/memcmptest.c/1.2/Mon Sep 23 09:36:25 2002// +/memcmptest.stderr.exp/1.7/Thu Nov 13 17:35:04 2003// +/memcmptest.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/memcmptest.vgtest/1.3/Mon Jul 7 00:43:34 2003// +/mempool.c/1.1/Sat Jun 19 18:12:36 2004// +/mempool.stderr.exp/1.1/Sat Jun 19 18:12:36 2004// +/mempool.vgtest/1.1/Sat Jun 19 18:12:36 2004// +/metadata.c/1.2/Sat Jan 3 14:18:02 2004// +/metadata.stderr.exp/1.2/Thu Nov 13 17:35:04 2003// +/metadata.stdout.exp/1.1/Mon Jul 7 00:03:52 2003// +/metadata.vgtest/1.1/Mon Jul 7 00:03:52 2003// +/mismatches.cpp/1.2/Mon Sep 23 09:36:25 2002// +/mismatches.stderr.exp/1.10/Thu Nov 13 17:53:43 2003// +/mismatches.stderr.exp2/1.1/Sun Mar 7 19:40:33 2004// +/mismatches.vgtest/1.5/Tue Sep 30 16:52:47 2003// +/mmaptest.c/1.2/Mon Sep 23 09:36:25 2002// +/mmaptest.stderr.exp/1.3/Mon Jul 7 00:43:34 2003// +/mmaptest.vgtest/1.3/Mon Jul 7 00:43:34 2003// +/nanoleak.c/1.3/Sat Jan 3 14:18:02 2004// +/nanoleak.stderr.exp/1.9/Tue Dec 2 10:17:44 2003// +/nanoleak.supp/1.2/Tue Dec 16 02:05:15 2003// +/nanoleak.vgtest/1.3/Mon Jul 7 00:43:34 2003// +/nanoleak_supp.stderr.exp/1.3/Tue Dec 2 10:17:44 2003// +/nanoleak_supp.vgtest/1.2/Mon Jul 7 23:56:10 2003// +/new_nothrow.cpp/1.1/Thu Oct 9 15:40:38 2003// +/new_nothrow.stderr.exp/1.1/Thu Oct 9 15:40:38 2003// +/new_nothrow.vgtest/1.1/Thu Oct 9 15:40:38 2003// +/new_override.cpp/1.3/Tue Apr 13 19:11:27 2004// +/new_override.stderr.exp/1.6/Sun Jun 1 20:04:10 2003// +/new_override.stdout.exp/1.1/Tue Apr 13 19:11:27 2004// +/new_override.vgtest/1.3/Wed Feb 26 10:16:02 2003// +/null_socket.c/1.1/Fri May 2 16:19:10 2003// +/null_socket.stderr.exp/1.2/Mon Jul 7 23:56:10 2003// +/null_socket.vgtest/1.2/Mon Jul 7 23:56:10 2003// +/overlap.c/1.3/Sun Nov 2 17:43:27 2003// +/overlap.stderr.exp/1.7/Thu Nov 13 17:35:04 2003// +/overlap.stdout.exp/1.1/Tue Apr 15 13:03:21 2003// +/overlap.vgtest/1.2/Mon Jul 7 23:56:10 2003// +/pth_once.stderr.exp/1.1/Thu Oct 30 09:11:03 2003// +/pth_once.stdout.exp/1.1/Thu Oct 30 09:11:03 2003// +/pth_once.vgtest/1.1/Thu Oct 30 09:11:03 2003// +/pushfpopf.stderr.exp/1.7/Thu Nov 13 17:35:04 2003// +/pushfpopf.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/pushfpopf.vgtest/1.4/Mon Jul 7 23:56:10 2003// +/pushfpopf_c.c/1.2/Mon Sep 23 09:36:25 2002// +/pushfpopf_s.s/1.2/Mon Sep 23 09:36:25 2002// +/realloc1.c/1.3/Mon Jul 7 23:56:10 2003// +/realloc1.stderr.exp/1.3/Mon Jul 7 23:56:10 2003// +/realloc1.vgtest/1.3/Mon Jul 7 23:56:10 2003// +/realloc2.c/1.3/Sat Jan 3 14:18:02 2004// +/realloc2.stderr.exp/1.3/Mon Jul 7 23:56:10 2003// +/realloc2.vgtest/1.3/Mon Jul 7 23:56:10 2003// +/realloc3.c/1.1/Thu Jul 24 17:39:59 2003// +/realloc3.stderr.exp/1.3/Thu Nov 13 17:53:43 2003// +/realloc3.vgtest/1.1/Thu Jul 24 17:39:59 2003// +/sigaltstack.c/1.7/Sat Jan 3 14:18:02 2004// +/sigaltstack.stderr.exp/1.8/Tue Dec 16 02:05:15 2003// +/sigaltstack.vgtest/1.4/Mon Jul 7 23:56:10 2003// +/signal2.c/1.2/Mon Sep 23 09:36:25 2002// +/signal2.stderr.exp/1.9/Tue Apr 13 08:36:35 2004// +/signal2.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/signal2.vgtest/1.3/Mon Jul 7 23:56:10 2003// +/supp.c/1.3/Mon Jul 7 23:56:10 2003// +/supp.supp/1.5/Mon Oct 7 14:46:07 2002// +/supp1.stderr.exp/1.3/Mon Jul 7 23:56:10 2003// +/supp1.vgtest/1.3/Mon Jul 7 23:56:10 2003// +/supp2.stderr.exp/1.6/Thu Nov 13 17:35:04 2003// +/supp2.vgtest/1.3/Mon Jul 7 23:56:10 2003// +/suppfree.c/1.2/Mon Sep 23 09:36:25 2002// +/suppfree.stderr.exp/1.5/Thu Nov 13 17:53:43 2003// +/suppfree.vgtest/1.3/Mon Jul 7 23:56:10 2003// +/threadederrno.c/1.4/Wed Jan 21 17:40:16 2004// +/threadederrno.stderr.exp/1.2/Tue Dec 16 02:05:15 2003// +/threadederrno.stdout.exp/1.4/Wed Jan 21 17:40:16 2004// +/threadederrno.vgtest/1.1/Sun Jul 13 11:13:37 2003// +/toobig-allocs.stderr.exp/1.1/Sat Jul 10 14:56:27 2004// +/toobig-allocs.vgtest/1.1/Sat Jul 10 14:56:27 2004// +/trivialleak.c/1.2/Mon Sep 23 09:36:25 2002// +/trivialleak.stderr.exp/1.8/Tue Dec 2 10:17:44 2003// +/trivialleak.stderr.exp2/1.1/Sat Apr 10 00:53:45 2004// +/trivialleak.vgtest/1.3/Mon Jul 7 23:56:10 2003// +/tronical.S/1.3/Sun Oct 6 00:08:57 2002// +/tronical.stderr.exp/1.7/Thu Nov 13 17:35:04 2003// +/tronical.vgtest/1.4/Mon Jul 7 23:56:10 2003// +/vgtest_ume.c/1.4/Mon Oct 18 11:52:17 2004// +/vgtest_ume.stderr.exp/1.3/Thu Oct 14 09:28:11 2004// +/vgtest_ume.vgtest/1.1/Thu Oct 14 08:38:06 2004// +/weirdioctl.c/1.2/Mon Sep 23 09:36:25 2002// +/weirdioctl.stderr.exp/1.8/Mon Dec 15 09:00:21 2003// +/weirdioctl.stdout.exp/1.3/Mon Jul 7 23:56:10 2003// +/weirdioctl.vgtest/1.3/Mon Jul 7 23:56:10 2003// +/writev.c/1.1/Fri Sep 5 23:02:38 2003// +/writev.stderr.exp/1.6/Tue Apr 13 08:36:35 2004// +/writev.stderr.exp2/1.2/Tue Oct 12 08:38:19 2004// +/writev.vgtest/1.1/Fri Sep 5 23:02:38 2003// +/zeropage.c/1.3/Tue Aug 3 13:29:09 2004// +/zeropage.stderr.exp/1.2/Tue Dec 16 02:05:15 2003// +/zeropage.stderr.exp2/1.1/Wed Oct 13 16:48:21 2004// +/zeropage.vgtest/1.1/Tue Dec 2 14:56:04 2003// +D diff --git a/VEX/head20041019/memcheck/tests/CVS/Repository b/VEX/head20041019/memcheck/tests/CVS/Repository new file mode 100644 index 0000000000..357815a7e6 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/CVS/Repository @@ -0,0 +1 @@ +valgrind/memcheck/tests diff --git a/VEX/head20041019/memcheck/tests/CVS/Root b/VEX/head20041019/memcheck/tests/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/memcheck/tests/CVS/Template b/VEX/head20041019/memcheck/tests/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/Makefile.am b/VEX/head20041019/memcheck/tests/Makefile.am new file mode 100644 index 0000000000..f3e7f947aa --- /dev/null +++ b/VEX/head20041019/memcheck/tests/Makefile.am @@ -0,0 +1,172 @@ +##--------------------------------------------------------------------------- +## Need more tests: +## - lots more mmap/munmap/mremap/mprotect ones +##--------------------------------------------------------------------------- + +noinst_SCRIPTS = filter_allocs filter_leak_check_size \ + filter_stderr filter_stderr_backtrace filter_pushfpopf \ + filter_tronical + +INSN_TESTS=insn_basic insn_fpu insn_cmov insn_mmx insn_mmxext insn_sse insn_sse2 + +EXTRA_DIST = $(noinst_SCRIPTS) \ + badaddrvalue.stderr.exp \ + badaddrvalue.stdout.exp badaddrvalue.vgtest \ + badfree-2trace.stderr.exp badfree-2trace.vgtest \ + badfree.stderr.exp badfree.vgtest \ + badjump.stderr.exp badjump.vgtest \ + badjump2.stderr.exp badjump2.vgtest \ + badloop.stderr.exp badloop.vgtest \ + badrw.stderr.exp badrw.vgtest \ + brk.stderr.exp brk.vgtest \ + brk2.stderr.exp brk2.vgtest \ + buflen_check.stderr.exp buflen_check.vgtest \ + clientperm.stderr.exp \ + clientperm.stdout.exp clientperm.vgtest \ + custom_alloc.stderr.exp custom_alloc.vgtest \ + doublefree.stderr.exp doublefree.vgtest \ + error_counts.stderr.exp error_counts.stdout.exp error_counts.vgtest \ + errs1.stderr.exp errs1.vgtest \ + exitprog.stderr.exp exitprog.vgtest \ + execve.stderr.exp execve.vgtest \ + execve2.stderr.exp execve2.vgtest \ + fpeflags.stderr.exp fpeflags.vgtest \ + fprw.stderr.exp fprw.vgtest \ + fwrite.stderr.exp fwrite.stdout.exp fwrite.vgtest \ + inits.stderr.exp inits.vgtest \ + inline.stderr.exp inline.stdout.exp inline.vgtest \ + $(addsuffix .stderr.exp,$(INSN_TESTS)) \ + $(addsuffix .stdout.exp,$(INSN_TESTS)) \ + $(addsuffix .vgtest,$(INSN_TESTS)) \ + malloc1.stderr.exp malloc1.vgtest \ + malloc2.stderr.exp malloc2.vgtest \ + malloc3.stderr.exp malloc3.stdout.exp malloc3.vgtest \ + manuel1.stderr.exp manuel1.stdout.exp manuel1.vgtest \ + manuel2.stderr.exp manuel2.stdout.exp manuel2.vgtest \ + manuel3.stderr.exp manuel3.vgtest \ + memalign_test.stderr.exp memalign_test.vgtest \ + memalign2.stderr.exp memalign2.vgtest \ + memcmptest.stderr.exp memcmptest.stdout.exp memcmptest.vgtest \ + mempool.stderr.exp mempool.vgtest \ + mismatches.stderr.exp mismatches.vgtest \ + mmaptest.stderr.exp mmaptest.vgtest \ + nanoleak.stderr.exp nanoleak.vgtest \ + nanoleak_supp.stderr.exp nanoleak_supp.vgtest nanoleak.supp \ + new_nothrow.stderr.exp new_nothrow.vgtest \ + new_override.stderr.exp new_override.stdout.exp new_override.vgtest \ + null_socket.stderr.exp null_socket.vgtest \ + overlap.stderr.exp overlap.stdout.exp overlap.vgtest \ + pth_once.stderr.exp pth_once.stdout.exp pth_once.vgtest \ + pushfpopf.stderr.exp pushfpopf.stdout.exp pushfpopf.vgtest \ + realloc1.stderr.exp realloc1.vgtest \ + realloc2.stderr.exp realloc2.vgtest \ + realloc3.stderr.exp realloc3.vgtest \ + sigaltstack.stderr.exp sigaltstack.vgtest \ + signal2.stderr.exp \ + signal2.stdout.exp signal2.vgtest \ + supp1.stderr.exp supp1.vgtest \ + supp2.stderr.exp supp2.vgtest \ + supp.supp \ + suppfree.stderr.exp suppfree.vgtest \ + toobig-allocs.stderr.exp toobig-allocs.vgtest \ + trivialleak.stderr.exp trivialleak.vgtest \ + tronical.stderr.exp tronical.vgtest \ + weirdioctl.stderr.exp weirdioctl.stdout.exp weirdioctl.vgtest \ + metadata.stderr.exp metadata.stdout.exp metadata.vgtest \ + threadederrno.stderr.exp threadederrno.stdout.exp \ + threadederrno.vgtest \ + vgtest_ume.stderr.exp vgtest_ume.vgtest \ + writev.stderr.exp writev.vgtest \ + zeropage.stderr.exp zeropage.stderr.exp2 zeropage.vgtest + +check_PROGRAMS = \ + badaddrvalue badfree badjump badjump2 \ + badloop badrw brk brk2 buflen_check \ + clientperm custom_alloc \ + doublefree error_counts errs1 exitprog execve execve2 \ + fpeflags fprw fwrite hello inits inline \ + malloc1 malloc2 malloc3 manuel1 manuel2 manuel3 \ + memalign_test memalign2 memcmptest mempool mmaptest \ + nanoleak new_nothrow \ + null_socket overlap pushfpopf \ + realloc1 realloc2 realloc3 sigaltstack signal2 supp1 supp2 suppfree \ + trivialleak tronical weirdioctl \ + mismatches new_override metadata threadederrno \ + vgtest_ume \ + writev zeropage + + +AM_CPPFLAGS = -I$(top_srcdir)/include +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -g +AM_CXXFLAGS = $(AM_CFLAGS) + +# C ones +badaddrvalue_SOURCES = badaddrvalue.c +badfree_SOURCES = badfree.c +badjump_SOURCES = badjump.c +badjump2_SOURCES = badjump2.c +badloop_SOURCES = badloop.c +badrw_SOURCES = badrw.c +brk_SOURCES = brk.c +brk2_SOURCES = brk2.c +buflen_check_SOURCES = buflen_check.c +clientperm_SOURCES = clientperm.c +custom_alloc_SOURCES = custom_alloc.c +doublefree_SOURCES = doublefree.c +error_counts_SOURCES = error_counts.c +errs1_SOURCES = errs1.c +execve_SOURCES = execve.c +execve2_SOURCES = execve2.c +exitprog_SOURCES = exitprog.c +fpeflags_SOURCES = fpeflags.c +fprw_SOURCES = fprw.c +fwrite_SOURCES = fwrite.c +inits_SOURCES = inits.c +inline_SOURCES = inline.c +malloc1_SOURCES = malloc1.c +malloc2_SOURCES = malloc2.c +malloc3_SOURCES = malloc3.c +manuel1_SOURCES = manuel1.c +manuel2_SOURCES = manuel2.c +manuel3_SOURCES = manuel3.c +mmaptest_SOURCES = mmaptest.c +memalign_test_SOURCES = memalign_test.c +memalign2_SOURCES = memalign2.c +memcmptest_SOURCES = memcmptest.c +mempool_SOURCES = mempool.c +nanoleak_SOURCES = nanoleak.c +null_socket_SOURCES = null_socket.c +overlap_SOURCES = overlap.c +pushfpopf_SOURCES = pushfpopf_c.c pushfpopf_s.s +realloc1_SOURCES = realloc1.c +realloc2_SOURCES = realloc2.c +realloc3_SOURCES = realloc3.c +signal2_SOURCES = signal2.c +supp1_SOURCES = supp.c +supp2_SOURCES = supp.c +suppfree_SOURCES = suppfree.c +sigaltstack_SOURCES = sigaltstack.c +trivialleak_SOURCES = trivialleak.c +tronical_SOURCES = tronical.S +weirdioctl_SOURCES = weirdioctl.c +metadata_SOURCES = metadata.c +threadederrno_SOURCES = threadederrno.c +threadederrno_LDADD = -lpthread +writev_SOURCES = writev.c +zeropage_SOURCES = zeropage.c + +# C++ ones +mismatches_SOURCES = mismatches.cpp +new_nothrow_SOURCES = new_nothrow.cpp +new_override_SOURCES = new_override.cpp + +# Valgrind unit self-tests +hello_SOURCES = hello.c +hello_LDFLAGS = -Wl,-defsym,kickstart_base=0x50000000 \ + -Wl,-T,../../coregrind/${VG_ARCH}/stage2.lds +vgtest_ume_SOURCES = vgtest_ume.c +vgtest_ume_LDADD = ../../coregrind/ume.o \ + ../../coregrind/jmp_with_stack.o + +# must be built with these flags -- bug only occurred with them +fpeflags.o: CFLAGS += -march=i686 diff --git a/VEX/head20041019/memcheck/tests/badaddrvalue.c b/VEX/head20041019/memcheck/tests/badaddrvalue.c new file mode 100644 index 0000000000..1bb2047472 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badaddrvalue.c @@ -0,0 +1,12 @@ + +#include +#include + +int main ( void ) +{ + char* aa = malloc(8); + aa[-1] = 17; + if (aa[-1] == 17) + printf("17\n"); else printf("not 17\n"); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/badaddrvalue.stderr.exp b/VEX/head20041019/memcheck/tests/badaddrvalue.stderr.exp new file mode 100644 index 0000000000..f71df0bb09 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badaddrvalue.stderr.exp @@ -0,0 +1,11 @@ +Invalid write of size 1 + at 0x........: main (badaddrvalue.c:8) + Address 0x........ is 1 bytes before a block of size 8 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badaddrvalue.c:7) + +Invalid read of size 1 + at 0x........: main (badaddrvalue.c:9) + Address 0x........ is 1 bytes before a block of size 8 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badaddrvalue.c:7) diff --git a/VEX/head20041019/memcheck/tests/badaddrvalue.stdout.exp b/VEX/head20041019/memcheck/tests/badaddrvalue.stdout.exp new file mode 100644 index 0000000000..98d9bcb75a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badaddrvalue.stdout.exp @@ -0,0 +1 @@ +17 diff --git a/VEX/head20041019/memcheck/tests/badaddrvalue.vgtest b/VEX/head20041019/memcheck/tests/badaddrvalue.vgtest new file mode 100644 index 0000000000..4d4b7f6806 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badaddrvalue.vgtest @@ -0,0 +1,2 @@ +prog: badaddrvalue +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/badfree-2trace.stderr.exp b/VEX/head20041019/memcheck/tests/badfree-2trace.stderr.exp new file mode 100644 index 0000000000..fe7c5577b6 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badfree-2trace.stderr.exp @@ -0,0 +1,9 @@ +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (badfree.c:12) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (badfree.c:15) + Address 0x........ is on thread 1's stack diff --git a/VEX/head20041019/memcheck/tests/badfree-2trace.vgtest b/VEX/head20041019/memcheck/tests/badfree-2trace.vgtest new file mode 100644 index 0000000000..afeb3bb783 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badfree-2trace.vgtest @@ -0,0 +1,2 @@ +vgopts: --num-callers=2 -q +prog: badfree diff --git a/VEX/head20041019/memcheck/tests/badfree.c b/VEX/head20041019/memcheck/tests/badfree.c new file mode 100644 index 0000000000..3a22567764 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badfree.c @@ -0,0 +1,18 @@ + + +#include +#include + +int main ( void ) +{ + void* p = (void*)0x87654321; + int q[] = { 1, 2, 3 }; + + /* Free a pointer to Never-Never Land */ + free(p); + + /* Free a pointer to a stack block */ + free(q); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/badfree.stderr.exp b/VEX/head20041019/memcheck/tests/badfree.stderr.exp new file mode 100644 index 0000000000..fe7c5577b6 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badfree.stderr.exp @@ -0,0 +1,9 @@ +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (badfree.c:12) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (badfree.c:15) + Address 0x........ is on thread 1's stack diff --git a/VEX/head20041019/memcheck/tests/badfree.vgtest b/VEX/head20041019/memcheck/tests/badfree.vgtest new file mode 100644 index 0000000000..dfe6e4227f --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badfree.vgtest @@ -0,0 +1,2 @@ +prog: badfree +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/badjump.c b/VEX/head20041019/memcheck/tests/badjump.c new file mode 100644 index 0000000000..053663be48 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badjump.c @@ -0,0 +1,6 @@ + +int main ( void ) +{ + char* p = (char*)0xE000000; + return ((int(*)(void)) p) (); +} diff --git a/VEX/head20041019/memcheck/tests/badjump.stderr.exp b/VEX/head20041019/memcheck/tests/badjump.stderr.exp new file mode 100644 index 0000000000..155c5bafb5 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badjump.stderr.exp @@ -0,0 +1,18 @@ + +Jump to the invalid address stated on the next line + at 0x........: ??? + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Process terminating with default action of signal 11 (SIGSEGV) + Access not within mapped region at address 0x........ + at 0x........: ??? + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + +ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) +malloc/free: in use at exit: 0 bytes in 0 blocks. +malloc/free: 0 allocs, 0 frees, 0 bytes allocated. +For a detailed leak analysis, rerun with: --leak-check=yes +For counts of detected errors, rerun with: -v diff --git a/VEX/head20041019/memcheck/tests/badjump.vgtest b/VEX/head20041019/memcheck/tests/badjump.vgtest new file mode 100644 index 0000000000..1e82b868d4 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badjump.vgtest @@ -0,0 +1 @@ +prog: badjump diff --git a/VEX/head20041019/memcheck/tests/badjump2.c b/VEX/head20041019/memcheck/tests/badjump2.c new file mode 100644 index 0000000000..361966b6c1 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badjump2.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include + +// Regression test for bug 91162: if a client had a SEGV signal handler, +// and jumped to a bogus address, Valgrind would abort. With the fix, +// the following test runs to completion correctly. + +static jmp_buf myjmpbuf; + +static +void SIGSEGV_handler(int signum) +{ + __builtin_longjmp(myjmpbuf, 1); +} + +int main(void) +{ + struct sigaction sigsegv_new, sigsegv_saved; + int res; + + /* Install own SIGSEGV handler */ + sigsegv_new.sa_handler = SIGSEGV_handler; + sigsegv_new.sa_flags = 0; + sigsegv_new.sa_restorer = NULL; + res = sigemptyset( &sigsegv_new.sa_mask ); + assert(res == 0); + + res = sigaction( SIGSEGV, &sigsegv_new, &sigsegv_saved ); + assert(res == 0); + + if (__builtin_setjmp(myjmpbuf) == 0) { + // Jump to zero; will cause seg fault + void (*fn)(void) = 0; + fn(); + fprintf(stderr, "Got here??\n"); + } else { + fprintf(stderr, "Signal caught, as expected\n"); + } + + return 0; +} + diff --git a/VEX/head20041019/memcheck/tests/badjump2.stderr.exp b/VEX/head20041019/memcheck/tests/badjump2.stderr.exp new file mode 100644 index 0000000000..04db2d9b78 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badjump2.stderr.exp @@ -0,0 +1,6 @@ +Jump to the invalid address stated on the next line + at 0x........: ??? + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +Signal caught, as expected diff --git a/VEX/head20041019/memcheck/tests/badjump2.vgtest b/VEX/head20041019/memcheck/tests/badjump2.vgtest new file mode 100644 index 0000000000..4256086d5c --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badjump2.vgtest @@ -0,0 +1,2 @@ +prog: badjump2 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/badloop.c b/VEX/head20041019/memcheck/tests/badloop.c new file mode 100644 index 0000000000..8780cf7f83 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badloop.c @@ -0,0 +1,15 @@ + +#include + +int main ( void ) +{ + int a[5]; + int i, s; + a[0] = a[1] = a[3] = a[4] = 0; + s = 0; + for (i = 0; i < 5; i++) + s += a[i]; + if (s == 377) + printf("sum is %d\n", s); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/badloop.stderr.exp b/VEX/head20041019/memcheck/tests/badloop.stderr.exp new file mode 100644 index 0000000000..4c53707ae3 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badloop.stderr.exp @@ -0,0 +1,2 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (badloop.c:12) diff --git a/VEX/head20041019/memcheck/tests/badloop.vgtest b/VEX/head20041019/memcheck/tests/badloop.vgtest new file mode 100644 index 0000000000..f0e2158757 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badloop.vgtest @@ -0,0 +1,2 @@ +prog: badloop +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/badrw.c b/VEX/head20041019/memcheck/tests/badrw.c new file mode 100644 index 0000000000..d711ec969b --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badrw.c @@ -0,0 +1,29 @@ +#include + +int main(void) +{ + void* x = malloc(10); + + int *x4; + short *x2; + char *x1; + int y4; + short y2; + char y1; + + x4 = x-4; + x2 = x-4; + x1 = x-1; + + // Invalid reads and writes of sizes 4, 2, 1 + y4 = *x4; + *x4 = y4; + + y2 = *x2; + *x2 = y2; + + y1 = *x1; + *x1 = y1; + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/badrw.stderr.exp b/VEX/head20041019/memcheck/tests/badrw.stderr.exp new file mode 100644 index 0000000000..77c3f4a21c --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badrw.stderr.exp @@ -0,0 +1,35 @@ +Invalid read of size 4 + at 0x........: main (badrw.c:19) + Address 0x........ is 4 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid write of size 4 + at 0x........: main (badrw.c:20) + Address 0x........ is 4 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid read of size 2 + at 0x........: main (badrw.c:22) + Address 0x........ is 4 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid write of size 2 + at 0x........: main (badrw.c:23) + Address 0x........ is 4 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid read of size 1 + at 0x........: main (badrw.c:25) + Address 0x........ is 1 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) + +Invalid write of size 1 + at 0x........: main (badrw.c:26) + Address 0x........ is 1 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (badrw.c:5) diff --git a/VEX/head20041019/memcheck/tests/badrw.vgtest b/VEX/head20041019/memcheck/tests/badrw.vgtest new file mode 100644 index 0000000000..09c70a0ec1 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/badrw.vgtest @@ -0,0 +1,2 @@ +prog: badrw +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/brk.c b/VEX/head20041019/memcheck/tests/brk.c new file mode 100644 index 0000000000..58c42604aa --- /dev/null +++ b/VEX/head20041019/memcheck/tests/brk.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include + +// kernel brk() and libc brk() act quite differently... + +int main(void) +{ + int i; + void* orig_ds = sbrk(0); + void* ds = orig_ds; + void* vals[10]; + void* res; + + vals[0] = (void*)0; + vals[1] = (void*)1; + vals[2] = ds - 0x1; // small shrink + vals[3] = ds; + vals[4] = ds + 0x1000; // small growth + vals[5] = ds + 0x40000000; // too-big growth + vals[6] = ds + 0x500; // shrink a little, but still above start size + vals[7] = ds - 0x1; // shrink below start size +// vals[8] = ds - 0x1000; // shrink a lot below start size (into text) +// vals[9] = (void*)0xffffffff; + vals[8] = (void*)0xffffffff; + + for (i = 0; (void*)0xffffffff != vals[i]; i++) { + res = (void*)syscall(__NR_brk, vals[i]); + } + + assert( 0 == brk(orig_ds) ); // libc brk() + + for (i = 0; (void*)0xffffffff != vals[i]; i++) { + res = (void*)brk(vals[i]); + } + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/brk.stderr.exp b/VEX/head20041019/memcheck/tests/brk.stderr.exp new file mode 100644 index 0000000000..c4aa6f04fe --- /dev/null +++ b/VEX/head20041019/memcheck/tests/brk.stderr.exp @@ -0,0 +1,7 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +malloc/free: in use at exit: 0 bytes in 0 blocks. +malloc/free: 0 allocs, 0 frees, 0 bytes allocated. +For a detailed leak analysis, rerun with: --leak-check=yes +For counts of detected errors, rerun with: -v diff --git a/VEX/head20041019/memcheck/tests/brk.vgtest b/VEX/head20041019/memcheck/tests/brk.vgtest new file mode 100644 index 0000000000..b1766ffc38 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/brk.vgtest @@ -0,0 +1 @@ +prog: brk diff --git a/VEX/head20041019/memcheck/tests/brk2.c b/VEX/head20041019/memcheck/tests/brk2.c new file mode 100644 index 0000000000..3a881335e4 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/brk2.c @@ -0,0 +1,25 @@ +#include +#include + +#define MAX 3000 + +// At one time, this was causing a seg fault within Valgrind -- it was when +// extending the brk segment onto a new page. Fixed in vg_syscalls.c 1.129. + +int main () { + char* ptr; + int i; + + for (i=0; i +#include +#include + +int main(void) +{ + struct sockaddr name; + int res1, res2; + int len = 10; + + res1 = socket(PF_UNIX, SOCK_STREAM, 0); + if (res1 == 0) { + fprintf(stderr, "socket() failed\n"); + exit(1); + } + + /* Valgrind 1.0.X doesn't report the second error */ + res1 = getsockname(-1, NULL, &len); /* NULL is bogus */ + res2 = getsockname(-1, &name, NULL); /* NULL is bogus */ + if (res1 == -1) { + fprintf(stderr, "getsockname(1) failed\n"); + } + if (res2 == -1) { + fprintf(stderr, "getsockname(2) failed\n"); + } + + return 0; +} + diff --git a/VEX/head20041019/memcheck/tests/buflen_check.stderr.exp b/VEX/head20041019/memcheck/tests/buflen_check.stderr.exp new file mode 100644 index 0000000000..090ef489b9 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/buflen_check.stderr.exp @@ -0,0 +1,13 @@ +Syscall param socketcall.getsockname(name) contains unaddressable byte(s) + at 0x........: getsockname (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Syscall param socketcall.getsockname(namelen_in) contains uninitialised or unaddressable byte(s) + at 0x........: getsockname (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + Address 0x........ is not stack'd, malloc'd or (recently) free'd +getsockname(1) failed +getsockname(2) failed diff --git a/VEX/head20041019/memcheck/tests/buflen_check.vgtest b/VEX/head20041019/memcheck/tests/buflen_check.vgtest new file mode 100644 index 0000000000..c4c2f3dd58 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/buflen_check.vgtest @@ -0,0 +1,2 @@ +prog: buflen_check +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/clientperm.c b/VEX/head20041019/memcheck/tests/clientperm.c new file mode 100644 index 0000000000..27cb11a078 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/clientperm.c @@ -0,0 +1,39 @@ + +#include +#include + +#include "../memcheck.h" + +int main1 ( void ) +{ + int xxx, i; + for (i = 0; i < 10; i++) VALGRIND_CHECK_DEFINED(xxx); + return 0; +} + +int main ( void ) +{ + int i, sum, m; + char* aa = calloc(100,1); + sum = 0; + + VALGRIND_CHECK_READABLE(aa,100); + + m = VALGRIND_MAKE_WRITABLE( &aa[49], 1 ); + VALGRIND_CHECK_WRITABLE(aa,100); + + printf("m_na: returned value is %d\n", m ); + + for (i = 0; i < 100; i++) + sum += aa[i]; + printf("sum is %s\n", sum > 0 ? "positive" : "non-positive"); + + m = VALGRIND_DISCARD(m); + printf("m_rm: returned value is %d\n", m ); + + for (i = 0; i < 100; i++) + sum += aa[i]; + printf("sum is %s\n", sum > 0 ? "positive" : "non-positive"); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/clientperm.stderr.exp b/VEX/head20041019/memcheck/tests/clientperm.stderr.exp new file mode 100644 index 0000000000..5f93d81a25 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/clientperm.stderr.exp @@ -0,0 +1,5 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (clientperm.c:29) + +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (clientperm.c:36) diff --git a/VEX/head20041019/memcheck/tests/clientperm.stdout.exp b/VEX/head20041019/memcheck/tests/clientperm.stdout.exp new file mode 100644 index 0000000000..7ab39d358e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/clientperm.stdout.exp @@ -0,0 +1,4 @@ +m_na: returned value is 0 +sum is non-positive +m_rm: returned value is 0 +sum is non-positive diff --git a/VEX/head20041019/memcheck/tests/clientperm.vgtest b/VEX/head20041019/memcheck/tests/clientperm.vgtest new file mode 100644 index 0000000000..7ddd5af993 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/clientperm.vgtest @@ -0,0 +1,2 @@ +prog: clientperm +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/custom_alloc.c b/VEX/head20041019/memcheck/tests/custom_alloc.c new file mode 100644 index 0000000000..36c7952e5e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/custom_alloc.c @@ -0,0 +1,96 @@ +#include +#include +#include +#include + +#include "../memcheck.h" + +#define SUPERBLOCK_SIZE 100000 + +//------------------------------------------------------------------------- +// Allocator +//------------------------------------------------------------------------- + +void* get_superblock(void) +{ + void* p = mmap( 0, SUPERBLOCK_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANON, -1, 0 ); + + assert(p != ((void*)(-1))); + + // Mark it no access; although it's addressible we don't want the + // program to be using it unless its handed out by custom_alloc() + + // with redzones, better not to have it + VALGRIND_MAKE_NOACCESS(p, SUPERBLOCK_SIZE); + + return p; +} + +// has a redzone +static void* custom_alloc(int size) +{ +#define RZ 8 + static void* hp = 0; // current heap pointer + static void* hp_lim = 0; // maximum usable byte in current block + int size2 = size + RZ*2; + void* p; + + if (hp + size2 > hp_lim) { + hp = get_superblock(); + hp_lim = hp + SUPERBLOCK_SIZE - 1; + } + + p = hp + RZ; + hp += size2; + + VALGRIND_MALLOCLIKE_BLOCK( p, size, RZ, /*is_zeroed*/1 ); + return (void*)p; +} + +static void custom_free(void* p) +{ + // don't actually free any memory... but mark it as freed + VALGRIND_FREELIKE_BLOCK( p, RZ ); +} +#undef RZ + + + +//------------------------------------------------------------------------- +// Rest +//------------------------------------------------------------------------- + +void make_leak(void) +{ + int* array2 = custom_alloc(sizeof(int) * 10); + array2 = 0; // leak + return; +} + +int main(void) +{ + int* array; + int* array3; + + array = custom_alloc(sizeof(int) * 10); + array[8] = 8; + array[9] = 8; + array[10] = 10; // invalid write (ok w/o MALLOCLIKE -- in superblock) + + custom_free(array); // ok + + custom_free(NULL); // invalid free (ok without MALLOCLIKE) + + array3 = malloc(sizeof(int) * 10); + custom_free(array3); // mismatched free (ok without MALLOCLIKE) + + make_leak(); + return array[0]; // use after free (ok without MALLOCLIKE/MAKE_NOACCESS) + // (nb: initialised because is_zeroed==1 above) + // unfortunately not identified as being in a free'd + // block because the freeing of the block and shadow + // chunk isn't postponed. + + // leak from make_leak() +} diff --git a/VEX/head20041019/memcheck/tests/custom_alloc.stderr.exp b/VEX/head20041019/memcheck/tests/custom_alloc.stderr.exp new file mode 100644 index 0000000000..b727b5fd1e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/custom_alloc.stderr.exp @@ -0,0 +1,25 @@ +Invalid write of size 4 + at 0x........: main (custom_alloc.c:79) + Address 0x........ is 48 bytes inside a block of size 100000 client-defined + at 0x........: get_superblock (custom_alloc.c:25) + by 0x........: custom_alloc (custom_alloc.c:40) + by 0x........: main (custom_alloc.c:76) + +Invalid free() / delete / delete[] + at 0x........: custom_free (custom_alloc.c:54) + by 0x........: main (custom_alloc.c:83) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Mismatched free() / delete / delete [] + at 0x........: custom_free (custom_alloc.c:54) + by 0x........: main (custom_alloc.c:86) + Address 0x........ is 0 bytes inside a block of size 40 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (custom_alloc.c:85) + +Invalid read of size 4 + at 0x........: main (custom_alloc.c:89) + Address 0x........ is 8 bytes inside a block of size 100000 client-defined + at 0x........: get_superblock (custom_alloc.c:25) + by 0x........: custom_alloc (custom_alloc.c:40) + by 0x........: main (custom_alloc.c:76) diff --git a/VEX/head20041019/memcheck/tests/custom_alloc.vgtest b/VEX/head20041019/memcheck/tests/custom_alloc.vgtest new file mode 100644 index 0000000000..9c7fa41061 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/custom_alloc.vgtest @@ -0,0 +1,2 @@ +prog: custom_alloc +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/doublefree.c b/VEX/head20041019/memcheck/tests/doublefree.c new file mode 100644 index 0000000000..3c27050813 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/doublefree.c @@ -0,0 +1,12 @@ + +#include +#include + +int main ( void ) +{ + int i; + void* p = malloc(177); + for (i = 0; i < 2; i++) + free(p); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/doublefree.stderr.exp b/VEX/head20041019/memcheck/tests/doublefree.stderr.exp new file mode 100644 index 0000000000..364f73842d --- /dev/null +++ b/VEX/head20041019/memcheck/tests/doublefree.stderr.exp @@ -0,0 +1,6 @@ +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (doublefree.c:10) + Address 0x........ is 0 bytes inside a block of size 177 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (doublefree.c:10) diff --git a/VEX/head20041019/memcheck/tests/doublefree.vgtest b/VEX/head20041019/memcheck/tests/doublefree.vgtest new file mode 100644 index 0000000000..f2be68ce07 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/doublefree.vgtest @@ -0,0 +1,2 @@ +prog: doublefree +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/error_counts.c b/VEX/head20041019/memcheck/tests/error_counts.c new file mode 100644 index 0000000000..2cf1060b3d --- /dev/null +++ b/VEX/head20041019/memcheck/tests/error_counts.c @@ -0,0 +1,53 @@ +#include +#include +#include "../memcheck.h" + +int main(void) +{ + int x; + int y = 0; + int* reachable; + int* dubious; + int* leaked; + int n_reachable = 0; + int n_dubious = 0; + int n_leaked = 0; + int n_suppressed = 0; + + /* Error counting */ + printf("errors: %d\n", VALGRIND_COUNT_ERRORS); + + if (x == 0) { + y++; + } else { + y--; + } + + printf("errors: %d\n", VALGRIND_COUNT_ERRORS); + + /* Leak checking */ + VALGRIND_DO_LEAK_CHECK; + VALGRIND_COUNT_LEAKS(n_leaked, n_dubious, n_reachable, n_suppressed); + if (n_reachable == 24) n_reachable = 0; /* handle glibc differences */ + printf("leaks: %dB, %dB, %dB, %dB\n", + n_leaked, n_dubious, n_reachable, n_suppressed); + + leaked = malloc(77); + leaked = 0; + + dubious = malloc(88); + dubious += 10; + + reachable = malloc(99); + + VALGRIND_DO_LEAK_CHECK; + VALGRIND_DO_LEAK_CHECK; + VALGRIND_COUNT_LEAKS(n_leaked, n_dubious, n_reachable, n_suppressed); + if (n_reachable == 147) n_reachable = 99; /* handle glibc differences */ + printf("leaks: %dB, %dB, %dB, %dB\n", + n_leaked, n_dubious, n_reachable, n_suppressed); + + printf("errors: %d\n", VALGRIND_COUNT_ERRORS); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/error_counts.stderr.exp b/VEX/head20041019/memcheck/tests/error_counts.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/error_counts.stdout.exp b/VEX/head20041019/memcheck/tests/error_counts.stdout.exp new file mode 100644 index 0000000000..ea11aad9b3 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/error_counts.stdout.exp @@ -0,0 +1,5 @@ +errors: 0 +errors: 1 +leaks: 0B, 0B, 0B, 0B +leaks: 77B, 88B, 99B, 0B +errors: 1 diff --git a/VEX/head20041019/memcheck/tests/error_counts.vgtest b/VEX/head20041019/memcheck/tests/error_counts.vgtest new file mode 100644 index 0000000000..a2bed401c7 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/error_counts.vgtest @@ -0,0 +1,2 @@ +vgopts: --log-fd=-1 +prog: error_counts diff --git a/VEX/head20041019/memcheck/tests/errs1.c b/VEX/head20041019/memcheck/tests/errs1.c new file mode 100644 index 0000000000..754f844430 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/errs1.c @@ -0,0 +1,17 @@ + +#include +#include + +char* p; + +void ddd ( void ) { p[-1] += 'z'; } +void ccc ( void ) { ddd(); } +void bbb ( void ) { ccc(); } +void aaa ( void ) { bbb(); } + +void zzzzzzz ( void ) { p = malloc(10); } +void yyy ( void ) { zzzzzzz(); } +void xxx ( void ) { yyy(); } +void www ( void ) { xxx(); } + +int main ( void ) { www(); aaa(); return 0; } diff --git a/VEX/head20041019/memcheck/tests/errs1.stderr.exp b/VEX/head20041019/memcheck/tests/errs1.stderr.exp new file mode 100644 index 0000000000..7b5f0fe2f8 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/errs1.stderr.exp @@ -0,0 +1,21 @@ +Invalid read of size 1 + at 0x........: ddd (errs1.c:7) + by 0x........: bbb (errs1.c:9) + by 0x........: aaa (errs1.c:10) + by 0x........: main (errs1.c:17) + Address 0x........ is 1 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: zzzzzzz (errs1.c:12) + by 0x........: yyy (errs1.c:13) + by 0x........: xxx (errs1.c:14) + +Invalid write of size 1 + at 0x........: ddd (errs1.c:7) + by 0x........: bbb (errs1.c:9) + by 0x........: aaa (errs1.c:10) + by 0x........: main (errs1.c:17) + Address 0x........ is 1 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: zzzzzzz (errs1.c:12) + by 0x........: yyy (errs1.c:13) + by 0x........: xxx (errs1.c:14) diff --git a/VEX/head20041019/memcheck/tests/errs1.vgtest b/VEX/head20041019/memcheck/tests/errs1.vgtest new file mode 100644 index 0000000000..bd780f9afe --- /dev/null +++ b/VEX/head20041019/memcheck/tests/errs1.vgtest @@ -0,0 +1,2 @@ +prog: errs1 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/execve.c b/VEX/head20041019/memcheck/tests/execve.c new file mode 100644 index 0000000000..0e8222d439 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/execve.c @@ -0,0 +1,12 @@ +#include + +int main(void) +{ + char* bad[2] = { (char*)1, NULL }; + char* good[1] = { NULL }; + + execve(NULL, bad, bad); + execve("/bin/true", good, good); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/execve.stderr.exp b/VEX/head20041019/memcheck/tests/execve.stderr.exp new file mode 100644 index 0000000000..14dd1d13f4 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/execve.stderr.exp @@ -0,0 +1,14 @@ +Syscall param execve(filename) contains uninitialised or unaddressable byte(s) + at 0x........: execve (in /...libc...) + by 0x........: main (execve.c:8) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Syscall param execve(argv[i]) contains uninitialised or unaddressable byte(s) + at 0x........: execve (in /...libc...) + by 0x........: main (execve.c:8) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Syscall param execve(envp[i]) contains uninitialised or unaddressable byte(s) + at 0x........: execve (in /...libc...) + by 0x........: main (execve.c:8) + Address 0x........ is not stack'd, malloc'd or (recently) free'd diff --git a/VEX/head20041019/memcheck/tests/execve.vgtest b/VEX/head20041019/memcheck/tests/execve.vgtest new file mode 100644 index 0000000000..51a8d514ca --- /dev/null +++ b/VEX/head20041019/memcheck/tests/execve.vgtest @@ -0,0 +1,2 @@ +prog: execve +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/execve2.c b/VEX/head20041019/memcheck/tests/execve2.c new file mode 100644 index 0000000000..c3eae2c2fa --- /dev/null +++ b/VEX/head20041019/memcheck/tests/execve2.c @@ -0,0 +1,9 @@ +#include +#include + +int main(void) +{ + execve(NULL, NULL, NULL); + execve("../../tests/true", NULL, NULL); + assert(0); // shouldn't get here +} diff --git a/VEX/head20041019/memcheck/tests/execve2.stderr.exp b/VEX/head20041019/memcheck/tests/execve2.stderr.exp new file mode 100644 index 0000000000..c2cadf7d93 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/execve2.stderr.exp @@ -0,0 +1,4 @@ +Syscall param execve(filename) contains uninitialised or unaddressable byte(s) + at 0x........: execve (in /...libc...) + by 0x........: main (execve2.c:6) + Address 0x........ is not stack'd, malloc'd or (recently) free'd diff --git a/VEX/head20041019/memcheck/tests/execve2.vgtest b/VEX/head20041019/memcheck/tests/execve2.vgtest new file mode 100644 index 0000000000..9636ad835e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/execve2.vgtest @@ -0,0 +1,2 @@ +prog: execve2 +vgopts: -q --trace-children=yes diff --git a/VEX/head20041019/memcheck/tests/exitprog.c b/VEX/head20041019/memcheck/tests/exitprog.c new file mode 100644 index 0000000000..4f1c46a884 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/exitprog.c @@ -0,0 +1,26 @@ + + +#include +#include +#include + +#define ZILLION 1000000 + +void foo ( void ); +void bar ( void ); + +int main ( void ) +{ + int i; + char* a = malloc(ZILLION * sizeof(char)); + for (i = 0; i <= ZILLION; i++) { + foo(); + a[i] = 0; + bar(); + } + a = (char*)177; + _exit(1); +} + +void foo ( void ) { } +void bar ( void ) { } diff --git a/VEX/head20041019/memcheck/tests/exitprog.stderr.exp b/VEX/head20041019/memcheck/tests/exitprog.stderr.exp new file mode 100644 index 0000000000..c1fa55be71 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/exitprog.stderr.exp @@ -0,0 +1,5 @@ +Invalid write of size 1 + at 0x........: main (exitprog.c:18) + Address 0x........ is 0 bytes after a block of size 1000000 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (exitprog.c:15) diff --git a/VEX/head20041019/memcheck/tests/exitprog.vgtest b/VEX/head20041019/memcheck/tests/exitprog.vgtest new file mode 100644 index 0000000000..970762ad8b --- /dev/null +++ b/VEX/head20041019/memcheck/tests/exitprog.vgtest @@ -0,0 +1,2 @@ +prog: exitprog +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/filter_allocs b/VEX/head20041019/memcheck/tests/filter_allocs new file mode 100755 index 0000000000..9ad6d23c4f --- /dev/null +++ b/VEX/head20041019/memcheck/tests/filter_allocs @@ -0,0 +1,6 @@ +#! /bin/sh + +./filter_stderr | +sed "s/malloc\/free: in use at exit: [0-9]* bytes in [0-9]* blocks./malloc\/free: in use at exit: ... bytes in ... blocks./" | +sed "s/malloc.free: [0-9]* allocs, [0-9]* frees, [0-9]* bytes allocated./malloc\/free: ... allocs, ... frees, ... bytes allocated./" + diff --git a/VEX/head20041019/memcheck/tests/filter_leak_check_size b/VEX/head20041019/memcheck/tests/filter_leak_check_size new file mode 100755 index 0000000000..0cb482a467 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/filter_leak_check_size @@ -0,0 +1,4 @@ +#! /bin/sh + +./filter_stderr | +sed "s/checked [0-9]* bytes./checked ... bytes./" diff --git a/VEX/head20041019/memcheck/tests/filter_pushfpopf b/VEX/head20041019/memcheck/tests/filter_pushfpopf new file mode 100755 index 0000000000..90e3d70c17 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/filter_pushfpopf @@ -0,0 +1,5 @@ +#! /bin/sh + +./filter_stderr | +sed "s/: fooble ([^)]*)/: fooble (...)/" + diff --git a/VEX/head20041019/memcheck/tests/filter_stderr b/VEX/head20041019/memcheck/tests/filter_stderr new file mode 100755 index 0000000000..4c583fbadc --- /dev/null +++ b/VEX/head20041019/memcheck/tests/filter_stderr @@ -0,0 +1,26 @@ +#! /bin/sh + +dir=`dirname $0` + +$dir/../../tests/filter_stderr_basic | + +# Anonymise addresses +$dir/../../tests/filter_addresses | + +# Anonymise line numbers in mac_replace_strmem.c +sed "s/mac_replace_strmem.c:[0-9]*/mac_replace_strmem.c:.../" | + +$dir/../../tests/filter_test_paths | + +# Anonymise paths like "(in /foo/bar/libc-baz.so)" +sed "s/(in \/.*libc.*)$/(in \/...libc...)/" | + +# Anonymise paths like "(within /foo/bar/libc-baz.so)" +sed "s/(within \/.*libc.*)$/(within \/...libc...)/" | + +# Anonymise paths like "xxx (../sysdeps/unix/sysv/linux/quux.c:129)" +sed "s/(\.\.\/sysdeps\/unix\/sysv\/linux\/.*\.c:[0-9]*)$/(in \/...libc...)/" | + +# Anonymise paths like "__libc_start_main (../foo/bar/libc-quux.c:129)" +sed "s/__libc_\(.*\) (.*)$/__libc_\1 (...libc...)/" + diff --git a/VEX/head20041019/memcheck/tests/filter_stderr_backtrace b/VEX/head20041019/memcheck/tests/filter_stderr_backtrace new file mode 100755 index 0000000000..f5ba472f41 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/filter_stderr_backtrace @@ -0,0 +1,3 @@ +#! /bin/sh +./filter_stderr | +sed "/^ by 0x........: /d" diff --git a/VEX/head20041019/memcheck/tests/filter_tronical b/VEX/head20041019/memcheck/tests/filter_tronical new file mode 100755 index 0000000000..e1739bbd99 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/filter_tronical @@ -0,0 +1,6 @@ +#! /bin/sh + +./filter_stderr | \ +sed "s/: get ([^)]*)/: get (...)/" | +sed "s/: main ([^)]*)/: main (...)/" + diff --git a/VEX/head20041019/memcheck/tests/fpeflags.c b/VEX/head20041019/memcheck/tests/fpeflags.c new file mode 100644 index 0000000000..4bedcd5b42 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/fpeflags.c @@ -0,0 +1,22 @@ +#include + +struct instance +{ + unsigned myVal:1; +}; + +static struct instance* myInstance; + +int main(int argc, char** argv) +{ + float g = 1.0f; + + myInstance = malloc(sizeof(struct instance)); + + myInstance->myVal = 1; + + if (g == 1.0f) + return 0; + else + return 1; +} diff --git a/VEX/head20041019/memcheck/tests/fpeflags.stderr.exp b/VEX/head20041019/memcheck/tests/fpeflags.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/fpeflags.vgtest b/VEX/head20041019/memcheck/tests/fpeflags.vgtest new file mode 100644 index 0000000000..88ae11d59a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/fpeflags.vgtest @@ -0,0 +1,2 @@ +vgopts: -q +prog: fpeflags diff --git a/VEX/head20041019/memcheck/tests/fprw.c b/VEX/head20041019/memcheck/tests/fprw.c new file mode 100644 index 0000000000..556d8a035d --- /dev/null +++ b/VEX/head20041019/memcheck/tests/fprw.c @@ -0,0 +1,26 @@ + +/* most of the nasties in this are in the same bb, so you need to run +with --single-step=yes to get them properly distinguished. */ + +#include + +int main ( void ) +{ + volatile double d; + volatile float f; + double* dp = malloc(sizeof(double)); + float* fp = malloc(sizeof(float)); + int* ip = (int*)0x1234567; + d += 1.0; + f += 10.0; + *dp += 2.0; + *fp += 20.0; + free(dp); + free(fp); + *dp += 3.0; + *fp += 30.0; + free(ip); + ip = malloc(sizeof(int)); + * ((double*)ip) = 1.2 + d; + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/fprw.stderr.exp b/VEX/head20041019/memcheck/tests/fprw.stderr.exp new file mode 100644 index 0000000000..cf082691b1 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/fprw.stderr.exp @@ -0,0 +1,46 @@ +Use of uninitialised value of size 8 + at 0x........: main (fprw.c:14) + +Use of uninitialised value of size 4 + at 0x........: main (fprw.c:15) + +Use of uninitialised value of size 8 + at 0x........: main (fprw.c:16) + +Use of uninitialised value of size 4 + at 0x........: main (fprw.c:17) + +Invalid read of size 8 + at 0x........: main (fprw.c:20) + Address 0x........ is 0 bytes inside a block of size 8 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:18) + +Invalid write of size 8 + at 0x........: main (fprw.c:20) + Address 0x........ is 0 bytes inside a block of size 8 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:18) + +Invalid read of size 4 + at 0x........: main (fprw.c:21) + Address 0x........ is 0 bytes inside a block of size 4 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:19) + +Invalid write of size 4 + at 0x........: main (fprw.c:21) + Address 0x........ is 0 bytes inside a block of size 4 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:19) + +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:22) + Address 0x........ is not stack'd, malloc'd or (recently) free'd + +Invalid write of size 8 + at 0x........: main (fprw.c:24) + Address 0x........ is 0 bytes inside a block of size 4 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (fprw.c:23) diff --git a/VEX/head20041019/memcheck/tests/fprw.vgtest b/VEX/head20041019/memcheck/tests/fprw.vgtest new file mode 100644 index 0000000000..6dfbf0cb40 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/fprw.vgtest @@ -0,0 +1,2 @@ +vgopts: -q +prog: fprw diff --git a/VEX/head20041019/memcheck/tests/fwrite.c b/VEX/head20041019/memcheck/tests/fwrite.c new file mode 100644 index 0000000000..1eec4a4f8e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/fwrite.c @@ -0,0 +1,9 @@ + +#include +#include +int main ( void ) +{ + char* arr = malloc(10); + (void) write( 1 /* stdout */, arr, 10 ); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/fwrite.stderr.exp b/VEX/head20041019/memcheck/tests/fwrite.stderr.exp new file mode 100644 index 0000000000..76f922190a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/fwrite.stderr.exp @@ -0,0 +1,7 @@ +Syscall param write(buf) contains uninitialised or unaddressable byte(s) + at 0x........: write (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + Address 0x........ is 0 bytes inside a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (fwrite.c:6) diff --git a/VEX/head20041019/memcheck/tests/fwrite.stdout.exp b/VEX/head20041019/memcheck/tests/fwrite.stdout.exp new file mode 100644 index 0000000000..cb43b5ce13 Binary files /dev/null and b/VEX/head20041019/memcheck/tests/fwrite.stdout.exp differ diff --git a/VEX/head20041019/memcheck/tests/fwrite.vgtest b/VEX/head20041019/memcheck/tests/fwrite.vgtest new file mode 100644 index 0000000000..83d2341eeb --- /dev/null +++ b/VEX/head20041019/memcheck/tests/fwrite.vgtest @@ -0,0 +1,2 @@ +prog: fwrite +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/hello.c b/VEX/head20041019/memcheck/tests/hello.c new file mode 100644 index 0000000000..836b36f2c6 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/hello.c @@ -0,0 +1,9 @@ + +#include + +int main(void) +{ + fprintf(stderr, "Hello, world!\n"); + return 0; +} + diff --git a/VEX/head20041019/memcheck/tests/inits.c b/VEX/head20041019/memcheck/tests/inits.c new file mode 100644 index 0000000000..7dd0c93c0d --- /dev/null +++ b/VEX/head20041019/memcheck/tests/inits.c @@ -0,0 +1,20 @@ + +#include + +/* Static and global vars are inited to zero, non-static local vars aren't. */ + +int g; +static int gs; + +int main(void) +{ + int l; + static int ls; + + if (gs == 0xDEADBEEF) printf("1!\n"); + if (g == 0xDEADBEEF) printf("2!\n"); + if (ls == 0xDEADBEEF) printf("3!\n"); + if (l == 0xDEADBEEF) printf("4!\n"); // complains + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/inits.stderr.exp b/VEX/head20041019/memcheck/tests/inits.stderr.exp new file mode 100644 index 0000000000..a190cfa918 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/inits.stderr.exp @@ -0,0 +1,2 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (inits.c:17) diff --git a/VEX/head20041019/memcheck/tests/inits.vgtest b/VEX/head20041019/memcheck/tests/inits.vgtest new file mode 100644 index 0000000000..6d77500c5c --- /dev/null +++ b/VEX/head20041019/memcheck/tests/inits.vgtest @@ -0,0 +1,2 @@ +prog: inits +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/inline.c b/VEX/head20041019/memcheck/tests/inline.c new file mode 100644 index 0000000000..cb023b27d1 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/inline.c @@ -0,0 +1,21 @@ + +#include +#include + +__inline__ +static int addemup ( int* arr ) +{ + int i, j = 0; + for (i = 0; i <= 10; i++) + j += arr[i]; + return j; +} + +int main ( void ) +{ + int sum; + int* a = calloc(10, sizeof(int)); + sum = addemup(a); + printf("sum is %d\n", sum); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/inline.stderr.exp b/VEX/head20041019/memcheck/tests/inline.stderr.exp new file mode 100644 index 0000000000..639b31e97f --- /dev/null +++ b/VEX/head20041019/memcheck/tests/inline.stderr.exp @@ -0,0 +1,6 @@ +Invalid read of size 4 + at 0x........: addemup (inline.c:10) + by 0x........: main (inline.c:18) + Address 0x........ is 0 bytes after a block of size 40 alloc'd + at 0x........: calloc (vg_replace_malloc.c:...) + by 0x........: main (inline.c:17) diff --git a/VEX/head20041019/memcheck/tests/inline.stdout.exp b/VEX/head20041019/memcheck/tests/inline.stdout.exp new file mode 100644 index 0000000000..ad1401ed87 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/inline.stdout.exp @@ -0,0 +1 @@ +sum is 0 diff --git a/VEX/head20041019/memcheck/tests/inline.vgtest b/VEX/head20041019/memcheck/tests/inline.vgtest new file mode 100644 index 0000000000..4f5e143673 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/inline.vgtest @@ -0,0 +1,2 @@ +prog: inline +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/insn_basic.stderr.exp b/VEX/head20041019/memcheck/tests/insn_basic.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/insn_basic.stdout.exp b/VEX/head20041019/memcheck/tests/insn_basic.stdout.exp new file mode 100644 index 0000000000..40cabbcd02 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_basic.stdout.exp @@ -0,0 +1,1083 @@ +aaa_1 ... ok +aaa_2 ... ok +aaa_3 ... ok +aaa_4 ... ok +aaa_5 ... ok +aaa_6 ... ok +aaa_7 ... ok +aaa_8 ... ok +aad_1 ... ok +aad_2 ... ok +aam_1 ... ok +aam_2 ... ok +aas_1 ... ok +aas_2 ... ok +aas_3 ... ok +aas_4 ... ok +aas_5 ... ok +aas_6 ... ok +aas_7 ... ok +aas_8 ... ok +adcb_1 ... ok +adcb_2 ... ok +adcb_3 ... ok +adcb_4 ... ok +adcb_5 ... ok +adcb_6 ... ok +adcb_7 ... ok +adcb_8 ... ok +adcb_9 ... ok +adcb_10 ... ok +adcb_11 ... ok +adcb_12 ... ok +adcw_1 ... ok +adcw_2 ... ok +adcw_3 ... ok +adcw_4 ... ok +adcw_5 ... ok +adcw_6 ... ok +adcw_7 ... ok +adcw_8 ... ok +adcw_9 ... ok +adcw_10 ... ok +adcw_11 ... ok +adcw_12 ... ok +adcw_13 ... ok +adcw_14 ... ok +adcl_1 ... ok +adcl_2 ... ok +adcl_3 ... ok +adcl_4 ... ok +adcl_5 ... ok +adcl_6 ... ok +adcl_7 ... ok +adcl_8 ... ok +adcl_9 ... ok +adcl_10 ... ok +adcl_11 ... ok +adcl_12 ... ok +adcl_13 ... ok +adcl_14 ... ok +addb_1 ... ok +addb_2 ... ok +addb_3 ... ok +addb_4 ... ok +addb_5 ... ok +addb_6 ... ok +addw_1 ... ok +addw_2 ... ok +addw_3 ... ok +addw_4 ... ok +addw_5 ... ok +addw_6 ... ok +addw_7 ... ok +addl_1 ... ok +addl_2 ... ok +addl_3 ... ok +addl_4 ... ok +addl_5 ... ok +addl_6 ... ok +addl_7 ... ok +andb_1 ... ok +andb_2 ... ok +andb_3 ... ok +andb_4 ... ok +andb_5 ... ok +andb_6 ... ok +andw_1 ... ok +andw_2 ... ok +andw_3 ... ok +andw_4 ... ok +andw_5 ... ok +andw_6 ... ok +andw_7 ... ok +andl_1 ... ok +andl_2 ... ok +andl_3 ... ok +andl_4 ... ok +andl_5 ... ok +andl_6 ... ok +andl_7 ... ok +bsfw_1 ... ok +bsfw_2 ... ok +bsfl_1 ... ok +bsfl_2 ... ok +bsrw_1 ... ok +bsrw_2 ... ok +bsrl_1 ... ok +bsrl_2 ... ok +bswapl_1 ... ok +btw_1 ... ok +btw_2 ... ok +btw_3 ... ok +btw_4 ... ok +btw_5 ... ok +btw_6 ... ok +btw_7 ... ok +btw_8 ... ok +btl_1 ... ok +btl_2 ... ok +btl_3 ... ok +btl_4 ... ok +btl_5 ... ok +btl_6 ... ok +btl_7 ... ok +btl_8 ... ok +btcw_1 ... ok +btcw_2 ... ok +btcw_3 ... ok +btcw_4 ... ok +btcw_5 ... ok +btcw_6 ... ok +btcw_7 ... ok +btcw_8 ... ok +btcl_1 ... ok +btcl_2 ... ok +btcl_3 ... ok +btcl_4 ... ok +btcl_5 ... ok +btcl_6 ... ok +btcl_7 ... ok +btcl_8 ... ok +btrw_1 ... ok +btrw_2 ... ok +btrw_3 ... ok +btrw_4 ... ok +btrw_5 ... ok +btrw_6 ... ok +btrw_7 ... ok +btrw_8 ... ok +btrl_1 ... ok +btrl_2 ... ok +btrl_3 ... ok +btrl_4 ... ok +btrl_5 ... ok +btrl_6 ... ok +btrl_7 ... ok +btrl_8 ... ok +btsw_1 ... ok +btsw_2 ... ok +btsw_3 ... ok +btsw_4 ... ok +btsw_5 ... ok +btsw_6 ... ok +btsw_7 ... ok +btsw_8 ... ok +btsl_1 ... ok +btsl_2 ... ok +btsl_3 ... ok +btsl_4 ... ok +btsl_5 ... ok +btsl_6 ... ok +btsl_7 ... ok +btsl_8 ... ok +cbw_1 ... ok +cbw_2 ... ok +cdq_1 ... ok +cdq_2 ... ok +clc_1 ... ok +clc_2 ... ok +cld_1 ... ok +cld_2 ... ok +cmc_1 ... ok +cmc_2 ... ok +cmpb_1 ... ok +cmpb_2 ... ok +cmpb_3 ... ok +cmpb_4 ... ok +cmpb_5 ... ok +cmpb_6 ... ok +cmpb_7 ... ok +cmpb_8 ... ok +cmpb_9 ... ok +cmpb_10 ... ok +cmpb_11 ... ok +cmpb_12 ... ok +cmpb_13 ... ok +cmpb_14 ... ok +cmpb_15 ... ok +cmpb_16 ... ok +cmpb_17 ... ok +cmpb_18 ... ok +cmpb_19 ... ok +cmpb_20 ... ok +cmpb_21 ... ok +cmpb_22 ... ok +cmpb_23 ... ok +cmpb_24 ... ok +cmpb_25 ... ok +cmpb_26 ... ok +cmpb_27 ... ok +cmpb_28 ... ok +cmpb_29 ... ok +cmpb_30 ... ok +cmpb_31 ... ok +cmpb_32 ... ok +cmpb_33 ... ok +cmpb_34 ... ok +cmpb_35 ... ok +cmpb_36 ... ok +cmpb_37 ... ok +cmpb_38 ... ok +cmpb_39 ... ok +cmpb_40 ... ok +cmpb_41 ... ok +cmpb_42 ... ok +cmpb_43 ... ok +cmpb_44 ... ok +cmpb_45 ... ok +cmpb_46 ... ok +cmpb_47 ... ok +cmpb_48 ... ok +cmpb_49 ... ok +cmpb_50 ... ok +cmpb_51 ... ok +cmpb_52 ... ok +cmpb_53 ... ok +cmpb_54 ... ok +cmpb_55 ... ok +cmpb_56 ... ok +cmpb_57 ... ok +cmpb_58 ... ok +cmpb_59 ... ok +cmpb_60 ... ok +cmpw_1 ... ok +cmpw_2 ... ok +cmpw_3 ... ok +cmpw_4 ... ok +cmpw_5 ... ok +cmpw_6 ... ok +cmpw_7 ... ok +cmpw_8 ... ok +cmpw_9 ... ok +cmpw_10 ... ok +cmpw_11 ... ok +cmpw_12 ... ok +cmpw_13 ... ok +cmpw_14 ... ok +cmpw_15 ... ok +cmpw_16 ... ok +cmpw_17 ... ok +cmpw_18 ... ok +cmpw_19 ... ok +cmpw_20 ... ok +cmpw_21 ... ok +cmpw_22 ... ok +cmpw_23 ... ok +cmpw_24 ... ok +cmpw_25 ... ok +cmpw_26 ... ok +cmpw_27 ... ok +cmpw_28 ... ok +cmpw_29 ... ok +cmpw_30 ... ok +cmpw_31 ... ok +cmpw_32 ... ok +cmpw_33 ... ok +cmpw_34 ... ok +cmpw_35 ... ok +cmpw_36 ... ok +cmpw_37 ... ok +cmpw_38 ... ok +cmpw_39 ... ok +cmpw_40 ... ok +cmpw_41 ... ok +cmpw_42 ... ok +cmpw_43 ... ok +cmpw_44 ... ok +cmpw_45 ... ok +cmpw_46 ... ok +cmpw_47 ... ok +cmpw_48 ... ok +cmpw_49 ... ok +cmpw_50 ... ok +cmpw_51 ... ok +cmpw_52 ... ok +cmpw_53 ... ok +cmpw_54 ... ok +cmpw_55 ... ok +cmpw_56 ... ok +cmpw_57 ... ok +cmpw_58 ... ok +cmpw_59 ... ok +cmpw_60 ... ok +cmpw_61 ... ok +cmpw_62 ... ok +cmpw_63 ... ok +cmpw_64 ... ok +cmpw_65 ... ok +cmpw_66 ... ok +cmpw_67 ... ok +cmpw_68 ... ok +cmpw_69 ... ok +cmpw_70 ... ok +cmpw_71 ... ok +cmpw_72 ... ok +cmpw_73 ... ok +cmpw_74 ... ok +cmpw_75 ... ok +cmpw_76 ... ok +cmpw_77 ... ok +cmpw_78 ... ok +cmpw_79 ... ok +cmpw_80 ... ok +cmpl_1 ... ok +cmpl_2 ... ok +cmpl_3 ... ok +cmpl_4 ... ok +cmpl_5 ... ok +cmpl_6 ... ok +cmpl_7 ... ok +cmpl_8 ... ok +cmpl_9 ... ok +cmpl_10 ... ok +cmpl_11 ... ok +cmpl_12 ... ok +cmpl_13 ... ok +cmpl_14 ... ok +cmpl_15 ... ok +cmpl_16 ... ok +cmpl_17 ... ok +cmpl_18 ... ok +cmpl_19 ... ok +cmpl_20 ... ok +cmpl_21 ... ok +cmpl_22 ... ok +cmpl_23 ... ok +cmpl_24 ... ok +cmpl_25 ... ok +cmpl_26 ... ok +cmpl_27 ... ok +cmpl_28 ... ok +cmpl_29 ... ok +cmpl_30 ... ok +cmpl_31 ... ok +cmpl_32 ... ok +cmpl_33 ... ok +cmpl_34 ... ok +cmpl_35 ... ok +cmpl_36 ... ok +cmpl_37 ... ok +cmpl_38 ... ok +cmpl_39 ... ok +cmpl_40 ... ok +cmpl_41 ... ok +cmpl_42 ... ok +cmpl_43 ... ok +cmpl_44 ... ok +cmpl_45 ... ok +cmpl_46 ... ok +cmpl_47 ... ok +cmpl_48 ... ok +cmpl_49 ... ok +cmpl_50 ... ok +cmpl_51 ... ok +cmpl_52 ... ok +cmpl_53 ... ok +cmpl_54 ... ok +cmpl_55 ... ok +cmpl_56 ... ok +cmpl_57 ... ok +cmpl_58 ... ok +cmpl_59 ... ok +cmpl_60 ... ok +cmpl_61 ... ok +cmpl_62 ... ok +cmpl_63 ... ok +cmpl_64 ... ok +cmpl_65 ... ok +cmpl_66 ... ok +cmpl_67 ... ok +cmpl_68 ... ok +cmpl_69 ... ok +cmpl_70 ... ok +cmpl_71 ... ok +cmpl_72 ... ok +cmpl_73 ... ok +cmpl_74 ... ok +cmpl_75 ... ok +cmpl_76 ... ok +cmpl_77 ... ok +cmpl_78 ... ok +cmpl_79 ... ok +cmpl_80 ... ok +cmpxchgb_1 ... ok +cmpxchgb_2 ... ok +cmpxchgb_3 ... ok +cmpxchgb_4 ... ok +cmpxchgw_1 ... ok +cmpxchgw_2 ... ok +cmpxchgw_3 ... ok +cmpxchgw_4 ... ok +cmpxchgl_1 ... ok +cmpxchgl_2 ... ok +cmpxchgl_3 ... ok +cmpxchgl_4 ... ok +cwd_1 ... ok +cwd_2 ... ok +cwde_1 ... ok +cwde_2 ... ok +daa_1 ... ok +daa_2 ... ok +das_1 ... ok +decb_1 ... ok +decb_2 ... ok +decw_1 ... ok +decw_2 ... ok +decl_1 ... ok +decl_2 ... ok +divb_1 ... ok +divb_2 ... ok +divw_1 ... ok +divw_2 ... ok +divl_1 ... ok +divl_2 ... ok +idivb_1 ... ok +idivb_2 ... ok +idivw_1 ... ok +idivw_2 ... ok +idivl_1 ... ok +idivl_2 ... ok +imulb_1 ... ok +imulb_2 ... ok +imulw_1 ... ok +imulw_2 ... ok +imull_1 ... ok +imull_2 ... ok +imulw_3 ... ok +imulw_4 ... ok +imulw_5 ... ok +imulw_6 ... ok +imulw_7 ... ok +imulw_8 ... ok +imulw_9 ... ok +imulw_10 ... ok +imull_3 ... ok +imull_4 ... ok +imull_5 ... ok +imull_6 ... ok +imull_7 ... ok +imull_8 ... ok +imull_9 ... ok +imull_10 ... ok +incb_1 ... ok +incb_2 ... ok +incw_1 ... ok +incw_2 ... ok +incl_1 ... ok +incl_2 ... ok +lahf_1 ... ok +lahf_2 ... ok +movb_1 ... ok +movb_2 ... ok +movb_3 ... ok +movb_4 ... ok +movb_5 ... ok +movw_1 ... ok +movw_2 ... ok +movw_3 ... ok +movw_4 ... ok +movw_5 ... ok +movl_1 ... ok +movl_2 ... ok +movl_3 ... ok +movl_4 ... ok +movl_5 ... ok +movsbw_1 ... ok +movsbw_2 ... ok +movsbl_1 ... ok +movsbl_2 ... ok +movswl_1 ... ok +movswl_2 ... ok +movzbw_1 ... ok +movzbw_2 ... ok +movzbl_1 ... ok +movzbl_2 ... ok +movzwl_1 ... ok +movzwl_2 ... ok +mulb_1 ... ok +mulb_2 ... ok +mulw_1 ... ok +mulw_2 ... ok +mull_1 ... ok +mull_2 ... ok +negb_1 ... ok +negb_2 ... ok +negw_1 ... ok +negw_2 ... ok +negl_1 ... ok +negl_2 ... ok +notb_1 ... ok +notb_2 ... ok +notw_1 ... ok +notw_2 ... ok +notl_1 ... ok +notl_2 ... ok +orb_1 ... ok +orb_2 ... ok +orb_3 ... ok +orb_4 ... ok +orb_5 ... ok +orb_6 ... ok +orw_1 ... ok +orw_2 ... ok +orw_3 ... ok +orw_4 ... ok +orw_5 ... ok +orw_6 ... ok +orw_7 ... ok +orl_1 ... ok +orl_2 ... ok +orl_3 ... ok +orl_4 ... ok +orl_5 ... ok +orl_6 ... ok +orl_7 ... ok +rclb_1 ... ok +rclb_2 ... ok +rclb_3 ... ok +rclb_4 ... ok +rclb_5 ... ok +rclb_6 ... ok +rclw_1 ... ok +rclw_2 ... ok +rclw_3 ... ok +rclw_4 ... ok +rclw_5 ... ok +rclw_6 ... ok +rcll_1 ... ok +rcll_2 ... ok +rcll_3 ... ok +rcll_4 ... ok +rcll_5 ... ok +rcll_6 ... ok +rcrb_1 ... ok +rcrb_2 ... ok +rcrb_3 ... ok +rcrb_4 ... ok +rcrb_5 ... ok +rcrb_6 ... ok +rcrw_1 ... ok +rcrw_2 ... ok +rcrw_3 ... ok +rcrw_4 ... ok +rcrw_5 ... ok +rcrw_6 ... ok +rcrl_1 ... ok +rcrl_2 ... ok +rcrl_3 ... ok +rcrl_4 ... ok +rcrl_5 ... ok +rcrl_6 ... ok +rolb_1 ... ok +rolb_2 ... ok +rolb_3 ... ok +rolb_4 ... ok +rolb_5 ... ok +rolb_6 ... ok +rolw_1 ... ok +rolw_2 ... ok +rolw_3 ... ok +rolw_4 ... ok +rolw_5 ... ok +rolw_6 ... ok +roll_1 ... ok +roll_2 ... ok +roll_3 ... ok +roll_4 ... ok +roll_5 ... ok +roll_6 ... ok +rorb_1 ... ok +rorb_2 ... ok +rorb_3 ... ok +rorb_4 ... ok +rorb_5 ... ok +rorb_6 ... ok +rorw_1 ... ok +rorw_2 ... ok +rorw_3 ... ok +rorw_4 ... ok +rorw_5 ... ok +rorw_6 ... ok +rorl_1 ... ok +rorl_2 ... ok +rorl_3 ... ok +rorl_4 ... ok +rorl_5 ... ok +rorl_6 ... ok +sahf_1 ... ok +sahf_2 ... ok +salb_1 ... ok +salb_2 ... ok +salb_3 ... ok +salb_4 ... ok +salb_5 ... ok +salb_6 ... ok +salw_1 ... ok +salw_2 ... ok +salw_3 ... ok +salw_4 ... ok +salw_5 ... ok +salw_6 ... ok +sall_1 ... ok +sall_2 ... ok +sall_3 ... ok +sall_4 ... ok +sall_5 ... ok +sall_6 ... ok +sarb_1 ... ok +sarb_2 ... ok +sarb_3 ... ok +sarb_4 ... ok +sarb_5 ... ok +sarb_6 ... ok +sarw_1 ... ok +sarw_2 ... ok +sarw_3 ... ok +sarw_4 ... ok +sarw_5 ... ok +sarw_6 ... ok +sarl_1 ... ok +sarl_2 ... ok +sarl_3 ... ok +sarl_4 ... ok +sarl_5 ... ok +sarl_6 ... ok +sbbb_1 ... ok +sbbb_2 ... ok +sbbb_3 ... ok +sbbb_4 ... ok +sbbb_5 ... ok +sbbb_6 ... ok +sbbb_7 ... ok +sbbb_8 ... ok +sbbb_9 ... ok +sbbb_10 ... ok +sbbb_11 ... ok +sbbb_12 ... ok +sbbw_1 ... ok +sbbw_2 ... ok +sbbw_3 ... ok +sbbw_4 ... ok +sbbw_5 ... ok +sbbw_6 ... ok +sbbw_7 ... ok +sbbw_8 ... ok +sbbw_9 ... ok +sbbw_10 ... ok +sbbw_11 ... ok +sbbw_12 ... ok +sbbw_13 ... ok +sbbw_14 ... ok +sbbl_1 ... ok +sbbl_2 ... ok +sbbl_3 ... ok +sbbl_4 ... ok +sbbl_5 ... ok +sbbl_6 ... ok +sbbl_7 ... ok +sbbl_8 ... ok +sbbl_9 ... ok +sbbl_10 ... ok +sbbl_11 ... ok +sbbl_12 ... ok +sbbl_13 ... ok +sbbl_14 ... ok +seta_1 ... ok +seta_2 ... ok +seta_3 ... ok +seta_4 ... ok +seta_5 ... ok +seta_6 ... ok +seta_7 ... ok +seta_8 ... ok +setae_1 ... ok +setae_2 ... ok +setae_3 ... ok +setae_4 ... ok +setb_1 ... ok +setb_2 ... ok +setb_3 ... ok +setb_4 ... ok +setbe_1 ... ok +setbe_2 ... ok +setbe_3 ... ok +setbe_4 ... ok +setbe_5 ... ok +setbe_6 ... ok +setbe_7 ... ok +setbe_8 ... ok +setc_1 ... ok +setc_2 ... ok +setc_3 ... ok +setc_4 ... ok +sete_1 ... ok +sete_2 ... ok +sete_3 ... ok +sete_4 ... ok +setg_1 ... ok +setg_2 ... ok +setg_3 ... ok +setg_4 ... ok +setg_5 ... ok +setg_6 ... ok +setg_7 ... ok +setg_8 ... ok +setg_9 ... ok +setg_10 ... ok +setg_11 ... ok +setg_12 ... ok +setg_13 ... ok +setg_14 ... ok +setg_15 ... ok +setg_16 ... ok +setge_1 ... ok +setge_2 ... ok +setge_3 ... ok +setge_4 ... ok +setge_5 ... ok +setge_6 ... ok +setge_7 ... ok +setge_8 ... ok +setl_1 ... ok +setl_2 ... ok +setl_3 ... ok +setl_4 ... ok +setl_5 ... ok +setl_6 ... ok +setl_7 ... ok +setl_8 ... ok +setle_1 ... ok +setle_2 ... ok +setle_3 ... ok +setle_4 ... ok +setle_5 ... ok +setle_6 ... ok +setle_7 ... ok +setle_8 ... ok +setle_9 ... ok +setle_10 ... ok +setle_11 ... ok +setle_12 ... ok +setle_13 ... ok +setle_14 ... ok +setle_15 ... ok +setle_16 ... ok +setna_1 ... ok +setna_2 ... ok +setna_3 ... ok +setna_4 ... ok +setna_5 ... ok +setna_6 ... ok +setna_7 ... ok +setna_8 ... ok +setnae_1 ... ok +setnae_2 ... ok +setnae_3 ... ok +setnae_4 ... ok +setnb_1 ... ok +setnb_2 ... ok +setnb_3 ... ok +setnb_4 ... ok +setnbe_1 ... ok +setnbe_2 ... ok +setnbe_3 ... ok +setnbe_4 ... ok +setnbe_5 ... ok +setnbe_6 ... ok +setnbe_7 ... ok +setnbe_8 ... ok +setnc_1 ... ok +setnc_2 ... ok +setnc_3 ... ok +setnc_4 ... ok +setne_1 ... ok +setne_2 ... ok +setne_3 ... ok +setne_4 ... ok +setng_1 ... ok +setng_2 ... ok +setng_3 ... ok +setng_4 ... ok +setng_5 ... ok +setng_6 ... ok +setng_7 ... ok +setng_8 ... ok +setng_9 ... ok +setng_10 ... ok +setng_11 ... ok +setng_12 ... ok +setng_13 ... ok +setng_14 ... ok +setng_15 ... ok +setng_16 ... ok +setnge_1 ... ok +setnge_2 ... ok +setnge_3 ... ok +setnge_4 ... ok +setnge_5 ... ok +setnge_6 ... ok +setnge_7 ... ok +setnge_8 ... ok +setnl_1 ... ok +setnl_2 ... ok +setnl_3 ... ok +setnl_4 ... ok +setnl_5 ... ok +setnl_6 ... ok +setnl_7 ... ok +setnl_8 ... ok +setnle_1 ... ok +setnle_2 ... ok +setnle_3 ... ok +setnle_4 ... ok +setnle_5 ... ok +setnle_6 ... ok +setnle_7 ... ok +setnle_8 ... ok +setnle_9 ... ok +setnle_10 ... ok +setnle_11 ... ok +setnle_12 ... ok +setnle_13 ... ok +setnle_14 ... ok +setnle_15 ... ok +setnle_16 ... ok +setno_1 ... ok +setno_2 ... ok +setno_3 ... ok +setno_4 ... ok +setnp_1 ... ok +setnp_2 ... ok +setnp_3 ... ok +setnp_4 ... ok +setns_1 ... ok +setns_2 ... ok +setns_3 ... ok +setns_4 ... ok +setnz_1 ... ok +setnz_2 ... ok +setnz_3 ... ok +setnz_4 ... ok +seto_1 ... ok +seto_2 ... ok +seto_3 ... ok +seto_4 ... ok +setp_1 ... ok +setp_2 ... ok +setp_3 ... ok +setp_4 ... ok +sets_1 ... ok +sets_2 ... ok +sets_3 ... ok +sets_4 ... ok +setz_1 ... ok +setz_2 ... ok +setz_3 ... ok +setz_4 ... ok +shlb_1 ... ok +shlb_2 ... ok +shlb_3 ... ok +shlb_4 ... ok +shlb_5 ... ok +shlb_6 ... ok +shlw_1 ... ok +shlw_2 ... ok +shlw_3 ... ok +shlw_4 ... ok +shlw_5 ... ok +shlw_6 ... ok +shll_1 ... ok +shll_2 ... ok +shll_3 ... ok +shll_4 ... ok +shll_5 ... ok +shll_6 ... ok +shrb_1 ... ok +shrb_2 ... ok +shrb_3 ... ok +shrb_4 ... ok +shrb_5 ... ok +shrb_6 ... ok +shrw_1 ... ok +shrw_2 ... ok +shrw_3 ... ok +shrw_4 ... ok +shrw_5 ... ok +shrw_6 ... ok +shrl_1 ... ok +shrl_2 ... ok +shrl_3 ... ok +shrl_4 ... ok +shrl_5 ... ok +shrl_6 ... ok +shldw_1 ... ok +shldw_2 ... ok +shldw_3 ... ok +shldw_4 ... ok +shldw_5 ... ok +shldw_6 ... ok +shldw_7 ... ok +shldw_8 ... ok +shldl_1 ... ok +shldl_2 ... ok +shldl_3 ... ok +shldl_4 ... ok +shldl_5 ... ok +shldl_6 ... ok +shldl_7 ... ok +shldl_8 ... ok +shrdw_1 ... ok +shrdw_2 ... ok +shrdw_3 ... ok +shrdw_4 ... ok +shrdw_5 ... ok +shrdw_6 ... ok +shrdw_7 ... ok +shrdw_8 ... ok +shrdl_1 ... ok +shrdl_2 ... ok +shrdl_3 ... ok +shrdl_4 ... ok +shrdl_5 ... ok +shrdl_6 ... ok +shrdl_7 ... ok +shrdl_8 ... ok +stc_1 ... ok +stc_2 ... ok +std_1 ... ok +std_2 ... ok +subb_1 ... ok +subb_2 ... ok +subb_3 ... ok +subb_4 ... ok +subb_5 ... ok +subb_6 ... ok +subw_1 ... ok +subw_2 ... ok +subw_3 ... ok +subw_4 ... ok +subw_5 ... ok +subw_6 ... ok +subw_7 ... ok +subl_1 ... ok +subl_2 ... ok +subl_3 ... ok +subl_4 ... ok +subl_5 ... ok +subl_6 ... ok +subl_7 ... ok +testb_1 ... ok +testb_2 ... ok +testb_3 ... ok +testb_4 ... ok +testb_5 ... ok +testb_6 ... ok +testb_7 ... ok +testb_8 ... ok +testb_9 ... ok +testb_10 ... ok +testb_11 ... ok +testb_12 ... ok +testb_13 ... ok +testb_14 ... ok +testb_15 ... ok +testb_16 ... ok +testb_17 ... ok +testb_18 ... ok +testb_19 ... ok +testb_20 ... ok +testb_21 ... ok +testb_22 ... ok +testb_23 ... ok +testb_24 ... ok +testb_25 ... ok +testw_1 ... ok +testw_2 ... ok +testw_3 ... ok +testw_4 ... ok +testw_5 ... ok +testw_6 ... ok +testw_7 ... ok +testw_8 ... ok +testw_9 ... ok +testw_10 ... ok +testw_11 ... ok +testw_12 ... ok +testw_13 ... ok +testw_14 ... ok +testw_15 ... ok +testw_16 ... ok +testw_17 ... ok +testw_18 ... ok +testw_19 ... ok +testw_20 ... ok +testw_21 ... ok +testw_22 ... ok +testw_23 ... ok +testw_24 ... ok +testw_25 ... ok +testl_1 ... ok +testl_2 ... ok +testl_3 ... ok +testl_4 ... ok +testl_5 ... ok +testl_6 ... ok +testl_7 ... ok +testl_8 ... ok +testl_9 ... ok +testl_10 ... ok +testl_11 ... ok +testl_12 ... ok +testl_13 ... ok +testl_14 ... ok +testl_15 ... ok +testl_16 ... ok +testl_17 ... ok +testl_18 ... ok +testl_19 ... ok +testl_20 ... ok +testl_21 ... ok +testl_22 ... ok +testl_23 ... ok +testl_24 ... ok +testl_25 ... ok +xaddb_1 ... ok +xaddb_2 ... ok +xaddw_1 ... ok +xaddw_2 ... ok +xaddl_1 ... ok +xaddl_2 ... ok +xchgb_1 ... ok +xchgb_2 ... ok +xchgb_3 ... ok +xchgw_1 ... ok +xchgw_2 ... ok +xchgw_3 ... ok +xchgw_4 ... ok +xchgw_5 ... ok +xchgl_1 ... ok +xchgl_2 ... ok +xchgl_3 ... ok +xchgl_4 ... ok +xchgl_5 ... ok +xorb_1 ... ok +xorb_2 ... ok +xorb_3 ... ok +xorb_4 ... ok +xorb_5 ... ok +xorb_6 ... ok +xorw_1 ... ok +xorw_2 ... ok +xorw_3 ... ok +xorw_4 ... ok +xorw_5 ... ok +xorw_6 ... ok +xorw_7 ... ok +xorl_1 ... ok +xorl_2 ... ok +xorl_3 ... ok +xorl_4 ... ok +xorl_5 ... ok +xorl_6 ... ok +xorl_7 ... ok diff --git a/VEX/head20041019/memcheck/tests/insn_basic.vgtest b/VEX/head20041019/memcheck/tests/insn_basic.vgtest new file mode 100644 index 0000000000..f5329ea810 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_basic.vgtest @@ -0,0 +1,2 @@ +vgopts: -q +prog: ../../none/tests/insn_basic diff --git a/VEX/head20041019/memcheck/tests/insn_cmov.stderr.exp b/VEX/head20041019/memcheck/tests/insn_cmov.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/insn_cmov.stdout.exp b/VEX/head20041019/memcheck/tests/insn_cmov.stdout.exp new file mode 100644 index 0000000000..31ac17204d --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_cmov.stdout.exp @@ -0,0 +1,384 @@ +cmova_1 ... ok +cmova_2 ... ok +cmova_3 ... ok +cmova_4 ... ok +cmova_5 ... ok +cmova_6 ... ok +cmova_7 ... ok +cmova_8 ... ok +cmovae_1 ... ok +cmovae_2 ... ok +cmovae_3 ... ok +cmovae_4 ... ok +cmovb_1 ... ok +cmovb_2 ... ok +cmovb_3 ... ok +cmovb_4 ... ok +cmovbe_1 ... ok +cmovbe_2 ... ok +cmovbe_3 ... ok +cmovbe_4 ... ok +cmovbe_5 ... ok +cmovbe_6 ... ok +cmovbe_7 ... ok +cmovbe_8 ... ok +cmovc_1 ... ok +cmovc_2 ... ok +cmovc_3 ... ok +cmovc_4 ... ok +cmove_1 ... ok +cmove_2 ... ok +cmove_3 ... ok +cmove_4 ... ok +cmovg_1 ... ok +cmovg_2 ... ok +cmovg_3 ... ok +cmovg_4 ... ok +cmovg_5 ... ok +cmovg_6 ... ok +cmovg_7 ... ok +cmovg_8 ... ok +cmovg_9 ... ok +cmovg_10 ... ok +cmovg_11 ... ok +cmovg_12 ... ok +cmovg_13 ... ok +cmovg_14 ... ok +cmovg_15 ... ok +cmovg_16 ... ok +cmovge_1 ... ok +cmovge_2 ... ok +cmovge_3 ... ok +cmovge_4 ... ok +cmovge_5 ... ok +cmovge_6 ... ok +cmovge_7 ... ok +cmovge_8 ... ok +cmovl_1 ... ok +cmovl_2 ... ok +cmovl_3 ... ok +cmovl_4 ... ok +cmovl_5 ... ok +cmovl_6 ... ok +cmovl_7 ... ok +cmovl_8 ... ok +cmovle_1 ... ok +cmovle_2 ... ok +cmovle_3 ... ok +cmovle_4 ... ok +cmovle_5 ... ok +cmovle_6 ... ok +cmovle_7 ... ok +cmovle_8 ... ok +cmovle_9 ... ok +cmovle_10 ... ok +cmovle_11 ... ok +cmovle_12 ... ok +cmovle_13 ... ok +cmovle_14 ... ok +cmovle_15 ... ok +cmovle_16 ... ok +cmovna_1 ... ok +cmovna_2 ... ok +cmovna_3 ... ok +cmovna_4 ... ok +cmovna_5 ... ok +cmovna_6 ... ok +cmovna_7 ... ok +cmovna_8 ... ok +cmovnae_1 ... ok +cmovnae_2 ... ok +cmovnae_3 ... ok +cmovnae_4 ... ok +cmovnb_1 ... ok +cmovnb_2 ... ok +cmovnb_3 ... ok +cmovnb_4 ... ok +cmovnbe_1 ... ok +cmovnbe_2 ... ok +cmovnbe_3 ... ok +cmovnbe_4 ... ok +cmovnbe_5 ... ok +cmovnbe_6 ... ok +cmovnbe_7 ... ok +cmovnbe_8 ... ok +cmovnc_1 ... ok +cmovnc_2 ... ok +cmovnc_3 ... ok +cmovnc_4 ... ok +cmovne_1 ... ok +cmovne_2 ... ok +cmovne_3 ... ok +cmovne_4 ... ok +cmovng_1 ... ok +cmovng_2 ... ok +cmovng_3 ... ok +cmovng_4 ... ok +cmovng_5 ... ok +cmovng_6 ... ok +cmovng_7 ... ok +cmovng_8 ... ok +cmovng_9 ... ok +cmovng_10 ... ok +cmovng_11 ... ok +cmovng_12 ... ok +cmovng_13 ... ok +cmovng_14 ... ok +cmovng_15 ... ok +cmovng_16 ... ok +cmovnge_1 ... ok +cmovnge_2 ... ok +cmovnge_3 ... ok +cmovnge_4 ... ok +cmovnge_5 ... ok +cmovnge_6 ... ok +cmovnge_7 ... ok +cmovnge_8 ... ok +cmovnl_1 ... ok +cmovnl_2 ... ok +cmovnl_3 ... ok +cmovnl_4 ... ok +cmovnl_5 ... ok +cmovnl_6 ... ok +cmovnl_7 ... ok +cmovnl_8 ... ok +cmovnle_1 ... ok +cmovnle_2 ... ok +cmovnle_3 ... ok +cmovnle_4 ... ok +cmovnle_5 ... ok +cmovnle_6 ... ok +cmovnle_7 ... ok +cmovnle_8 ... ok +cmovnle_9 ... ok +cmovnle_10 ... ok +cmovnle_11 ... ok +cmovnle_12 ... ok +cmovnle_13 ... ok +cmovnle_14 ... ok +cmovnle_15 ... ok +cmovnle_16 ... ok +cmovno_1 ... ok +cmovno_2 ... ok +cmovno_3 ... ok +cmovno_4 ... ok +cmovnp_1 ... ok +cmovnp_2 ... ok +cmovnp_3 ... ok +cmovnp_4 ... ok +cmovns_1 ... ok +cmovns_2 ... ok +cmovns_3 ... ok +cmovns_4 ... ok +cmovnz_1 ... ok +cmovnz_2 ... ok +cmovnz_3 ... ok +cmovnz_4 ... ok +cmovo_1 ... ok +cmovo_2 ... ok +cmovo_3 ... ok +cmovo_4 ... ok +cmovp_1 ... ok +cmovp_2 ... ok +cmovp_3 ... ok +cmovp_4 ... ok +cmovs_1 ... ok +cmovs_2 ... ok +cmovs_3 ... ok +cmovs_4 ... ok +cmovz_1 ... ok +cmovz_2 ... ok +cmovz_3 ... ok +cmovz_4 ... ok +cmova_9 ... ok +cmova_10 ... ok +cmova_11 ... ok +cmova_12 ... ok +cmova_13 ... ok +cmova_14 ... ok +cmova_15 ... ok +cmova_16 ... ok +cmovae_5 ... ok +cmovae_6 ... ok +cmovae_7 ... ok +cmovae_8 ... ok +cmovb_5 ... ok +cmovb_6 ... ok +cmovb_7 ... ok +cmovb_8 ... ok +cmovbe_9 ... ok +cmovbe_10 ... ok +cmovbe_11 ... ok +cmovbe_12 ... ok +cmovbe_13 ... ok +cmovbe_14 ... ok +cmovbe_15 ... ok +cmovbe_16 ... ok +cmovc_5 ... ok +cmovc_6 ... ok +cmovc_7 ... ok +cmovc_8 ... ok +cmove_5 ... ok +cmove_6 ... ok +cmove_7 ... ok +cmove_8 ... ok +cmovg_17 ... ok +cmovg_18 ... ok +cmovg_19 ... ok +cmovg_20 ... ok +cmovg_21 ... ok +cmovg_22 ... ok +cmovg_23 ... ok +cmovg_24 ... ok +cmovg_25 ... ok +cmovg_26 ... ok +cmovg_27 ... ok +cmovg_28 ... ok +cmovg_29 ... ok +cmovg_30 ... ok +cmovg_31 ... ok +cmovg_32 ... ok +cmovge_9 ... ok +cmovge_10 ... ok +cmovge_11 ... ok +cmovge_12 ... ok +cmovge_13 ... ok +cmovge_14 ... ok +cmovge_15 ... ok +cmovge_16 ... ok +cmovl_9 ... ok +cmovl_10 ... ok +cmovl_11 ... ok +cmovl_12 ... ok +cmovl_13 ... ok +cmovl_14 ... ok +cmovl_15 ... ok +cmovl_16 ... ok +cmovle_17 ... ok +cmovle_18 ... ok +cmovle_19 ... ok +cmovle_20 ... ok +cmovle_21 ... ok +cmovle_22 ... ok +cmovle_23 ... ok +cmovle_24 ... ok +cmovle_25 ... ok +cmovle_26 ... ok +cmovle_27 ... ok +cmovle_28 ... ok +cmovle_29 ... ok +cmovle_30 ... ok +cmovle_31 ... ok +cmovle_32 ... ok +cmovna_9 ... ok +cmovna_10 ... ok +cmovna_11 ... ok +cmovna_12 ... ok +cmovna_13 ... ok +cmovna_14 ... ok +cmovna_15 ... ok +cmovna_16 ... ok +cmovnae_5 ... ok +cmovnae_6 ... ok +cmovnae_7 ... ok +cmovnae_8 ... ok +cmovnb_5 ... ok +cmovnb_6 ... ok +cmovnb_7 ... ok +cmovnb_8 ... ok +cmovnbe_9 ... ok +cmovnbe_10 ... ok +cmovnbe_11 ... ok +cmovnbe_12 ... ok +cmovnbe_13 ... ok +cmovnbe_14 ... ok +cmovnbe_15 ... ok +cmovnbe_16 ... ok +cmovnc_5 ... ok +cmovnc_6 ... ok +cmovnc_7 ... ok +cmovnc_8 ... ok +cmovne_5 ... ok +cmovne_6 ... ok +cmovne_7 ... ok +cmovne_8 ... ok +cmovng_17 ... ok +cmovng_18 ... ok +cmovng_19 ... ok +cmovng_20 ... ok +cmovng_21 ... ok +cmovng_22 ... ok +cmovng_23 ... ok +cmovng_24 ... ok +cmovng_25 ... ok +cmovng_26 ... ok +cmovng_27 ... ok +cmovng_28 ... ok +cmovng_29 ... ok +cmovng_30 ... ok +cmovng_31 ... ok +cmovng_32 ... ok +cmovnge_9 ... ok +cmovnge_10 ... ok +cmovnge_11 ... ok +cmovnge_12 ... ok +cmovnge_13 ... ok +cmovnge_14 ... ok +cmovnge_15 ... ok +cmovnge_16 ... ok +cmovnl_9 ... ok +cmovnl_10 ... ok +cmovnl_11 ... ok +cmovnl_12 ... ok +cmovnl_13 ... ok +cmovnl_14 ... ok +cmovnl_15 ... ok +cmovnl_16 ... ok +cmovnle_17 ... ok +cmovnle_18 ... ok +cmovnle_19 ... ok +cmovnle_20 ... ok +cmovnle_21 ... ok +cmovnle_22 ... ok +cmovnle_23 ... ok +cmovnle_24 ... ok +cmovnle_25 ... ok +cmovnle_26 ... ok +cmovnle_27 ... ok +cmovnle_28 ... ok +cmovnle_29 ... ok +cmovnle_30 ... ok +cmovnle_31 ... ok +cmovnle_32 ... ok +cmovno_5 ... ok +cmovno_6 ... ok +cmovno_7 ... ok +cmovno_8 ... ok +cmovnp_5 ... ok +cmovnp_6 ... ok +cmovnp_7 ... ok +cmovnp_8 ... ok +cmovns_5 ... ok +cmovns_6 ... ok +cmovns_7 ... ok +cmovns_8 ... ok +cmovnz_5 ... ok +cmovnz_6 ... ok +cmovnz_7 ... ok +cmovnz_8 ... ok +cmovo_5 ... ok +cmovo_6 ... ok +cmovo_7 ... ok +cmovo_8 ... ok +cmovp_5 ... ok +cmovp_6 ... ok +cmovp_7 ... ok +cmovp_8 ... ok +cmovs_5 ... ok +cmovs_6 ... ok +cmovs_7 ... ok +cmovs_8 ... ok +cmovz_5 ... ok +cmovz_6 ... ok +cmovz_7 ... ok +cmovz_8 ... ok diff --git a/VEX/head20041019/memcheck/tests/insn_cmov.vgtest b/VEX/head20041019/memcheck/tests/insn_cmov.vgtest new file mode 100644 index 0000000000..0321a3ca84 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_cmov.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_cmov +cpu_test: cmov diff --git a/VEX/head20041019/memcheck/tests/insn_fpu.stderr.exp b/VEX/head20041019/memcheck/tests/insn_fpu.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/insn_fpu.stdout.exp b/VEX/head20041019/memcheck/tests/insn_fpu.stdout.exp new file mode 100644 index 0000000000..2dbaa07ce0 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_fpu.stdout.exp @@ -0,0 +1,452 @@ +fabs_1 ... ok +fabs_2 ... ok +fabs_3 ... ok +fabs_4 ... ok +fadds_1 ... ok +fadds_2 ... ok +fadds_3 ... ok +fadds_4 ... ok +faddl_1 ... ok +faddl_2 ... ok +faddl_3 ... ok +faddl_4 ... ok +fadd_1 ... ok +fadd_2 ... ok +fadd_3 ... ok +fadd_4 ... ok +fadd_5 ... ok +fadd_6 ... ok +fadd_7 ... ok +fadd_8 ... ok +fadd_9 ... ok +fadd_10 ... ok +fadd_11 ... ok +fadd_12 ... ok +fadd_13 ... ok +fadd_14 ... ok +fadd_15 ... ok +fadd_16 ... ok +faddp_1 ... ok +faddp_2 ... ok +faddp_3 ... ok +faddp_4 ... ok +faddp_5 ... ok +faddp_6 ... ok +faddp_7 ... ok +faddp_8 ... ok +faddp_9 ... ok +faddp_10 ... ok +faddp_11 ... ok +faddp_12 ... ok +faddp_13 ... ok +faddp_14 ... ok +faddp_15 ... ok +faddp_16 ... ok +fiadds_1 ... ok +fiadds_2 ... ok +fiadds_3 ... ok +fiadds_4 ... ok +fiadds_5 ... ok +fiadds_6 ... ok +fiadds_7 ... ok +fiadds_8 ... ok +fiaddl_1 ... ok +fiaddl_2 ... ok +fiaddl_3 ... ok +fiaddl_4 ... ok +fiaddl_5 ... ok +fiaddl_6 ... ok +fiaddl_7 ... ok +fiaddl_8 ... ok +fcomi_1 ... ok +fcomi_2 ... ok +fcomi_3 ... ok +fcomi_4 ... ok +fcomi_5 ... ok +fcomi_6 ... ok +fcomip_1 ... ok +fcomip_2 ... ok +fcomip_3 ... ok +fcomip_4 ... ok +fcomip_5 ... ok +fcomip_6 ... ok +fucomi_1 ... ok +fucomi_2 ... ok +fucomi_3 ... ok +fucomi_4 ... ok +fucomi_5 ... ok +fucomi_6 ... ok +fucomip_1 ... ok +fucomip_2 ... ok +fucomip_3 ... ok +fucomip_4 ... ok +fucomip_5 ... ok +fucomip_6 ... ok +fchs_1 ... ok +fchs_2 ... ok +fchs_3 ... ok +fchs_4 ... ok +fdivs_1 ... ok +fdivs_2 ... ok +fdivs_3 ... ok +fdivs_4 ... ok +fdivl_1 ... ok +fdivl_2 ... ok +fdivl_3 ... ok +fdivl_4 ... ok +fdiv_1 ... ok +fdiv_2 ... ok +fdiv_3 ... ok +fdiv_4 ... ok +fdiv_5 ... ok +fdiv_6 ... ok +fdiv_7 ... ok +fdiv_8 ... ok +fdiv_9 ... ok +fdiv_10 ... ok +fdiv_11 ... ok +fdiv_12 ... ok +fdiv_13 ... ok +fdiv_14 ... ok +fdiv_15 ... ok +fdiv_16 ... ok +fdivp_1 ... ok +fdivp_2 ... ok +fdivp_3 ... ok +fdivp_4 ... ok +fdivp_5 ... ok +fdivp_6 ... ok +fdivp_7 ... ok +fdivp_8 ... ok +fdivp_9 ... ok +fdivp_10 ... ok +fdivp_11 ... ok +fdivp_12 ... ok +fdivp_13 ... ok +fdivp_14 ... ok +fdivp_15 ... ok +fdivp_16 ... ok +fidivs_1 ... ok +fidivs_2 ... ok +fidivs_3 ... ok +fidivs_4 ... ok +fidivs_5 ... ok +fidivs_6 ... ok +fidivs_7 ... ok +fidivs_8 ... ok +fidivl_1 ... ok +fidivl_2 ... ok +fidivl_3 ... ok +fidivl_4 ... ok +fidivl_5 ... ok +fidivl_6 ... ok +fidivl_7 ... ok +fidivl_8 ... ok +fdivrs_1 ... ok +fdivrs_2 ... ok +fdivrs_3 ... ok +fdivrs_4 ... ok +fdivrl_1 ... ok +fdivrl_2 ... ok +fdivrl_3 ... ok +fdivrl_4 ... ok +fdivr_1 ... ok +fdivr_2 ... ok +fdivr_3 ... ok +fdivr_4 ... ok +fdivr_5 ... ok +fdivr_6 ... ok +fdivr_7 ... ok +fdivr_8 ... ok +fdivr_9 ... ok +fdivr_10 ... ok +fdivr_11 ... ok +fdivr_12 ... ok +fdivr_13 ... ok +fdivr_14 ... ok +fdivr_15 ... ok +fdivr_16 ... ok +fdivrp_1 ... ok +fdivrp_2 ... ok +fdivrp_3 ... ok +fdivrp_4 ... ok +fdivrp_5 ... ok +fdivrp_6 ... ok +fdivrp_7 ... ok +fdivrp_8 ... ok +fdivrp_9 ... ok +fdivrp_10 ... ok +fdivrp_11 ... ok +fdivrp_12 ... ok +fdivrp_13 ... ok +fdivrp_14 ... ok +fdivrp_15 ... ok +fdivrp_16 ... ok +fidivrs_1 ... ok +fidivrs_2 ... ok +fidivrs_3 ... ok +fidivrs_4 ... ok +fidivrs_5 ... ok +fidivrs_6 ... ok +fidivrs_7 ... ok +fidivrs_8 ... ok +fidivrl_1 ... ok +fidivrl_2 ... ok +fidivrl_3 ... ok +fidivrl_4 ... ok +fidivrl_5 ... ok +fidivrl_6 ... ok +fidivrl_7 ... ok +fidivrl_8 ... ok +filds_1 ... ok +filds_2 ... ok +filds_3 ... ok +filds_4 ... ok +fildl_1 ... ok +fildl_2 ... ok +fildl_3 ... ok +fildl_4 ... ok +fildq_1 ... ok +fildq_2 ... ok +fildq_3 ... ok +fildq_4 ... ok +fists_1 ... ok +fists_2 ... ok +fists_3 ... ok +fists_4 ... ok +fists_5 ... ok +fists_6 ... ok +fists_7 ... ok +fists_8 ... ok +fistl_1 ... ok +fistl_2 ... ok +fistl_3 ... ok +fistl_4 ... ok +fistl_5 ... ok +fistl_6 ... ok +fistl_7 ... ok +fistl_8 ... ok +fistps_1 ... ok +fistps_2 ... ok +fistps_3 ... ok +fistps_4 ... ok +fistps_5 ... ok +fistps_6 ... ok +fistps_7 ... ok +fistps_8 ... ok +fistpl_1 ... ok +fistpl_2 ... ok +fistpl_3 ... ok +fistpl_4 ... ok +fistpl_5 ... ok +fistpl_6 ... ok +fistpl_7 ... ok +fistpl_8 ... ok +fistpq_1 ... ok +fistpq_2 ... ok +fistpq_3 ... ok +fistpq_4 ... ok +fistpq_5 ... ok +fistpq_6 ... ok +fistpq_7 ... ok +fistpq_8 ... ok +flds_1 ... ok +flds_2 ... ok +fldl_1 ... ok +fldl_2 ... ok +fld_1 ... ok +fld_2 ... ok +fld_3 ... ok +fld1_1 ... ok +fldl2t_1 ... ok +fldl2e_1 ... ok +fldpi_1 ... ok +fldlg2_1 ... ok +fldln2_1 ... ok +fldz_1 ... ok +fmuls_1 ... ok +fmuls_2 ... ok +fmuls_3 ... ok +fmuls_4 ... ok +fmull_1 ... ok +fmull_2 ... ok +fmull_3 ... ok +fmull_4 ... ok +fmul_1 ... ok +fmul_2 ... ok +fmul_3 ... ok +fmul_4 ... ok +fmul_5 ... ok +fmul_6 ... ok +fmul_7 ... ok +fmul_8 ... ok +fmul_9 ... ok +fmul_10 ... ok +fmul_11 ... ok +fmul_12 ... ok +fmul_13 ... ok +fmul_14 ... ok +fmul_15 ... ok +fmul_16 ... ok +fmulp_1 ... ok +fmulp_2 ... ok +fmulp_3 ... ok +fmulp_4 ... ok +fmulp_5 ... ok +fmulp_6 ... ok +fmulp_7 ... ok +fmulp_8 ... ok +fmulp_9 ... ok +fmulp_10 ... ok +fmulp_11 ... ok +fmulp_12 ... ok +fmulp_13 ... ok +fmulp_14 ... ok +fmulp_15 ... ok +fmulp_16 ... ok +fimuls_1 ... ok +fimuls_2 ... ok +fimuls_3 ... ok +fimuls_4 ... ok +fimuls_5 ... ok +fimuls_6 ... ok +fimuls_7 ... ok +fimuls_8 ... ok +fimull_1 ... ok +fimull_2 ... ok +fimull_3 ... ok +fimull_4 ... ok +fimull_5 ... ok +fimull_6 ... ok +fimull_7 ... ok +fimull_8 ... ok +frndint_1 ... ok +frndint_2 ... ok +frndint_3 ... ok +frndint_4 ... ok +frndint_5 ... ok +frndint_6 ... ok +frndint_7 ... ok +frndint_8 ... ok +frndint_9 ... ok +frndint_10 ... ok +frndint_11 ... ok +frndint_12 ... ok +frndint_13 ... ok +frndint_14 ... ok +frndint_15 ... ok +frndint_16 ... ok +fsubs_1 ... ok +fsubs_2 ... ok +fsubs_3 ... ok +fsubs_4 ... ok +fsubl_1 ... ok +fsubl_2 ... ok +fsubl_3 ... ok +fsubl_4 ... ok +fsub_1 ... ok +fsub_2 ... ok +fsub_3 ... ok +fsub_4 ... ok +fsub_5 ... ok +fsub_6 ... ok +fsub_7 ... ok +fsub_8 ... ok +fsub_9 ... ok +fsub_10 ... ok +fsub_11 ... ok +fsub_12 ... ok +fsub_13 ... ok +fsub_14 ... ok +fsub_15 ... ok +fsub_16 ... ok +fsubp_1 ... ok +fsubp_2 ... ok +fsubp_3 ... ok +fsubp_4 ... ok +fsubp_5 ... ok +fsubp_6 ... ok +fsubp_7 ... ok +fsubp_8 ... ok +fsubp_9 ... ok +fsubp_10 ... ok +fsubp_11 ... ok +fsubp_12 ... ok +fsubp_13 ... ok +fsubp_14 ... ok +fsubp_15 ... ok +fsubp_16 ... ok +fisubs_1 ... ok +fisubs_2 ... ok +fisubs_3 ... ok +fisubs_4 ... ok +fisubs_5 ... ok +fisubs_6 ... ok +fisubs_7 ... ok +fisubs_8 ... ok +fisubl_1 ... ok +fisubl_2 ... ok +fisubl_3 ... ok +fisubl_4 ... ok +fisubl_5 ... ok +fisubl_6 ... ok +fisubl_7 ... ok +fisubl_8 ... ok +fsubrs_1 ... ok +fsubrs_2 ... ok +fsubrs_3 ... ok +fsubrs_4 ... ok +fsubrl_1 ... ok +fsubrl_2 ... ok +fsubrl_3 ... ok +fsubrl_4 ... ok +fsubr_1 ... ok +fsubr_2 ... ok +fsubr_3 ... ok +fsubr_4 ... ok +fsubr_5 ... ok +fsubr_6 ... ok +fsubr_7 ... ok +fsubr_8 ... ok +fsubr_9 ... ok +fsubr_10 ... ok +fsubr_11 ... ok +fsubr_12 ... ok +fsubr_13 ... ok +fsubr_14 ... ok +fsubr_15 ... ok +fsubr_16 ... ok +fsubrp_1 ... ok +fsubrp_2 ... ok +fsubrp_3 ... ok +fsubrp_4 ... ok +fsubrp_5 ... ok +fsubrp_6 ... ok +fsubrp_7 ... ok +fsubrp_8 ... ok +fsubrp_9 ... ok +fsubrp_10 ... ok +fsubrp_11 ... ok +fsubrp_12 ... ok +fsubrp_13 ... ok +fsubrp_14 ... ok +fsubrp_15 ... ok +fsubrp_16 ... ok +fisubrs_1 ... ok +fisubrs_2 ... ok +fisubrs_3 ... ok +fisubrs_4 ... ok +fisubrs_5 ... ok +fisubrs_6 ... ok +fisubrs_7 ... ok +fisubrs_8 ... ok +fisubrl_1 ... ok +fisubrl_2 ... ok +fisubrl_3 ... ok +fisubrl_4 ... ok +fisubrl_5 ... ok +fisubrl_6 ... ok +fisubrl_7 ... ok +fisubrl_8 ... ok +fxch_1 ... ok +fxch_2 ... ok diff --git a/VEX/head20041019/memcheck/tests/insn_fpu.vgtest b/VEX/head20041019/memcheck/tests/insn_fpu.vgtest new file mode 100644 index 0000000000..1b9546f54a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_fpu.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_fpu +cpu_test: fpu diff --git a/VEX/head20041019/memcheck/tests/insn_mmx.stderr.exp b/VEX/head20041019/memcheck/tests/insn_mmx.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/insn_mmx.stdout.exp b/VEX/head20041019/memcheck/tests/insn_mmx.stdout.exp new file mode 100644 index 0000000000..95cbae1603 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_mmx.stdout.exp @@ -0,0 +1,103 @@ +movd_1 ... ok +movd_2 ... ok +movd_3 ... ok +movd_4 ... ok +movq_1 ... ok +movq_2 ... ok +movq_3 ... ok +packssdw_1 ... ok +packssdw_2 ... ok +packsswb_1 ... ok +packsswb_2 ... ok +packuswb_1 ... ok +packuswb_2 ... ok +paddb_1 ... ok +paddb_2 ... ok +paddd_1 ... ok +paddd_2 ... ok +paddsb_1 ... ok +paddsb_2 ... ok +paddsw_1 ... ok +paddsw_2 ... ok +paddusb_1 ... ok +paddusb_2 ... ok +paddusw_1 ... ok +paddusw_2 ... ok +paddw_1 ... ok +paddw_2 ... ok +pand_1 ... ok +pand_2 ... ok +pandn_1 ... ok +pandn_2 ... ok +pcmpeqb_1 ... ok +pcmpeqb_2 ... ok +pcmpeqd_1 ... ok +pcmpeqd_2 ... ok +pcmpeqw_1 ... ok +pcmpeqw_2 ... ok +pcmpgtb_1 ... ok +pcmpgtb_2 ... ok +pcmpgtd_1 ... ok +pcmpgtd_2 ... ok +pcmpgtw_1 ... ok +pcmpgtw_2 ... ok +pmaddwd_1 ... ok +pmaddwd_2 ... ok +pmulhw_1 ... ok +pmulhw_2 ... ok +pmullw_1 ... ok +pmullw_2 ... ok +por_1 ... ok +por_2 ... ok +pslld_1 ... ok +pslld_2 ... ok +pslld_3 ... ok +psllq_1 ... ok +psllq_2 ... ok +psllq_3 ... ok +psllw_1 ... ok +psllw_2 ... ok +psllw_3 ... ok +psrad_1 ... ok +psrad_2 ... ok +psrad_3 ... ok +psraw_1 ... ok +psraw_2 ... ok +psraw_3 ... ok +psrld_1 ... ok +psrld_2 ... ok +psrld_3 ... ok +psrlq_1 ... ok +psrlq_2 ... ok +psrlq_3 ... ok +psrlw_1 ... ok +psrlw_2 ... ok +psrlw_3 ... ok +psubb_1 ... ok +psubb_2 ... ok +psubd_1 ... ok +psubd_2 ... ok +psubsb_1 ... ok +psubsb_2 ... ok +psubsw_1 ... ok +psubsw_2 ... ok +psubusb_1 ... ok +psubusb_2 ... ok +psubusw_1 ... ok +psubusw_2 ... ok +psubw_1 ... ok +psubw_2 ... ok +punpckhbw_1 ... ok +punpckhbw_2 ... ok +punpckhdq_1 ... ok +punpckhdq_2 ... ok +punpckhwd_1 ... ok +punpckhwd_2 ... ok +punpcklbw_1 ... ok +punpcklbw_2 ... ok +punpckldq_1 ... ok +punpckldq_2 ... ok +punpcklwd_1 ... ok +punpcklwd_2 ... ok +pxor_1 ... ok +pxor_2 ... ok diff --git a/VEX/head20041019/memcheck/tests/insn_mmx.vgtest b/VEX/head20041019/memcheck/tests/insn_mmx.vgtest new file mode 100644 index 0000000000..ddbb977263 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_mmx.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_mmx +cpu_test: mmx diff --git a/VEX/head20041019/memcheck/tests/insn_mmxext.stderr.exp b/VEX/head20041019/memcheck/tests/insn_mmxext.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/insn_mmxext.stdout.exp b/VEX/head20041019/memcheck/tests/insn_mmxext.stdout.exp new file mode 100644 index 0000000000..23b2e55abd --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_mmxext.stdout.exp @@ -0,0 +1,29 @@ +movntq_1 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufw_1 ... ok +pshufw_2 ... ok +sfence_1 ... ok diff --git a/VEX/head20041019/memcheck/tests/insn_mmxext.vgtest b/VEX/head20041019/memcheck/tests/insn_mmxext.vgtest new file mode 100644 index 0000000000..bb667097fb --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_mmxext.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_mmxext +cpu_test: mmxext diff --git a/VEX/head20041019/memcheck/tests/insn_sse.stderr.exp b/VEX/head20041019/memcheck/tests/insn_sse.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/insn_sse.stdout.exp b/VEX/head20041019/memcheck/tests/insn_sse.stdout.exp new file mode 100644 index 0000000000..f15bd81f0b --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_sse.stdout.exp @@ -0,0 +1,142 @@ +addps_1 ... ok +addps_2 ... ok +addss_1 ... ok +addss_2 ... ok +andnps_1 ... ok +andnps_2 ... ok +andps_1 ... ok +andps_2 ... ok +cmpeqps_1 ... ok +cmpeqps_2 ... ok +cmpeqss_1 ... ok +cmpeqss_2 ... ok +cmpleps_1 ... ok +cmpleps_2 ... ok +cmpless_1 ... ok +cmpless_2 ... ok +cmpltps_1 ... ok +cmpltps_2 ... ok +cmpltss_1 ... ok +cmpltss_2 ... ok +cmpneqps_1 ... ok +cmpneqps_2 ... ok +cmpneqss_1 ... ok +cmpneqss_2 ... ok +cmpnleps_1 ... ok +cmpnleps_2 ... ok +cmpnless_1 ... ok +cmpnless_2 ... ok +cmpnltps_1 ... ok +cmpnltps_2 ... ok +cmpnltss_1 ... ok +cmpnltss_2 ... ok +comiss_1 ... ok +comiss_2 ... ok +comiss_3 ... ok +comiss_4 ... ok +comiss_5 ... ok +comiss_6 ... ok +cvtpi2ps_1 ... ok +cvtpi2ps_2 ... ok +cvtps2pi_1 ... ok +cvtps2pi_2 ... ok +cvtsi2ss_1 ... ok +cvtsi2ss_2 ... ok +cvtss2si_1 ... ok +cvtss2si_2 ... ok +cvttps2pi_1 ... ok +cvttps2pi_2 ... ok +cvttss2si_1 ... ok +cvttss2si_2 ... ok +divps_1 ... ok +divps_2 ... ok +divss_1 ... ok +divss_2 ... ok +maxps_1 ... ok +maxps_2 ... ok +maxss_1 ... ok +maxss_2 ... ok +minps_1 ... ok +minps_2 ... ok +minss_1 ... ok +minss_2 ... ok +movaps_1 ... ok +movaps_2 ... ok +movhlps_1 ... ok +movhps_1 ... ok +movhps_2 ... ok +movlhps_1 ... ok +movlps_1 ... ok +movlps_2 ... ok +movmskps_1 ... ok +movntps_1 ... ok +movntq_1 ... ok +movss_1 ... ok +movss_2 ... ok +movss_3 ... ok +movups_1 ... ok +movups_2 ... ok +mulps_1 ... ok +mulps_2 ... ok +mulss_1 ... ok +mulss_2 ... ok +orps_1 ... ok +orps_2 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufw_1 ... ok +pshufw_2 ... ok +rcpps_1 ... ok +rcpps_2 ... ok +rcpss_1 ... ok +rcpss_2 ... ok +rsqrtps_1 ... ok +rsqrtps_2 ... ok +rsqrtss_1 ... ok +rsqrtss_2 ... ok +sfence_1 ... ok +shufps_1 ... ok +shufps_2 ... ok +sqrtps_1 ... ok +sqrtps_2 ... ok +sqrtss_1 ... ok +sqrtss_2 ... ok +subps_1 ... ok +subps_2 ... ok +subss_1 ... ok +subss_2 ... ok +ucomiss_1 ... ok +ucomiss_2 ... ok +ucomiss_3 ... ok +ucomiss_4 ... ok +ucomiss_5 ... ok +ucomiss_6 ... ok +unpckhps_1 ... ok +unpckhps_2 ... ok +unpcklps_1 ... ok +unpcklps_2 ... ok +xorps_1 ... ok +xorps_2 ... ok diff --git a/VEX/head20041019/memcheck/tests/insn_sse.vgtest b/VEX/head20041019/memcheck/tests/insn_sse.vgtest new file mode 100644 index 0000000000..167c8e290b --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_sse.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_sse +cpu_test: sse diff --git a/VEX/head20041019/memcheck/tests/insn_sse2.stderr.exp b/VEX/head20041019/memcheck/tests/insn_sse2.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/insn_sse2.stdout.exp b/VEX/head20041019/memcheck/tests/insn_sse2.stdout.exp new file mode 100644 index 0000000000..9c24f72621 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_sse2.stdout.exp @@ -0,0 +1,294 @@ +addpd_1 ... ok +addpd_2 ... ok +addsd_1 ... ok +addsd_2 ... ok +andpd_1 ... ok +andpd_2 ... ok +andnpd_1 ... ok +andnpd_2 ... ok +cmpeqpd_1 ... ok +cmpeqpd_2 ... ok +cmpltpd_1 ... ok +cmpltpd_2 ... ok +cmplepd_1 ... ok +cmplepd_2 ... ok +cmpneqpd_1 ... ok +cmpneqpd_2 ... ok +cmpnltpd_1 ... ok +cmpnltpd_2 ... ok +cmpnlepd_1 ... ok +cmpnlepd_2 ... ok +cmpeqsd_1 ... ok +cmpeqsd_2 ... ok +cmpltsd_1 ... ok +cmpltsd_2 ... ok +cmplesd_1 ... ok +cmplesd_2 ... ok +cmpneqsd_1 ... ok +cmpneqsd_2 ... ok +cmpnltsd_1 ... ok +cmpnltsd_2 ... ok +cmpnlesd_1 ... ok +cmpnlesd_2 ... ok +comisd_1 ... ok +comisd_2 ... ok +comisd_3 ... ok +comisd_4 ... ok +comisd_5 ... ok +comisd_6 ... ok +cvtdq2pd_1 ... ok +cvtdq2pd_2 ... ok +cvtdq2ps_1 ... ok +cvtdq2ps_2 ... ok +cvtpd2dq_1 ... ok +cvtpd2dq_2 ... ok +cvtpd2pi_1 ... ok +cvtpd2pi_2 ... ok +cvtpd2ps_1 ... ok +cvtpd2ps_2 ... ok +cvtpi2pd_1 ... ok +cvtpi2pd_2 ... ok +cvtps2dq_1 ... ok +cvtps2dq_2 ... ok +cvtps2pd_1 ... ok +cvtps2pd_2 ... ok +cvtsd2si_1 ... ok +cvtsd2si_2 ... ok +cvtsd2ss_1 ... ok +cvtsd2ss_2 ... ok +cvtsi2sd_1 ... ok +cvtsi2sd_2 ... ok +cvtss2sd_1 ... ok +cvtss2sd_2 ... ok +cvttpd2pi_1 ... ok +cvttpd2pi_2 ... ok +cvttpd2dq_1 ... ok +cvttpd2dq_2 ... ok +cvttps2dq_1 ... ok +cvttps2dq_2 ... ok +cvttsd2si_1 ... ok +cvttsd2si_2 ... ok +divpd_1 ... ok +divpd_2 ... ok +divsd_1 ... ok +divsd_2 ... ok +lfence_1 ... ok +maxpd_1 ... ok +maxpd_2 ... ok +maxsd_1 ... ok +maxsd_2 ... ok +mfence_1 ... ok +minpd_1 ... ok +minpd_2 ... ok +minsd_1 ... ok +minsd_2 ... ok +movapd_1 ... ok +movapd_2 ... ok +movd_1 ... ok +movd_2 ... ok +movd_3 ... ok +movd_4 ... ok +movdqa_1 ... ok +movdqa_2 ... ok +movdqa_3 ... ok +movdqu_1 ... ok +movdqu_2 ... ok +movdqu_3 ... ok +movdq2q_1 ... ok +movhpd_1 ... ok +movhpd_2 ... ok +movlpd_1 ... ok +movlpd_2 ... ok +movmskpd_1 ... ok +movntdq_1 ... ok +movnti_1 ... ok +movntpd_1 ... ok +movq2dq_1 ... ok +movsd_1 ... ok +movsd_2 ... ok +movsd_3 ... ok +movupd_1 ... ok +movupd_2 ... ok +mulpd_1 ... ok +mulpd_2 ... ok +mulsd_1 ... ok +mulsd_2 ... ok +orpd_1 ... ok +orpd_2 ... ok +packssdw_1 ... ok +packssdw_2 ... ok +packsswb_1 ... ok +packsswb_2 ... ok +packuswb_1 ... ok +packuswb_2 ... ok +paddb_1 ... ok +paddb_2 ... ok +paddd_1 ... ok +paddd_2 ... ok +paddq_1 ... ok +paddq_2 ... ok +paddq_3 ... ok +paddq_4 ... ok +paddsb_1 ... ok +paddsb_2 ... ok +paddsw_1 ... ok +paddsw_2 ... ok +paddusb_1 ... ok +paddusb_2 ... ok +paddusw_1 ... ok +paddusw_2 ... ok +paddw_1 ... ok +paddw_2 ... ok +pand_1 ... ok +pand_2 ... ok +pandn_1 ... ok +pandn_2 ... ok +pavgb_1 ... ok +pavgb_2 ... ok +pavgw_1 ... ok +pavgw_2 ... ok +pcmpeqb_1 ... ok +pcmpeqb_2 ... ok +pcmpeqd_1 ... ok +pcmpeqd_2 ... ok +pcmpeqw_1 ... ok +pcmpeqw_2 ... ok +pcmpgtb_1 ... ok +pcmpgtb_2 ... ok +pcmpgtd_1 ... ok +pcmpgtd_2 ... ok +pcmpgtw_1 ... ok +pcmpgtw_2 ... ok +pextrw_1 ... ok +pextrw_2 ... ok +pextrw_3 ... ok +pextrw_4 ... ok +pextrw_5 ... ok +pextrw_6 ... ok +pextrw_7 ... ok +pextrw_8 ... ok +pinsrw_1 ... ok +pinsrw_2 ... ok +pinsrw_3 ... ok +pinsrw_4 ... ok +pinsrw_5 ... ok +pinsrw_6 ... ok +pinsrw_7 ... ok +pinsrw_8 ... ok +pmaddwd_1 ... ok +pmaddwd_2 ... ok +pmaxsw_1 ... ok +pmaxsw_2 ... ok +pmaxub_1 ... ok +pmaxub_2 ... ok +pminsw_1 ... ok +pminsw_2 ... ok +pminub_1 ... ok +pminub_2 ... ok +pmovmskb_1 ... ok +pmulhuw_1 ... ok +pmulhuw_2 ... ok +pmulhw_1 ... ok +pmulhw_2 ... ok +pmullw_1 ... ok +pmullw_2 ... ok +pmuludq_1 ... ok +pmuludq_2 ... ok +pmuludq_3 ... ok +pmuludq_4 ... ok +por_1 ... ok +por_2 ... ok +psadbw_1 ... ok +psadbw_2 ... ok +pshufd_1 ... ok +pshufd_2 ... ok +pshufhw_1 ... ok +pshufhw_2 ... ok +pshuflw_1 ... ok +pshuflw_2 ... ok +pslld_1 ... ok +pslld_2 ... ok +pslld_3 ... ok +pslldq_1 ... ok +pslldq_2 ... ok +psllq_1 ... ok +psllq_2 ... ok +psllq_3 ... ok +psllw_1 ... ok +psllw_2 ... ok +psllw_3 ... ok +psrad_1 ... ok +psrad_2 ... ok +psrad_3 ... ok +psraw_1 ... ok +psraw_2 ... ok +psraw_3 ... ok +psrld_1 ... ok +psrld_2 ... ok +psrld_3 ... ok +psrldq_1 ... ok +psrldq_2 ... ok +psrlq_1 ... ok +psrlq_2 ... ok +psrlq_3 ... ok +psrlw_1 ... ok +psrlw_2 ... ok +psrlw_3 ... ok +psubb_1 ... ok +psubb_2 ... ok +psubd_1 ... ok +psubd_2 ... ok +psubq_1 ... ok +psubq_2 ... ok +psubq_3 ... ok +psubq_4 ... ok +psubsb_1 ... ok +psubsb_2 ... ok +psubsw_1 ... ok +psubsw_2 ... ok +psubusb_1 ... ok +psubusb_2 ... ok +psubusw_1 ... ok +psubusw_2 ... ok +psubw_1 ... ok +psubw_2 ... ok +punpckhbw_1 ... ok +punpckhbw_2 ... ok +punpckhdq_1 ... ok +punpckhdq_2 ... ok +punpckhqdq_1 ... ok +punpckhqdq_2 ... ok +punpckhwd_1 ... ok +punpckhwd_2 ... ok +punpcklbw_1 ... ok +punpcklbw_2 ... ok +punpckldq_1 ... ok +punpckldq_2 ... ok +punpcklqdq_1 ... ok +punpcklqdq_2 ... ok +punpcklwd_1 ... ok +punpcklwd_2 ... ok +pxor_1 ... ok +pxor_2 ... ok +shufpd_1 ... ok +shufpd_2 ... ok +sqrtpd_1 ... ok +sqrtpd_2 ... ok +sqrtsd_1 ... ok +sqrtsd_2 ... ok +subpd_1 ... ok +subpd_2 ... ok +subsd_1 ... ok +subsd_2 ... ok +ucomisd_1 ... ok +ucomisd_2 ... ok +ucomisd_3 ... ok +ucomisd_4 ... ok +ucomisd_5 ... ok +ucomisd_6 ... ok +unpckhpd_1 ... ok +unpckhpd_2 ... ok +unpcklpd_1 ... ok +unpcklpd_2 ... ok +xorpd_1 ... ok +xorpd_2 ... ok diff --git a/VEX/head20041019/memcheck/tests/insn_sse2.vgtest b/VEX/head20041019/memcheck/tests/insn_sse2.vgtest new file mode 100644 index 0000000000..42e82f38d3 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/insn_sse2.vgtest @@ -0,0 +1,3 @@ +vgopts: -q +prog: ../../none/tests/insn_sse2 +cpu_test: sse2 diff --git a/VEX/head20041019/memcheck/tests/malloc1.c b/VEX/head20041019/memcheck/tests/malloc1.c new file mode 100644 index 0000000000..dff5250eb4 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc1.c @@ -0,0 +1,24 @@ + +#include +#include + +void really ( void ); + +int main ( void ) +{ + really(); + return 0; +} + +void really ( void ) +{ + int i; + char* p = malloc(10); + for (i = 0; i < 10; i++) + p[i] = 'z'; + free(p); + p[1] = 'z'; + p = malloc(10); + p[2] = 'z'; + p[-1] = 'z'; +} diff --git a/VEX/head20041019/memcheck/tests/malloc1.stderr.exp b/VEX/head20041019/memcheck/tests/malloc1.stderr.exp new file mode 100644 index 0000000000..9739e92c0a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc1.stderr.exp @@ -0,0 +1,15 @@ +Invalid write of size 1 + at 0x........: really (malloc1.c:20) + by 0x........: main (malloc1.c:9) + Address 0x........ is 1 bytes inside a block of size 10 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: really (malloc1.c:19) + by 0x........: main (malloc1.c:9) + +Invalid write of size 1 + at 0x........: really (malloc1.c:23) + by 0x........: main (malloc1.c:9) + Address 0x........ is 1 bytes before a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: really (malloc1.c:21) + by 0x........: main (malloc1.c:9) diff --git a/VEX/head20041019/memcheck/tests/malloc1.vgtest b/VEX/head20041019/memcheck/tests/malloc1.vgtest new file mode 100644 index 0000000000..5849aa77f4 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc1.vgtest @@ -0,0 +1,2 @@ +prog: malloc1 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/malloc2.c b/VEX/head20041019/memcheck/tests/malloc2.c new file mode 100644 index 0000000000..44cc7bb3d6 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc2.c @@ -0,0 +1,49 @@ + +#include +#include + +/* The original test driver machinery. */ +#define N_TEST_TRANSACTIONS 500 +#define N_TEST_ARR 2000 + +#define M_TEST_MALLOC 1000 + +void* test_arr[N_TEST_ARR]; + +int main ( int argc, char** argv ) +{ + int i, j, k, nbytes; + unsigned char* chp; + + for (i = 0; i < N_TEST_ARR; i++) + test_arr[i] = NULL; + + for (i = 0; i < N_TEST_TRANSACTIONS; i++) { + j = random() % N_TEST_ARR; + if (test_arr[j]) { + free(test_arr[j]); + test_arr[j] = NULL; + } else { + nbytes = 1 + random() % M_TEST_MALLOC; + if (random()%64 == 32) + nbytes *= 17; + test_arr[j] = malloc( nbytes ); + chp = test_arr[j]; + for (k = 1; k < nbytes; k++) + chp[k] = (unsigned char)(k + 99); + } + } + + for (i = 0; test_arr[i] == NULL; i++) ; + free(test_arr[i]); + ((char*)test_arr[i])[0] = 0; + + for (i = 0; i < N_TEST_ARR; i++) { + if (test_arr[i]) { + free(test_arr[i]); + test_arr[i] = NULL; + } + } + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/malloc2.stderr.exp b/VEX/head20041019/memcheck/tests/malloc2.stderr.exp new file mode 100644 index 0000000000..29ad572510 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc2.stderr.exp @@ -0,0 +1,12 @@ +Invalid write of size 1 + at 0x........: main (malloc2.c:39) + Address 0x........ is 0 bytes inside a block of size 429 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (malloc2.c:38) + +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (malloc2.c:43) + Address 0x........ is 0 bytes inside a block of size 429 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (malloc2.c:38) diff --git a/VEX/head20041019/memcheck/tests/malloc2.vgtest b/VEX/head20041019/memcheck/tests/malloc2.vgtest new file mode 100644 index 0000000000..4191dae503 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc2.vgtest @@ -0,0 +1,2 @@ +prog: malloc2 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/malloc3.c b/VEX/head20041019/memcheck/tests/malloc3.c new file mode 100644 index 0000000000..896645c3eb --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc3.c @@ -0,0 +1,32 @@ + +/* test of plausible behaviour with malloc and stupid args */ + +#include +#include + +int main ( void ) +{ + char* p; + + p = malloc(0); + printf("malloc(0) = %p\n", p); + free(p); + + p = malloc(-1); + printf("malloc(-1) = %p\n", p); + free(p); + + p = calloc(0,1); + printf("calloc(0,1) = %p\n", p); + free(p); + + p = calloc(0,-1); + printf("calloc(0,-1) = %p\n", p); + free(p); + + p = calloc(-1,-1); + printf("calloc(-1,-1) = %p\n", p); + free(p); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/malloc3.stderr.exp b/VEX/head20041019/memcheck/tests/malloc3.stderr.exp new file mode 100644 index 0000000000..30a6968a39 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc3.stderr.exp @@ -0,0 +1,3 @@ +Warning: silly arg (-1) to malloc() +Warning: silly args (0,-1) to calloc() +Warning: silly args (-1,-1) to calloc() diff --git a/VEX/head20041019/memcheck/tests/malloc3.stdout.exp b/VEX/head20041019/memcheck/tests/malloc3.stdout.exp new file mode 100644 index 0000000000..681c9ece22 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc3.stdout.exp @@ -0,0 +1,5 @@ +malloc(0) = 0x........ +malloc(-1) = (nil) +calloc(0,1) = 0x........ +calloc(0,-1) = (nil) +calloc(-1,-1) = (nil) diff --git a/VEX/head20041019/memcheck/tests/malloc3.vgtest b/VEX/head20041019/memcheck/tests/malloc3.vgtest new file mode 100644 index 0000000000..417cbedca9 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/malloc3.vgtest @@ -0,0 +1,3 @@ +prog: malloc3 +stdout_filter: ../../tests/filter_addresses +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/manuel1.c b/VEX/head20041019/memcheck/tests/manuel1.c new file mode 100644 index 0000000000..ac1f3c89ec --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel1.c @@ -0,0 +1,10 @@ +#include + +int main () +{ + int x; + + printf ("x = %d\n", x==0xDEADBEEF ? 99 : 88); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/manuel1.stderr.exp b/VEX/head20041019/memcheck/tests/manuel1.stderr.exp new file mode 100644 index 0000000000..34f0a93787 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel1.stderr.exp @@ -0,0 +1,2 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (manuel1.c:7) diff --git a/VEX/head20041019/memcheck/tests/manuel1.stdout.exp b/VEX/head20041019/memcheck/tests/manuel1.stdout.exp new file mode 100644 index 0000000000..d26cbc9687 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel1.stdout.exp @@ -0,0 +1 @@ +x = 88 diff --git a/VEX/head20041019/memcheck/tests/manuel1.vgtest b/VEX/head20041019/memcheck/tests/manuel1.vgtest new file mode 100644 index 0000000000..853f8dfdbc --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel1.vgtest @@ -0,0 +1,2 @@ +prog: manuel1 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/manuel2.c b/VEX/head20041019/memcheck/tests/manuel2.c new file mode 100644 index 0000000000..8c6f85b713 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel2.c @@ -0,0 +1,11 @@ +#include +#include + +int main () +{ + int *x; + + printf ("x = %d\n", *x==0xDEADBEEF ? 99 : 88); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/manuel2.stderr.exp b/VEX/head20041019/memcheck/tests/manuel2.stderr.exp new file mode 100644 index 0000000000..2d66c1dc77 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel2.stderr.exp @@ -0,0 +1,2 @@ +Use of uninitialised value of size 4 + at 0x........: main (manuel2.c:8) diff --git a/VEX/head20041019/memcheck/tests/manuel2.stdout.exp b/VEX/head20041019/memcheck/tests/manuel2.stdout.exp new file mode 100644 index 0000000000..d26cbc9687 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel2.stdout.exp @@ -0,0 +1 @@ +x = 88 diff --git a/VEX/head20041019/memcheck/tests/manuel2.vgtest b/VEX/head20041019/memcheck/tests/manuel2.vgtest new file mode 100644 index 0000000000..b3729b1663 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel2.vgtest @@ -0,0 +1,2 @@ +prog: manuel2 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/manuel3.c b/VEX/head20041019/memcheck/tests/manuel3.c new file mode 100644 index 0000000000..91030fc65d --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel3.c @@ -0,0 +1,28 @@ +#include +#include + +int gcc_cant_inline_me ( int ); + +int main () +{ + int *x, y; + + x = (int *) malloc (sizeof (int)); + + y = *x == 173; + + if (gcc_cant_inline_me(y)) { } + + return 0; +} + +/* must be AFTER main */ +int gcc_cant_inline_me ( int n ) +{ + if (n == 42) + return 1; /* forty-two, dudes! */ + else + return 0; /* some other number, dudes! */ +} + + diff --git a/VEX/head20041019/memcheck/tests/manuel3.stderr.exp b/VEX/head20041019/memcheck/tests/manuel3.stderr.exp new file mode 100644 index 0000000000..2f56776ea3 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel3.stderr.exp @@ -0,0 +1,3 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: gcc_cant_inline_me (manuel3.c:22) + by 0x........: main (manuel3.c:14) diff --git a/VEX/head20041019/memcheck/tests/manuel3.vgtest b/VEX/head20041019/memcheck/tests/manuel3.vgtest new file mode 100644 index 0000000000..14e5f24f34 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/manuel3.vgtest @@ -0,0 +1,2 @@ +prog: manuel3 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/memalign2.c b/VEX/head20041019/memcheck/tests/memalign2.c new file mode 100644 index 0000000000..0da0b4f32a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memalign2.c @@ -0,0 +1,69 @@ + +// These #defines attempt to ensure that posix_memalign() is declared, and +// so no spurious warning is given about using it. + +// Advertise compliance of the code to the XSI (a POSIX superset that +// defines what a system must be like to be called "UNIX") +#undef _XOPEN_SOURCE +#define _XOPEN_SOURCE 600 + +// Advertise compliance to POSIX +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L + +#include +#include +#include +#include +#include + +int main ( void ) +{ + // Nb: assuming VG_MIN_MALLOC_SZB is 8! + // Should work with both 32-bit and 64-bit pointers, though. + + int* p; + int res; + assert(sizeof(long int) == sizeof(void*)); + + p = memalign(0, 100); assert(0 == (long)p % 8); + p = memalign(1, 100); assert(0 == (long)p % 8); + p = memalign(2, 100); assert(0 == (long)p % 8); + p = memalign(3, 100); assert(0 == (long)p % 8); + p = memalign(4, 100); assert(0 == (long)p % 8); + p = memalign(5, 100); assert(0 == (long)p % 8); + + p = memalign(7, 100); assert(0 == (long)p % 8); + p = memalign(8, 100); assert(0 == (long)p % 8); + p = memalign(9, 100); assert(0 == (long)p % 16); + + p = memalign(31, 100); assert(0 == (long)p % 32); + p = memalign(32, 100); assert(0 == (long)p % 32); + p = memalign(33, 100); assert(0 == (long)p % 64); + + p = memalign(4095, 100); assert(0 == (long)p % 4096); + p = memalign(4096, 100); assert(0 == (long)p % 4096); + p = memalign(4097, 100); assert(0 == (long)p % 8192); + + #define PM(a,b,c) posix_memalign((void**)a, b, c) + + res = PM(&p, -1,100); assert(EINVAL == res); + res = PM(&p, 0, 100); assert(0 == res && 0 == (long)p % 8); + res = PM(&p, 1, 100); assert(EINVAL == res); + res = PM(&p, 2, 100); assert(EINVAL == res); + res = PM(&p, 3, 100); assert(EINVAL == res); + res = PM(&p, sizeof(void*), 100); + assert(0 == res && 0 == (long)p % sizeof(void*)); + + res = PM(&p, 31, 100); assert(EINVAL == res); + res = PM(&p, 32, 100); assert(0 == res && + 0 == (long)p % 32); + res = PM(&p, 33, 100); assert(EINVAL == res); + + res = PM(&p, 4095, 100); assert(EINVAL == res); + res = PM(&p, 4096, 100); assert(0 == res && + 0 == (long)p % 4096); + res = PM(&p, 4097, 100); assert(EINVAL == res); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/memalign2.stderr.exp b/VEX/head20041019/memcheck/tests/memalign2.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/memalign2.vgtest b/VEX/head20041019/memcheck/tests/memalign2.vgtest new file mode 100644 index 0000000000..f15cb63206 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memalign2.vgtest @@ -0,0 +1,2 @@ +prog: memalign2 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/memalign_test.c b/VEX/head20041019/memcheck/tests/memalign_test.c new file mode 100644 index 0000000000..a24808c55a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memalign_test.c @@ -0,0 +1,19 @@ + +#include +#include + +int main ( void ) +{ + void* a[10]; + int i; + for (i = 0; i < 10; i++) { + a[i] = valloc(11111 * (i+1)); + // printf("I acquire %p\n", a[i]); + } + for (i = 0; i < 10; i++) { + // printf("I release %p\n", a[i]); + free(a[i]); + } + free(a[9]); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/memalign_test.stderr.exp b/VEX/head20041019/memcheck/tests/memalign_test.stderr.exp new file mode 100644 index 0000000000..a43369c6fb --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memalign_test.stderr.exp @@ -0,0 +1,6 @@ +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (memalign_test.c:17) + Address 0x........ is 0 bytes inside a block of size 111110 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (memalign_test.c:15) diff --git a/VEX/head20041019/memcheck/tests/memalign_test.vgtest b/VEX/head20041019/memcheck/tests/memalign_test.vgtest new file mode 100644 index 0000000000..097ceddab6 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memalign_test.vgtest @@ -0,0 +1,2 @@ +prog: memalign_test +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/memcmptest.c b/VEX/head20041019/memcheck/tests/memcmptest.c new file mode 100644 index 0000000000..83eb2d4662 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memcmptest.c @@ -0,0 +1,20 @@ + +#include +#include +#include + +char* s1; +char* s2; + +int main ( void ) +{ + s1 = malloc(10); strcpy(s1,"fooble"); + s2 = malloc(10); strcpy(s2,"fooble"); + if (memcmp(s1, s2, 8) != 0) + printf("different\n"); + else + printf("same (?!)\n"); + return 0; +} + + diff --git a/VEX/head20041019/memcheck/tests/memcmptest.stderr.exp b/VEX/head20041019/memcheck/tests/memcmptest.stderr.exp new file mode 100644 index 0000000000..71367f932b --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memcmptest.stderr.exp @@ -0,0 +1,3 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: memcmp (mac_replace_strmem.c:...) + by 0x........: main (memcmptest.c:13) diff --git a/VEX/head20041019/memcheck/tests/memcmptest.stdout.exp b/VEX/head20041019/memcheck/tests/memcmptest.stdout.exp new file mode 100644 index 0000000000..7164804762 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memcmptest.stdout.exp @@ -0,0 +1 @@ +same (?!) diff --git a/VEX/head20041019/memcheck/tests/memcmptest.vgtest b/VEX/head20041019/memcheck/tests/memcmptest.vgtest new file mode 100644 index 0000000000..0509f9fad9 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/memcmptest.vgtest @@ -0,0 +1,2 @@ +prog: memcmptest +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/mempool.c b/VEX/head20041019/memcheck/tests/mempool.c new file mode 100644 index 0000000000..f40c1b88ab --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mempool.c @@ -0,0 +1,150 @@ +#include +#include +#include +#include + +#include "../memcheck.h" + +#define SUPERBLOCK_SIZE 100000 +#define REDZONE_SIZE 8 + +static const int USE_MMAP = 0; + +typedef struct _level_list +{ + struct _level_list *next; + char *where; +} level_list; + +typedef struct _pool { + char *mem; + char *where; + int size, left; + level_list *levels; +} pool; + +pool *make_pool() +{ + pool *p; + + if(USE_MMAP) { + p = (pool *)mmap(0, sizeof(pool), PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + p->where = p->mem = (char *)mmap(NULL, SUPERBLOCK_SIZE, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + } else { + p = (pool *)malloc(sizeof(pool)); + p->where = p->mem = (char *)malloc(SUPERBLOCK_SIZE); + } + + p->size = p->left = SUPERBLOCK_SIZE; + p->levels = NULL; + VALGRIND_MAKE_NOACCESS(p->where, SUPERBLOCK_SIZE); + return p; +} + +void push(pool *p) +{ + level_list *l; + + if(USE_MMAP) + l = (level_list *)mmap(0, sizeof(level_list), + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + else + l = (level_list *)malloc(sizeof(level_list)); + + l->next = p->levels; + l->where = p->where; + VALGRIND_CREATE_MEMPOOL(l->where, REDZONE_SIZE, 0); + p->levels = l; +} + +void pop(pool *p) +{ + level_list *l = p->levels; + p->levels = l->next; + VALGRIND_DESTROY_MEMPOOL(l->where); + VALGRIND_MAKE_NOACCESS(l->where, p->where-l->where); + p->where = l->where; + if(USE_MMAP) + munmap(l, sizeof(level_list)); + else + free(l); +} + +void destroy_pool(pool *p) +{ + level_list *l = p->levels; + + while(l) { + pop(p); + } + if(USE_MMAP) { + munmap(p->mem, SUPERBLOCK_SIZE); + munmap(p, sizeof(pool)); + } else { + free(p->mem); + free(p); + } +} + +char *allocate(pool *p, int size) +{ + char *where; + p->left -= size + (REDZONE_SIZE*2); + where = p->where + REDZONE_SIZE; + p->where += size + (REDZONE_SIZE*2); + VALGRIND_MEMPOOL_ALLOC(p->levels->where, where, size); + return where; +} + +//------------------------------------------------------------------------- +// Rest +//------------------------------------------------------------------------- + +void test(void) +{ + char *x1, *x2, *x3, *x4, *x5; + + pool *p = make_pool(); + + push(p); + + x1 = allocate(p, 10); + x2 = allocate(p, 20); + push(p); + x3 = allocate(p, 10); + x4 = allocate(p, 20); + + *x1 = 'a'; // valid + *x2 = 'b'; // valid + + x1[-1] = 'h'; // invalid + x1[10] = 'i'; // invalid + + pop(p); + + *x3 = 'c'; // invalid + *x4 = 'd'; // invalid + + *x1 = 'e'; // valid + *x2 = 'f'; // valid + + x5 = allocate(p, 10); + + *x5 = 'g'; // valid + + // pop(p); + + // *x5 = 'g'; // invalid + + // destroy_pool(p); +} + +int main(void) +{ + test(); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/mempool.stderr.exp b/VEX/head20041019/memcheck/tests/mempool.stderr.exp new file mode 100644 index 0000000000..26de5ac3fa --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mempool.stderr.exp @@ -0,0 +1,38 @@ +Invalid write of size 1 + at 0x........: test (mempool.c:124) + by 0x........: main (mempool.c:148) + Address 0x........ is 1 bytes before a block of size 10 client-defined + at 0x........: allocate (mempool.c:99) + by 0x........: test (mempool.c:115) + by 0x........: main (mempool.c:148) + +Invalid write of size 1 + at 0x........: test (mempool.c:125) + by 0x........: main (mempool.c:148) + Address 0x........ is 0 bytes after a block of size 10 client-defined + at 0x........: allocate (mempool.c:99) + by 0x........: test (mempool.c:115) + by 0x........: main (mempool.c:148) + +Invalid write of size 1 + at 0x........: test (mempool.c:129) + by 0x........: main (mempool.c:148) + Address 0x........ is 70 bytes inside a mempool of size 100000 client-defined + at 0x........: make_pool (mempool.c:43) + by 0x........: test (mempool.c:111) + by 0x........: main (mempool.c:148) + +Invalid write of size 1 + at 0x........: test (mempool.c:130) + by 0x........: main (mempool.c:148) + Address 0x........ is 96 bytes inside a mempool of size 100000 client-defined + at 0x........: make_pool (mempool.c:43) + by 0x........: test (mempool.c:111) + by 0x........: main (mempool.c:148) + + +20 bytes in 1 blocks are definitely lost in loss record 2 of 3 + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: make_pool (mempool.c:37) + by 0x........: test (mempool.c:111) + by 0x........: main (mempool.c:148) diff --git a/VEX/head20041019/memcheck/tests/mempool.vgtest b/VEX/head20041019/memcheck/tests/mempool.vgtest new file mode 100644 index 0000000000..bcf4ede6f9 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mempool.vgtest @@ -0,0 +1,2 @@ +prog: mempool +vgopts: -q --leak-check=yes diff --git a/VEX/head20041019/memcheck/tests/metadata.c b/VEX/head20041019/memcheck/tests/metadata.c new file mode 100644 index 0000000000..c2ea7f1ded --- /dev/null +++ b/VEX/head20041019/memcheck/tests/metadata.c @@ -0,0 +1,46 @@ + +#include +#include +#include "../memcheck.h" + +/* Program demonstrating copying of metadata in memcheck. */ + +int main ( void ) +{ + int* a = malloc(10 * sizeof(int)); + int* b = malloc(10 * sizeof(int)); + int* v = malloc(10 * sizeof(int)); + int i, sum, res; + + for (i = 0; i < 10; i++) { + if (i != 5) + a[i] = i; + } + + /* a[0 .. 4] and [6 .. 9] are defined, [5] is undefined. */ + for (i = 0; i < 10; i++) + b[i] = 0; + + /* b[0 .. 9] is defined. */ + + /* Get metadata for a and put it in v. */ + res = VALGRIND_GET_VBITS(a, v, 10*sizeof(int) ); + printf("result of GET is %d (1 for success)\n", res); + /* and copy to b. */ + res = VALGRIND_SET_VBITS(b, v, 10*sizeof(int) ); + printf("result of SET is %d (1 for success)\n", res); + + /* Now we should have that b[5] is undefined since a[5] is + undefined. */ + sum = 100; + for (i = 0; i < 10; i++) + sum += b[i]; + + /* V should yelp at this point, that sum is undefined. */ + if (sum == 0) + printf("sum == 0\n"); + else + printf("sum != 0\n"); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/metadata.stderr.exp b/VEX/head20041019/memcheck/tests/metadata.stderr.exp new file mode 100644 index 0000000000..cae2d5f45e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/metadata.stderr.exp @@ -0,0 +1,2 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (metadata.c:40) diff --git a/VEX/head20041019/memcheck/tests/metadata.stdout.exp b/VEX/head20041019/memcheck/tests/metadata.stdout.exp new file mode 100644 index 0000000000..445a17b23e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/metadata.stdout.exp @@ -0,0 +1,3 @@ +result of GET is 1 (1 for success) +result of SET is 1 (1 for success) +sum != 0 diff --git a/VEX/head20041019/memcheck/tests/metadata.vgtest b/VEX/head20041019/memcheck/tests/metadata.vgtest new file mode 100644 index 0000000000..6673a75ed0 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/metadata.vgtest @@ -0,0 +1,2 @@ +prog: metadata +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/mismatches.cpp b/VEX/head20041019/memcheck/tests/mismatches.cpp new file mode 100644 index 0000000000..857a07532e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mismatches.cpp @@ -0,0 +1,27 @@ +#include + +int main() +{ + int* fpointer = (int*)malloc(10); + delete fpointer; // should give warning + fpointer = (int*)malloc(10); + delete [] fpointer; // should give warning + fpointer = (int*)malloc(10); + free (fpointer); // should work! + + int* nvec = new int[10]; + delete nvec; // should give a warning + nvec = new int[10]; + free (nvec); // should give a warning + nvec = new int[10]; + delete [] nvec; // should work! + + int* n = new int; + delete [] n; // should give a warning + n = new int; + free(n); // should give a warning + n = new int; + delete n; // should work! + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/mismatches.stderr.exp b/VEX/head20041019/memcheck/tests/mismatches.stderr.exp new file mode 100644 index 0000000000..283b65f4bb --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mismatches.stderr.exp @@ -0,0 +1,41 @@ +Mismatched free() / delete / delete [] + at 0x........: operator delete(void*) (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:6) + Address 0x........ is 0 bytes inside a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:5) + +Mismatched free() / delete / delete [] + at 0x........: operator delete[](void*) (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:8) + Address 0x........ is 0 bytes inside a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:7) + +Mismatched free() / delete / delete [] + at 0x........: operator delete(void*) (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:13) + Address 0x........ is 0 bytes inside a block of size 40 alloc'd + at 0x........: operator new[](unsigned) (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:12) + +Mismatched free() / delete / delete [] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:15) + Address 0x........ is 0 bytes inside a block of size 40 alloc'd + at 0x........: operator new[](unsigned) (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:14) + +Mismatched free() / delete / delete [] + at 0x........: operator delete[](void*) (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:20) + Address 0x........ is 0 bytes inside a block of size 4 alloc'd + at 0x........: operator new(unsigned) (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:19) + +Mismatched free() / delete / delete [] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:22) + Address 0x........ is 0 bytes inside a block of size 4 alloc'd + at 0x........: operator new(unsigned) (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:21) diff --git a/VEX/head20041019/memcheck/tests/mismatches.stderr.exp2 b/VEX/head20041019/memcheck/tests/mismatches.stderr.exp2 new file mode 100644 index 0000000000..448414a1aa --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mismatches.stderr.exp2 @@ -0,0 +1,41 @@ +Mismatched free() / delete / delete [] + at 0x........: __builtin_delete (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:6) + Address 0x........ is 0 bytes inside a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:5) + +Mismatched free() / delete / delete [] + at 0x........: __builtin_vec_delete (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:8) + Address 0x........ is 0 bytes inside a block of size 10 alloc'd + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:7) + +Mismatched free() / delete / delete [] + at 0x........: __builtin_delete (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:13) + Address 0x........ is 0 bytes inside a block of size 40 alloc'd + at 0x........: __builtin_vec_new (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:12) + +Mismatched free() / delete / delete [] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:15) + Address 0x........ is 0 bytes inside a block of size 40 alloc'd + at 0x........: __builtin_vec_new (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:14) + +Mismatched free() / delete / delete [] + at 0x........: __builtin_vec_delete (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:20) + Address 0x........ is 0 bytes inside a block of size 4 alloc'd + at 0x........: __builtin_new (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:19) + +Mismatched free() / delete / delete [] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:22) + Address 0x........ is 0 bytes inside a block of size 4 alloc'd + at 0x........: __builtin_new (vg_replace_malloc.c:...) + by 0x........: main (mismatches.cpp:21) diff --git a/VEX/head20041019/memcheck/tests/mismatches.vgtest b/VEX/head20041019/memcheck/tests/mismatches.vgtest new file mode 100644 index 0000000000..5574afdd4f --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mismatches.vgtest @@ -0,0 +1,2 @@ +prog: mismatches +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/mmaptest.c b/VEX/head20041019/memcheck/tests/mmaptest.c new file mode 100644 index 0000000000..74a21ed260 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mmaptest.c @@ -0,0 +1,15 @@ +#include +#include +#include +#include +#include + +int main() +{ + int fd; + + mkdir("dir", 0777); + fd = open("dir", O_RDONLY); + mmap(NULL, 4711, PROT_READ, MAP_PRIVATE, fd, 0); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/mmaptest.stderr.exp b/VEX/head20041019/memcheck/tests/mmaptest.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/mmaptest.vgtest b/VEX/head20041019/memcheck/tests/mmaptest.vgtest new file mode 100644 index 0000000000..09d73d39ee --- /dev/null +++ b/VEX/head20041019/memcheck/tests/mmaptest.vgtest @@ -0,0 +1,2 @@ +prog: mmaptest +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/nanoleak.c b/VEX/head20041019/memcheck/tests/nanoleak.c new file mode 100644 index 0000000000..7f5b0ee80a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/nanoleak.c @@ -0,0 +1,9 @@ + +#include + +int main ( void ) +{ + volatile int* a = malloc(1000); + a[0] = 0; + return a[0]; +} diff --git a/VEX/head20041019/memcheck/tests/nanoleak.stderr.exp b/VEX/head20041019/memcheck/tests/nanoleak.stderr.exp new file mode 100644 index 0000000000..57fdf3c3b8 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/nanoleak.stderr.exp @@ -0,0 +1,4 @@ + +1000 bytes in 1 blocks are definitely lost in loss record 1 of 1 + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: main (nanoleak.c:6) diff --git a/VEX/head20041019/memcheck/tests/nanoleak.supp b/VEX/head20041019/memcheck/tests/nanoleak.supp new file mode 100644 index 0000000000..584c93e20e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/nanoleak.supp @@ -0,0 +1,7 @@ +{ + this_is_the_nanoleak_suppression_name + Addrcheck,Memcheck:Leak + fun:malloc + fun:main +} + diff --git a/VEX/head20041019/memcheck/tests/nanoleak.vgtest b/VEX/head20041019/memcheck/tests/nanoleak.vgtest new file mode 100644 index 0000000000..d633a3626d --- /dev/null +++ b/VEX/head20041019/memcheck/tests/nanoleak.vgtest @@ -0,0 +1,3 @@ +vgopts: --leak-check=yes -q +prog: nanoleak +stderr_filter: filter_leak_check_size diff --git a/VEX/head20041019/memcheck/tests/nanoleak_supp.stderr.exp b/VEX/head20041019/memcheck/tests/nanoleak_supp.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/nanoleak_supp.vgtest b/VEX/head20041019/memcheck/tests/nanoleak_supp.vgtest new file mode 100644 index 0000000000..57e8947012 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/nanoleak_supp.vgtest @@ -0,0 +1,3 @@ +vgopts: --leak-check=yes --suppressions=nanoleak.supp -q +prog: nanoleak +stderr_filter: filter_leak_check_size diff --git a/VEX/head20041019/memcheck/tests/new_nothrow.cpp b/VEX/head20041019/memcheck/tests/new_nothrow.cpp new file mode 100644 index 0000000000..82e5bc5141 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/new_nothrow.cpp @@ -0,0 +1,13 @@ +#include + +// At one point, Valgrind wasn't overriding these 'nothrow' versions; since +// they call malloc(), the calls to 'delete' caused bogus mismatch errors. + +int main() +{ + int * a = new (std::nothrow) int; + int * b = new (std::nothrow) int[5]; + delete a; + delete [] b; +} + diff --git a/VEX/head20041019/memcheck/tests/new_nothrow.stderr.exp b/VEX/head20041019/memcheck/tests/new_nothrow.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/new_nothrow.vgtest b/VEX/head20041019/memcheck/tests/new_nothrow.vgtest new file mode 100644 index 0000000000..ea8dda3cf7 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/new_nothrow.vgtest @@ -0,0 +1,2 @@ +prog: new_nothrow +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/new_override.cpp b/VEX/head20041019/memcheck/tests/new_override.cpp new file mode 100644 index 0000000000..b3feb0c726 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/new_override.cpp @@ -0,0 +1,30 @@ +#include +#include + +class Test { +public: + int a, b, c, d; +}; + +void *operator new[](size_t size) +{ + void *ret = malloc(size); + printf("Here.\n"); + for (unsigned int i = 0; i < size; i++) ((char *) ret)[i] = 0xFF; + return ret; +} + +int main(int argc, char *argv[]) { + Test *toto; + int i; + int j = 0; + + toto = new Test[2]; + + for (i = 0; i < 2; i++) { + if (toto[i].a) { + j++; + } + //printf("%d : %08x %08x %08x %08x\n", i, toto[i].a, toto[i].b, toto[i].c, toto[i].d); + } +} diff --git a/VEX/head20041019/memcheck/tests/new_override.stderr.exp b/VEX/head20041019/memcheck/tests/new_override.stderr.exp new file mode 100644 index 0000000000..7a5680c58f --- /dev/null +++ b/VEX/head20041019/memcheck/tests/new_override.stderr.exp @@ -0,0 +1,7 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +malloc/free: in use at exit: ... bytes in ... blocks. +malloc/free: ... allocs, ... frees, ... bytes allocated. +For a detailed leak analysis, rerun with: --leak-check=yes +For counts of detected errors, rerun with: -v diff --git a/VEX/head20041019/memcheck/tests/new_override.stdout.exp b/VEX/head20041019/memcheck/tests/new_override.stdout.exp new file mode 100644 index 0000000000..fe1846ca2e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/new_override.stdout.exp @@ -0,0 +1 @@ +Here. diff --git a/VEX/head20041019/memcheck/tests/new_override.vgtest b/VEX/head20041019/memcheck/tests/new_override.vgtest new file mode 100644 index 0000000000..50e6240342 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/new_override.vgtest @@ -0,0 +1,2 @@ +prog: new_override +stderr_filter: filter_allocs diff --git a/VEX/head20041019/memcheck/tests/null_socket.c b/VEX/head20041019/memcheck/tests/null_socket.c new file mode 100644 index 0000000000..a2e203e080 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/null_socket.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include + + +// It's legit to have NULL buffers; before the bug was fixed Valgrind +// reported spurious errors for the buffers. +int main(void) +{ + if (-1 != accept(99, NULL, 0)) + printf("accept succeeded?\n"); + + if (-1 != recvfrom(0, NULL, 0, 0, NULL, 0)) + printf("recvfrom succeeded?\n"); + + if (-1 != getsockopt(0, 0, 0, NULL, 0)) + printf("getsockopt succeeded?\n"); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/null_socket.stderr.exp b/VEX/head20041019/memcheck/tests/null_socket.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/null_socket.vgtest b/VEX/head20041019/memcheck/tests/null_socket.vgtest new file mode 100644 index 0000000000..7f3a2a4921 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/null_socket.vgtest @@ -0,0 +1,2 @@ +prog: null_socket +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/overlap.c b/VEX/head20041019/memcheck/tests/overlap.c new file mode 100644 index 0000000000..d868886f38 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/overlap.c @@ -0,0 +1,125 @@ +#include +#include + +char b[50]; + +void reset_b(void) +{ + int i; + + for (i = 0; i < 50; i++) + b[i] = '_'; + b[49] = '\0'; +} + +void reset_b2(void) +{ + reset_b(); + strcpy(b, "ABCDEFG"); +} + +int main(void) +{ + char x[100]; + char a[] = "abcdefghijklmnopqrstuvwxyz"; + int i; + + /* testing memcpy/strcpy overlap */ + + for (i = 0; i < 50; i++) { + x[i] = i+1; // don't put any zeroes in there + } + for (i = 50; i < 100; i++) { + // because of the errors, the strcpy's will overrun, so put some + // zeroes in the second half to stop them eventually + x[i] = 0; + + } + + memcpy(x+20, x, 20); // ok + memcpy(x+20, x, 21); // overlap + memcpy(x, x+20, 20); // ok + memcpy(x, x+20, 21); // overlap + + strncpy(x+20, x, 20); // ok + strncpy(x+20, x, 21); // overlap + strncpy(x, x+20, 20); // ok + strncpy(x, x+20, 21); // overlap + + x[39] = '\0'; + strcpy(x, x+20); // ok + + x[39] = 39; + x[40] = '\0'; + strcpy(x, x+20); // overlap + + x[19] = '\0'; + strcpy(x+20, x); // ok + +/* + x[19] = 19; + x[20] = '\0'; + strcpy(x+20, x); // overlap, but runs forever (or until it seg faults) +*/ + + /* testing strcpy, strncpy() */ + + reset_b(); + printf("`%s'\n", b); + + strcpy(b, a); + printf("`%s'\n", b); + + reset_b(); + strncpy(b, a, 25); + printf("`%s'\n", b); + + reset_b(); + strncpy(b, a, 26); + printf("`%s'\n", b); + + reset_b(); + strncpy(b, a, 27); + printf("`%s'\n", b); + + printf("\n"); + + /* testing strncat() */ + + reset_b2(); + printf("`%s'\n", b); + + reset_b2(); + strcat(b, a); + printf("`%s'\n", b); + + reset_b2(); + strncat(b, a, 25); + printf("`%s'\n", b); + + reset_b2(); + strncat(b, a, 26); + printf("`%s'\n", b); + + reset_b2(); + strncat(b, a, 27); + printf("`%s'\n", b); + + /* Nb: can't actually get strcat warning -- if any overlap occurs, it will + always run forever, I think... */ + + for ( i = 0; i < 2; i++) + strncat(a+20, a, 21); // run twice to check 2nd error isn't shown + strncat(a, a+20, 21); + + /* This is ok, but once gave a warning when strncpy() was wrong, + and used 'n' for the length, even when the src was shorter than 'n' */ + { + char dest[64]; + char src [16]; + strcpy( src, "short" ); + strncpy( dest, src, 20 ); + } + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/overlap.stderr.exp b/VEX/head20041019/memcheck/tests/overlap.stderr.exp new file mode 100644 index 0000000000..6fcbcd1ca5 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/overlap.stderr.exp @@ -0,0 +1,27 @@ +Source and destination overlap in memcpy(0x........, 0x........, 21) + at 0x........: memcpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:40) + +Source and destination overlap in memcpy(0x........, 0x........, 21) + at 0x........: memcpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:42) + +Source and destination overlap in strncpy(0x........, 0x........, 21) + at 0x........: strncpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:45) + +Source and destination overlap in strncpy(0x........, 0x........, 21) + at 0x........: strncpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:47) + +Source and destination overlap in strcpy(0x........, 0x........) + at 0x........: strcpy (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:54) + +Source and destination overlap in strncat(0x........, 0x........, 21) + at 0x........: strncat (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:112) + +Source and destination overlap in strncat(0x........, 0x........, 21) + at 0x........: strncat (mac_replace_strmem.c:...) + by 0x........: main (overlap.c:113) diff --git a/VEX/head20041019/memcheck/tests/overlap.stdout.exp b/VEX/head20041019/memcheck/tests/overlap.stdout.exp new file mode 100644 index 0000000000..12cb02e548 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/overlap.stdout.exp @@ -0,0 +1,11 @@ +`_________________________________________________' +`abcdefghijklmnopqrstuvwxyz' +`abcdefghijklmnopqrstuvwxy________________________' +`abcdefghijklmnopqrstuvwxyz_______________________' +`abcdefghijklmnopqrstuvwxyz' + +`ABCDEFG' +`ABCDEFGabcdefghijklmnopqrstuvwxyz' +`ABCDEFGabcdefghijklmnopqrstuvwxy' +`ABCDEFGabcdefghijklmnopqrstuvwxyz' +`ABCDEFGabcdefghijklmnopqrstuvwxyz' diff --git a/VEX/head20041019/memcheck/tests/overlap.vgtest b/VEX/head20041019/memcheck/tests/overlap.vgtest new file mode 100644 index 0000000000..54a0baacfb --- /dev/null +++ b/VEX/head20041019/memcheck/tests/overlap.vgtest @@ -0,0 +1,2 @@ +prog: overlap +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/pth_once.stderr.exp b/VEX/head20041019/memcheck/tests/pth_once.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/pth_once.stdout.exp b/VEX/head20041019/memcheck/tests/pth_once.stdout.exp new file mode 100644 index 0000000000..97e25d16c0 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/pth_once.stdout.exp @@ -0,0 +1,22 @@ +welcome: Welcome +identify_yourself: Hi, I'm thread # 0 +identify_yourself: Hi, I'm thread # 1 +identify_yourself: Hi, I'm thread # 2 +identify_yourself: Hi, I'm thread # 3 +identify_yourself: Hi, I'm thread # 4 +identify_yourself: Hi, I'm thread # 5 +identify_yourself: Hi, I'm thread # 6 +identify_yourself: Hi, I'm thread # 7 +identify_yourself: Hi, I'm thread # 8 +identify_yourself: Hi, I'm thread # 9 +main: joined to thread 0 +main: joined to thread 1 +main: joined to thread 2 +main: joined to thread 3 +main: joined to thread 4 +main: joined to thread 5 +main: joined to thread 6 +main: joined to thread 7 +main: joined to thread 8 +main: joined to thread 9 +main: Goodbye diff --git a/VEX/head20041019/memcheck/tests/pth_once.vgtest b/VEX/head20041019/memcheck/tests/pth_once.vgtest new file mode 100644 index 0000000000..bc571a2a82 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/pth_once.vgtest @@ -0,0 +1,2 @@ +prog: ../../corecheck/tests/pth_once +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/pushfpopf.stderr.exp b/VEX/head20041019/memcheck/tests/pushfpopf.stderr.exp new file mode 100644 index 0000000000..2d2d623e44 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/pushfpopf.stderr.exp @@ -0,0 +1,3 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: fooble (...) + by 0x........: main (pushfpopf_c.c:12) diff --git a/VEX/head20041019/memcheck/tests/pushfpopf.stdout.exp b/VEX/head20041019/memcheck/tests/pushfpopf.stdout.exp new file mode 100644 index 0000000000..180f871a19 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/pushfpopf.stdout.exp @@ -0,0 +1 @@ +fooble: result is 22 diff --git a/VEX/head20041019/memcheck/tests/pushfpopf.vgtest b/VEX/head20041019/memcheck/tests/pushfpopf.vgtest new file mode 100644 index 0000000000..94cdcef78a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/pushfpopf.vgtest @@ -0,0 +1,3 @@ +prog: pushfpopf +stderr_filter: filter_pushfpopf +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/pushfpopf_c.c b/VEX/head20041019/memcheck/tests/pushfpopf_c.c new file mode 100644 index 0000000000..f45271e0ff --- /dev/null +++ b/VEX/head20041019/memcheck/tests/pushfpopf_c.c @@ -0,0 +1,14 @@ + +#include + +// in pushfpopf.s +extern int fooble ( int, int ); + +int main ( void ) +{ + int arr[2]; + arr[0] = 3; + // arr[1] = 45; + printf("fooble: result is %d\n", fooble(arr[0], arr[1])); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/pushfpopf_s.s b/VEX/head20041019/memcheck/tests/pushfpopf_s.s new file mode 100644 index 0000000000..f1409950b4 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/pushfpopf_s.s @@ -0,0 +1,38 @@ + .file "twoparams.c" + .version "01.01" +gcc2_compiled.: +.text + .align 4 +.globl fooble + .type fooble,@function +fooble: + pushl %ebp + movl %esp, %ebp + movl 8(%ebp), %eax + + subl 12(%ebp), %eax + # flags are now undef if either operand is + # save possibly undef flags on stack + pushfl + + movl $0, %eax + addl $0, %eax + # flags are now definitely defined + + popfl + # resulting flag definedness depends on outcome of sub above + # should override that created by 0 + 0 above + + # now use the condition codes to generate a value + # in a way which will cause undefinedness to get reported + jz labelz + movl $22, %eax + jmp theend +labelz: + movl $33, %eax +theend: + popl %ebp + ret +.Lfe1: + .size fooble,.Lfe1-fooble + .ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.1 2.96-98)" diff --git a/VEX/head20041019/memcheck/tests/realloc1.c b/VEX/head20041019/memcheck/tests/realloc1.c new file mode 100644 index 0000000000..91878b3324 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/realloc1.c @@ -0,0 +1,19 @@ + +/* Anyone know what this is supposed to test? I don't + (JRS 2003-07-07) -- which ain't good considering I + probably created it :) +*/ + +#include +#include + +int main ( void ) +{ + int i; + char* p = malloc(1); + for (i = 2; i < 50; i++) { + p = realloc(p, i); + p[i-1] = 'z'; + } + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/realloc1.stderr.exp b/VEX/head20041019/memcheck/tests/realloc1.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/realloc1.vgtest b/VEX/head20041019/memcheck/tests/realloc1.vgtest new file mode 100644 index 0000000000..bab181f51f --- /dev/null +++ b/VEX/head20041019/memcheck/tests/realloc1.vgtest @@ -0,0 +1,2 @@ +prog: realloc1 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/realloc2.c b/VEX/head20041019/memcheck/tests/realloc2.c new file mode 100644 index 0000000000..7b1fbf1e01 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/realloc2.c @@ -0,0 +1,21 @@ +/* This test demonstrated an obscure bug in malloclists handling caused by + multiple blocks hashing to the same list and one being overwritten at + realloc time due to bad ordering of the things happening. Now runs + without error. */ + +#include +#include + +int main ( void ) +{ + char* p; + int i; + for (i = 0; i < 10000; i++) { + p = malloc(10 + 10 * (i % 100)); + p = realloc(p, 500); + p = realloc(p, 600); + free(p); + } + return 0; +} + diff --git a/VEX/head20041019/memcheck/tests/realloc2.stderr.exp b/VEX/head20041019/memcheck/tests/realloc2.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/realloc2.vgtest b/VEX/head20041019/memcheck/tests/realloc2.vgtest new file mode 100644 index 0000000000..8d7803953a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/realloc2.vgtest @@ -0,0 +1,2 @@ +prog: realloc2 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/realloc3.c b/VEX/head20041019/memcheck/tests/realloc3.c new file mode 100644 index 0000000000..0c6a5f8a09 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/realloc3.c @@ -0,0 +1,28 @@ +/* For a long time (from Valgrind 1.0 to 1.9.6, AFAICT) when realloc() was + called and made a block smaller, or didn't change its size, the + ExeContext of the block was not updated; therefore any errors that + referred to it would state that it was allocated not by the realloc(), + but by the previous malloc() or whatever. While this is true in one + sense, it is misleading and not what you'd expect. This test + demonstrates this -- 'x' and 'y' are unchanged and shrunk, and their + ExeContexts should be updated upon their realloc(). I hope that's clear. +*/ +#include + +int main(void) +{ + int* x = malloc(5); + int* y = malloc(10); + int* z = malloc(2); + int a, b, c; + + x = realloc(x, 5); // same size + y = realloc(y, 5); // make smaller + z = realloc(z, 5); // make bigger + + a = (x[5] == 0xdeadbeef ? 1 : 0); + b = (y[5] == 0xdeadbeef ? 1 : 0); + c = (z[5] == 0xdeadbeef ? 1 : 0); + + return a + b + c; +} diff --git a/VEX/head20041019/memcheck/tests/realloc3.stderr.exp b/VEX/head20041019/memcheck/tests/realloc3.stderr.exp new file mode 100644 index 0000000000..51e5da3a18 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/realloc3.stderr.exp @@ -0,0 +1,17 @@ +Invalid read of size 4 + at 0x........: main (realloc3.c:23) + Address 0x........ is 15 bytes after a block of size 5 alloc'd + at 0x........: realloc (vg_replace_malloc.c:...) + by 0x........: main (realloc3.c:19) + +Invalid read of size 4 + at 0x........: main (realloc3.c:24) + Address 0x........ is 15 bytes after a block of size 5 alloc'd + at 0x........: realloc (vg_replace_malloc.c:...) + by 0x........: main (realloc3.c:20) + +Invalid read of size 4 + at 0x........: main (realloc3.c:25) + Address 0x........ is 15 bytes after a block of size 5 alloc'd + at 0x........: realloc (vg_replace_malloc.c:...) + by 0x........: main (realloc3.c:21) diff --git a/VEX/head20041019/memcheck/tests/realloc3.vgtest b/VEX/head20041019/memcheck/tests/realloc3.vgtest new file mode 100644 index 0000000000..ff295abac3 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/realloc3.vgtest @@ -0,0 +1,2 @@ +prog: realloc3 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/sigaltstack.c b/VEX/head20041019/memcheck/tests/sigaltstack.c new file mode 100644 index 0000000000..be42442bcf --- /dev/null +++ b/VEX/head20041019/memcheck/tests/sigaltstack.c @@ -0,0 +1,41 @@ + + +#include +#include +#include +#include + +void sig_handler(int sig){ + int var; + fprintf(stderr, "caught signal, local var is on %p\n", &var); +} + +int main(int argv, char** argc) { + int res, i; + stack_t sigstk; + struct sigaction act; + static const int size = SIGSTKSZ*2; + char *stk = (char *)mmap(0, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); + sigstk.ss_sp = stk; + + sigstk.ss_size = size; + sigstk.ss_flags = 0; + fprintf(stderr, "calling sigaltstack, stack base is %p\n", sigstk.ss_sp); + if (sigaltstack(&sigstk,0)<0) perror("sigaltstack"); + + fprintf(stderr,"setting sigaction\n"); + act.sa_flags=SA_ONSTACK; + act.sa_handler=&sig_handler; + sigemptyset(&act.sa_mask); + res = sigaction(SIGUSR1,&act,0); + fprintf(stderr, "res = %d\n", res); + fprintf(stderr, "raising the signal\n"); + raise(SIGUSR1); + + /* Loop long enough so valgrind has a forced context switch and + actually delivers the signal before the thread exits. */ + for (i = 0; i < 1000000; i++) ; + + fprintf(stderr, "done\n"); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/sigaltstack.stderr.exp b/VEX/head20041019/memcheck/tests/sigaltstack.stderr.exp new file mode 100644 index 0000000000..b95833f617 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/sigaltstack.stderr.exp @@ -0,0 +1,6 @@ +calling sigaltstack, stack base is 0x........ +setting sigaction +res = 0 +raising the signal +caught signal, local var is on 0x........ +done diff --git a/VEX/head20041019/memcheck/tests/sigaltstack.vgtest b/VEX/head20041019/memcheck/tests/sigaltstack.vgtest new file mode 100644 index 0000000000..532bb26ba0 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/sigaltstack.vgtest @@ -0,0 +1,3 @@ +prog: sigaltstack +stderr_filter: ./filter_stderr_backtrace +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/signal2.c b/VEX/head20041019/memcheck/tests/signal2.c new file mode 100644 index 0000000000..a1df7056e7 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/signal2.c @@ -0,0 +1,20 @@ + +#include +#include +#include + +void sig_hdlr ( int signo ) +{ + printf ( "caught sig segv\n" ); + exit(1); +} + +int main ( void ) +{ + printf ( "installing sig handler\n" ); + signal(SIGSEGV, sig_hdlr); + printf ( "doing bad thing\n" ); + * (int*) 65536 = 0; + printf ( "exited normally ?!\n" ); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/signal2.stderr.exp b/VEX/head20041019/memcheck/tests/signal2.stderr.exp new file mode 100644 index 0000000000..32f63f7a4b --- /dev/null +++ b/VEX/head20041019/memcheck/tests/signal2.stderr.exp @@ -0,0 +1,3 @@ +Invalid write of size 4 + at 0x........: main (signal2.c:17) + Address 0x........ is not stack'd, malloc'd or (recently) free'd diff --git a/VEX/head20041019/memcheck/tests/signal2.stdout.exp b/VEX/head20041019/memcheck/tests/signal2.stdout.exp new file mode 100644 index 0000000000..3e16af0821 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/signal2.stdout.exp @@ -0,0 +1,3 @@ +installing sig handler +doing bad thing +caught sig segv diff --git a/VEX/head20041019/memcheck/tests/signal2.vgtest b/VEX/head20041019/memcheck/tests/signal2.vgtest new file mode 100644 index 0000000000..f55551cea9 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/signal2.vgtest @@ -0,0 +1,2 @@ +prog: signal2 +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/supp.c b/VEX/head20041019/memcheck/tests/supp.c new file mode 100644 index 0000000000..fe1200e3f7 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/supp.c @@ -0,0 +1,12 @@ +#include + +int +main () +{ + volatile int x; /* make sure it isn't in a register */ + + if (x == 0) + return 0; + else + return 1; +} diff --git a/VEX/head20041019/memcheck/tests/supp.supp b/VEX/head20041019/memcheck/tests/supp.supp new file mode 100644 index 0000000000..c80b76bbe7 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/supp.supp @@ -0,0 +1,6 @@ +{ + name_of_this_suppression + Memcheck:Cond + obj:*supp1 + fun:__libc_start_main +} diff --git a/VEX/head20041019/memcheck/tests/supp1.stderr.exp b/VEX/head20041019/memcheck/tests/supp1.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/supp1.vgtest b/VEX/head20041019/memcheck/tests/supp1.vgtest new file mode 100644 index 0000000000..02e23ba0c1 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/supp1.vgtest @@ -0,0 +1,2 @@ +vgopts: --suppressions=supp.supp -q +prog: supp1 diff --git a/VEX/head20041019/memcheck/tests/supp2.stderr.exp b/VEX/head20041019/memcheck/tests/supp2.stderr.exp new file mode 100644 index 0000000000..27e6842094 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/supp2.stderr.exp @@ -0,0 +1,2 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: main (supp.c:8) diff --git a/VEX/head20041019/memcheck/tests/supp2.vgtest b/VEX/head20041019/memcheck/tests/supp2.vgtest new file mode 100644 index 0000000000..c4f3350e91 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/supp2.vgtest @@ -0,0 +1,2 @@ +vgopts: --suppressions=supp.supp -q +prog: supp2 diff --git a/VEX/head20041019/memcheck/tests/suppfree.c b/VEX/head20041019/memcheck/tests/suppfree.c new file mode 100644 index 0000000000..8298f021a8 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/suppfree.c @@ -0,0 +1,30 @@ + +#include + +void ddd ( char* x ) +{ + free(x); + free(x); +} + +void ccc (char* x) +{ + ddd(x); +} + +void bbb (char* x) +{ + ccc(x); +} + +void aaa (char* x) +{ + bbb(x); +} + +int main ( void ) +{ + char* x = malloc(10); + aaa(x); + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/suppfree.stderr.exp b/VEX/head20041019/memcheck/tests/suppfree.stderr.exp new file mode 100644 index 0000000000..8beea0719a --- /dev/null +++ b/VEX/head20041019/memcheck/tests/suppfree.stderr.exp @@ -0,0 +1,10 @@ +Invalid free() / delete / delete[] + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: ddd (suppfree.c:7) + by 0x........: ccc (suppfree.c:12) + by 0x........: bbb (suppfree.c:17) + Address 0x........ is 0 bytes inside a block of size 10 free'd + at 0x........: free (vg_replace_malloc.c:...) + by 0x........: ddd (suppfree.c:6) + by 0x........: ccc (suppfree.c:12) + by 0x........: bbb (suppfree.c:17) diff --git a/VEX/head20041019/memcheck/tests/suppfree.vgtest b/VEX/head20041019/memcheck/tests/suppfree.vgtest new file mode 100644 index 0000000000..a7789c3edc --- /dev/null +++ b/VEX/head20041019/memcheck/tests/suppfree.vgtest @@ -0,0 +1,2 @@ +prog: suppfree +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/threadederrno.c b/VEX/head20041019/memcheck/tests/threadederrno.c new file mode 100644 index 0000000000..3cad5fbfda --- /dev/null +++ b/VEX/head20041019/memcheck/tests/threadederrno.c @@ -0,0 +1,35 @@ + +#include +#include +#include +#include + + +void* thr2 ( void* v ) +{ + FILE* f = fopen("bogus2", "r"); + printf("f = %p, errno = %d (%s)\n", f, errno, strerror(errno)); + return NULL; +} + +void* thr3 ( void* v ) +{ + FILE* f = fopen("bogus3", "r"); + printf("f = %p, errno = %d (%s)\n", f, errno, strerror(errno)); + return NULL; +} + + +int main ( void ) +{ + FILE* f; + pthread_t tid2, tid3; + pthread_create(&tid2, NULL, &thr2, NULL); + pthread_create(&tid3, NULL, &thr3, NULL); + f = fopen("bogus", "r"); + printf("f = %p, errno = %d (%s)\n", f, errno, strerror(errno)); + pthread_join(tid2, NULL); + pthread_join(tid3, NULL); + return 0; +} + diff --git a/VEX/head20041019/memcheck/tests/threadederrno.stderr.exp b/VEX/head20041019/memcheck/tests/threadederrno.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/memcheck/tests/threadederrno.stdout.exp b/VEX/head20041019/memcheck/tests/threadederrno.stdout.exp new file mode 100644 index 0000000000..671ad2af5e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/threadederrno.stdout.exp @@ -0,0 +1,3 @@ +f = (nil), errno = 2 (No such file or directory) +f = (nil), errno = 2 (No such file or directory) +f = (nil), errno = 2 (No such file or directory) diff --git a/VEX/head20041019/memcheck/tests/threadederrno.vgtest b/VEX/head20041019/memcheck/tests/threadederrno.vgtest new file mode 100644 index 0000000000..a9fa6bedff --- /dev/null +++ b/VEX/head20041019/memcheck/tests/threadederrno.vgtest @@ -0,0 +1,2 @@ +prog: threadederrno +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/toobig-allocs.stderr.exp b/VEX/head20041019/memcheck/tests/toobig-allocs.stderr.exp new file mode 100644 index 0000000000..a5ba60a368 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/toobig-allocs.stderr.exp @@ -0,0 +1,9 @@ + +Attempting too-big malloc()... +Attempting too-big mmap()... + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) +malloc/free: in use at exit: 0 bytes in 0 blocks. +malloc/free: 1 allocs, 0 frees, 2145386496 bytes allocated. +For a detailed leak analysis, rerun with: --leak-check=yes +For counts of detected errors, rerun with: -v diff --git a/VEX/head20041019/memcheck/tests/toobig-allocs.vgtest b/VEX/head20041019/memcheck/tests/toobig-allocs.vgtest new file mode 100644 index 0000000000..186cf5f901 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/toobig-allocs.vgtest @@ -0,0 +1 @@ +prog: ../../tests/toobig-allocs diff --git a/VEX/head20041019/memcheck/tests/trivialleak.c b/VEX/head20041019/memcheck/tests/trivialleak.c new file mode 100644 index 0000000000..f3a8963359 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/trivialleak.c @@ -0,0 +1,14 @@ +#include + +static void test() + { + void* leak; + int i; + for (i = 0; i < 1000; i++) + leak = (void*)malloc( 1 ); + } + int main() + { + test(); + return 0; + } diff --git a/VEX/head20041019/memcheck/tests/trivialleak.stderr.exp b/VEX/head20041019/memcheck/tests/trivialleak.stderr.exp new file mode 100644 index 0000000000..1bd6e545f9 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/trivialleak.stderr.exp @@ -0,0 +1,5 @@ + +1000 bytes in 1000 blocks are definitely lost in loss record 1 of 1 + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: test (trivialleak.c:8) + by 0x........: main (trivialleak.c:12) diff --git a/VEX/head20041019/memcheck/tests/trivialleak.stderr.exp2 b/VEX/head20041019/memcheck/tests/trivialleak.stderr.exp2 new file mode 100644 index 0000000000..6b061496a4 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/trivialleak.stderr.exp2 @@ -0,0 +1,5 @@ + +999 bytes in 999 blocks are definitely lost in loss record 2 of 2 + at 0x........: malloc (vg_replace_malloc.c:...) + by 0x........: test (trivialleak.c:8) + by 0x........: main (trivialleak.c:12) diff --git a/VEX/head20041019/memcheck/tests/trivialleak.vgtest b/VEX/head20041019/memcheck/tests/trivialleak.vgtest new file mode 100644 index 0000000000..7d66a3c0d3 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/trivialleak.vgtest @@ -0,0 +1,3 @@ +vgopts: --leak-check=yes -q +prog: trivialleak +stderr_filter: filter_leak_check_size diff --git a/VEX/head20041019/memcheck/tests/tronical.S b/VEX/head20041019/memcheck/tests/tronical.S new file mode 100644 index 0000000000..30d336a88c --- /dev/null +++ b/VEX/head20041019/memcheck/tests/tronical.S @@ -0,0 +1,102 @@ +/* + +Assembly derived from the following program compiled with -O2. +This fools Valgrind, causing it to give a false error. + +#include + +struct Foo +{ + int a1 : 1; + int a2 : 1; + int a3 : 1; + int a4 : 1; + int a5 : 1; + int a6 : 1; + int a7 : 1; + int bleh : 1; +}; + +struct Foo* foo; + +void set() +{ + foo->bleh = 1; +} + +void get() +{ + if ( foo->bleh == 0 ) + printf( "blieb\n" ); +} + +int main() +{ + foo = malloc(sizeof(struct Foo)); + set(); + + get(); + + return 0; +} + +*/ + + .file "tronical.c" + .version "01.01" +gcc2_compiled.: +.text + .align 4 +.globl set + .type set,@function +set: + pushl %ebp + movl foo, %eax + orb $128, (%eax) + movl %esp, %ebp + popl %ebp + ret +.Lfe1: + .size set,.Lfe1-set + .section .rodata +.LC0: + .string "blieb\n" +.text + .align 4 +.globl get + .type get,@function +get: + pushl %ebp + movl %esp, %ebp + subl $8, %esp + movl foo, %eax + cmpb $0, (%eax) + js .L4 + subl $12, %esp + pushl $.LC0 + call printf + addl $16, %esp +.L4: + leave + ret +.Lfe2: + .size get,.Lfe2-get + .align 4 +.globl main + .type main,@function +main: + pushl %ebp + movl %esp, %ebp + subl $20, %esp + pushl $4 + call malloc + movl %eax, foo + call set + call get + xorl %eax, %eax + leave + ret +.Lfe3: + .size main,.Lfe3-main + .comm foo,4,4 + .ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.1 2.96-98)" diff --git a/VEX/head20041019/memcheck/tests/tronical.stderr.exp b/VEX/head20041019/memcheck/tests/tronical.stderr.exp new file mode 100644 index 0000000000..c70a82ce26 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/tronical.stderr.exp @@ -0,0 +1,3 @@ +Conditional jump or move depends on uninitialised value(s) + at 0x........: get (...) + by 0x........: main (...) diff --git a/VEX/head20041019/memcheck/tests/tronical.vgtest b/VEX/head20041019/memcheck/tests/tronical.vgtest new file mode 100644 index 0000000000..1fdf863b61 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/tronical.vgtest @@ -0,0 +1,3 @@ +prog: tronical +stderr_filter: filter_tronical +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/vgtest_ume.c b/VEX/head20041019/memcheck/tests/vgtest_ume.c new file mode 100644 index 0000000000..84ca3910e1 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/vgtest_ume.c @@ -0,0 +1,150 @@ +#define ELFSZ 32 + +// This file is a unit self-test for ume.c, jmp_with_stack.c + +#include +#include +#include +#include +#include "../../coregrind/ume.h" + +#define STKSZ (64*1024) + +static void* init_sp; + +//------------------------------------------------------------------- +// Test foreach_map() +//------------------------------------------------------------------- + +static int x[8]; + +static int f(char *start, char *end, const char *perm, off_t off, + int maj, int min, int ino, void* dummy) { + // Just do some nonsense action with each of the values so that Memcheck + // checks that they are valid. + x[0] = ( start == 0 ? 0 : 1 ); + x[1] = ( end == 0 ? 0 : 1 ); + x[2] = ( perm == 0 ? 0 : 1 ); + x[3] = ( off == 0 ? 0 : 1 ); + x[4] = ( maj == 0 ? 0 : 1 ); + x[5] = ( min == 0 ? 0 : 1 ); + x[6] = ( ino == 0 ? 0 : 1 ); + x[7] = ( dummy == 0 ? 0 : 1 ); + + return /*True*/1 + x[0] + x[1] + x[2] + x[3] + x[4] + x[5] + x[6] + x[7]; +} + +static void test__foreach_map(void) +{ + fprintf(stderr, "Calling foreach_map()\n"); + foreach_map(f, /*dummy*/NULL); +} + +//------------------------------------------------------------------- +// Test find_auxv() +//------------------------------------------------------------------- + +static void test__find_auxv(void) +{ + struct ume_auxv *auxv; + + assert(init_sp != NULL); + + fprintf(stderr, "Calling find_auxv()\n"); + auxv = find_auxv((int*)init_sp); + + // Check the auxv value looks sane + assert((void*)auxv > (void*)init_sp); + assert((unsigned int)auxv - (unsigned int)init_sp < 0x10000); + + // Scan the auxv, check it looks sane + for (; auxv->a_type != AT_NULL; auxv++) { + switch(auxv->a_type) { + // Check a_type value looks like a plausible small constant + case 1 ... 64: + break; + + default: + assert(0); + } + } +} + +//------------------------------------------------------------------- +// Test do_exec() +//------------------------------------------------------------------- + +static void push_auxv(unsigned char **espp, int type, void *val) +{ + struct ume_auxv *auxp = (struct ume_auxv *)*espp; + auxp--; + auxp->a_type = type; + auxp->u.a_ptr = val; + *espp = (unsigned char *)auxp; +} + +static void push(unsigned char **espp, void *v) +{ + void **vp = *(void ***)espp; + *--vp = v; + *espp = (unsigned char *)vp; +} + +static void test__do_exec(void) +{ + struct exeinfo info; + int err; + unsigned char* newstack; + unsigned char *esp; + + info.argv = NULL; + info.exe_base = 0x50000000; + info.exe_end = 0x50ffffff; + info.map_base = 0x51000000; + + fprintf(stderr, "Calling do_exec(\"hello\")\n"); + err = do_exec("hello", &info); + assert(0 == err); + +// printf("info.exe_base=%p exe_end=%p\n", +// (void*)info.exe_base, (void*)info.exe_end); + + newstack = malloc(STKSZ); + assert(0 != newstack); + + esp = newstack+STKSZ; + + /* + Set the new executable's stack up like the kernel would after + exec. + + These are being pushed onto the stack, towards decreasing + addresses. + */ + push_auxv(&esp, AT_NULL, 0); // auxv terminator + push_auxv(&esp, AT_ENTRY, (void *)info.entry); // entrypoint of the main executable */ + push_auxv(&esp, AT_BASE, (void *)info.interp_base); // base address of ld-linux.so + push_auxv(&esp, AT_PHDR, (void *)info.phdr); // where the ELF PHDRs are mapped + push_auxv(&esp, AT_PHNUM, (void*)info.phnum); // and how many of them + + push(&esp, 0); /* no env */ + push(&esp, 0); /* no argv */ + push(&esp, 0); /* argc=0 */ + +// fprintf(stderr, "ume_go: %p %p\n", (void*)info.init_eip, (void*)esp); + + jmp_with_stack(info.init_eip, (addr_t)esp); + + assert(0); // UNREACHABLE +} + +int main(int argc, char** argv) +{ + init_sp = argv - 1; + + test__foreach_map(); + test__find_auxv(); + test__do_exec(); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/vgtest_ume.stderr.exp b/VEX/head20041019/memcheck/tests/vgtest_ume.stderr.exp new file mode 100644 index 0000000000..777f9d110e --- /dev/null +++ b/VEX/head20041019/memcheck/tests/vgtest_ume.stderr.exp @@ -0,0 +1,4 @@ +Calling foreach_map() +Calling find_auxv() +Calling do_exec("hello") +Hello, world! diff --git a/VEX/head20041019/memcheck/tests/vgtest_ume.vgtest b/VEX/head20041019/memcheck/tests/vgtest_ume.vgtest new file mode 100644 index 0000000000..24da9a3e22 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/vgtest_ume.vgtest @@ -0,0 +1,2 @@ +prog: vgtest_ume +vgopts: -q diff --git a/VEX/head20041019/memcheck/tests/weirdioctl.c b/VEX/head20041019/memcheck/tests/weirdioctl.c new file mode 100644 index 0000000000..a78de65dd8 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/weirdioctl.c @@ -0,0 +1,44 @@ + +/* A program which sets a readable fd to have a timeout, and therefore + needs --weird-hacks=ioctl-VTIME in order to run without + blocking. */ + +#include +#include +#include + +int main ( void ) +{ + int c, i; + int res; + struct termio tty, oldtty; + + /** + ** Save the old tty settings, and get rid of echo + ** for the new tty settings + **/ + ioctl(0, TCGETA, &oldtty); + tty = oldtty; + tty.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); + tty.c_cc[VMIN] = 0; + tty.c_cc[VTIME] = 5; + res = ioctl(0, TCSETA, &tty); + printf("first ioctl returned %d\n", res); + + /** + ** Now do whatever stuff you want non-echoed + **/ + i = 0; + while (i++ < 50) { + c = getchar(); + printf("got %d\n", c); + } + + /** + ** Now reset the old settings + **/ + res = ioctl(0, TCSETA, &oldtty); + printf("second ioctl returned %d\n", res); + +return 0; +} diff --git a/VEX/head20041019/memcheck/tests/weirdioctl.stderr.exp b/VEX/head20041019/memcheck/tests/weirdioctl.stderr.exp new file mode 100644 index 0000000000..37a24aac15 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/weirdioctl.stderr.exp @@ -0,0 +1,5 @@ +Syscall param ioctl(TCSET{A,AW,AF}) contains uninitialised or unaddressable byte(s) + at 0x........: ioctl (in /...libc...) + by 0x........: __libc_start_main (...libc...) + by 0x........: ... + Address 0x........ is on thread 1's stack diff --git a/VEX/head20041019/memcheck/tests/weirdioctl.stdout.exp b/VEX/head20041019/memcheck/tests/weirdioctl.stdout.exp new file mode 100644 index 0000000000..f9d4a59346 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/weirdioctl.stdout.exp @@ -0,0 +1,52 @@ +first ioctl returned -1 +got 118 +got 103 +got 111 +got 112 +got 116 +got 115 +got 58 +got 32 +got 45 +got 45 +got 119 +got 101 +got 105 +got 114 +got 100 +got 45 +got 104 +got 97 +got 99 +got 107 +got 115 +got 61 +got 105 +got 111 +got 99 +got 116 +got 108 +got 45 +got 86 +got 84 +got 73 +got 77 +got 69 +got 32 +got 45 +got 113 +got 10 +got 112 +got 114 +got 111 +got 103 +got 58 +got 32 +got 32 +got 32 +got 119 +got 101 +got 105 +got 114 +got 100 +second ioctl returned -1 diff --git a/VEX/head20041019/memcheck/tests/weirdioctl.vgtest b/VEX/head20041019/memcheck/tests/weirdioctl.vgtest new file mode 100644 index 0000000000..8f5d430bde --- /dev/null +++ b/VEX/head20041019/memcheck/tests/weirdioctl.vgtest @@ -0,0 +1,3 @@ +vgopts: --weird-hacks=ioctl-VTIME -q +prog: weirdioctl +args: < weirdioctl.vgtest diff --git a/VEX/head20041019/memcheck/tests/writev.c b/VEX/head20041019/memcheck/tests/writev.c new file mode 100644 index 0000000000..7d405409bb --- /dev/null +++ b/VEX/head20041019/memcheck/tests/writev.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +#define K_1 8192 + +#define NBUFS 2 +#define CHUNK K_1 /* single chunk */ +#define MAX_IOVEC 2 +#define DATA_FILE "writev_data_file" + +static char buf1[K_1]; +static char buf2[K_1]; +static char *buf_list[NBUFS], f_name[]="writev_data_file"; +static int fd; + +struct iovec wr_iovec[MAX_IOVEC] = { + {(caddr_t)-1, CHUNK}, + {(caddr_t)NULL, 0} +}; + +int main(void) +{ + int nbytes; + + /* Fill the buf_list[0] and buf_list[1] with 0 zeros */ + buf_list[0] = buf1; + buf_list[1] = buf2; + memset(buf_list[0], 0, K_1); + memset(buf_list[1], 0, K_1); + + if ((fd = open(f_name, O_WRONLY | O_CREAT, 0666)) < 0) { + fprintf(stderr, "open(2) failed: fname = %s, errno = %d\n", + f_name, errno); + return 1; + } else if ((nbytes = write(fd, buf_list[1], K_1)) != K_1) { + fprintf(stderr, "write(2) failed: nbytes = %d, errno = %d\n", + nbytes, errno); + return 1; + } + if (close(fd) < 0) { + fprintf(stderr, "close failed: errno = %d\n", errno); + return 1; + } + fprintf(stderr, "Test file created.\n"); + if ((fd = open(f_name, O_RDWR, 0666)) < 0) { + fprintf(stderr, "open failed: fname = %s, errno = %d\n", + f_name, errno); + return 1; + } + + lseek(fd, 0, 0); + if (writev(fd, wr_iovec, 2) < 0) { + if (errno == EFAULT) + fprintf(stderr, "Received EFAULT as expected\n"); + else + fprintf(stderr, "Expected EFAULT, got %d\n", errno); + lseek(fd, K_1, 0); + if ((nbytes = read(fd, buf_list[0], CHUNK)) != 0) + fprintf(stderr, "Expected nbytes = 0, got %d\n", nbytes); + } + else + fprintf(stderr, "Error writev returned a positive value\n"); + // Now check invalid vector count + if (writev(fd, wr_iovec, -1) < 0) { + if (errno == EINVAL) + fprintf(stderr, "Received EINVAL as expected\n"); + else + fprintf(stderr, "expected errno = EINVAL, got %d\n", errno); + } + else + fprintf(stderr, "Error writev returned a positive value\n"); + if (readv(fd, wr_iovec, -1) < 0) { + if (errno == EINVAL) + fprintf(stderr, "Received EINVAL as expected\n"); + else + fprintf(stderr, "expected errno = EINVAL, got %d\n", errno); + } + else + fprintf(stderr, "Error writev returned a positive value\n"); + + unlink(f_name); + + return 0; +} + diff --git a/VEX/head20041019/memcheck/tests/writev.stderr.exp b/VEX/head20041019/memcheck/tests/writev.stderr.exp new file mode 100644 index 0000000000..685d9c6383 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/writev.stderr.exp @@ -0,0 +1,25 @@ + +Test file created. +Syscall param writev(vector[...]) contains uninitialised or unaddressable byte(s) + at 0x........: writev (in /...libc...) + by 0x........: main (writev.c:56) + Address 0x........ is not stack'd, malloc'd or (recently) free'd +Received EFAULT as expected + +Syscall param writev(vector) contains uninitialised or unaddressable byte(s) + at 0x........: writev (in /...libc...) + by 0x........: main (writev.c:68) + Address 0x........ is not stack'd, malloc'd or (recently) free'd +Received EINVAL as expected + +Syscall param readv(vector) contains uninitialised or unaddressable byte(s) + at 0x........: readv (in /...libc...) + by 0x........: main (writev.c:76) + Address 0x........ is not stack'd, malloc'd or (recently) free'd +Received EINVAL as expected + +ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0) +malloc/free: in use at exit: 0 bytes in 0 blocks. +malloc/free: 0 allocs, 0 frees, 0 bytes allocated. +For a detailed leak analysis, rerun with: --leak-check=yes +For counts of detected errors, rerun with: -v diff --git a/VEX/head20041019/memcheck/tests/writev.stderr.exp2 b/VEX/head20041019/memcheck/tests/writev.stderr.exp2 new file mode 100644 index 0000000000..001b2b224f --- /dev/null +++ b/VEX/head20041019/memcheck/tests/writev.stderr.exp2 @@ -0,0 +1,25 @@ + +Test file created. +Syscall param writev(vector[...]) contains uninitialised or unaddressable byte(s) + at 0x........: (within /...libc...) + by 0x........: main (writev.c:56) + Address 0x........ is not stack'd, malloc'd or (recently) free'd +Received EFAULT as expected + +Syscall param writev(vector) contains uninitialised or unaddressable byte(s) + at 0x........: (within /...libc...) + by 0x........: main (writev.c:68) + Address 0x........ is not stack'd, malloc'd or (recently) free'd +Received EINVAL as expected + +Syscall param readv(vector) contains uninitialised or unaddressable byte(s) + at 0x........: readv (in /...libc...) + by 0x........: main (writev.c:76) + Address 0x........ is not stack'd, malloc'd or (recently) free'd +Received EINVAL as expected + +ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0) +malloc/free: in use at exit: 0 bytes in 0 blocks. +malloc/free: 0 allocs, 0 frees, 0 bytes allocated. +For a detailed leak analysis, rerun with: --leak-check=yes +For counts of detected errors, rerun with: -v diff --git a/VEX/head20041019/memcheck/tests/writev.vgtest b/VEX/head20041019/memcheck/tests/writev.vgtest new file mode 100644 index 0000000000..0670cc702f --- /dev/null +++ b/VEX/head20041019/memcheck/tests/writev.vgtest @@ -0,0 +1 @@ +prog: writev diff --git a/VEX/head20041019/memcheck/tests/zeropage.c b/VEX/head20041019/memcheck/tests/zeropage.c new file mode 100644 index 0000000000..5a52def7a9 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/zeropage.c @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include + +/* The quick sanity check of Memcheck (and other tools with shadow memory) + relies on the first 64KB of memory never being used. So our mmap() + refuses to touch this area. This program tests for that. */ + +int main(void) +{ + /* mmap(0x0, ... FIXED) should fail */ + int* m = mmap(0x0, 1000000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0); + if (m != (int*)-1) + printf("succeeded?!\n"); + + /* mmap(0x1000, ... FIXED) should fail */ + m = mmap((void*)0x1000, 1000000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0); + if (m != (int*)-1) + printf("succeeded?!\n"); + + /* mmap(0xa000, ... FIXED) should fail */ + m = mmap((void*)0xa000, 1000000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0); + if (m != (int*)-1) + printf("succeeded?!\n"); + + /* mmap(0x10000, ... FIXED) should fail */ + m = mmap((void*)0x10000, 1000000, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANON|MAP_FIXED, -1, 0); + if (m == (int*)-1) + printf("failed?!\n"); + + return 0; +} diff --git a/VEX/head20041019/memcheck/tests/zeropage.stderr.exp b/VEX/head20041019/memcheck/tests/zeropage.stderr.exp new file mode 100644 index 0000000000..d3e3f43bf0 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/zeropage.stderr.exp @@ -0,0 +1,3 @@ +Warning: client syscall mmap2 tried to modify addresses 0x........-0x........ +Warning: client syscall mmap2 tried to modify addresses 0x........-0x........ +Warning: client syscall mmap2 tried to modify addresses 0x........-0x........ diff --git a/VEX/head20041019/memcheck/tests/zeropage.stderr.exp2 b/VEX/head20041019/memcheck/tests/zeropage.stderr.exp2 new file mode 100644 index 0000000000..247d6a93f9 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/zeropage.stderr.exp2 @@ -0,0 +1,3 @@ +Warning: client syscall mmap tried to modify addresses 0x........-0x........ +Warning: client syscall mmap tried to modify addresses 0x........-0x........ +Warning: client syscall mmap tried to modify addresses 0x........-0x........ diff --git a/VEX/head20041019/memcheck/tests/zeropage.vgtest b/VEX/head20041019/memcheck/tests/zeropage.vgtest new file mode 100644 index 0000000000..89038e4d14 --- /dev/null +++ b/VEX/head20041019/memcheck/tests/zeropage.vgtest @@ -0,0 +1,2 @@ +prog: zeropage +vgopts: -q diff --git a/VEX/head20041019/nightly/CVS/Entries b/VEX/head20041019/nightly/CVS/Entries new file mode 100644 index 0000000000..3bf72ab38a --- /dev/null +++ b/VEX/head20041019/nightly/CVS/Entries @@ -0,0 +1,3 @@ +/README.txt/1.1/Wed Feb 25 13:14:39 2004// +D/bin//// +D/conf//// diff --git a/VEX/head20041019/nightly/CVS/Repository b/VEX/head20041019/nightly/CVS/Repository new file mode 100644 index 0000000000..ce2a83a158 --- /dev/null +++ b/VEX/head20041019/nightly/CVS/Repository @@ -0,0 +1 @@ +valgrind/nightly diff --git a/VEX/head20041019/nightly/CVS/Root b/VEX/head20041019/nightly/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/nightly/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/nightly/CVS/Template b/VEX/head20041019/nightly/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/nightly/README.txt b/VEX/head20041019/nightly/README.txt new file mode 100644 index 0000000000..a00dc11e58 --- /dev/null +++ b/VEX/head20041019/nightly/README.txt @@ -0,0 +1,15 @@ + +This directory (nightly/) contains a simple, automatic build-and-test +system for Valgrind, intended to be run by cron. + +Note (importantly) it doesn't test the sources in the tree of which +this directory is a part (viz, nightly/..). Instead it checks out +a complete new tree, builds and tests that independently of the +existing tree. + +To use, choose a tag, probably a machine name, and run + + bin/nightly /path/to/valgrind/nightly tag + +and supply conf/tag.conf and conf/tag.sendmail. + diff --git a/VEX/head20041019/nightly/bin/CVS/Entries b/VEX/head20041019/nightly/bin/CVS/Entries new file mode 100644 index 0000000000..93b479544e --- /dev/null +++ b/VEX/head20041019/nightly/bin/CVS/Entries @@ -0,0 +1,2 @@ +/nightly/1.1/Wed Feb 25 13:14:39 2004// +D diff --git a/VEX/head20041019/nightly/bin/CVS/Repository b/VEX/head20041019/nightly/bin/CVS/Repository new file mode 100644 index 0000000000..072f2cbe4a --- /dev/null +++ b/VEX/head20041019/nightly/bin/CVS/Repository @@ -0,0 +1 @@ +valgrind/nightly/bin diff --git a/VEX/head20041019/nightly/bin/CVS/Root b/VEX/head20041019/nightly/bin/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/nightly/bin/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/nightly/bin/CVS/Template b/VEX/head20041019/nightly/bin/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/nightly/bin/nightly b/VEX/head20041019/nightly/bin/nightly new file mode 100755 index 0000000000..c9d324119c --- /dev/null +++ b/VEX/head20041019/nightly/bin/nightly @@ -0,0 +1,50 @@ +#!/bin/sh + +# Automated build and test for Valgrind. +# Use: two args, first is path to top of ValgrindABT tree +# second is name of machine + +ABT_TOP=$1 +ABT_MACHINE=$2 + +ABT_START=`date "+%F %H:%M:%S %Z"` + +cd $ABT_TOP + +source $ABT_TOP/conf/$ABT_MACHINE.conf + +rm -rf log.verbose log.short valgrind + +echo > log.short +echo > log.verbose + +echo "Nightly build on" $ABT_MACHINE "(" $ABT_DETAILS ") started at" $ABT_START >> log.short +echo >> log.short + +echo "Nightly build on" $ABT_MACHINE "(" $ABT_DETAILS ") started at" $ABT_START >> log.verbose +echo >> log.verbose + +echo -n " Checking out source tree ... " >> log.short +cvs co valgrind 2>&1 >> log.verbose +echo "done" >> log.short + +echo -n " Configuring ... " >> log.short +(cd valgrind && ./autogen.sh 2>&1 && ./configure --prefix=`pwd`/Inst 2>&1 ) >> log.verbose +echo "done" >> log.short + +echo -n " Building ... " >> log.short +(cd valgrind && make install 2>&1 ) >> log.verbose +echo "done" >> log.short + +echo -n " Running regression tests ... " >> log.short +(cd valgrind && make regtest 2>&1 ) >> log.verbose +echo "done" >> log.short + +echo >> log.short +echo "Last 20 lines of log.verbose follow" >> log.short +echo >> log.short +tail -20 log.verbose >> log.short + +$ABT_TOP/conf/$ABT_MACHINE.sendmail "$ABT_START nightly build ($ABT_MACHINE, $ABT_DETAILS)" \ + $ABT_TOP/log.short + diff --git a/VEX/head20041019/nightly/conf/CVS/Entries b/VEX/head20041019/nightly/conf/CVS/Entries new file mode 100644 index 0000000000..995497f943 --- /dev/null +++ b/VEX/head20041019/nightly/conf/CVS/Entries @@ -0,0 +1,3 @@ +/nemesis.conf/1.1/Wed Feb 25 13:14:39 2004// +/nemesis.sendmail/1.1/Wed Feb 25 13:14:39 2004// +D diff --git a/VEX/head20041019/nightly/conf/CVS/Repository b/VEX/head20041019/nightly/conf/CVS/Repository new file mode 100644 index 0000000000..f6943744a2 --- /dev/null +++ b/VEX/head20041019/nightly/conf/CVS/Repository @@ -0,0 +1 @@ +valgrind/nightly/conf diff --git a/VEX/head20041019/nightly/conf/CVS/Root b/VEX/head20041019/nightly/conf/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/nightly/conf/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/nightly/conf/CVS/Template b/VEX/head20041019/nightly/conf/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/nightly/conf/nemesis.conf b/VEX/head20041019/nightly/conf/nemesis.conf new file mode 100644 index 0000000000..4aef6c7c79 --- /dev/null +++ b/VEX/head20041019/nightly/conf/nemesis.conf @@ -0,0 +1,6 @@ + +# Specifics for nemesis (SuSE 9.0, VIA Nehemiah) + +export ABT_DETAILS="SuSE 9.0" +export CVSROOT=":ext:jseward@cvs.kde.org:/home/kde" +export CVS_RSH=ssh diff --git a/VEX/head20041019/nightly/conf/nemesis.sendmail b/VEX/head20041019/nightly/conf/nemesis.sendmail new file mode 100755 index 0000000000..ca6af71e3c --- /dev/null +++ b/VEX/head20041019/nightly/conf/nemesis.sendmail @@ -0,0 +1,5 @@ + +#use: subject file-to-mail + +/usr/bin/mail -s "$1" -R jseward@acm.org -r jseward@acm.org valgrind-developers@lists.sourceforge.net < $2 + \ No newline at end of file diff --git a/VEX/head20041019/none/.cvsignore b/VEX/head20041019/none/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/none/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/none/CVS/Entries b/VEX/head20041019/none/CVS/Entries new file mode 100644 index 0000000000..7acd9a1df7 --- /dev/null +++ b/VEX/head20041019/none/CVS/Entries @@ -0,0 +1,5 @@ +/.cvsignore/1.1/Mon Sep 23 11:36:38 2002// +/Makefile.am/1.46/Wed Sep 1 23:20:49 2004// +/nl_main.c/1.20/Thu Sep 2 08:51:43 2004// +D/docs//// +D/tests//// diff --git a/VEX/head20041019/none/CVS/Repository b/VEX/head20041019/none/CVS/Repository new file mode 100644 index 0000000000..3a215703b0 --- /dev/null +++ b/VEX/head20041019/none/CVS/Repository @@ -0,0 +1 @@ +valgrind/none diff --git a/VEX/head20041019/none/CVS/Root b/VEX/head20041019/none/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/none/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/none/CVS/Template b/VEX/head20041019/none/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/none/Makefile.am b/VEX/head20041019/none/Makefile.am new file mode 100644 index 0000000000..a4fc9e6faf --- /dev/null +++ b/VEX/head20041019/none/Makefile.am @@ -0,0 +1,7 @@ +include $(top_srcdir)/Makefile.tool.am + +val_PROGRAMS = vgskin_none.so + +vgskin_none_so_SOURCES = nl_main.c +vgskin_none_so_LDFLAGS = -shared + diff --git a/VEX/head20041019/none/docs/.cvsignore b/VEX/head20041019/none/docs/.cvsignore new file mode 100644 index 0000000000..3dda72986f --- /dev/null +++ b/VEX/head20041019/none/docs/.cvsignore @@ -0,0 +1,2 @@ +Makefile.in +Makefile diff --git a/VEX/head20041019/none/docs/CVS/Entries b/VEX/head20041019/none/docs/CVS/Entries new file mode 100644 index 0000000000..76fd4d008b --- /dev/null +++ b/VEX/head20041019/none/docs/CVS/Entries @@ -0,0 +1,4 @@ +/.cvsignore/1.1/Thu Oct 3 09:12:58 2002// +/Makefile.am/1.3/Wed Aug 25 11:40:07 2004// +/nl_main.html/1.4/Sun Jan 4 16:43:23 2004// +D diff --git a/VEX/head20041019/none/docs/CVS/Repository b/VEX/head20041019/none/docs/CVS/Repository new file mode 100644 index 0000000000..2c351a4c73 --- /dev/null +++ b/VEX/head20041019/none/docs/CVS/Repository @@ -0,0 +1 @@ +valgrind/none/docs diff --git a/VEX/head20041019/none/docs/CVS/Root b/VEX/head20041019/none/docs/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/none/docs/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/none/docs/CVS/Template b/VEX/head20041019/none/docs/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/none/docs/Makefile.am b/VEX/head20041019/none/docs/Makefile.am new file mode 100644 index 0000000000..bbd22965fa --- /dev/null +++ b/VEX/head20041019/none/docs/Makefile.am @@ -0,0 +1,3 @@ +docdir = $(datadir)/doc/valgrind + +dist_doc_DATA = nl_main.html diff --git a/VEX/head20041019/none/docs/nl_main.html b/VEX/head20041019/none/docs/nl_main.html new file mode 100644 index 0000000000..a431944cc4 --- /dev/null +++ b/VEX/head20041019/none/docs/nl_main.html @@ -0,0 +1,57 @@ + + + + Cachegrind + + + + + +

    Nulgrind

    +
    This manual was last updated on 2002-10-02
    +

    + +

    +njn25@cam.ac.uk
    +Copyright © 2000-2004 Nicholas Nethercote +

    +Nulgrind is licensed under the GNU General Public License, +version 2
    +Nulgrind is a Valgrind tool that does not very much at all. +

    + +

    + +

    1  Nulgrind

    + +Nulgrind is the minimal tool for Valgrind. It does no initialisation or +finalisation, and adds no instrumentation to the program's code. It is mainly +of use for Valgrind's developers for debugging and regression testing. +

    +Nonetheless you can run programs with Nulgrind. They will run roughly 5-10 +times more slowly than normal, for no useful effect. Note that you need to use +the option --tool=none to run Nulgrind (ie. not +--tool=nulgrind). + +


    + + + diff --git a/VEX/head20041019/none/nl_main.c b/VEX/head20041019/none/nl_main.c new file mode 100644 index 0000000000..f9b2f466f3 --- /dev/null +++ b/VEX/head20041019/none/nl_main.c @@ -0,0 +1,62 @@ + +/*--------------------------------------------------------------------*/ +/*--- Nulgrind: The null tool. nl_main.c ---*/ +/*--------------------------------------------------------------------*/ + +/* + This file is part of Nulgrind, the simplest possible Valgrind tool, + which does nothing. + + Copyright (C) 2002-2004 Nicholas Nethercote + njn25@cam.ac.uk + + 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 2 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA. + + The GNU General Public License is contained in the file COPYING. +*/ + +#include "tool.h" + +void SK_(pre_clo_init)(void) +{ + VG_(details_name) ("Nulgrind"); + VG_(details_version) (NULL); + VG_(details_description) ("a binary JIT-compiler"); + VG_(details_copyright_author)( + "Copyright (C) 2002-2004, and GNU GPL'd, by Nicholas Nethercote."); + VG_(details_bug_reports_to) (VG_BUGS_TO); + + /* No needs, no core events to track */ +} + +void SK_(post_clo_init)(void) +{ +} + +UCodeBlock* SK_(instrument)(UCodeBlock* cb, Addr a) +{ + return cb; +} + +void SK_(fini)(Int exitcode) +{ +} + +VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 0) + +/*--------------------------------------------------------------------*/ +/*--- end ---*/ +/*--------------------------------------------------------------------*/ diff --git a/VEX/head20041019/none/tests/.cvsignore b/VEX/head20041019/none/tests/.cvsignore new file mode 100644 index 0000000000..c1bcb1ae7f --- /dev/null +++ b/VEX/head20041019/none/tests/.cvsignore @@ -0,0 +1,77 @@ +Makefile.in +Makefile +args +badseg +bitfield1 +bt_everything +bt_literal +closeall +coolo_sigaction +coolo_strlen +cpuid +dastest +discard +exec-sigmask +execve +fcntl_setown +floored +fork +fpu_lazy_eflags +fucomip +gxx304 +insn_basic +insn_basic.c +insn_fpu +insn_fpu.c +insn_cmov +insn_cmov.c +insn_mmx +insn_mmx.c +insn_mmxext +insn_mmxext.c +insn_sse +insn_sse.c +insn_sse2 +insn_sse2.c +int +map_unmap +mq +mremap +munmap_exe +pluto +pth_blockedsig +rcl_assert +rcrl +readline1 +resolv +rlimit_nofile +seg_override +sem +semlimit +sha1_test +shortpush +shorts +smc1 +susphello +syscall-restart1 +syscall-restart2 +system +pth_atfork1 +pth_cancel1 +pth_cancel2 +pth_cvsimple +pth_mutexspeed +pth_once +pth_semaphore1 +pth_simple_mutex +pth_simple_threads +pth_specific +pth_yield +pushpopseg +*.stdout.diff +*.stderr.diff +*.stdout.out +*.stderr.out +tls +vgcore.pid* +yield diff --git a/VEX/head20041019/none/tests/CVS/Entries b/VEX/head20041019/none/tests/CVS/Entries new file mode 100644 index 0000000000..6d4ca95b73 --- /dev/null +++ b/VEX/head20041019/none/tests/CVS/Entries @@ -0,0 +1,219 @@ +/.cvsignore/1.19/Wed Sep 1 23:34:37 2004// +/Makefile.am/1.43/Sat Aug 14 18:52:27 2004// +/args.c/1.1/Mon Feb 24 22:05:36 2003// +/args.stderr.exp/1.1/Mon Feb 24 22:05:37 2003// +/args.stdout.exp/1.1/Mon Feb 24 22:05:37 2003// +/args.vgtest/1.1/Mon Feb 24 22:05:38 2003// +/badseg.c/1.2/Tue Mar 16 10:51:40 2004// +/badseg.stderr.exp/1.1/Tue Mar 16 09:49:08 2004// +/badseg.stdout.exp/1.1/Tue Mar 16 09:49:08 2004// +/badseg.vgtest/1.1/Tue Mar 16 09:49:08 2004// +/bitfield1.c/1.3/Sat Jan 3 14:24:42 2004// +/bitfield1.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/bitfield1.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/bt_everything.c/1.2/Mon Sep 23 09:36:25 2002// +/bt_everything.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/bt_everything.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/bt_everything.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/bt_literal.c/1.2/Mon Sep 23 09:36:25 2002// +/bt_literal.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/bt_literal.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/bt_literal.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/closeall.c/1.1/Sun Feb 15 16:12:35 2004// +/closeall.stderr.exp/1.1/Sun Feb 15 16:12:35 2004// +/closeall.vgtest/1.1/Sun Feb 15 16:12:35 2004// +/cmdline1.stderr.exp/1.1/Tue Jun 15 10:54:39 2004// +/cmdline1.stdout.exp/1.7/Sat Aug 21 11:10:44 2004// +/cmdline1.vgtest/1.1/Tue Jun 15 10:54:39 2004// +/cmdline2.stderr.exp/1.1/Tue Jun 15 10:54:39 2004// +/cmdline2.stdout.exp/1.7/Sat Aug 21 11:10:44 2004// +/cmdline2.vgtest/1.1/Tue Jun 15 10:54:39 2004// +/cmdline3.stderr.exp/1.1/Tue Jun 15 10:54:39 2004// +/cmdline3.vgtest/1.1/Tue Jun 15 10:54:39 2004// +/cmdline4.stderr.exp/1.1/Tue Jun 15 10:54:39 2004// +/cmdline4.vgtest/1.1/Tue Jun 15 10:54:39 2004// +/cmdline5.stderr.exp/1.1/Tue Jun 15 10:54:39 2004// +/cmdline5.vgtest/1.1/Tue Jun 15 10:54:39 2004// +/cmdline6.stderr.exp/1.1/Tue Jun 15 10:54:39 2004// +/cmdline6.vgtest/1.1/Tue Jun 15 10:54:39 2004// +/coolo_sigaction.cpp/1.2/Mon Sep 23 09:36:25 2002// +/coolo_sigaction.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/coolo_sigaction.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/coolo_sigaction.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/coolo_strlen.c/1.2/Mon Sep 23 09:36:25 2002// +/coolo_strlen.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/coolo_strlen.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/cpuid.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/cpuid.stdout.exp/1.4/Sun Jun 29 10:12:58 2003// +/cpuid.vgtest/1.3/Sun Jun 29 10:12:58 2003// +/cpuid_c.c/1.2/Mon Sep 23 09:36:25 2002// +/cpuid_s.s/1.2/Mon Sep 23 09:36:25 2002// +/dastest.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/dastest.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/dastest.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/dastest_c.c/1.2/Mon Sep 23 09:36:25 2002// +/dastest_s.s/1.2/Mon Sep 23 09:36:25 2002// +/discard.c/1.3/Thu Nov 13 23:02:16 2003// +/discard.stderr.exp/1.2/Sun Dec 15 02:51:21 2002// +/discard.stdout.exp/1.1/Fri Oct 4 14:16:37 2002// +/discard.vgtest/1.1/Fri Oct 4 14:16:37 2002// +/exec-sigmask.c/1.1/Fri Jan 16 05:37:46 2004// +/exec-sigmask.stderr.exp/1.1/Fri Jan 16 02:17:29 2004// +/exec-sigmask.stdout.exp/1.1/Fri Jan 16 02:19:15 2004// +/exec-sigmask.vgtest/1.1/Wed Jan 21 01:20:38 2004// +/execve.c/1.2/Wed Jul 21 16:23:38 2004// +/execve.stderr.exp/1.1/Sat Jun 19 13:02:34 2004// +/execve.stdout.exp/1.1/Sat Jun 19 13:02:34 2004// +/execve.vgtest/1.1/Sat Jun 19 13:02:34 2004// +/fcntl_setown.c/1.1/Thu Jul 29 21:20:11 2004// +/fcntl_setown.stderr.exp/1.1/Thu Jul 29 21:20:11 2004// +/fcntl_setown.stdout.exp/1.1/Thu Jul 29 21:20:11 2004// +/fcntl_setown.vgtest/1.1/Thu Jul 29 21:20:11 2004// +/filter_cpuid/1.1/Sun Jun 29 10:12:58 2003// +/filter_int/1.1/Sat Mar 6 12:53:24 2004// +/filter_none_discards/1.1/Wed Oct 30 15:22:03 2002// +/filter_stderr/1.3/Mon Sep 23 11:21:55 2002// +/floored.c/1.2/Mon Sep 23 09:36:25 2002// +/floored.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/floored.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/floored.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/fork.c/1.4/Tue Jan 6 21:46:02 2004// +/fork.stderr.exp/1.4/Tue Jan 6 21:46:02 2004// +/fork.stdout.exp/1.3/Fri Sep 27 10:38:20 2002// +/fork.vgtest/1.3/Tue Jan 6 21:46:02 2004// +/fpu_lazy_eflags.c/1.1/Tue Apr 8 10:04:31 2003// +/fpu_lazy_eflags.stderr.exp/1.1/Tue Apr 8 10:04:32 2003// +/fpu_lazy_eflags.stdout.exp/1.1/Tue Apr 8 10:04:32 2003// +/fpu_lazy_eflags.vgtest/1.1/Tue Apr 8 10:04:32 2003// +/fucomip.c/1.2/Mon Sep 23 09:36:25 2002// +/fucomip.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/fucomip.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/gen_insn_test.pl/1.8/Sun Jul 25 15:18:21 2004// +/gxx304.cpp/1.2/Mon Sep 23 09:36:25 2002// +/gxx304.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/gxx304.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/insn_basic.def/1.1/Thu Feb 12 08:35:57 2004// +/insn_basic.stderr.exp/1.1/Thu Feb 12 08:35:57 2004// +/insn_basic.stdout.exp/1.1/Thu Feb 12 08:35:57 2004// +/insn_basic.vgtest/1.1/Thu Feb 12 08:35:57 2004// +/insn_cmov.def/1.1/Thu Feb 12 08:35:57 2004// +/insn_cmov.stderr.exp/1.1/Thu Feb 12 08:35:57 2004// +/insn_cmov.stdout.exp/1.1/Thu Feb 12 08:35:57 2004// +/insn_cmov.vgtest/1.1/Thu Feb 12 08:35:57 2004// +/insn_fpu.def/1.4/Wed Mar 31 22:47:52 2004// +/insn_fpu.stderr.exp/1.1/Sat Mar 27 18:02:37 2004// +/insn_fpu.stdout.exp/1.4/Wed Mar 31 22:47:52 2004// +/insn_fpu.vgtest/1.1/Sat Mar 27 18:02:37 2004// +/insn_mmx.def/1.2/Wed Feb 11 23:33:29 2004// +/insn_mmx.stderr.exp/1.1/Tue Jan 20 09:24:53 2004// +/insn_mmx.stdout.exp/1.1/Tue Jan 20 09:24:53 2004// +/insn_mmx.vgtest/1.1/Tue Jan 20 09:24:53 2004// +/insn_mmxext.def/1.2/Sun Jul 25 15:18:21 2004// +/insn_mmxext.stderr.exp/1.1/Thu Feb 12 08:35:57 2004// +/insn_mmxext.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_mmxext.vgtest/1.1/Thu Feb 12 08:35:57 2004// +/insn_sse.def/1.3/Sun Jul 25 15:18:21 2004// +/insn_sse.stderr.exp/1.1/Tue Jan 20 09:24:53 2004// +/insn_sse.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_sse.vgtest/1.1/Tue Jan 20 09:24:53 2004// +/insn_sse2.def/1.3/Sun Jul 25 15:18:21 2004// +/insn_sse2.stderr.exp/1.1/Tue Jan 20 09:24:53 2004// +/insn_sse2.stdout.exp/1.2/Sun Jul 25 15:18:21 2004// +/insn_sse2.vgtest/1.1/Tue Jan 20 09:24:53 2004// +/int.c/1.1/Sat Mar 6 12:53:24 2004// +/int.stderr.exp/1.2/Tue Mar 16 11:03:09 2004// +/int.stdout.exp/1.1/Sat Mar 6 12:53:24 2004// +/int.vgtest/1.1/Sat Mar 6 12:53:24 2004// +/map_unmap.c/1.4/Mon Jan 19 21:47:52 2004// +/map_unmap.stderr.exp/1.1/Fri Dec 19 21:56:04 2003// +/map_unmap.stdout.exp/1.1/Fri Dec 19 21:56:04 2003// +/map_unmap.vgtest/1.1/Fri Dec 19 21:56:04 2003// +/mq.c/1.1/Sat Aug 14 18:52:27 2004// +/mq.stderr.exp/1.1/Sat Aug 14 18:52:27 2004// +/mq.vgtest/1.1/Sat Aug 14 18:52:27 2004// +/mremap.c/1.1/Mon Dec 22 08:48:50 2003// +/mremap.stderr.exp/1.1/Mon Dec 22 08:48:50 2003// +/mremap.stdout.exp/1.1/Mon Dec 22 08:48:50 2003// +/mremap.vgtest/1.1/Mon Dec 22 08:48:50 2003// +/munmap_exe.c/1.3/Sat Jan 3 14:24:42 2004// +/munmap_exe.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/munmap_exe.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/pth_blockedsig.c/1.2/Mon Sep 23 09:36:25 2002// +/pth_blockedsig.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/pth_blockedsig.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/pth_blockedsig.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/pth_specific.c/1.2/Mon Sep 23 09:36:25 2002// +/pushpopseg.c/1.2/Mon Mar 15 12:58:55 2004// +/pushpopseg.stderr.exp/1.1/Thu Mar 4 23:36:58 2004// +/pushpopseg.stdout.exp/1.1/Thu Mar 4 23:36:58 2004// +/pushpopseg.vgtest/1.1/Thu Mar 4 23:36:58 2004// +/rcl_assert.S/1.2/Mon Sep 23 09:36:25 2002// +/rcl_assert.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/rcl_assert.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/rcrl.c/1.2/Mon Sep 23 09:36:25 2002// +/rcrl.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/rcrl.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/rcrl.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/readline1.c/1.2/Mon Sep 23 09:36:25 2002// +/readline1.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/readline1.stdout.exp/1.2/Mon Sep 23 09:36:25 2002// +/readline1.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/resolv.c/1.3/Sat Jan 3 14:24:42 2004// +/resolv.stderr.exp/1.1/Tue Oct 1 11:45:34 2002// +/resolv.stdout.exp/1.2/Thu Apr 3 00:50:21 2003// +/resolv.vgtest/1.1/Tue Oct 1 11:45:34 2002// +/rlimit_nofile.c/1.1/Sat Jun 26 11:27:52 2004// +/rlimit_nofile.stderr.exp/1.1/Sat Jun 26 11:27:52 2004// +/rlimit_nofile.stdout.exp/1.1/Sat Jun 26 11:27:52 2004// +/rlimit_nofile.vgtest/1.1/Sat Jun 26 11:27:52 2004// +/seg_override.c/1.2/Wed Jan 21 01:27:27 2004// +/seg_override.stderr.exp/1.1/Wed Oct 2 10:36:46 2002// +/seg_override.stdout.exp/1.1/Wed Oct 2 10:36:46 2002// +/seg_override.vgtest/1.1/Wed Oct 2 10:36:46 2002// +/sem.c/1.4/Mon Jun 14 17:27:41 2004// +/sem.stderr.exp/1.1/Sun Jun 13 14:23:00 2004// +/sem.stdout.exp/1.1/Sun Jun 13 14:23:00 2004// +/sem.vgtest/1.1/Sun Jun 13 14:23:00 2004// +/semlimit.c/1.1/Tue Mar 23 19:48:54 2004// +/semlimit.stderr.exp/1.1/Tue Mar 23 19:48:54 2004// +/semlimit.stdout.exp/1.1/Tue Mar 23 19:48:54 2004// +/semlimit.vgtest/1.1/Tue Mar 23 19:48:54 2004// +/sha1_test.c/1.4/Sat Jan 3 14:24:42 2004// +/sha1_test.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/sha1_test.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/shortpush.c/1.2/Mon Sep 23 09:36:25 2002// +/shortpush.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/shortpush.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/shorts.c/1.2/Mon Sep 23 09:36:25 2002// +/shorts.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/shorts.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/smc1.c/1.3/Tue Dec 16 02:05:15 2003// +/smc1.stderr.exp/1.2/Mon Sep 23 09:36:25 2002// +/smc1.stdout.exp/1.3/Tue Dec 16 02:05:15 2003// +/smc1.vgtest/1.2/Mon Sep 23 09:36:25 2002// +/susphello.c/1.1/Wed Mar 17 18:20:46 2004// +/susphello.stderr.exp/1.1/Wed Mar 17 18:20:46 2004// +/susphello.stdout.exp/1.1/Wed Mar 17 18:20:46 2004// +/susphello.vgtest/1.1/Wed Mar 17 18:20:46 2004// +/syscall-restart1.c/1.1/Fri Jan 16 02:15:23 2004// +/syscall-restart1.stderr.exp/1.1/Fri Jan 16 02:15:23 2004// +/syscall-restart1.stdout.exp/1.1/Fri Jan 16 02:15:23 2004// +/syscall-restart1.vgtest/1.1/Fri Jan 16 02:15:23 2004// +/syscall-restart2.c/1.1/Fri Jan 16 02:15:23 2004// +/syscall-restart2.stderr.exp/1.1/Fri Jan 16 02:15:23 2004// +/syscall-restart2.stdout.exp/1.1/Fri Jan 16 02:15:23 2004// +/syscall-restart2.vgtest/1.1/Fri Jan 16 02:15:23 2004// +/system.c/1.1/Sun Feb 15 16:15:05 2004// +/system.stderr.exp/1.1/Sun Feb 15 16:15:05 2004// +/system.vgtest/1.1/Sun Feb 15 16:15:05 2004// +/tls.c/1.1/Wed Jan 21 01:27:27 2004// +/tls.stderr.exp/1.1/Wed Jan 21 01:27:27 2004// +/tls.stdout.exp/1.1/Wed Jan 21 01:27:27 2004// +/tls2.c/1.1/Wed Jan 21 01:27:27 2004// +/tls2_so.c/1.1/Wed Jan 21 01:27:27 2004// +/tls_so.c/1.1/Wed Jan 21 01:27:27 2004// +/yield.c/1.1/Thu Dec 18 09:08:51 2003// +/yield.stderr.exp/1.1/Thu Dec 18 09:08:51 2003// +/yield.stdout.exp/1.1/Thu Dec 18 09:08:51 2003// +/yield.vgtest/1.1/Thu Dec 18 09:08:51 2003// +D diff --git a/VEX/head20041019/none/tests/CVS/Repository b/VEX/head20041019/none/tests/CVS/Repository new file mode 100644 index 0000000000..4c318ac4ba --- /dev/null +++ b/VEX/head20041019/none/tests/CVS/Repository @@ -0,0 +1 @@ +valgrind/none/tests diff --git a/VEX/head20041019/none/tests/CVS/Root b/VEX/head20041019/none/tests/CVS/Root new file mode 100644 index 0000000000..0cb7c52e1e --- /dev/null +++ b/VEX/head20041019/none/tests/CVS/Root @@ -0,0 +1 @@ +:ext:jseward@cvs.kde.org:/home/kde diff --git a/VEX/head20041019/none/tests/CVS/Template b/VEX/head20041019/none/tests/CVS/Template new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/none/tests/Makefile.am b/VEX/head20041019/none/tests/Makefile.am new file mode 100644 index 0000000000..6ddfca3c88 --- /dev/null +++ b/VEX/head20041019/none/tests/Makefile.am @@ -0,0 +1,167 @@ +noinst_SCRIPTS = filter_cpuid filter_none_discards filter_stderr filter_int gen_insn_test.pl + +CLEANFILES = $(addsuffix .c,$(INSN_TESTS)) +INSN_TESTS=insn_basic insn_fpu insn_cmov insn_mmx insn_mmxext insn_sse insn_sse2 + +EXTRA_DIST = $(noinst_SCRIPTS) \ + args.stderr.exp args.stdout.exp args.vgtest \ + badseg.stderr.exp badseg.stdout.exp badseg.vgtest \ + bitfield1.stderr.exp bitfield1.vgtest \ + bt_everything.stderr.exp \ + bt_everything.stdout.exp bt_everything.vgtest \ + bt_literal.stderr.exp bt_literal.stdout.exp \ + bt_literal.vgtest \ + closeall.stderr.exp closeall.vgtest \ + cmdline1.stderr.exp cmdline1.stdout.exp cmdline1.vgtest \ + cmdline2.stderr.exp cmdline2.stdout.exp cmdline2.vgtest \ + cmdline3.stderr.exp cmdline3.vgtest \ + cmdline4.stderr.exp cmdline4.vgtest \ + cmdline5.stderr.exp cmdline5.vgtest \ + cmdline6.stderr.exp cmdline6.vgtest \ + coolo_sigaction.stderr.exp \ + coolo_sigaction.stdout.exp coolo_sigaction.vgtest \ + coolo_strlen.stderr.exp coolo_strlen.vgtest \ + cpuid.stderr.exp cpuid.stdout.exp cpuid.vgtest \ + dastest.stderr.exp dastest.stdout.exp \ + dastest.vgtest \ + discard.stderr.exp discard.stdout.exp \ + discard.vgtest \ + exec-sigmask.vgtest exec-sigmask.stdout.exp exec-sigmask.stderr.exp \ + execve.vgtest execve.stdout.exp execve.stderr.exp \ + fcntl_setown.vgtest fcntl_setown.stdout.exp fcntl_setown.stderr.exp \ + floored.stderr.exp floored.stdout.exp \ + floored.vgtest \ + fork.stderr.exp fork.stdout.exp fork.vgtest \ + fpu_lazy_eflags.stderr.exp fpu_lazy_eflags.stdout.exp \ + fpu_lazy_eflags.vgtest \ + fucomip.stderr.exp fucomip.vgtest \ + gxx304.stderr.exp gxx304.vgtest \ + $(addsuffix .stderr.exp,$(INSN_TESTS)) \ + $(addsuffix .stdout.exp,$(INSN_TESTS)) \ + $(addsuffix .vgtest,$(INSN_TESTS)) \ + int.stderr.exp int.stdout.exp int.vgtest \ + map_unmap.stderr.exp map_unmap.stdout.exp map_unmap.vgtest \ + mq.stderr.exp mq.vgtest \ + mremap.stderr.exp mremap.stdout.exp mremap.vgtest \ + munmap_exe.stderr.exp munmap_exe.vgtest \ + pth_blockedsig.stderr.exp \ + pth_blockedsig.stdout.exp pth_blockedsig.vgtest \ + pushpopseg.stderr.exp pushpopseg.stdout.exp pushpopseg.vgtest \ + rcl_assert.stderr.exp rcl_assert.vgtest \ + rcrl.stderr.exp rcrl.stdout.exp rcrl.vgtest \ + readline1.stderr.exp readline1.stdout.exp \ + readline1.vgtest \ + resolv.stderr.exp resolv.stdout.exp resolv.vgtest \ + rlimit_nofile.stderr.exp rlimit_nofile.stdout.exp rlimit_nofile.vgtest \ + seg_override.stderr.exp \ + seg_override.stdout.exp seg_override.vgtest \ + sem.stderr.exp sem.stdout.exp sem.vgtest \ + semlimit.stderr.exp semlimit.stdout.exp semlimit.vgtest \ + susphello.stdout.exp susphello.stderr.exp susphello.vgtest \ + sha1_test.stderr.exp sha1_test.vgtest \ + shortpush.stderr.exp shortpush.vgtest \ + shorts.stderr.exp shorts.vgtest \ + tls.stderr.exp tls.stdout.exp \ + smc1.stderr.exp smc1.stdout.exp smc1.vgtest \ + syscall-restart1.vgtest syscall-restart1.stdout.exp syscall-restart1.stderr.exp \ + syscall-restart2.vgtest syscall-restart2.stdout.exp syscall-restart2.stderr.exp \ + system.stderr.exp system.vgtest \ + yield.stderr.exp yield.stdout.exp yield.vgtest + +check_PROGRAMS = \ + args badseg bitfield1 bt_everything bt_literal closeall coolo_strlen \ + cpuid dastest discard exec-sigmask execve fcntl_setown floored fork \ + fpu_lazy_eflags fucomip $(INSN_TESTS) \ + int munmap_exe map_unmap mq mremap rcl_assert rcrl readline1 \ + resolv rlimit_nofile seg_override sem semlimit sha1_test \ + shortpush shorts smc1 susphello pth_blockedsig pushpopseg \ + syscall-restart1 syscall-restart2 system \ + coolo_sigaction gxx304 yield + +AM_CFLAGS = $(WERROR) -Winline -Wall -Wshadow -g -I$(top_srcdir)/include +AM_CXXFLAGS = $(AM_CFLAGS) + +# generic C ones +args_SOURCES = args.c +badseg_SOURCES = badseg.c +bitfield1_SOURCES = bitfield1.c +bt_everything_SOURCES = bt_everything.c +bt_literal_SOURCES = bt_literal.c +closeall_SOURCES = closeall.c +cpuid_SOURCES = cpuid_c.c cpuid_s.s +coolo_strlen_SOURCES = coolo_strlen.c +dastest_SOURCES = dastest_c.c dastest_s.s +discard_SOURCES = discard.c +exec_sigmask_SOURCES = exec-sigmask.c +execve_SOURCES = execve.c +fcntl_setown_SOURCES = fcntl_setown.c +fork_SOURCES = fork.c +floored_SOURCES = floored.c +floored_LDADD = -lm +fpu_lazy_eflags_SOURCES = fpu_lazy_eflags.c +fucomip_SOURCES = fucomip.c +insn_basic_SOURCES = insn_basic.def +insn_basic_LDADD = -lm +insn_fpu_SOURCES = insn_fpu.def +insn_fpu_LDADD = -lm +insn_cmov_SOURCES = insn_cmov.def +insn_cmov_LDADD = -lm +insn_mmx_SOURCES = insn_mmx.def +insn_mmx_LDADD = -lm +insn_mmxext_SOURCES = insn_mmxext.def +insn_mmxext_LDADD = -lm +insn_sse_SOURCES = insn_sse.def +insn_sse_LDADD = -lm +insn_sse2_SOURCES = insn_sse2.def +insn_sse2_LDADD = -lm +int_SOURCES = int.c +map_unmap_SOURCES = map_unmap.c +mq_SOURCES = mq.c +mq_LDADD = -lrt +mremap_SOURCES = mremap.c +munmap_exe_SOURCES = munmap_exe.c +pushpopseg_SOURCES = pushpopseg.c +rcl_assert_SOURCES = rcl_assert.S +rcrl_SOURCES = rcrl.c +readline1_SOURCES = readline1.c +resolv_SOURCES = resolv.c +rlimit_nofile_SOURCES = rlimit_nofile.c +seg_override_SOURCES = seg_override.c +sem_SOURCES = sem.c +semlimit_SOURCES = semlimit.c +semlimit_LDADD = -lpthread +smc1_SOURCES = smc1.c +sha1_test_SOURCES = sha1_test.c +shortpush_SOURCES = shortpush.c +shorts_SOURCES = shorts.c +susphello_SOURCES = susphello.c +susphello_LDADD = -lpthread +syscall_restart1_SOURCES = syscall-restart1.c +syscall_restart2_SOURCES = syscall-restart2.c +system_SOURCES = system.c +#tls_SOURCES = tls.c tls2.c +#tls_DEPENDENCIES = tls.so +#tls_LDFLAGS = -Wl,-rpath,$(srcdir) +#tls_LDADD = tls.so -lpthread +#tls_so_SOURCES = tls_so.c +#tls_so_LDADD = tls2.so +#tls_so_DEPENDENCIES = tls2.so +#tls_so_LDFLAGS = -Wl,-rpath,$(srcdir) -shared +#tls2_so_SOURCES = tls2_so.c +#tls2_so_LDFLAGS = -shared +yield_SOURCES = yield.c +yield_LDADD = -lpthread + +# pthread C ones +pth_blockedsig_SOURCES = pth_blockedsig.c +pth_blockedsig_LDADD = -lpthread + +# generic C++ ones +coolo_sigaction_SOURCES = coolo_sigaction.cpp +gxx304_SOURCES = gxx304.cpp + +# must be built with these flags -- bug only occurred with them +fpu_lazy_eflags.o: CFLAGS += -O2 -mcpu=pentiumpro -march=pentiumpro + +.def.c: $(srcdir)/gen_insn_test.pl + $(PERL) $(srcdir)/gen_insn_test.pl < $< > $@ diff --git a/VEX/head20041019/none/tests/args.c b/VEX/head20041019/none/tests/args.c new file mode 100644 index 0000000000..e7307a09d9 --- /dev/null +++ b/VEX/head20041019/none/tests/args.c @@ -0,0 +1,11 @@ +#include + +int main(int argc, char* argv[]) +{ + int i; + + for (i = 0; i < argc; i++) { + printf("arg %d: `%s'\n", i, argv[i]); + } + return 0; +} diff --git a/VEX/head20041019/none/tests/args.stderr.exp b/VEX/head20041019/none/tests/args.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/none/tests/args.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/none/tests/args.stdout.exp b/VEX/head20041019/none/tests/args.stdout.exp new file mode 100644 index 0000000000..0d9db35c02 --- /dev/null +++ b/VEX/head20041019/none/tests/args.stdout.exp @@ -0,0 +1,4 @@ +arg 0: `./args' +arg 1: `a' +arg 2: `b' +arg 3: `1 2 3' diff --git a/VEX/head20041019/none/tests/args.vgtest b/VEX/head20041019/none/tests/args.vgtest new file mode 100644 index 0000000000..4b3529d67e --- /dev/null +++ b/VEX/head20041019/none/tests/args.vgtest @@ -0,0 +1,2 @@ +prog: args +args: a b "1 2 3" diff --git a/VEX/head20041019/none/tests/badseg.c b/VEX/head20041019/none/tests/badseg.c new file mode 100644 index 0000000000..0c27d4fd5a --- /dev/null +++ b/VEX/head20041019/none/tests/badseg.c @@ -0,0 +1,28 @@ +#include +#include +#include + +static void handler(int sig, siginfo_t *info, void *v) +{ + printf("info: sig=%d code=%d addr=%p\n", + info->si_signo, info->si_code, info->si_addr); + exit(0); +} + +int main() +{ + struct sigaction sa; + int val; + + sa.sa_sigaction = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + + sigaction(SIGSEGV, &sa, NULL); + + asm volatile("mov %1, %%fs; mov %%fs:0, %0" : "=r" (val) : "r"(4)); + + printf("val=%d\n", val); + + return 0; +} diff --git a/VEX/head20041019/none/tests/badseg.stderr.exp b/VEX/head20041019/none/tests/badseg.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/none/tests/badseg.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/none/tests/badseg.stdout.exp b/VEX/head20041019/none/tests/badseg.stdout.exp new file mode 100644 index 0000000000..40a293750f --- /dev/null +++ b/VEX/head20041019/none/tests/badseg.stdout.exp @@ -0,0 +1 @@ +info: sig=11 code=128 addr=(nil) diff --git a/VEX/head20041019/none/tests/badseg.vgtest b/VEX/head20041019/none/tests/badseg.vgtest new file mode 100644 index 0000000000..e5622a3a64 --- /dev/null +++ b/VEX/head20041019/none/tests/badseg.vgtest @@ -0,0 +1 @@ +prog: badseg diff --git a/VEX/head20041019/none/tests/bitfield1.c b/VEX/head20041019/none/tests/bitfield1.c new file mode 100644 index 0000000000..1dcf4c9493 --- /dev/null +++ b/VEX/head20041019/none/tests/bitfield1.c @@ -0,0 +1,19 @@ + +#include + +typedef + struct { + int x; + unsigned int y:1; + int z; + } + Fooble; + +int main ( void ) +{ + Fooble* f = malloc(sizeof(Fooble)); + f->x = 1; + f->z = 1; + f->y = (f == (Fooble*)17 ? 1 : 0); + return 0; +} diff --git a/VEX/head20041019/none/tests/bitfield1.stderr.exp b/VEX/head20041019/none/tests/bitfield1.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/none/tests/bitfield1.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/none/tests/bitfield1.vgtest b/VEX/head20041019/none/tests/bitfield1.vgtest new file mode 100644 index 0000000000..88260d87f9 --- /dev/null +++ b/VEX/head20041019/none/tests/bitfield1.vgtest @@ -0,0 +1 @@ +prog: bitfield1 diff --git a/VEX/head20041019/none/tests/bt_everything.c b/VEX/head20041019/none/tests/bt_everything.c new file mode 100644 index 0000000000..123d417a66 --- /dev/null +++ b/VEX/head20041019/none/tests/bt_everything.c @@ -0,0 +1,217 @@ + +#include +#include +#include + + +unsigned int btsl_mem ( char* base, int bitno ) +{ + unsigned char res; + __asm__ + __volatile__("btsl\t%2, %0\n\t" + "setc\t%1" + : "=m" (*base), "=q" (res) + : "r" (bitno)); + /* Pretty meaningless to dereference base here, but that's what you + have to do to get a btsl insn which refers to memory starting at + base. */ + return res; +} + +unsigned int btrl_mem ( char* base, int bitno ) +{ + unsigned char res; + __asm__ + __volatile__("btrl\t%2, %0\n\t" + "setc\t%1" + : "=m" (*base), "=q" (res) + : "r" (bitno)); + return res; +} + +unsigned int btcl_mem ( char* base, int bitno ) +{ + unsigned char res; + __asm__ + __volatile__("btcl\t%2, %0\n\t" + "setc\t%1" + : "=m" (*base), "=q" (res) + : "r" (bitno)); + return res; +} + +unsigned int btl_mem ( char* base, int bitno ) +{ + unsigned char res; + __asm__ + __volatile__("btl\t%2, %0\n\t" + "setc\t%1" + : "=m" (*base), "=q" (res) + : "r" (bitno) + : "cc", "memory"); + return res; +} + + + + +unsigned int btsl_reg ( unsigned int reg_in, int bitno, + unsigned int* reg_out_p ) +{ + unsigned char res; + unsigned int reg_out; + __asm__ + __volatile__("movl\t%3, %%eax\n\t" + "btsl\t%2, %%eax\n\t" + "movl\t%%eax, %1\n\t" + "setc\t%0" + : "=q" (res), "=r" (reg_out) + : "r" (bitno), "r" (reg_in) + : "cc", "eax"); + *reg_out_p = reg_out; + return res; +} + + +unsigned int btrl_reg ( unsigned int reg_in, int bitno, + unsigned int* reg_out_p ) +{ + unsigned char res; + unsigned int reg_out; + __asm__ + __volatile__("movl\t%3, %%eax\n\t" + "btrl\t%2, %%eax\n\t" + "movl\t%%eax, %1\n\t" + "setc\t%0" + : "=q" (res), "=r" (reg_out) + : "r" (bitno), "r" (reg_in) + : "cc", "eax"); + *reg_out_p = reg_out; + return res; +} + + +unsigned int btcl_reg ( unsigned int reg_in, int bitno, + unsigned int* reg_out_p ) +{ + unsigned char res; + unsigned int reg_out; + __asm__ + __volatile__("movl\t%3, %%eax\n\t" + "btcl\t%2, %%eax\n\t" + "movl\t%%eax, %1\n\t" + "setc\t%0" + : "=q" (res), "=r" (reg_out) + : "r" (bitno), "r" (reg_in) + : "cc", "eax"); + *reg_out_p = reg_out; + return res; +} + + +unsigned int btl_reg ( unsigned int reg_in, int bitno, + unsigned int* reg_out_p ) +{ + unsigned char res; + unsigned int reg_out; + __asm__ + __volatile__("movl\t%3, %%eax\n\t" + "btl\t%2, %%eax\n\t" + "movl\t%%eax, %1\n\t" + "setc\t%0" + : "=q" (res), "=r" (reg_out) + : "r" (bitno), "r" (reg_in) + : "cc", "eax"); + *reg_out_p = reg_out; + return res; +} + + + + + + + +typedef unsigned int UInt; +typedef unsigned char UChar; + +UInt rol1 ( UInt x ) +{ + return (x << 1) | (x >> 31); +} + +int main ( void ) +{ + UInt n, bitoff, op; + UInt carrydep, c, res; + UChar* block; + UInt reg; + + /*------------------------ MEM-L -----------------------*/ + + carrydep = 0; + block = calloc(200,1); + block += 100; + /* Valid bit offsets are -800 .. 799 inclusive. */ + + for (n = 0; n < 10000; n++) { + bitoff = (random() % 1600) - 800; + op = random() % 4; + c = 2; + switch (op) { + case 0: c = btsl_mem(block, bitoff); break; + case 1: c = btrl_mem(block, bitoff); break; + case 2: c = btcl_mem(block, bitoff); break; + case 3: c = btl_mem(block, bitoff); break; + } + assert(c == 0 || c == 1); + carrydep = c ? (rol1(carrydep) ^ bitoff) : carrydep; + } + + /* Compute final result */ + block -= 100; + res = 0; + for (n = 0; n < 200; n++) { + UChar ch = block[n]; + /* printf("%d ", (int)block[n]); */ + res = rol1(res) ^ (UInt)ch; + } + + printf("MEM-L: final res 0x%x, carrydep 0x%x\n", res, carrydep); + + /*------------------------ REG-L -----------------------*/ + + carrydep = 0; + reg = 0; + + for (n = 0; n < 1000; n++) { + bitoff = (random() % 100) - 50; + op = random() % 4; + c = 2; + switch (op) { + case 0: c = btsl_reg(reg, bitoff, ®); break; + case 1: c = btrl_reg(reg, bitoff, ®); break; + case 2: c = btcl_reg(reg, bitoff, ®); break; + case 3: c = btl_reg(reg, bitoff, ®); break; + } + assert(c == 0 || c == 1); + carrydep = c ? (rol1(carrydep) ^ bitoff) : carrydep; + } + + printf("REG-L: final res 0x%x, carrydep 0x%x\n", reg, carrydep); + + block += 100; + + /* Just try one of these at once; more than one can cause a + confusing merging of error messages. */ + //btsl_mem(block, -800); /* should not complain */ + //btsl_mem(block, -801); /* should complain */ + //btsl_mem(block, 799); /* should not complain */ + //btsl_mem(block, 800); /* should complain */ + + block -= 100; + free(block); + + return 0; +} + diff --git a/VEX/head20041019/none/tests/bt_everything.stderr.exp b/VEX/head20041019/none/tests/bt_everything.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/none/tests/bt_everything.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/none/tests/bt_everything.stdout.exp b/VEX/head20041019/none/tests/bt_everything.stdout.exp new file mode 100644 index 0000000000..fd67221961 --- /dev/null +++ b/VEX/head20041019/none/tests/bt_everything.stdout.exp @@ -0,0 +1,2 @@ +MEM-L: final res 0xd2bfea53, carrydep 0x5b80deee +REG-L: final res 0x605d78ff, carrydep 0x7c0dc86a diff --git a/VEX/head20041019/none/tests/bt_everything.vgtest b/VEX/head20041019/none/tests/bt_everything.vgtest new file mode 100644 index 0000000000..711210d7e6 --- /dev/null +++ b/VEX/head20041019/none/tests/bt_everything.vgtest @@ -0,0 +1 @@ +prog: bt_everything diff --git a/VEX/head20041019/none/tests/bt_literal.c b/VEX/head20041019/none/tests/bt_literal.c new file mode 100644 index 0000000000..3f7e764f1d --- /dev/null +++ b/VEX/head20041019/none/tests/bt_literal.c @@ -0,0 +1,150 @@ + +#include +#include +#include + +typedef unsigned int UInt; + +/* Given a word, do bt/bts/btr/btc on bits 0, 1, 2 and 3 of it, and + also reconstruct the original bits 0, 1, 2, 3 by looking at the + carry flag. Returned result has mashed bits 0-3 at the bottom and + the reconstructed original bits 0-3 as 4-7. */ +UInt mash_reg_L ( UInt orig ) +{ + UInt reconstructed, mashed; + __asm__ __volatile__ ( + "movl %2, %%edx\n\t" + "" + "movl $0, %%eax\n\t" + "\n\t" + "btl $0, %%edx\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btsl $1, %%edx\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $1, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btrl $2, %%edx\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $2, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btcl $3, %%edx\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $3, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "movl %%eax, %0\n\t" + "movl %%edx, %1" + + : "=r" (reconstructed), "=r" (mashed) + : "r" (orig) + : "eax", "ecx", "edx", "cc"); + return (mashed & 0xF) | ((reconstructed & 0xF) << 4); +} + + + + +UInt mash_mem_L ( UInt* origp ) +{ + UInt reconstructed, mashed; + __asm__ __volatile__ ( + "movl %2, %%edx\n\t" + "" + "movl $0, %%eax\n\t" + "\n\t" + "btl $0, (%%edx)\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btsl $1, (%%edx)\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $1, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btrl $2, (%%edx)\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $2, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btcl $3, (%%edx)\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $3, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "movl %%eax, %0\n\t" + "movl (%%edx), %1" + + : "=r" (reconstructed), "=r" (mashed) + : "r" (origp) + : "eax", "ecx", "edx", "cc"); + return (mashed & 0xF) | ((reconstructed & 0xF) << 4); +} + + + +UInt mash_reg_W ( UInt orig ) +{ + UInt reconstructed, mashed; + __asm__ __volatile__ ( + "movl %2, %%edx\n\t" + "" + "movl $0, %%eax\n\t" + "\n\t" + "btw $0, %%dx\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btsw $1, %%dx\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $1, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btrw $2, %%dx\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $2, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "btcw $3, %%dx\n\t" + "setb %%cl\n\t" + "movzbl %%cl, %%ecx\n\t" + "shll $3, %%ecx\n\t" + "orl %%ecx, %%eax\n\t" + "\n\t" + "movl %%eax, %0\n\t" + "movl %%edx, %1" + + : "=r" (reconstructed), "=r" (mashed) + : "r" (orig) + : "eax", "ecx", "edx", "cc"); + return (mashed & 0xF) | ((reconstructed & 0xF) << 4); +} + + + + +int main ( void ) +{ + int i, ii; + for (i = 0; i < 0x10; i++) { + ii = i; + printf("0x%x -> 0x%2x 0x%2x 0x%2x\n", i, + mash_reg_L(i), mash_mem_L(&ii), mash_reg_W(i)); + } + return 1; +} + diff --git a/VEX/head20041019/none/tests/bt_literal.stderr.exp b/VEX/head20041019/none/tests/bt_literal.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/none/tests/bt_literal.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/none/tests/bt_literal.stdout.exp b/VEX/head20041019/none/tests/bt_literal.stdout.exp new file mode 100644 index 0000000000..c9bb28d529 --- /dev/null +++ b/VEX/head20041019/none/tests/bt_literal.stdout.exp @@ -0,0 +1,16 @@ +0x0 -> 0x a 0x a 0x a +0x1 -> 0x1b 0x1b 0x1b +0x2 -> 0x2a 0x2a 0x2a +0x3 -> 0x3b 0x3b 0x3b +0x4 -> 0x4a 0x4a 0x4a +0x5 -> 0x5b 0x5b 0x5b +0x6 -> 0x6a 0x6a 0x6a +0x7 -> 0x7b 0x7b 0x7b +0x8 -> 0x82 0x82 0x82 +0x9 -> 0x93 0x93 0x93 +0xa -> 0xa2 0xa2 0xa2 +0xb -> 0xb3 0xb3 0xb3 +0xc -> 0xc2 0xc2 0xc2 +0xd -> 0xd3 0xd3 0xd3 +0xe -> 0xe2 0xe2 0xe2 +0xf -> 0xf3 0xf3 0xf3 diff --git a/VEX/head20041019/none/tests/bt_literal.vgtest b/VEX/head20041019/none/tests/bt_literal.vgtest new file mode 100644 index 0000000000..9c06c648ef --- /dev/null +++ b/VEX/head20041019/none/tests/bt_literal.vgtest @@ -0,0 +1 @@ +prog: bt_literal diff --git a/VEX/head20041019/none/tests/closeall.c b/VEX/head20041019/none/tests/closeall.c new file mode 100644 index 0000000000..facf6a580c --- /dev/null +++ b/VEX/head20041019/none/tests/closeall.c @@ -0,0 +1,17 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + struct rlimit lim; + int fd; + + getrlimit(RLIMIT_NOFILE, &lim); + + for ( fd = 3; fd < lim.rlim_cur; fd++ ) + close( fd ); + + exit( 0 ); +} diff --git a/VEX/head20041019/none/tests/closeall.stderr.exp b/VEX/head20041019/none/tests/closeall.stderr.exp new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/VEX/head20041019/none/tests/closeall.stderr.exp @@ -0,0 +1,2 @@ + + diff --git a/VEX/head20041019/none/tests/closeall.vgtest b/VEX/head20041019/none/tests/closeall.vgtest new file mode 100644 index 0000000000..23738b1575 --- /dev/null +++ b/VEX/head20041019/none/tests/closeall.vgtest @@ -0,0 +1 @@ +prog: closeall diff --git a/VEX/head20041019/none/tests/cmdline1.stderr.exp b/VEX/head20041019/none/tests/cmdline1.stderr.exp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/VEX/head20041019/none/tests/cmdline1.stdout.exp b/VEX/head20041019/none/tests/cmdline1.stdout.exp new file mode 100644 index 0000000000..ac28bbb029 --- /dev/null +++ b/VEX/head20041019/none/tests/cmdline1.stdout.exp @@ -0,0 +1,47 @@ +usage: valgrind --tool= [options] prog-and-args + + common user options for all Valgrind tools, with defaults in [ ]: + --tool= use the Valgrind tool named + -h --help show this message + --help-debug show this message, plus debugging options + --version show version + -q --quiet run silently; only print error msgs + -v --verbose be more verbose, incl counts of errors + --trace-children=no|yes Valgrind-ise child processes? [no] + --track-fds=no|yes track open file descriptors? [no] + --time-stamp=no|yes add timestamps to log messages? [no] + + uncommon user options for all Valgrind tools: + --run-libc-freeres=no|yes free up glibc memory at exit? [yes] + --weird-hacks=hack1,hack2,... recognised hacks: lax-ioctls [none] + --signal-polltime=