From 85a53ca45cffcebe7f565de9c0f2db66bd164dfe Mon Sep 17 00:00:00 2001 From: MSVSphere Packaging Team Date: Tue, 26 Nov 2024 18:49:24 +0300 Subject: [PATCH] import ps_mem-3.14-8.el10 --- .gitignore | 0 .ps_mem.metadata | 0 SOURCES/lgpl-2.1.txt | 502 ++++++++++++++++++++++++++++++++ SOURCES/ps_mem.1 | 56 ++++ SOURCES/ps_mem.py | 666 +++++++++++++++++++++++++++++++++++++++++++ SPECS/ps_mem.spec | 142 +++++++++ 6 files changed, 1366 insertions(+) create mode 100644 .gitignore create mode 100644 .ps_mem.metadata create mode 100644 SOURCES/lgpl-2.1.txt create mode 100644 SOURCES/ps_mem.1 create mode 100644 SOURCES/ps_mem.py create mode 100644 SPECS/ps_mem.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/.ps_mem.metadata b/.ps_mem.metadata new file mode 100644 index 0000000..e69de29 diff --git a/SOURCES/lgpl-2.1.txt b/SOURCES/lgpl-2.1.txt new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/SOURCES/lgpl-2.1.txt @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, 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 library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete 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 distribute a copy of this License along with the +Library. + + 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 Library or any portion +of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +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 Library, 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 Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you 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. + + If distribution of 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 satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library 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. + + 9. 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 Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +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 with +this License. + + 11. 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 Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library 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 Library. + +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. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library 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. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +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 Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +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 + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. 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 LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/SOURCES/ps_mem.1 b/SOURCES/ps_mem.1 new file mode 100644 index 0000000..f2771c1 --- /dev/null +++ b/SOURCES/ps_mem.1 @@ -0,0 +1,56 @@ +.\" Simple man page to ps_mem script +.\" Contact fholec@redhat.com +.TH ps_mem 1 "25 May 2018" "" "" +.SH NAME +ps_mem \- Memory profiling tool +.SH SYNOPSIS +.B ps_mem [OPTION]... +.SH DESCRIPTION +Display the core memory used per program (not per process) +.br +.PP +In detail it reports: +.br +.PP +.RS +sum (private RAM for program processes) + +.br +sum (shared RAM for program processes) +.br +.RE +.PP +The shared RAM is problematic to calculate, and the tool automatically selects the most accurate method available for the running kernel. +.SH OPTIONS +.TP +\-h \-\-help +Show help message +.TP +\-\-version +Show version info +.TP +\-p PID[,PID2,...PIDN] +Show memory consumption for the specified processes +.TP +\-S \-\-swap +Show swap information of each item +(and Shared swap if the kernel supports SwapPss). +.TP +\-d \-\-discriminate-by-pid +Show and separate memory usage entries by process +rather than by program. +.TP +\-s \-\-split\-args +Show and separate memory usage entries by command line arguments +and not just the program name. +.TP +\-t \-\-total +Only display the total number of bytes for the selected processes +.TP +\-w N +Report memory consumption every N seconds +.\".SH SEE ALSO +.\" +.\".SH BUGS +.\"No known bugs for this template, except you might want to replace the quotes if you copy from my blog. +.SH AUTHOR +Pádraig Brady diff --git a/SOURCES/ps_mem.py b/SOURCES/ps_mem.py new file mode 100644 index 0000000..d03a987 --- /dev/null +++ b/SOURCES/ps_mem.py @@ -0,0 +1,666 @@ +#!/usr/bin/env python + +# Try to determine how much RAM is currently being used per program. +# Note per _program_, not per process. So for example this script +# will report RAM used by all httpd process together. In detail it reports: +# sum(private RAM for program processes) + sum(Shared RAM for program processes) +# The shared RAM is problematic to calculate, and this script automatically +# selects the most accurate method available for your kernel. + +# Licence: LGPLv2 +# Author: P@draigBrady.com +# Source: https://www.pixelbeat.org/scripts/ps_mem.py + +# V1.0 06 Jul 2005 Initial release +# V1.1 11 Aug 2006 root permission required for accuracy +# V1.2 08 Nov 2006 Add total to output +# Use KiB,MiB,... for units rather than K,M,... +# V1.3 22 Nov 2006 Ignore shared col from /proc/$pid/statm for +# 2.6 kernels up to and including 2.6.9. +# There it represented the total file backed extent +# V1.4 23 Nov 2006 Remove total from output as it's meaningless +# (the shared values overlap with other programs). +# Display the shared column. This extra info is +# useful, especially as it overlaps between programs. +# V1.5 26 Mar 2007 Remove redundant recursion from human() +# V1.6 05 Jun 2007 Also report number of processes with a given name. +# Patch from riccardo.murri@gmail.com +# V1.7 20 Sep 2007 Use PSS from /proc/$pid/smaps if available, which +# fixes some over-estimation and allows totalling. +# Enumerate the PIDs directly rather than using ps, +# which fixes the possible race between reading +# RSS with ps, and shared memory with this program. +# Also we can show non truncated command names. +# V1.8 28 Sep 2007 More accurate matching for stats in /proc/$pid/smaps +# as otherwise could match libraries causing a crash. +# Patch from patrice.bouchand.fedora@gmail.com +# V1.9 20 Feb 2008 Fix invalid values reported when PSS is available. +# Reported by Andrey Borzenkov +# V3.14 28 May 2022 +# https://github.com/pixelb/ps_mem/commits/master/ps_mem.py + +# Notes: +# +# All interpreted programs where the interpreter is started +# by the shell or with env, will be merged to the interpreter +# (as that's what's given to exec). For e.g. all python programs +# starting with "#!/usr/bin/env python" will be grouped under python. +# You can change this by using the full command line but that will +# have the undesirable affect of splitting up programs started with +# differing parameters (for e.g. mingetty tty[1-6]). +# +# For 2.6 kernels up to and including 2.6.13 and later 2.4 redhat kernels +# (rmap vm without smaps) it can not be accurately determined how many pages +# are shared between processes in general or within a program in our case: +# http://lkml.org/lkml/2005/7/6/250 +# A warning is printed if overestimation is possible. +# In addition for 2.6 kernels up to 2.6.9 inclusive, the shared +# value in /proc/$pid/statm is the total file-backed extent of a process. +# We ignore that, introducing more overestimation, again printing a warning. +# Since kernel 2.6.23-rc8-mm1 PSS is available in smaps, which allows +# us to calculate a more accurate value for the total RAM used by programs. +# +# Programs that use CLONE_VM without CLONE_THREAD are discounted by assuming +# they're the only programs that have the same /proc/$PID/smaps file for +# each instance. This will fail if there are multiple real instances of a +# program that then use CLONE_VM without CLONE_THREAD, or if a clone changes +# its memory map while we're checksumming each /proc/$PID/smaps. +# +# I don't take account of memory allocated for a program +# by other programs. For e.g. memory used in the X server for +# a program could be determined, but is not. +# +# FreeBSD is supported if linprocfs is mounted at /compat/linux/proc/ +# FreeBSD 8.0 supports up to a level of Linux 2.6.16 + +import argparse +import errno +import os +import sys +import time +import io + +# The following exits cleanly on Ctrl-C or EPIPE +# while treating other exceptions as before. +def std_exceptions(etype, value, tb): + sys.excepthook = sys.__excepthook__ + if issubclass(etype, KeyboardInterrupt): + pass + elif issubclass(etype, IOError) and value.errno == errno.EPIPE: + pass + else: + sys.__excepthook__(etype, value, tb) +sys.excepthook = std_exceptions + +# +# Define some global variables +# + +PAGESIZE = os.sysconf("SC_PAGE_SIZE") / 1024 #KiB +our_pid = os.getpid() + +have_pss = 0 +have_swap_pss = 0 + +class Unbuffered(io.TextIOBase): + def __init__(self, stream): + super(Unbuffered, self).__init__() + self.stream = stream + def write(self, data): + self.stream.write(data) + self.stream.flush() + def close(self): + self.stream.close() + +class Proc: + def __init__(self): + uname = os.uname() + if uname[0] == "FreeBSD": + self.proc = '/compat/linux/proc' + else: + self.proc = '/proc' + + def path(self, *args): + return os.path.join(self.proc, *(str(a) for a in args)) + + def open(self, *args): + try: + if sys.version_info < (3,): + return open(self.path(*args)) + else: + return open(self.path(*args), errors='ignore') + except (IOError, OSError): + if type(args[0]) is not int: + raise + val = sys.exc_info()[1] + if (val.errno == errno.ENOENT or # kernel thread or process gone + val.errno == errno.EPERM or + val.errno == errno.EACCES): + raise LookupError + raise + +proc = Proc() + + +# +# Functions +# + +def parse_options(): + help_msg = 'Show program core memory usage.' + parser = argparse.ArgumentParser(prog='ps_mem', description=help_msg) + parser.add_argument('--version', action='version', version='3.14') + parser.add_argument( + '-s', '--split-args', + action='store_true', + help='Show and separate by, all command line arguments', + ) + parser.add_argument( + '-t', '--total', + dest='only_total', + action='store_true', + help='Show only the total value', + ) + parser.add_argument( + '-d', '--discriminate-by-pid', + action='store_true', + help='Show by process rather than by program', + ) + parser.add_argument( + '-S', '--swap', + dest='show_swap', + action='store_true', + help='Show swap information', + ) + parser.add_argument( + '-p', + dest='pids', + metavar='[,pid2,...pidN]', + help='Only show memory usage PIDs in the specified list', + ) + parser.add_argument( + '-w', + dest='watch', + metavar='', + type=int, + help='Measure and show process memory every N seconds', + ) + args = parser.parse_args() + + args.pids_to_show = [] + if args.pids: + try: + args.pids_to_show = [int(x) for x in args.pids.split(',')] + except ValueError: + parser.error('Invalid PID(s): %s' % args.pids) + + if args.watch is not None: + if args.watch <= 0: + parser.error('Seconds must be positive! (%s)' % args.watch) + + return ( + args.split_args, + args.pids_to_show, + args.watch, + args.only_total, + args.discriminate_by_pid, + args.show_swap, + ) + + +# (major,minor,release) +def kernel_ver(): + kv = proc.open('sys/kernel/osrelease').readline().split(".")[:3] + last = len(kv) + if last == 2: + kv.append('0') + last -= 1 + while last > 0: + for char in "-_": + kv[last] = kv[last].split(char)[0] + try: + int(kv[last]) + except: + kv[last] = 0 + last -= 1 + return (int(kv[0]), int(kv[1]), int(kv[2])) + + +#return Private,Shared,Swap(Pss),unique_id +#Note shared is always a subset of rss (trs is not always) +def getMemStats(pid): + global have_pss + global have_swap_pss + mem_id = pid #unique + Private_lines = [] + Shared_lines = [] + Private_huge_lines = [] + Shared_huge_lines = [] + Pss_lines = [] + Rss = (int(proc.open(pid, 'statm').readline().split()[1]) + * PAGESIZE) + Swap_lines = [] + Swap_pss_lines = [] + + Swap = 0 + + if os.path.exists(proc.path(pid, 'smaps')): # stat + smaps = 'smaps' + if os.path.exists(proc.path(pid, 'smaps_rollup')): + smaps = 'smaps_rollup' # faster to process + lines = proc.open(pid, smaps).readlines() # open + # Note we checksum smaps as maps is usually but + # not always different for separate processes. + mem_id = hash(''.join(lines)) + for line in lines: + # {Private,Shared}_Hugetlb is not included in Pss (why?) + # so we need to account for separately. + if line.startswith("Private_Hugetlb:"): + Private_huge_lines.append(line) + elif line.startswith("Shared_Hugetlb:"): + Shared_huge_lines.append(line) + elif line.startswith("Shared"): + Shared_lines.append(line) + elif line.startswith("Private"): + Private_lines.append(line) + elif line.startswith("Pss:"): + have_pss = 1 + Pss_lines.append(line) + elif line.startswith("Swap:"): + Swap_lines.append(line) + elif line.startswith("SwapPss:"): + have_swap_pss = 1 + Swap_pss_lines.append(line) + Shared = sum([int(line.split()[1]) for line in Shared_lines]) + Private = sum([int(line.split()[1]) for line in Private_lines]) + Shared_huge = sum([int(line.split()[1]) for line in Shared_huge_lines]) + Private_huge = sum([int(line.split()[1]) for line in Private_huge_lines]) + #Note Shared + Private = Rss above + #The Rss in smaps includes video card mem etc. + if have_pss: + pss_adjust = 0.5 # add 0.5KiB as this avg error due to truncation + Pss = sum([float(line.split()[1])+pss_adjust for line in Pss_lines]) + Shared = Pss - Private + Private += Private_huge # Add after as PSS doesn't a/c for huge pages + if have_swap_pss: + # The kernel supports SwapPss, that shows proportional swap share. + # Note that Swap - SwapPss is not Private Swap. + Swap = sum([int(line.split()[1]) for line in Swap_pss_lines]) + else: + # Note that Swap = Private swap + Shared swap. + Swap = sum([int(line.split()[1]) for line in Swap_lines]) + elif (2,6,1) <= kernel_ver() <= (2,6,9): + Shared = 0 #lots of overestimation, but what can we do? + Shared_huge = 0 + Private = Rss + else: + Shared = int(proc.open(pid, 'statm').readline().split()[2]) + Shared *= PAGESIZE + Shared_huge = 0 + Private = Rss - Shared + return (Private, Shared, Shared_huge, Swap, mem_id) + + +def getCmdName(pid, split_args, discriminate_by_pid, exe_only=False): + cmdline = proc.open(pid, 'cmdline').read().split("\0") + while cmdline[-1] == '' and len(cmdline) > 1: + cmdline = cmdline[:-1] + + path = proc.path(pid, 'exe') + try: + path = os.readlink(path) + # Some symlink targets were seen to contain NULs on RHEL 5 at least + # https://github.com/pixelb/scripts/pull/10, so take string up to NUL + path = path.split('\0')[0] + except OSError: + val = sys.exc_info()[1] + if (val.errno == errno.ENOENT or # either kernel thread or process gone + val.errno == errno.EPERM or + val.errno == errno.EACCES): + raise LookupError + raise + + if split_args: + return ' '.join(cmdline).replace('\n', ' ') + if path.endswith(" (deleted)"): + path = path[:-10] + if os.path.exists(path): + path += " [updated]" + else: + #The path could be have prelink stuff so try cmdline + #which might have the full path present. This helped for: + #/usr/libexec/notification-area-applet.#prelink#.fX7LCT (deleted) + if os.path.exists(cmdline[0]): + path = cmdline[0] + " [updated]" + else: + path += " [deleted]" + exe = os.path.basename(path) + if exe_only: return exe + + proc_status = proc.open(pid, 'status').readlines() + cmd = proc_status[0][6:-1] + if exe.startswith(cmd): + cmd = exe #show non truncated version + #Note because we show the non truncated name + #one can have separated programs as follows: + #584.0 KiB + 1.0 MiB = 1.6 MiB mozilla-thunder (exe -> bash) + # 56.0 MiB + 22.2 MiB = 78.2 MiB mozilla-thunderbird-bin + else: + #Lookup the parent's exe and use that if matching + #which will merge "Web Content" with "firefox" for example + ppid = 0 + for l in range(10): + ps_line = proc_status[l] + if ps_line.startswith('PPid:'): + ppid = int(ps_line[6:-1]) + break + if ppid: + try: + p_exe = getCmdName(ppid, False, False, exe_only=True) + except LookupError: + pass + else: + if exe == p_exe: + cmd = exe + if sys.version_info >= (3,): + cmd = cmd.encode(errors='replace').decode() + if discriminate_by_pid: + cmd = '%s [%d]' % (cmd, pid) + return cmd + + +#The following matches "du -h" output +#see also human.py +def human(num, power="Ki", units=None): + if units is None: + powers = ["Ki", "Mi", "Gi", "Ti"] + while num >= 1000: #4 digits + num /= 1024.0 + power = powers[powers.index(power)+1] + return "%.1f %sB" % (num, power) + else: + return "%.f" % ((num * 1024) / units) + + +def cmd_with_count(cmd, count): + if count > 1: + return "%s (%u)" % (cmd, count) + else: + return cmd + +#Warn of possible inaccuracies +#RAM: +#2 = accurate & can total +#1 = accurate only considering each process in isolation +#0 = some shared mem not reported +#-1= all shared mem not reported +#SWAP: +#2 = accurate & can total +#1 = accurate only considering each process in isolation +#-1= not available +def val_accuracy(show_swap): + """http://wiki.apache.org/spamassassin/TopSharedMemoryBug""" + kv = kernel_ver() + pid = os.getpid() + swap_accuracy = -1 + if kv[:2] == (2,4): + if proc.open('meminfo').read().find("Inact_") == -1: + return 1, swap_accuracy + return 0, swap_accuracy + elif kv[:2] == (2,6): + if os.path.exists(proc.path(pid, 'smaps')): + swap_accuracy = 1 + if proc.open(pid, 'smaps').read().find("Pss:")!=-1: + return 2, swap_accuracy + else: + return 1, swap_accuracy + if (2,6,1) <= kv <= (2,6,9): + return -1, swap_accuracy + return 0, swap_accuracy + elif kv[0] > 2 and os.path.exists(proc.path(pid, 'smaps')): + swap_accuracy = 1 + if show_swap and proc.open(pid, 'smaps').read().find("SwapPss:")!=-1: + swap_accuracy = 2 + return 2, swap_accuracy + else: + return 1, swap_accuracy + +def show_val_accuracy( ram_inacc, swap_inacc, only_total, show_swap ): + level = ("Warning","Error")[only_total] + + # Only show significant warnings + if not show_swap: + swap_inacc = 2 + elif only_total: + ram_inacc = 2 + + if ram_inacc == -1: + sys.stderr.write( + "%s: Shared memory is not reported by this system.\n" % level + ) + sys.stderr.write( + "Values reported will be too large, and totals are not reported\n" + ) + elif ram_inacc == 0: + sys.stderr.write( + "%s: Shared memory is not reported accurately by this system.\n" % level + ) + sys.stderr.write( + "Values reported could be too large, and totals are not reported\n" + ) + elif ram_inacc == 1: + sys.stderr.write( + "%s: Shared memory is slightly over-estimated by this system\n" + "for each program, so totals are not reported.\n" % level + ) + + if swap_inacc == -1: + sys.stderr.write( + "%s: Swap is not reported by this system.\n" % level + ) + elif swap_inacc == 1: + sys.stderr.write( + "%s: Swap is over-estimated by this system for each program,\n" + "so totals are not reported.\n" % level + ) + + sys.stderr.close() + if only_total: + if show_swap: + accuracy = swap_inacc + else: + accuracy = ram_inacc + if accuracy != 2: + sys.exit(1) + + +def get_memory_usage(pids_to_show, split_args, discriminate_by_pid, + include_self=False, only_self=False): + cmds = {} + shareds = {} + shared_huges = {} + mem_ids = {} + count = {} + swaps = {} + for pid in os.listdir(proc.path('')): + if not pid.isdigit(): + continue + pid = int(pid) + + # Some filters + if only_self and pid != our_pid: + continue + if pid == our_pid and not include_self: + continue + if pids_to_show and pid not in pids_to_show: + continue + + try: + cmd = getCmdName(pid, split_args, discriminate_by_pid) + except LookupError: + #operation not permitted + #kernel threads don't have exe links or + #process gone + continue + + try: + private, shared, shared_huge, swap, mem_id = getMemStats(pid) + except RuntimeError: + continue #process gone + if shareds.get(cmd): + if have_pss: #add shared portion of PSS together + shareds[cmd] += shared + elif shareds[cmd] < shared: #just take largest shared val + shareds[cmd] = shared + else: + shareds[cmd] = shared + if shared_huges.get(cmd): + if shared_huges[cmd] < shared_huge: #just take largest shared_huge + shared_huges[cmd] = shared_huge + else: + shared_huges[cmd] = shared_huge + cmds[cmd] = cmds.setdefault(cmd, 0) + private + if cmd in count: + count[cmd] += 1 + else: + count[cmd] = 1 + mem_ids.setdefault(cmd, {}).update({mem_id: None}) + + # Swap (overcounting for now...) + swaps[cmd] = swaps.setdefault(cmd, 0) + swap + + # Total swaped mem for each program + total_swap = 0 + + # Add shared mem for each program + total = 0 + + for cmd in cmds: + cmd_count = count[cmd] + if len(mem_ids[cmd]) == 1 and cmd_count > 1: + # Assume this program is using CLONE_VM without CLONE_THREAD + # so only account for one of the processes + cmds[cmd] /= cmd_count + if have_pss: + shareds[cmd] /= cmd_count + # overestimation possible if shared_huges shared across commands + shareds[cmd] += shared_huges[cmd] + cmds[cmd] = cmds[cmd] + shareds[cmd] + total += cmds[cmd] # valid if PSS available + total_swap += swaps[cmd] + + sorted_cmds = sorted(cmds.items(), key=lambda x:x[1]) + sorted_cmds = [x for x in sorted_cmds if x[1]] + + return sorted_cmds, shareds, count, total, swaps, total_swap + +def print_header(show_swap, discriminate_by_pid): + output_string = " Private + Shared = RAM used" + if show_swap: + output_string += " Swap used" + output_string += "\tProgram" + if discriminate_by_pid: + output_string += "[pid]" + output_string += "\n\n" + sys.stdout.write(output_string) + + +def print_memory_usage(sorted_cmds, shareds, count, total, swaps, total_swap, + show_swap): + for cmd in sorted_cmds: + + output_string = "%9s + %9s = %9s" + output_data = (human(cmd[1]-shareds[cmd[0]]), + human(shareds[cmd[0]]), human(cmd[1])) + if show_swap: + output_string += " %9s" + output_data += (human(swaps[cmd[0]]),) + output_string += "\t%s\n" + output_data += (cmd_with_count(cmd[0], count[cmd[0]]),) + + sys.stdout.write(output_string % output_data) + + # Only show totals if appropriate + if have_swap_pss and show_swap: # kernel will have_pss + sys.stdout.write("%s\n%s%9s%s%9s\n%s\n" % + ("-" * 45, " " * 24, human(total), " " * 3, + human(total_swap), "=" * 45)) + elif have_pss: + sys.stdout.write("%s\n%s%9s\n%s\n" % + ("-" * 33, " " * 24, human(total), "=" * 33)) + + +def verify_environment(pids_to_show): + if os.geteuid() != 0 and not pids_to_show: + sys.stderr.write("Sorry, root permission required, or specify pids with -p\n") + sys.stderr.close() + sys.exit(1) + + try: + kernel_ver() + except (IOError, OSError): + val = sys.exc_info()[1] + if val.errno == errno.ENOENT: + sys.stderr.write( + "Couldn't access " + proc.path('') + "\n" + "Only GNU/Linux and FreeBSD (with linprocfs) are supported\n") + sys.exit(2) + else: + raise + +def main(): + # Force the stdout and stderr streams to be unbuffered + sys.stdout = Unbuffered(sys.stdout) + sys.stderr = Unbuffered(sys.stderr) + + split_args, pids_to_show, watch, only_total, discriminate_by_pid, \ + show_swap = parse_options() + + verify_environment(pids_to_show) + + if not only_total: + print_header(show_swap, discriminate_by_pid) + + if watch is not None: + try: + sorted_cmds = True + while sorted_cmds: + sorted_cmds, shareds, count, total, swaps, total_swap = \ + get_memory_usage(pids_to_show, split_args, + discriminate_by_pid) + if only_total and show_swap and have_swap_pss: + sys.stdout.write(human(total_swap, units=1)+'\n') + elif only_total and not show_swap and have_pss: + sys.stdout.write(human(total, units=1)+'\n') + elif not only_total: + print_memory_usage(sorted_cmds, shareds, count, total, + swaps, total_swap, show_swap) + + sys.stdout.flush() + time.sleep(watch) + else: + sys.stdout.write('Process does not exist anymore.\n') + except KeyboardInterrupt: + pass + else: + # This is the default behavior + sorted_cmds, shareds, count, total, swaps, total_swap = \ + get_memory_usage(pids_to_show, split_args, + discriminate_by_pid) + if only_total and show_swap and have_swap_pss: + sys.stdout.write(human(total_swap, units=1)+'\n') + elif only_total and not show_swap and have_pss: + sys.stdout.write(human(total, units=1)+'\n') + elif not only_total: + print_memory_usage(sorted_cmds, shareds, count, total, swaps, + total_swap, show_swap) + + # We must close explicitly, so that any EPIPE exception + # is handled by our excepthook, rather than the default + # one which is reenabled after this script finishes. + sys.stdout.close() + + ram_accuracy, swap_accuracy = val_accuracy( show_swap ) + show_val_accuracy( ram_accuracy, swap_accuracy, only_total, show_swap ) + +if __name__ == '__main__': main() diff --git a/SPECS/ps_mem.spec b/SPECS/ps_mem.spec new file mode 100644 index 0000000..2608b3f --- /dev/null +++ b/SPECS/ps_mem.spec @@ -0,0 +1,142 @@ + +Name: ps_mem +Version: 3.14 +Release: 8%{?dist} +Summary: Memory profiling tool +License: LGPL-2.1-only +URL: https://github.com/pixelb/ps_mem + +Source0: https://raw.githubusercontent.com/pixelb/ps_mem/c80287d/ps_mem.py +Source1: http://www.gnu.org/licenses/lgpl-2.1.txt +Source2: ps_mem.1 + +BuildArch: noarch + +BuildRequires: python3-devel + +%description +The ps_mem tool reports how much core memory is used per program +(not per process). In detail it reports: +sum(private RAM for program processes) + sum(Shared RAM for program processes) +The shared RAM is problematic to calculate, and the tool automatically +selects the most accurate method available for the running kernel. + + +%prep +%setup -q -T -c %{name}-%{version} + +cp -p %{SOURCE0} %{name} +cp -p %{SOURCE1} LICENSE +cp -p %{SOURCE2} %{name}.1 + +# use python3 +sed -i "s|/usr/bin/env python|%{__python3}|" %{name} +touch -r %{SOURCE0} %{name} + + +%install +install -Dpm755 %{name} %{buildroot}%{_bindir}/%{name} +install -Dpm644 %{name}.1 %{buildroot}%{_mandir}/man1/%{name}.1 + + +%files +%doc LICENSE +%{_bindir}/%{name} +%{_mandir}/man1/%{name}.1* + + +%changelog +* Mon Jun 24 2024 Troy Dawson - 3.14-8 +- Bump release for June 2024 mass rebuild + +* Fri Jan 26 2024 Fedora Release Engineering - 3.14-7 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Sun Jan 21 2024 Fedora Release Engineering - 3.14-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Fri Jul 21 2023 Fedora Release Engineering - 3.14-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Mon Apr 24 2023 Lukáš Zaoral - 3.14-4 +- migrate to SPDX license format + +* Fri Jan 20 2023 Fedora Release Engineering - 3.14-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild + +* Fri Jul 22 2022 Fedora Release Engineering - 3.14-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild + +* Mon Jul 11 2022 Pádraig Brady - 3.14-1 +- Latest upstream + +* Fri Jan 21 2022 Fedora Release Engineering - 3.6-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Fri Jul 23 2021 Fedora Release Engineering - 3.6-14 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Wed Jan 27 2021 Fedora Release Engineering - 3.6-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Tue Jul 28 2020 Fedora Release Engineering - 3.6-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Thu Jan 30 2020 Fedora Release Engineering - 3.6-11 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Fri Jul 26 2019 Fedora Release Engineering - 3.6-10 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Sat Feb 02 2019 Fedora Release Engineering - 3.6-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Fri Jul 13 2018 Fedora Release Engineering - 3.6-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Tue Jun 19 2018 Miro Hrončok - 3.6-7 +- Rebuilt for Python 3.7 + +* Fri Feb 09 2018 Fedora Release Engineering - 3.6-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 3.6-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Sat Feb 11 2017 Fedora Release Engineering - 3.6-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Dec 19 2016 Miro Hrončok - 3.6-3 +- Rebuild for Python 3.6 + +* Mon Jul 11 2016 Lumir Balhar - 3.6-2 +- Fixed missing BuildRequire + +* Wed Jun 08 2016 Lumir Balhar - 3.6-1 +- Latest upstream release +- Package ported to Python3 with dependencies + +* Thu Feb 04 2016 Fedora Release Engineering - 3.5-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Fri Oct 16 2015 Pádraig Brady - 3.5-1 +- Latest upstream +- Depend on default python implementation + +* Thu Jun 18 2015 Fedora Release Engineering - 3.1-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Sat Jun 07 2014 Fedora Release Engineering - 3.1-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Wed Aug 14 2013 Jaromir Capik - 3.1-4 +- RH man page scan (#989490) + +* Thu Jul 25 2013 Jaromir Capik - 3.1-3 +- Patching shebang to force python3 (#987036) + +* Thu May 30 2013 Jaromir Capik - 3.1-2 +- Preserving file timestamps + +* Wed May 29 2013 Jaromir Capik - 3.1-1 +- Initial package