├── LICENSE ├── README.md ├── cacheutils.c ├── cgget.c ├── crash-gcore-command-1.5.1.tar.gz ├── crash-gcore-command-1.6.0.tar.gz ├── crash-gcore-command-1.6.1.tar.gz ├── crash-trace-command-2.0.tar.gz ├── dminfo.c ├── echo.c ├── eppic.c ├── eppic.mk ├── fp.c ├── fp.mk ├── ipcs.c ├── ksm.c ├── lscgroup.c ├── memutils.c ├── proccgroup.c ├── pstruct.c ├── ptdump-1.0.7.tar.gz ├── qemu-vtop.c ├── snap.c ├── snap.mk ├── sockq.c ├── spu.c ├── swap_usage.c ├── trace.c └── vz.c /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # crash-extensions 2 | crash extension modules 3 | -------------------------------------------------------------------------------- /crash-gcore-command-1.5.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crash-utility/crash-extensions/88604fab8c91dc3072b71ae22e60015fc5ec26cf/crash-gcore-command-1.5.1.tar.gz -------------------------------------------------------------------------------- /crash-gcore-command-1.6.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crash-utility/crash-extensions/88604fab8c91dc3072b71ae22e60015fc5ec26cf/crash-gcore-command-1.6.0.tar.gz -------------------------------------------------------------------------------- /crash-gcore-command-1.6.1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crash-utility/crash-extensions/88604fab8c91dc3072b71ae22e60015fc5ec26cf/crash-gcore-command-1.6.1.tar.gz -------------------------------------------------------------------------------- /crash-trace-command-2.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crash-utility/crash-extensions/88604fab8c91dc3072b71ae22e60015fc5ec26cf/crash-trace-command-2.0.tar.gz -------------------------------------------------------------------------------- /echo.c: -------------------------------------------------------------------------------- 1 | /* echo.c - simple example of a crash extension 2 | * 3 | * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. 4 | * Copyright (C) 2002-2005, 2007, 2013 David Anderson 5 | * Copyright (C) 2002-2005, 2007, 2013 Red Hat, Inc. All rights reserved. 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | */ 17 | 18 | #include "defs.h" /* From the crash source top-level directory */ 19 | 20 | void echo_init(void); /* constructor function */ 21 | void echo_fini(void); /* destructor function (optional) */ 22 | 23 | void cmd_echo(void); /* Declare the commands and their help data. */ 24 | char *help_echo[]; 25 | 26 | static struct command_table_entry command_table[] = { 27 | { "echo", cmd_echo, help_echo, 0}, /* One or more commands, */ 28 | { NULL }, /* terminated by NULL, */ 29 | }; 30 | 31 | 32 | void __attribute__((constructor)) 33 | echo_init(void) /* Register the command set. */ 34 | { 35 | register_extension(command_table); 36 | } 37 | 38 | /* 39 | * This function is called if the shared object is unloaded. 40 | * If desired, perform any cleanups here. 41 | */ 42 | void __attribute__((destructor)) 43 | echo_fini(void) { } 44 | 45 | 46 | /* 47 | * Arguments are passed to the command functions in the global args[argcnt] 48 | * array. See getopt(3) for info on dash arguments. Check out defs.h and 49 | * other crash commands for usage of the myriad of utility routines available 50 | * to accomplish what your task. 51 | */ 52 | void 53 | cmd_echo(void) 54 | { 55 | int c; 56 | 57 | while ((c = getopt(argcnt, args, "")) != EOF) { 58 | switch(c) 59 | { 60 | default: 61 | argerrs++; 62 | break; 63 | } 64 | } 65 | 66 | if (argerrs) 67 | cmd_usage(pc->curcmd, SYNOPSIS); 68 | 69 | while (args[optind]) 70 | fprintf(fp, "%s ", args[optind++]); 71 | 72 | fprintf(fp, "\n"); 73 | } 74 | 75 | /* 76 | * The optional help data is simply an array of strings in a defined format. 77 | * For example, the "help echo" command will use the help_echo[] string 78 | * array below to create a help page that looks like this: 79 | * 80 | * NAME 81 | * echo - echoes back its arguments 82 | * 83 | * SYNOPSIS 84 | * echo arg ... 85 | * 86 | * DESCRIPTION 87 | * This command simply echoes back its arguments. 88 | * 89 | * EXAMPLE 90 | * Echo back all command arguments: 91 | * 92 | * crash> echo hello, world 93 | * hello, world 94 | * 95 | */ 96 | 97 | char *help_echo[] = { 98 | "echo", /* command name */ 99 | "echoes back its arguments", /* short description */ 100 | "arg ...", /* argument synopsis, or " " if none */ 101 | 102 | " This command simply echoes back its arguments.", 103 | "\nEXAMPLE", 104 | " Echo back all command arguments:\n", 105 | " crash> echo hello, world", 106 | " hello, world", 107 | NULL 108 | }; 109 | 110 | 111 | -------------------------------------------------------------------------------- /eppic.c: -------------------------------------------------------------------------------- 1 | /* 2 | Place holder for proper working of the extension Makefile. 3 | Eppic crash application file is in eppic/applications/crash/eppic.c 4 | */ 5 | -------------------------------------------------------------------------------- /eppic.mk: -------------------------------------------------------------------------------- 1 | # 2 | # This program is free software; you can redistribute it and/or modify 3 | # it under the terms of the GNU General Public License as published by 4 | # the Free Software Foundation; either version 2 of the License, or 5 | # (at your option) any later version. 6 | # 7 | # This program is distributed in the hope that it will be useful, 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | # GNU General Public License for more details. 11 | 12 | TARGET_FLAGS = -D$(TARGET) 13 | ifeq ($(TARGET), PPC64) 14 | TARGET_FLAGS += -m64 15 | endif 16 | ifeq ($(TARGET), ARM) 17 | TARGET_FLAGS += -m32 18 | endif 19 | ifeq ($(TARGET), MIPS) 20 | TARGET_FLAGS += -m32 21 | endif 22 | ifeq ($(TARGET), X86) 23 | TARGET_FLAGS += -m32 24 | endif 25 | 26 | APPFILE=eppic/applications/crash/eppic.c 27 | GITHUB := $(shell ping -c 1 github.com | grep "1 received") 28 | GIT := $(shell which git 2> /dev/null) 29 | 30 | all: 31 | @if [ -f /usr/bin/flex ] && [ -f /usr/bin/bison ]; then \ 32 | if [ -f ../$(GDB)/crash.target ]; \ 33 | then \ 34 | if [ ! -f $(APPFILE) ]; \ 35 | then \ 36 | if [ -f "$(GIT)" ]; \ 37 | then \ 38 | if [ -n "$(EPPIC_GIT_URL)" ]; then \ 39 | git clone "$(EPPIC_GIT_URL)" eppic; \ 40 | else \ 41 | if [ -n "$(GITHUB)" ] ; then \ 42 | git clone https://github.com/lucchouina/eppic.git eppic; \ 43 | fi; \ 44 | fi; \ 45 | else \ 46 | if [ ! -f "$(GIT)" ]; then \ 47 | echo "eppic.so: git command is needed for pulling eppic extension code"; \ 48 | fi; \ 49 | fi; \ 50 | fi; \ 51 | if [ -f $(APPFILE) ]; \ 52 | then \ 53 | make -f eppic.mk eppic.so; \ 54 | else \ 55 | echo "eppic.so: failed to pull eppic code from git repo"; \ 56 | fi; \ 57 | else \ 58 | echo "eppic.so: build failed: requires the crash $(GDB) module"; \ 59 | fi ;\ 60 | else \ 61 | echo "eppic.so: build failed: requires /usr/bin/flex and /usr/bin/bison"; \ 62 | fi 63 | 64 | lib-eppic: 65 | cd eppic/libeppic && make 66 | 67 | eppic.so: ../defs.h $(APPFILE) lib-eppic 68 | gcc -g -Ieppic/libeppic -I../$(GDB)/gdb -I../$(GDB)/bfd -I../$(GDB)/include -I../$(GDB)/gdb/config -I../$(GDB)/gdb/common -I../$(GDB) -nostartfiles -shared -rdynamic -o eppic.so $(APPFILE) -fPIC $(TARGET_FLAGS) $(GDB_FLAGS) -Leppic/libeppic -leppic 69 | 70 | clean: 71 | if [ -d eppic/libeppic ]; \ 72 | then \ 73 | cd eppic/libeppic && make -i clean; \ 74 | fi 75 | rm -f eppic.so 76 | -------------------------------------------------------------------------------- /fp.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2013 Alexandr Terekhov 3 | # Copyright (C) 2013 EPAM Systems. All rights reserved. 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | 16 | all: fp.so 17 | 18 | ifeq ($(shell arch), x86_64) 19 | TARGET=X86_64 20 | TARGET_CFLAGS= 21 | endif 22 | 23 | fp.so: fp.c 24 | gcc -Wall -I.. -shared -rdynamic -g -o fp.so fp.c -fPIC -D$(TARGET) -DGDB_7_6 $(TARGET_CFLAGS) 25 | 26 | clean: 27 | rm -f x86_64_params.o fp.o fp.so 28 | 29 | -------------------------------------------------------------------------------- /ksm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013-2014 FUJITSU LIMITED 3 | * Author: Zhang Yanfei 4 | * Signed-off-by: Qiao Nuohan 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #include "defs.h" 18 | 19 | void ksm_init(void); 20 | void ksm_fini(void); 21 | 22 | void cmd_ksm(void); 23 | char *help_ksm[]; 24 | 25 | static struct command_table_entry command_table[] = { 26 | { "ksm", cmd_ksm, help_ksm, 0}, 27 | { NULL }, 28 | }; 29 | 30 | struct ksm_offset_table { 31 | long stable_node_node; 32 | long stable_node_hlist; 33 | long stable_node_kpfn; 34 | long rmap_item_mm; 35 | long rmap_item_address; 36 | long rmap_item_hlist; 37 | } ksm_offset_table; 38 | 39 | #define KSM_ASSIGN_OFFSET(X) (ksm_offset_table.X) 40 | #define KSM_INVALID_MEMBER(X) (ksm_offset_table.X == INVALID_OFFSET) 41 | #define KSM_MEMBER_OFFSET_INIT(X, Y, Z) (KSM_ASSIGN_OFFSET(X) = MEMBER_OFFSET(Y, Z)) 42 | #define KSM_ANON_MEMBER_OFFSET_INIT(X, Y, Z) (KSM_ASSIGN_OFFSET(X) = ANON_MEMBER_OFFSET(Y, Z)) 43 | #define KSM_OFFSET(X) (OFFSET_verify(ksm_offset_table.X, (char *)__FUNCTION__, __FILE__, __LINE__, #X)) 44 | 45 | struct meminfo { 46 | int memtype; 47 | ulong flags; 48 | ulonglong spec_addr; 49 | }; 50 | 51 | struct page_ref { 52 | ulong mm; 53 | ulong pid; 54 | int ref; 55 | struct page_ref *next; 56 | }; 57 | 58 | static void dump_ksm(struct meminfo *); 59 | 60 | void __attribute__((constructor)) 61 | ksm_init(void) /* Register the command set. */ 62 | { 63 | if (STRUCT_EXISTS("stable_node")) { 64 | KSM_MEMBER_OFFSET_INIT(stable_node_node, "stable_node", "node"); 65 | if (KSM_INVALID_MEMBER(stable_node_node)) 66 | KSM_ANON_MEMBER_OFFSET_INIT(stable_node_node, "stable_node", "node"); 67 | 68 | KSM_MEMBER_OFFSET_INIT(stable_node_hlist, "stable_node", "hlist"); 69 | KSM_MEMBER_OFFSET_INIT(stable_node_kpfn, "stable_node", "kpfn"); 70 | KSM_MEMBER_OFFSET_INIT(rmap_item_mm, "rmap_item", "mm"); 71 | KSM_MEMBER_OFFSET_INIT(rmap_item_address, "rmap_item", "address"); 72 | KSM_ANON_MEMBER_OFFSET_INIT(rmap_item_hlist, "rmap_item", "hlist"); 73 | } else 74 | error(FATAL, "ksm_init: stable_node does not exist\n"); 75 | 76 | register_extension(command_table); 77 | } 78 | 79 | void __attribute__((destructor)) 80 | ksm_fini(void) { } 81 | 82 | void 83 | cmd_ksm(void) 84 | { 85 | int i, c, vflag, pflag; 86 | ulong ksm_pages_shared; 87 | ulonglong value[MAXARGS]; 88 | int spec_addr; 89 | struct meminfo meminfo; 90 | 91 | vflag = pflag = spec_addr = 0; 92 | BZERO(&meminfo, sizeof (struct meminfo)); 93 | 94 | while ((c = getopt(argcnt, args, "vp")) != EOF) { 95 | switch(c) 96 | { 97 | case 'v': 98 | vflag++; 99 | break; 100 | case 'p': 101 | pflag++; 102 | break; 103 | default: 104 | argerrs++; 105 | break; 106 | } 107 | } 108 | 109 | if (argerrs) 110 | cmd_usage(pc->curcmd, SYNOPSIS); 111 | 112 | get_symbol_data("ksm_pages_shared", sizeof(ulong), &ksm_pages_shared); 113 | if (!ksm_pages_shared) 114 | error(FATAL, "no ksm pages in the system\n"); 115 | 116 | while (args[optind]) { 117 | if (hexadecimal(args[optind], 0)) 118 | value[spec_addr++] = 119 | htoll(args[optind], FAULT_ON_ERROR, NULL); 120 | optind++; 121 | } 122 | 123 | for (i = 0; i < spec_addr; i++) { 124 | meminfo.spec_addr = value[i]; 125 | meminfo.flags = ADDRESS_SPECIFIED; 126 | if (pflag) 127 | meminfo.memtype = PHYSADDR; 128 | else 129 | meminfo.memtype = IS_KVADDR(value[i]) ? KVADDR : PHYSADDR; 130 | if (meminfo.memtype == PHYSADDR) 131 | meminfo.spec_addr = (ulonglong)PHYSPAGEBASE(meminfo.spec_addr); 132 | if (vflag) 133 | meminfo.flags |= VERBOSE; 134 | dump_ksm(&meminfo); 135 | } 136 | 137 | if (!spec_addr) { 138 | if (vflag) { 139 | meminfo.flags |= VERBOSE; 140 | dump_ksm(&meminfo); 141 | } else 142 | dump_ksm(NULL); 143 | } 144 | } 145 | 146 | char *help_ksm[] = { 147 | "ksm", 148 | "kernel samepage merging (KSM) information", 149 | "[-v] [[-p] address ...]", 150 | 151 | " This command displays information about all KSM pages currently", 152 | " in use. For each KSM page, the display includes its stable_node", 153 | " address, its page struct address, its physical address, the TGID/PID", 154 | " for each task that is using the page, the number of mappings in the", 155 | " task's address space for the page, and the mm_struct address of the", 156 | " task. If pid is '-', the task has exited and the ksm page has not", 157 | " been removed.", 158 | " ", 159 | " -v also dump each virtual address in a PID's virtual address", 160 | " space that maps the KSM page.", 161 | " address restricts the output to the KSM data associated with a", 162 | " stable_node address, a page's physical address, or a page", 163 | " pointer.", 164 | " -p specifies that the address argument is a physical address,", 165 | " for architectures that require it.", 166 | "\nEXAMPLE", 167 | " Display information about all KSM pages:\n", 168 | " %s> ksm", 169 | " PAGE: ffffea000451f180", 170 | " STABLE_NODE: ffff88004866b6c0", 171 | " PHYSICAL ADDRESS: 1147c6000", 172 | " PID: 1318 MAPPINGS: 7707 MM: ffff88007f8abe80", 173 | " PID: 1297 MAPPINGS: 4965 MM: ffff88007f8aa580", 174 | "", 175 | " PAGE: ffffea0003413c40", 176 | " STABLE_NODE: ffff880117bfbfc0", 177 | " PHYSICAL ADDRESS: d04f1000", 178 | " PID: 1297 MAPPINGS: 1 MM: ffff88007f8aa580", 179 | " PID: 1318 MAPPINGS: 1 MM: ffff88007f8abe80", 180 | "", 181 | " PAGE: ffffea00021e9880", 182 | " STABLE_NODE: ffff880054ee1f30", 183 | " PHYSICAL ADDRESS: 87a62000", 184 | " PID: 1297 MAPPINGS: 2 MM: ffff88007f8aa580", 185 | " ...", 186 | "", 187 | " Display all information about the KSM page whose physical", 188 | " address is 0xffffea000168cd00:\n", 189 | " %s> ksm -v ffffea000168cd00", 190 | " PAGE: ffffea000168cd00", 191 | " STABLE_NODE: ffff88007153ce10", 192 | " PHYSICAL ADDRESS: 5a334000", 193 | " PID: 1297 MAPPINGS: 4 MM: ffff88007f8aa580", 194 | " VIRTUAL:", 195 | " 7f8cb91f9000", 196 | " 7f8cb8f28000", 197 | " 7f8cb7abf000", 198 | " 7f8cb79c7000", 199 | "", 200 | " PID: 1318 MAPPINGS: 4 MM: ffff88007f8abe80", 201 | " VIRTUAL:", 202 | " 7f7ca0703000", 203 | " 7f7c9f15e000", 204 | " 7f7c9ef8f000", 205 | " 7f7c9e96b000", 206 | NULL 207 | }; 208 | 209 | /* 210 | * find the page_ref whose mm is same as mm 211 | */ 212 | static struct page_ref * 213 | find_match_ref(struct page_ref *ref_list, ulong mm) 214 | { 215 | struct page_ref *next_ref = ref_list; 216 | 217 | while (next_ref) { 218 | if (next_ref->mm == mm) { 219 | break; 220 | } else { 221 | next_ref = next_ref->next; 222 | } 223 | } 224 | 225 | return next_ref; 226 | } 227 | 228 | /* 229 | * get the pid of the task that mm_struct belongs to, if not find, 230 | * return (ulong)-1 231 | */ 232 | static ulong 233 | find_pid(ulong mm) 234 | { 235 | struct task_context *tc; 236 | int i; 237 | ulong pid = -1; 238 | 239 | tc = FIRST_CONTEXT(); 240 | for (i = 0; i < RUNNING_TASKS(); i++, tc++) { 241 | if (tc->mm_struct == mm) { 242 | pid = tc->pid; 243 | break; 244 | } 245 | } 246 | 247 | return pid; 248 | } 249 | 250 | static void 251 | add_to_ref_list(struct page_ref **ref_list_ptr, struct page_ref *ref) 252 | { 253 | ref->next = *ref_list_ptr; 254 | *ref_list_ptr = ref; 255 | } 256 | 257 | static void 258 | clean_ref_list(struct page_ref *ref_list) 259 | { 260 | struct page_ref *tmp_ref, *next_ref; 261 | 262 | tmp_ref = ref_list; 263 | 264 | while (tmp_ref) { 265 | next_ref = tmp_ref->next; 266 | FREEBUF(tmp_ref); 267 | tmp_ref = next_ref; 268 | } 269 | } 270 | 271 | /* 272 | * dump the ksm pages from the stable tree 273 | */ 274 | static void 275 | dump_stable_tree(struct meminfo *mi, struct rb_root *root) 276 | { 277 | ulong stable_node, kpfn; 278 | ulong rmap_item, mm, paddr; 279 | struct rb_node *node; 280 | ulong first, next; 281 | int found; 282 | struct page_ref *ref_list; 283 | ulong page, address; 284 | 285 | found = (mi && mi->flags & ADDRESS_SPECIFIED) ? 0 : -1; 286 | 287 | for (node = rb_first(root); node; node = rb_next(node)) { 288 | stable_node = (ulong) node - KSM_OFFSET(stable_node_node); 289 | if (CRASHDEBUG(1)) 290 | fprintf(fp, " stable_node = %lx\n", stable_node); 291 | 292 | readmem(stable_node + KSM_OFFSET(stable_node_kpfn), 293 | KVADDR, &kpfn, sizeof(ulong), 294 | "stable_node kpfn", FAULT_ON_ERROR); 295 | paddr = kpfn << PAGE_SHIFT; 296 | phys_to_page(paddr, &page); 297 | 298 | if (found == 0) { 299 | if ((mi->memtype == KVADDR) && 300 | (((mi->spec_addr & ~0x3) == stable_node) || 301 | (mi->spec_addr == page))) 302 | found = 1; 303 | if ((mi->memtype == PHYSADDR) && 304 | (mi->spec_addr == paddr)) 305 | found = 1; 306 | } 307 | if (found == 0) 308 | continue; 309 | 310 | fprintf(fp, " PAGE: %lx\n", page); 311 | fprintf(fp, " STABLE_NODE: %lx\n", stable_node); 312 | fprintf(fp, "PHYSICAL ADDRESS: %lx\n", paddr); 313 | 314 | readmem(stable_node + KSM_OFFSET(stable_node_hlist), 315 | KVADDR, &first, sizeof(ulong), 316 | "stable_node hlist", FAULT_ON_ERROR); 317 | 318 | next = first; 319 | ref_list = NULL; 320 | struct page_ref *tmp_ref = NULL; 321 | 322 | while (next) { 323 | rmap_item = next - KSM_OFFSET(rmap_item_hlist); 324 | readmem(rmap_item + KSM_OFFSET(rmap_item_mm), 325 | KVADDR, &mm, sizeof(ulong), 326 | "rmap_item mm", FAULT_ON_ERROR); 327 | 328 | //get the page_ref whose mm is equal to rmap_item's mm 329 | tmp_ref = find_match_ref(ref_list, mm); 330 | if (tmp_ref) { 331 | tmp_ref->ref += 1; 332 | } else { 333 | //create a new page_ref 334 | tmp_ref = (struct page_ref *)GETBUF( 335 | sizeof(struct page_ref)); 336 | tmp_ref->mm = mm; 337 | tmp_ref->pid = find_pid(mm); 338 | tmp_ref->ref = 1; 339 | 340 | add_to_ref_list(&ref_list, tmp_ref); 341 | } 342 | 343 | readmem(next + OFFSET(hlist_node_next), 344 | KVADDR, &next, sizeof(ulong), 345 | "hlist_node next", FAULT_ON_ERROR); 346 | }; 347 | 348 | tmp_ref = ref_list; 349 | while (tmp_ref) { 350 | if (tmp_ref->pid == (ulong)-1) { 351 | /* 352 | * the task has exited, but the ksm pages has 353 | * not been cleared yet. 354 | */ 355 | fprintf(fp, " PID: - "); 356 | } else { 357 | fprintf(fp, " PID: %ld ", tmp_ref->pid); 358 | } 359 | fprintf(fp, " MAPPINGS: %d ", tmp_ref->ref); 360 | fprintf(fp, " MM: %lx\n", tmp_ref->mm); 361 | 362 | if (!(mi && mi->flags & VERBOSE)) 363 | goto next_ref; 364 | 365 | fprintf(fp, " VIRTUAL:\n"); 366 | next = first; 367 | while (next) { 368 | rmap_item = next - KSM_OFFSET(rmap_item_hlist); 369 | readmem(rmap_item + KSM_OFFSET(rmap_item_mm), 370 | KVADDR, &mm, sizeof(ulong), 371 | "rmap_item mm", FAULT_ON_ERROR); 372 | if (tmp_ref->mm == mm) { 373 | readmem(rmap_item + KSM_OFFSET(rmap_item_address), 374 | KVADDR, &address, sizeof(ulong), 375 | "rmap_item address", FAULT_ON_ERROR); 376 | fprintf(fp, " %lx\n", 377 | PAGEBASE(address)); 378 | } 379 | readmem(next + OFFSET(hlist_node_next), 380 | KVADDR, &next, sizeof(ulong), 381 | "hlist_node next", FAULT_ON_ERROR); 382 | } 383 | fprintf(fp, "\n"); 384 | 385 | next_ref: 386 | tmp_ref = tmp_ref->next; 387 | } 388 | 389 | //clear all page_ref 390 | clean_ref_list(ref_list); 391 | 392 | if (!(mi && mi->flags & VERBOSE)) 393 | fprintf(fp, "\n"); 394 | 395 | if (found == 1) 396 | break; 397 | } 398 | 399 | if (found == 0) 400 | fprintf(fp, "address 0x%llx cannot specify a ksm stable tree node\n", 401 | mi->spec_addr); 402 | } 403 | 404 | /* 405 | * dump_ksm() displays information of ksm pages. 406 | */ 407 | static void 408 | dump_ksm(struct meminfo *mi) 409 | { 410 | ulong root_stable_tree_ptr; 411 | ulong ksm_nr_node_ids_ptr; 412 | int ksm_nr_node_ids; 413 | struct rb_root *root; 414 | int i; 415 | 416 | if (!symbol_exists("root_stable_tree")) { 417 | error(INFO, "cannot determine ksm stable tree address from root_stable_tree\n"); 418 | return; 419 | } 420 | root_stable_tree_ptr = symbol_value("root_stable_tree"); 421 | 422 | if (symbol_exists("ksm_nr_node_ids")) { 423 | //root_stable_tree_ptr is an array of stable tree root 424 | ksm_nr_node_ids_ptr = symbol_value("ksm_nr_node_ids"); 425 | readmem(ksm_nr_node_ids_ptr, KVADDR, &ksm_nr_node_ids, 426 | sizeof(ksm_nr_node_ids), "ksm_nr_node_ids", 427 | FAULT_ON_ERROR); 428 | 429 | readmem(root_stable_tree_ptr, KVADDR, &root, sizeof(ulong), 430 | "first stable tree root", FAULT_ON_ERROR); 431 | 432 | for (i = 0; i < ksm_nr_node_ids; i++) { 433 | dump_stable_tree(mi, root + i); 434 | } 435 | } else { 436 | root = (struct rb_root *)root_stable_tree_ptr; 437 | dump_stable_tree(mi, root); 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /lscgroup.c: -------------------------------------------------------------------------------- 1 | /* lscgroup.c - list cgroups 2 | * 3 | * Copyright (C) 2012 FUJITSU LIMITED 4 | * Author: Yu Yongming 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #include "defs.h" 18 | 19 | #define PATHNAME_MAX 200 20 | #define ROOT_MAX 64 21 | #define CG_CONTROLLER_MAX 20 22 | #define CG_HIER_MAX 20 23 | #define LIST_CER 0x1 24 | #define LIST_ADD 0x2 25 | 26 | 27 | #define CGROUP_MEMBER_OFFSET_INIT(X,Y,Z) (cgroup_offset_table.X=MEMBER_OFFSET(Y,Z)) 28 | #define CGROUP_OFFSET(X) (OFFSET_verify(cgroup_offset_table.X, (char *)__FUNCTION__, __FILE__, __LINE__, #X)) 29 | 30 | /* 31 | *struct declaration 32 | */ 33 | struct cgroup_offset_table { 34 | long cgroupfs_root_root_list; 35 | long cgroupfs_root_top_cgroup; 36 | long cgroupfs_root_subsys_list; 37 | long cgroupfs_root_actual_subsys_bits; 38 | long cgroupfs_root_name; 39 | long cgroup_sibling; 40 | long cgroup_children; 41 | long cgroup_dentry; 42 | long cgroup_subsys_name; 43 | long cgroup_subsys_sibling; 44 | }; 45 | 46 | struct cgroup_group_spec { 47 | char path[PATHNAME_MAX]; 48 | char *controllers[CG_CONTROLLER_MAX]; 49 | }; 50 | 51 | /* 52 | *function declaration 53 | */ 54 | int _init(void); 55 | int _fini(void); 56 | 57 | void cmd_lscgroup(void); 58 | char *help_lscgroup[]; 59 | 60 | static void cgroup_init(void); 61 | static void get_controller_name(ulong); 62 | static void get_cgroup_name(ulong, char *); 63 | static void parse_top_cgroup(ulong, char *); 64 | static void print_all_cgroups(void); 65 | static int in_cgroupfs_root(char *, struct cgroup_group_spec *); 66 | static ulong locate_top_cgroup(struct cgroup_group_spec *); 67 | static void parse_path(char *, char **); 68 | static ulong locate_cgroup(ulong, ulong, char *); 69 | static void standarlize_path(char *); 70 | static void cgroup_elem(struct cgroup_group_spec *); 71 | static void check_addr(ulong, ulong, char *, char *); 72 | static ulong addr_to_cgroup(ulong, char *); 73 | static void cgroup_addr(char *); 74 | static void cgroup_list_cgroups(char **, struct cgroup_group_spec **, int); 75 | static int parse_cgroup_spec(struct cgroup_group_spec **, char *); 76 | static void free_cgroup_spec(char **, struct cgroup_group_spec **); 77 | 78 | /* 79 | * global data 80 | */ 81 | 82 | static char controller_name[PATHNAME_MAX]; 83 | static int authen = FALSE; 84 | char *spe; 85 | 86 | static struct command_table_entry command_table[] = { 87 | {"lscgroup", cmd_lscgroup, help_lscgroup, 0}, 88 | {NULL}, 89 | }; 90 | 91 | static struct cgroup_offset_table cgroup_offset_table = { 0 }; 92 | 93 | char *help_lscgroup[] = { 94 | "lscgroup", 95 | "list all cgroups", 96 | "[[:] [...]] [[
][...]]", 97 | " The command list all present cgroups and their addresses, chosen cgroups", 98 | " and their addresses or cgroup according to input address. When no parameter", 99 | " is specified, the command list all cgroups which are present. When parameter", 100 | " controller/path is specified, the command list subcgroups of cgroup specified", 101 | " by parameter :, controllers in : can be", 102 | " a comma separated controller list. When the input is hexadecimal address, the", 103 | " command display the cgroup whose address equals to the specified parameter.", 104 | "\nEXAMPLE", 105 | " display all cgroups:\n", 106 | " %s>lscgroup", 107 | " CGROUP CONTROLLER:PATH", 108 | " ffff8801188f8030 blkio:/", 109 | " ffff8801188d3200 blkio:/libvirt", 110 | " ffff88011a724600 blkio:/libvirt/lxc", 111 | " ffff88011adac600 blkio:/libvirt/qemu", 112 | " ffff8801151f6030 net_cls:/", 113 | " ffff88011522a030 freezer:/", 114 | " ffff8801158f9200 freezer:/libvirt", 115 | " ffff88011a724800 freezer:/libvirt/lxc", 116 | " ffff88011adac800 freezer:/libvirt/qemu", 117 | " ffff880115264030 devices:/", 118 | " ffff880115846e00 devices:/libvirt", 119 | " ffff88011a724a00 devices:/libvirt/lxc", 120 | " ffff88011adaca00 devices:/libvirt/qemu", 121 | " ffff88011794c030 memory:/", 122 | " ffff88011738ca00 memory:/libvirt", 123 | " ffff88011a724c00 memory:/libvirt/lxc", 124 | " ffff88011adacc00 memory:/libvirt/qemu", 125 | " ffff880115b20030 cpuacct:/", 126 | " ffff8801191ed600 cpuacct:/libvirt", 127 | " ffff88011a08d200 cpuacct:/libvirt/lxc", 128 | " ffff8801188d3e00 cpuacct:/libvirt/qemu", 129 | " ffff88011923a030 cpu:/", 130 | " ffff880115846600 cpu:/libvirt", 131 | " ffff88011906e400 cpu:/libvirt/lxc", 132 | " ffff880117be6400 cpu:/libvirt/qemu", 133 | " ffff8801173f2030 cpuset:/", 134 | " ffff8801179e6200 cpuset:/libvirt", 135 | " ffff88011a08d000 cpuset:/libvirt/lxc", 136 | " ffff880119fe8000 cpuset:/libvirt/qemu", 137 | " ", 138 | " display chosen cgroups:\n", 139 | " %s>lscgroup cpu:/libvirt cpu:/", 140 | " CGROUP CONTROLLER:PATH", 141 | " ffff880115846600 cpu:/libvirt", 142 | " ffff88011906e400 cpu:/libvirt/lxc", 143 | " ffff880117be6400 cpu:/libvirt/qemu", 144 | " ffff88011923a030 cpu:/", 145 | " ffff880115846600 cpu:/libvirt", 146 | " ffff88011906e400 cpu:/libvirt/lxc", 147 | " ffff880117be6400 cpu:/libvirt/qemu", 148 | " ", 149 | " display cgroup according to input address:\n", 150 | " %s>lscgroup ffff880119fe8000 ffff880115846600", 151 | " CGROUP CONTROLLER:PATH", 152 | " ffff880119fe8000 cpuset:/libvirt/qemu", 153 | " ffff880115846600 cpu:/libvirt", 154 | NULL 155 | }; 156 | 157 | int 158 | _init(void) 159 | { 160 | register_extension(command_table); 161 | return 1; 162 | } 163 | 164 | int 165 | _fini(void) 166 | { 167 | return 1; 168 | } 169 | 170 | static void 171 | cgroup_init(void) 172 | { 173 | CGROUP_MEMBER_OFFSET_INIT(cgroupfs_root_root_list, "cgroupfs_root", 174 | "root_list"); 175 | CGROUP_MEMBER_OFFSET_INIT(cgroupfs_root_top_cgroup, "cgroupfs_root", 176 | "top_cgroup"); 177 | CGROUP_MEMBER_OFFSET_INIT(cgroupfs_root_subsys_list, "cgroupfs_root", 178 | "subsys_list"); 179 | CGROUP_MEMBER_OFFSET_INIT(cgroupfs_root_actual_subsys_bits, 180 | "cgroupfs_root", "actual_subsys_bits"); 181 | CGROUP_MEMBER_OFFSET_INIT(cgroupfs_root_name, "cgroupfs_root", 182 | "name"); 183 | CGROUP_MEMBER_OFFSET_INIT(cgroup_sibling, "cgroup", "sibling"); 184 | CGROUP_MEMBER_OFFSET_INIT(cgroup_children, "cgroup", "children"); 185 | CGROUP_MEMBER_OFFSET_INIT(cgroup_dentry, "cgroup", "dentry"); 186 | CGROUP_MEMBER_OFFSET_INIT(cgroup_subsys_name, "cgroup_subsys", "name"); 187 | CGROUP_MEMBER_OFFSET_INIT(cgroup_subsys_sibling, "cgroup_subsys", 188 | "sibling"); 189 | } 190 | 191 | static void 192 | get_controller_name(ulong cgroupfs_root) 193 | { 194 | char name[PATHNAME_MAX]; 195 | ulong subsys, next, subsys_list, name_buf, name_addr; 196 | int len; 197 | 198 | BZERO(name, PATHNAME_MAX); 199 | BZERO(controller_name, PATHNAME_MAX); 200 | 201 | readmem(cgroupfs_root + CGROUP_OFFSET(cgroupfs_root_actual_subsys_bits), 202 | KVADDR, &subsys, sizeof(ulong), "cgroupfs_root_actual_subsys", 203 | FAULT_ON_ERROR); 204 | if(subsys == 0) { 205 | readmem(cgroupfs_root + CGROUP_OFFSET(cgroupfs_root_name), 206 | KVADDR, &name, ROOT_MAX, "cgroupfs_root_name", 207 | FAULT_ON_ERROR); 208 | strcat(controller_name, name); 209 | return ; 210 | } 211 | subsys_list = cgroupfs_root + CGROUP_OFFSET(cgroupfs_root_subsys_list); 212 | readmem(subsys_list, KVADDR, &next, sizeof(ulong), 213 | "cgroupfs_root_subsys_list", FAULT_ON_ERROR); 214 | 215 | do { 216 | name_buf = next - CGROUP_OFFSET(cgroup_subsys_sibling) 217 | + CGROUP_OFFSET(cgroup_subsys_name); 218 | readmem(name_buf, KVADDR, &name_addr, sizeof(ulong), 219 | "cgroup_subsys_name", FAULT_ON_ERROR); 220 | readmem(name_addr, KVADDR, name, sizeof(name), 221 | "char* name", FAULT_ON_ERROR); 222 | 223 | strcat(controller_name, name); 224 | strcat(controller_name, ","); 225 | 226 | readmem(next, KVADDR, &next, sizeof(ulong), 227 | "list_head", FAULT_ON_ERROR); 228 | } while(next != subsys_list); 229 | 230 | len = strlen(controller_name); 231 | controller_name[len-1] = '\0'; 232 | } 233 | 234 | static void 235 | get_cgroup_name(ulong cgroup, char *name) 236 | { 237 | ulong dentry, name_addr; 238 | char *dentry_buf; 239 | int len; 240 | 241 | readmem(cgroup + CGROUP_OFFSET(cgroup_dentry), KVADDR, 242 | &dentry, sizeof(ulong), "cgroup dentry", 243 | FAULT_ON_ERROR); 244 | 245 | dentry_buf = GETBUF(SIZE(dentry)); 246 | readmem(dentry, KVADDR, dentry_buf, SIZE(dentry), 247 | "dentry", FAULT_ON_ERROR); 248 | len = UINT(dentry_buf + OFFSET(dentry_d_name) + OFFSET(qstr_len)); 249 | name_addr = ULONG(dentry_buf + OFFSET(dentry_d_name) + OFFSET(qstr_name)); 250 | readmem(name_addr, KVADDR, name, len, "qstr name", FAULT_ON_ERROR); 251 | FREEBUF(dentry_buf); 252 | } 253 | 254 | static void 255 | parse_top_cgroup(ulong top_cgroup, char *path) 256 | { 257 | int len; 258 | char tmp_buf[PATHNAME_MAX]; 259 | ulong list_head[2], next, child; 260 | 261 | BZERO(tmp_buf, PATHNAME_MAX); 262 | get_cgroup_name(top_cgroup, tmp_buf); 263 | if (strlen(path) > 1) 264 | strncat(path, "/", 1); 265 | strncat(path, tmp_buf, strlen(tmp_buf)); 266 | 267 | fprintf(fp, "%lx %s:%s\n", top_cgroup, controller_name, path); 268 | 269 | child = top_cgroup + CGROUP_OFFSET(cgroup_children); 270 | readmem(child, KVADDR, list_head, sizeof(ulong) * 2, 271 | "cgroup children", FAULT_ON_ERROR); 272 | 273 | if ((list_head[0] == child) && (list_head[1] == child)) 274 | return; 275 | 276 | next = list_head[0]; 277 | while (next != child) { 278 | top_cgroup = next - CGROUP_OFFSET(cgroup_sibling); 279 | readmem(top_cgroup + CGROUP_OFFSET(cgroup_sibling) + 280 | OFFSET(list_head_next), KVADDR, &next, sizeof(ulong), 281 | "cgroup siblings", FAULT_ON_ERROR); 282 | len = strlen(path); 283 | parse_top_cgroup(top_cgroup, path); 284 | path[len] = '\0'; 285 | } 286 | } 287 | 288 | static void 289 | print_all_cgroups(void) 290 | { 291 | struct syment *roots; 292 | ulong top_cgroup; 293 | ulong list_head[2], next, cgroupfs_root;; 294 | char path_name[PATHNAME_MAX]; 295 | 296 | BZERO(path_name, PATHNAME_MAX); 297 | 298 | if (!(roots = symbol_search("roots"))) 299 | error(FATAL, "roots symbol does not exist?\n"); 300 | 301 | readmem(roots->value, KVADDR, list_head, sizeof(ulong) * 2, 302 | "list_head", FAULT_ON_ERROR); 303 | 304 | if ((list_head[0] == roots->value) && (list_head[1] == roots->value)) { 305 | fprintf(fp, "no active cgroup hierarchy in the kernel."); 306 | return; 307 | } 308 | 309 | next = list_head[0]; 310 | while (next != roots->value) { 311 | cgroupfs_root = next - CGROUP_OFFSET(cgroupfs_root_root_list); 312 | get_controller_name(cgroupfs_root); 313 | 314 | top_cgroup = cgroupfs_root + 315 | CGROUP_OFFSET(cgroupfs_root_top_cgroup); 316 | /* print infomation of cgroup and subcgroup */ 317 | parse_top_cgroup(top_cgroup, path_name); 318 | readmem(next + OFFSET(list_head_next), KVADDR, &next, 319 | sizeof(ulong), "list_head next", FAULT_ON_ERROR); 320 | BZERO(path_name, PATHNAME_MAX); 321 | BZERO(controller_name, PATHNAME_MAX); 322 | } 323 | } 324 | 325 | static int 326 | in_cgroupfs_root(char *tmp_name, struct cgroup_group_spec *elem) 327 | { 328 | char *name; 329 | int i = 0; 330 | name = strtok(tmp_name, ","); 331 | while(name != NULL) { 332 | while(elem->controllers[i] != NULL && i < CG_CONTROLLER_MAX) { 333 | if(STREQ(name, elem->controllers[i])) 334 | return 1; 335 | i++; 336 | } 337 | name = strtok(NULL, ","); 338 | i = 0; 339 | } 340 | 341 | return 0; 342 | } 343 | 344 | static ulong 345 | locate_top_cgroup(struct cgroup_group_spec *elem) 346 | { 347 | struct syment *roots; 348 | ulong top_cgroup; 349 | ulong list_head[2], next, cgroupfs_root; 350 | char tmp_name[PATHNAME_MAX]; 351 | 352 | if (!(roots = symbol_search("roots"))) 353 | error(FATAL, "roots symbol does not exist?"); 354 | 355 | readmem(roots->value, KVADDR, list_head, sizeof(ulong)*2, 356 | "list_head", FAULT_ON_ERROR); 357 | 358 | if ((list_head[0] == roots->value) && (list_head[1] == roots->value)) 359 | return 0; 360 | 361 | next = list_head[0]; 362 | while (next != roots->value) { 363 | /* begin from first root */ 364 | cgroupfs_root = next - CGROUP_OFFSET(cgroupfs_root_root_list); 365 | get_controller_name(cgroupfs_root); 366 | strcpy(tmp_name, controller_name); 367 | 368 | if (in_cgroupfs_root(tmp_name, elem)) { 369 | top_cgroup = cgroupfs_root + 370 | CGROUP_OFFSET(cgroupfs_root_top_cgroup); 371 | return top_cgroup; 372 | } 373 | readmem(next + OFFSET(list_head_next), KVADDR, &next, 374 | sizeof(ulong), "list_head", FAULT_ON_ERROR); 375 | BZERO(controller_name, PATHNAME_MAX); 376 | } 377 | return 0; 378 | } 379 | 380 | /* 381 | * function parse_path and extracts the input path before the first '/' as 382 | * relatively path which will be used to compare with path name of cgroup. 383 | * In addition, it ignores the unnecessary '/' in the path 384 | */ 385 | static void 386 | parse_path(char *path, char **elem_path) 387 | { 388 | char *token; 389 | 390 | token = *elem_path; 391 | if (token[0] == '/') { 392 | *path++ = *token++; 393 | } else { 394 | while (*token) { 395 | if (*token == '/' && *(token + 1) == '/') { 396 | token++; 397 | } else if (*token == '/' && *(token + 1) != '/') { 398 | token++; 399 | break; 400 | } else { 401 | *path++ = *token++; 402 | } 403 | } 404 | } 405 | *elem_path = token; 406 | *path = '\0'; 407 | } 408 | 409 | static ulong 410 | locate_cgroup(ulong current, ulong prev_addr, char *elem_path) 411 | { 412 | char tmp_buf[PATHNAME_MAX]; 413 | char path[PATHNAME_MAX]; 414 | char *tmp_path = elem_path; 415 | ulong child, sibling, child_offset, sibling_offset; 416 | 417 | if (*elem_path == '\0') 418 | return prev_addr; 419 | 420 | BZERO(tmp_buf, PATHNAME_MAX); 421 | get_cgroup_name(current, tmp_buf); 422 | 423 | parse_path(path, &elem_path); 424 | 425 | readmem(current + CGROUP_OFFSET(cgroup_children), KVADDR, &child, 426 | sizeof(ulong), "cgroup children", FAULT_ON_ERROR); 427 | readmem(current + CGROUP_OFFSET(cgroup_sibling), KVADDR, &sibling, 428 | sizeof(ulong), "cgroup sibling", FAULT_ON_ERROR); 429 | 430 | child_offset = CGROUP_OFFSET(cgroup_children); 431 | sibling_offset = CGROUP_OFFSET(cgroup_sibling); 432 | /* parse the children if temp=path_name, do change elem_path */ 433 | if (STREQ(tmp_buf, path)) { 434 | if ((child - child_offset != current)) 435 | return locate_cgroup(child - sibling_offset, 436 | current, elem_path); 437 | else if (*elem_path == '\0') 438 | return current; 439 | } else { 440 | /* parse the sibling if temp !=path_name, do not chage elem_path */ 441 | if (((sibling - child_offset) != prev_addr) && 442 | ((sibling - sibling_offset) != current)) { 443 | elem_path = tmp_path; 444 | return locate_cgroup(sibling - sibling_offset, 445 | prev_addr, elem_path); 446 | } 447 | } 448 | return 0; 449 | } 450 | 451 | /* 452 | * make the input path begin with character '/' and end without character '/' 453 | * to cope with the situation that the input path may be like "/xxxxx", 454 | * "xxxxx", "xxxxx/", or "/xxxxx/" 455 | */ 456 | static void 457 | standarlize_path(char *path) 458 | { 459 | int len; 460 | 461 | len = strlen(path) - 1; 462 | if (path[len] == '/') 463 | path[len] = '\0'; 464 | 465 | if (path[0] != '/') { 466 | len = strlen(path); 467 | path[len + 1] = '\0'; 468 | while (len) { 469 | path[len] = path[len - 1]; 470 | len--; 471 | } 472 | path[0] = '/'; 473 | } 474 | } 475 | 476 | static void 477 | mani_path(char *path) 478 | { 479 | int len; 480 | 481 | standarlize_path(path); 482 | 483 | if (STREQ(path, "/")) { 484 | *path = '\0'; 485 | return ; 486 | } 487 | len = strlen(path); 488 | while (path[len] != '/') { 489 | path[len] = '\0'; 490 | len--; 491 | } 492 | path[len] = '\0'; 493 | path[0] = '/'; 494 | } 495 | 496 | static void 497 | cgroup_elem(struct cgroup_group_spec *list_elem) 498 | { 499 | ulong top_cgroup; 500 | ulong cgroup; 501 | char path_name[PATHNAME_MAX]; 502 | char buf1[BUFSIZE]; 503 | 504 | top_cgroup = locate_top_cgroup(list_elem); 505 | if (top_cgroup == 0) { 506 | fprintf(fp, "%s %s\n", 507 | mkstring(buf1, VADDR_PRLEN, CENTER, "no cgroup"), 508 | spe); 509 | return ; 510 | } 511 | 512 | strcpy(path_name, list_elem->path); 513 | standarlize_path(path_name); 514 | cgroup = locate_cgroup(top_cgroup, top_cgroup, path_name); 515 | if (cgroup == 0) { 516 | fprintf(fp, "%s %s\n", 517 | mkstring(buf1, VADDR_PRLEN, CENTER, "no cgroup"), 518 | spe); 519 | return ; 520 | } 521 | 522 | mani_path(list_elem->path); 523 | parse_top_cgroup(cgroup, list_elem->path); 524 | } 525 | 526 | static void 527 | check_addr(ulong top_cgroup, ulong addr_num, char *path, char *full_path) 528 | { 529 | int len; 530 | char tmp_buf[PATHNAME_MAX]; 531 | ulong list_head[2], next, child; 532 | 533 | BZERO(tmp_buf, PATHNAME_MAX); 534 | get_cgroup_name(top_cgroup, tmp_buf); 535 | if (strlen(path) > 1) 536 | strncat(path, "/", 1); 537 | strncat(path, tmp_buf, strlen(tmp_buf)); 538 | 539 | if (top_cgroup == addr_num) { 540 | strcpy(full_path, path); 541 | authen = TRUE; 542 | return ; 543 | } 544 | 545 | child = top_cgroup + CGROUP_OFFSET(cgroup_children); 546 | readmem(child, KVADDR, list_head, sizeof(ulong) * 2, 547 | "cgroup children", FAULT_ON_ERROR); 548 | 549 | next = list_head[0]; 550 | while (next != child) { 551 | top_cgroup = next - CGROUP_OFFSET(cgroup_sibling); 552 | readmem(top_cgroup + CGROUP_OFFSET(cgroup_sibling) + 553 | OFFSET(list_head_next), KVADDR, &next, sizeof(ulong), 554 | "cgroup siblings", FAULT_ON_ERROR); 555 | len = strlen(path); 556 | check_addr(top_cgroup, addr_num, path, full_path); 557 | path[len] = '\0'; 558 | } 559 | } 560 | 561 | static ulong 562 | addr_to_cgroup(ulong addr_num, char *path) 563 | { 564 | struct syment *roots; 565 | ulong top_cgroup; 566 | ulong list_head[2], next, cgroupfs_root; 567 | char path_name[PATHNAME_MAX]; 568 | 569 | BZERO(path_name, PATHNAME_MAX); 570 | if (!(roots = symbol_search("roots"))) 571 | error(FATAL, "roots symbol does not exist?"); 572 | 573 | readmem(roots->value, KVADDR, list_head, sizeof(ulong)*2, 574 | "list_head", FAULT_ON_ERROR); 575 | 576 | next = list_head[0]; 577 | while (next != roots->value) { 578 | /* begin from first root */ 579 | cgroupfs_root = next - CGROUP_OFFSET(cgroupfs_root_root_list); 580 | get_controller_name(cgroupfs_root); 581 | 582 | top_cgroup = cgroupfs_root + 583 | CGROUP_OFFSET(cgroupfs_root_top_cgroup); 584 | check_addr(top_cgroup, addr_num, path_name, path); 585 | if (authen) { 586 | authen = FALSE; 587 | return TRUE; 588 | } 589 | readmem(next + OFFSET(list_head_next), KVADDR, &next, 590 | sizeof(ulong), "list_head", FAULT_ON_ERROR); 591 | BZERO(path_name, PATHNAME_MAX); 592 | BZERO(controller_name, PATHNAME_MAX); 593 | } 594 | return FALSE; 595 | } 596 | 597 | static void 598 | cgroup_addr(char *addr) 599 | { 600 | ulong addr_num; 601 | char path[PATHNAME_MAX]; 602 | char buf1[BUFSIZE]; 603 | 604 | BZERO(path, PATHNAME_MAX); 605 | addr_num = stol (addr, FAULT_ON_ERROR, NULL); 606 | 607 | if (!addr_to_cgroup(addr_num, path)) { 608 | fprintf(fp, "%s can not find cgroup with address %s\n", 609 | mkstring(buf1, VADDR_PRLEN, CENTER, addr), addr); 610 | return ; 611 | } 612 | fprintf(fp, "%lx %s:%s\n", addr_num, controller_name, path); 613 | } 614 | 615 | static void 616 | cgroup_list_cgroups(char **addr, struct cgroup_group_spec *cgroup_list[], 617 | int flags) 618 | { 619 | int i,j; 620 | char buf1[BUFSIZE]; 621 | 622 | i = j = 0; 623 | cgroup_init(); 624 | if(cgroup_offset_table.cgroupfs_root_root_list == -1) { 625 | fprintf(fp, "cgroup does not exist, check kernel version."); 626 | return ; 627 | } 628 | fprintf(fp, "%s CONTROLLER:PATH\n", 629 | mkstring(buf1, VADDR_PRLEN, CENTER, "CGROUP")); 630 | if (flags == 0 ) { 631 | /* list all the cgroups */ 632 | print_all_cgroups(); 633 | } 634 | if (flags & LIST_CER) { 635 | /* list the specified cgroups */ 636 | while ((cgroup_list[i]->path != NULL)) { 637 | cgroup_elem(cgroup_list[i]); 638 | i++; 639 | } 640 | } 641 | if (flags & LIST_ADD) { 642 | /* list specified addr cgroups */ 643 | while ((addr[j] != NULL)) { 644 | cgroup_addr(addr[j]); 645 | j++; 646 | } 647 | } 648 | } 649 | 650 | static int 651 | parse_cgroup_spec(struct cgroup_group_spec **list, char *optarg) 652 | { 653 | struct cgroup_group_spec *ptr; 654 | int i, j; 655 | char *controller, *path, *temp; 656 | 657 | ptr = *list; 658 | 659 | for (i = 0; i < CG_HIER_MAX; i++, ptr++) { 660 | if (!list[i]) 661 | break; 662 | } 663 | 664 | if (i == CG_HIER_MAX) { 665 | fprintf(fp, "Max allowed hierarchies %d reached\n", 666 | CG_HIER_MAX); 667 | return -1; 668 | } 669 | 670 | controller = strtok(optarg, ":"); 671 | if (!controller) 672 | return -1; 673 | 674 | path = strtok(NULL, ":"); 675 | if (!path) 676 | return -1; 677 | 678 | list[i] = (struct cgroup_group_spec *)GETBUF(sizeof(**list)); 679 | j = 0; 680 | do { 681 | if (j == 0) 682 | temp = strtok(controller, ","); 683 | else 684 | temp = strtok(NULL, ","); 685 | 686 | if (temp) { 687 | list[i]->controllers[j] = strdup(temp); 688 | if (!list[i]->controllers[j]) { 689 | FREEBUF(list[i]); 690 | fprintf(fp, "%s\n", strerror(errno)); 691 | return -1; 692 | } 693 | } 694 | j++; 695 | } while (temp && (j < (CG_CONTROLLER_MAX - 1))); 696 | 697 | strncpy(list[i]->path, path, strlen(path)); 698 | 699 | return 0; 700 | } 701 | 702 | static int 703 | parse_addr_spec(char **addr,char *optarg) 704 | { 705 | int i; 706 | 707 | for(i = 0; i < CG_HIER_MAX; i++) 708 | if (!addr[i]) 709 | break; 710 | if (i == CG_HIER_MAX) { 711 | fprintf(fp, "Max allowed hierarchies %d reached\n", 712 | CG_HIER_MAX); 713 | return -1; 714 | } 715 | 716 | addr[i] = strdup(optarg); 717 | if (!addr[i]) { 718 | fprintf(fp, "%s\n",strerror(errno)); 719 | return -1; 720 | } 721 | return 0; 722 | } 723 | 724 | static void 725 | free_cgroup_spec(char **addr, struct cgroup_group_spec **list) 726 | { 727 | int i,j; 728 | 729 | for (i = 0; i < CG_HIER_MAX; i++) { 730 | if (list[i]) { 731 | for(j = 0; j< CG_CONTROLLER_MAX; j++) 732 | if (list[i]->controllers[j]) 733 | free(list[i]->controllers[j]); 734 | FREEBUF(list[i]); 735 | } 736 | else { 737 | break; 738 | } 739 | } 740 | for (i =0; i< CG_HIER_MAX; i++) { 741 | if (addr[i]) 742 | free(addr[i]); 743 | else 744 | break; 745 | } 746 | 747 | } 748 | 749 | void 750 | cmd_lscgroup(void) 751 | { 752 | int c, ret, flags; 753 | char *addr[CG_HIER_MAX]; 754 | struct cgroup_group_spec *cgroup_list[CG_HIER_MAX]; 755 | 756 | ret = flags = 0; 757 | BZERO(cgroup_list, sizeof(cgroup_list)); 758 | BZERO(addr, sizeof(addr)); 759 | 760 | while ((c = getopt(argcnt, args, "")) != EOF) { 761 | switch(c) { 762 | default: 763 | cmd_usage(pc->curcmd, SYNOPSIS); 764 | return ; 765 | } 766 | } 767 | if (argerrs) 768 | cmd_usage(pc->curcmd, SYNOPSIS); 769 | while (optind < argcnt) { 770 | spe = strdup(args[optind]); 771 | if (args[optind]) { 772 | if (hexadecimal(args[optind], 0)) { 773 | ret = parse_addr_spec(addr, args[optind]); 774 | if (ret) { 775 | fprintf(fp, "%s: cgroup controller " 776 | "and path parsing failed(%s)\n", 777 | args[0], args[optind]); 778 | free_cgroup_spec(addr, cgroup_list); 779 | return ; 780 | } 781 | } 782 | else { 783 | ret = parse_cgroup_spec(cgroup_list, 784 | args[optind]); 785 | if (ret) { 786 | fprintf(fp, "%s: cgroup controller " 787 | "and path parsing failed(%s)\n", 788 | args[0], args[optind]); 789 | free_cgroup_spec(addr, cgroup_list); 790 | return ; 791 | } 792 | 793 | } 794 | } 795 | optind++; 796 | } 797 | 798 | if (addr[0] != NULL) 799 | flags |= LIST_ADD; 800 | 801 | if (cgroup_list[0] != NULL) 802 | flags |= LIST_CER; 803 | 804 | cgroup_list_cgroups(addr, cgroup_list, flags); 805 | free_cgroup_spec(addr, cgroup_list); 806 | } 807 | 808 | -------------------------------------------------------------------------------- /memutils.c: -------------------------------------------------------------------------------- 1 | /* memutils.c - Provides memory related information 2 | * other than those provided by kmem command. 3 | * Currently the following are supported. 4 | * 5 | * 1) The page count per migrate type for all orders, 6 | * for all nodes. 7 | * 8 | * Copyright (C) 2013, Vinayak Menon 9 | * Author: Vinayak Menon 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | */ 21 | 22 | #include "defs.h" 23 | 24 | void memutils_init(void); 25 | void memutils_fini(void); 26 | 27 | void cmd_memutils(void); 28 | char *help_memutils[]; 29 | 30 | static struct command_table_entry command_table[] = { 31 | { "memutils", cmd_memutils, help_memutils, 0}, 32 | { NULL }, 33 | }; 34 | 35 | static void dump_pgtype_info(void); 36 | 37 | void __attribute__((constructor)) 38 | memutils_init(void) 39 | { 40 | register_extension(command_table); 41 | } 42 | 43 | void __attribute__((destructor)) 44 | memutils_fini(void) { } 45 | 46 | 47 | void 48 | cmd_memutils(void) 49 | { 50 | int c; 51 | int pflag = 0; 52 | 53 | while ((c = getopt(argcnt, args, "p")) != EOF) { 54 | switch (c) { 55 | 56 | case 'p': 57 | pflag = 1; 58 | break; 59 | 60 | default: 61 | argerrs++; 62 | break; 63 | } 64 | } 65 | 66 | if (argerrs) 67 | cmd_usage(pc->curcmd, SYNOPSIS); 68 | 69 | if (pflag == 1) 70 | dump_pgtype_info(); 71 | } 72 | 73 | static void dump_pgtype_info(void) 74 | { 75 | int n, m, z, o; 76 | int list_count = 0; 77 | ulong free_cnt = 0; 78 | int mtype_sym = 0; 79 | int mtype_len = 0; 80 | ulong *mtypes; 81 | ulong node_zones; 82 | ulong temp; 83 | ulong freelist; 84 | ulong *free_ptr; 85 | char *free_list_buf; 86 | char name_buf[BUFSIZE]; 87 | char buf[BUFSIZE]; 88 | struct node_table *nt; 89 | struct list_data list_data; 90 | 91 | if (!(vt->flags & (NODES|ZONES))) 92 | error(FATAL, 93 | "dump_pgtype_info called without (NODES|ZONES)\n"); 94 | 95 | if (!VALID_STRUCT(zone)) 96 | error(FATAL, 97 | "zone struct not available in this kernel\n"); 98 | 99 | if (VALID_STRUCT(free_area)) { 100 | if (SIZE(free_area) == (3 * sizeof(ulong))) 101 | error(FATAL, 102 | "free_area type not supported by command\n"); 103 | else 104 | list_count = MEMBER_SIZE("free_area", 105 | "free_list")/SIZE(list_head); 106 | } else 107 | error(FATAL, 108 | "free_area structure not found in this kernel\n"); 109 | 110 | free_list_buf = GETBUF(SIZE(list_head)); 111 | 112 | do { 113 | if (symbol_exists("migratetype_names") && 114 | (get_symbol_type("migratetype_names", 115 | NULL, NULL) == TYPE_CODE_ARRAY)) { 116 | 117 | open_tmpfile(); 118 | sprintf(buf, "whatis migratetype_names"); 119 | if (!gdb_pass_through(buf, fp, GNU_RETURN_ON_ERROR)) { 120 | close_tmpfile(); 121 | break; 122 | } 123 | 124 | rewind(pc->tmpfile); 125 | while (fgets(buf, BUFSIZE, pc->tmpfile)) { 126 | if (STRNEQ(buf, "type = ")) 127 | break; 128 | } 129 | close_tmpfile(); 130 | 131 | if (!strstr(buf, "char *") || 132 | (count_chars(buf, '[') != 1) || 133 | (count_chars(buf, ']') != 1)) 134 | break; 135 | 136 | mtype_len = get_array_length("migratetype_names", 137 | NULL, 0); 138 | 139 | mtypes = (ulong *)GETBUF(mtype_len * sizeof(ulong)); 140 | 141 | readmem(symbol_value("migratetype_names"), 142 | KVADDR, mtypes, 143 | (mtype_len * sizeof(ulong)), 144 | NULL, FAULT_ON_ERROR); 145 | 146 | mtype_sym = 1; 147 | } 148 | } while (0); 149 | 150 | fprintf(fp, "%-43s [%d-%d]:", 151 | "Free pages count per migrate type at order", 152 | 0, vt->nr_free_areas - 1); 153 | 154 | fprintf(fp, "\n"); 155 | 156 | for (n = 0; n < vt->numnodes; n++) { 157 | nt = &vt->node_table[n]; 158 | node_zones = nt->pgdat + OFFSET(pglist_data_node_zones); 159 | 160 | for (m = 0; m < list_count; m++) { 161 | 162 | for (z = 0; z < vt->nr_zones; z++) { 163 | readmem((node_zones + (z * SIZE(zone))) 164 | + OFFSET(zone_name), KVADDR, &temp, 165 | sizeof(void *), "node_zones name", 166 | FAULT_ON_ERROR); 167 | read_string(temp, name_buf, BUFSIZE-1); 168 | 169 | fprintf(fp, "Node %4d, ", nt->node_id); 170 | fprintf(fp, "zone %8s, ", name_buf); 171 | 172 | if (mtype_sym) { 173 | read_string(mtypes[m], 174 | name_buf, BUFSIZE-1); 175 | fprintf(fp, "type %12s ", name_buf); 176 | } else 177 | fprintf(fp, "type %12d ", m); 178 | 179 | for (o = 0; o < vt->nr_free_areas; o++) { 180 | freelist = 181 | (node_zones + (z * SIZE(zone))) 182 | + (OFFSET(zone_free_area) + 183 | (o * SIZE(free_area))) + 184 | (m * SIZE(list_head)); 185 | 186 | readmem(freelist, KVADDR, free_list_buf, 187 | SIZE(list_head), 188 | "free_area free_list", 189 | FAULT_ON_ERROR); 190 | 191 | free_ptr = (ulong *)free_list_buf; 192 | 193 | if (!(*free_ptr) || 194 | (*free_ptr == freelist)) { 195 | fprintf(fp, "%6lu ", (ulong)0); 196 | continue; 197 | } 198 | 199 | BZERO(&list_data, 200 | sizeof(struct list_data)); 201 | list_data.flags = RETURN_ON_DUPLICATE; 202 | list_data.start = *free_ptr; 203 | list_data.end = freelist; 204 | list_data.list_head_offset = 205 | OFFSET(page_lru) + 206 | OFFSET(list_head_next); 207 | 208 | free_cnt = do_list(&list_data); 209 | if (free_cnt < 0) { 210 | error(pc->curcmd_flags & 211 | IGNORE_ERRORS ? INFO : FATAL, 212 | "corrupted free list\n"); 213 | free_cnt = 0; 214 | } 215 | 216 | fprintf(fp, "%6lu ", free_cnt); 217 | } 218 | fprintf(fp, "\n"); 219 | } 220 | } 221 | } 222 | 223 | FREEBUF(free_list_buf); 224 | 225 | if (mtype_sym) 226 | FREEBUF(mtypes); 227 | } 228 | 229 | char *help_memutils[] = { 230 | "memutils", 231 | "memory information", 232 | "[-p]", 233 | 234 | "The command displays memory information", 235 | " -p displays the number of pages per migrate type for all orders, for all", 236 | " nodes.", 237 | "\nEXAMPLES", 238 | "\n Display pages per migrate type for all orders, for all nodes:\n", 239 | " %s> memutils -p", 240 | " Free pages count per migrate type at order [0-10]:", 241 | " Node 0, zone Normal, type Unmovable 155 172 92 39 20 8 10 15 7 3 1", 242 | " Node 0, zone HighMem, type Unmovable 1 2 0 0 0 0 0 0 0 0 0", 243 | " Node 0, zone Movable, type Unmovable 0 0 0 0 0 0 0 0 0 0 0", 244 | " Node 0, zone Normal, type Reclaimable 9 3 0 0 1 1 0 0 0 0 0", 245 | " Node 0, zone HighMem, type Reclaimable 0 0 0 0 0 0 0 0 0 0 0", 246 | " Node 0, zone Movable, type Reclaimable 0 0 0 0 0 0 0 0 0 0 0", 247 | " Node 0, zone Normal, type Movable 7 68 35 253 137 38 16 4 0 0 66", 248 | " Node 0, zone HighMem, type Movable 0 1 0 0 0 0 0 0 0 0 0", 249 | " Node 0, zone Movable, type Movable 0 0 0 0 0 0 0 0 0 0 0", 250 | " Node 0, zone Normal, type Reserve 0 0 0 0 0 0 0 0 0 0 1", 251 | " Node 0, zone HighMem, type Reserve 11 7 5 1 0 0 0 0 0 0 0", 252 | " Node 0, zone Movable, type Reserve 0 0 0 0 0 0 0 0 0 0 0", 253 | " Node 0, zone Normal, type Isolate 0 0 0 0 0 0 0 0 0 0 0", 254 | " Node 0, zone HighMem, type Isolate 0 0 0 0 0 0 0 0 0 0 0", 255 | " Node 0, zone Movable, type Isolate 0 0 0 0 0 0 0 0 0 0 0", 256 | NULL 257 | }; 258 | 259 | -------------------------------------------------------------------------------- /proccgroup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This program is distributed in the hope that it will be useful, 3 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 4 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 5 | * GNU General Public License for more details. 6 | * 7 | * Nikolay Borisov 8 | */ 9 | 10 | #include 11 | #include "defs.h" 12 | 13 | #define MAX_CGROUP_PATH 4096 14 | 15 | static void showcgrp(void); 16 | char *help_proc_cgroups[]; 17 | 18 | static bool have_ss_member; 19 | 20 | static struct command_table_entry command_table[] = { 21 | { "showcg", showcgrp, help_proc_cgroups, 0}, 22 | { NULL }, 23 | }; 24 | 25 | 26 | void __attribute__((constructor)) 27 | proccgroup_init(void) 28 | { 29 | 30 | if (!MEMBER_EXISTS("task_struct", "cgroups") || 31 | (!MEMBER_EXISTS("cgroup", "kn") && !MEMBER_EXISTS("cgroup", "name"))) 32 | { 33 | error(FATAL, "Unrecognised or disabled cgroup support\n"); 34 | } 35 | 36 | if (!MEMBER_EXISTS("cgroup_subsys_state", "ss")) { 37 | have_ss_member = false; 38 | error(WARNING, "pre-3.12 kernel detected, no support for getting subsys name\n"); 39 | } else 40 | have_ss_member = true; 41 | 42 | register_extension(command_table); 43 | } 44 | 45 | void __attribute__((destructor)) 46 | proccgroup_finish(void) { } 47 | 48 | /* Prepends contents of cgroup_name to buf, using start as a pointer 49 | * index into buf 50 | */ 51 | static void prepend_string(char *buf, char **start, char *cgroup_name) { 52 | 53 | int len = strlen(cgroup_name); 54 | *start -= len; 55 | 56 | if (*start < buf) { 57 | error(FATAL, "Cgroup too long to parse\n"); 58 | } 59 | 60 | memcpy(*start, cgroup_name, len); 61 | 62 | if (--*start < buf) { 63 | error(FATAL, "Cgroup too long to parse\n"); 64 | } 65 | 66 | **start = '/'; 67 | } 68 | 69 | /* For post-3.15 kernels */ 70 | static void get_cgroup_name_kn(ulong cgroup, char *buf, int buflen) 71 | { 72 | ulong kernfs_node; 73 | ulong cgroup_name_ptr; 74 | ulong kernfs_parent; 75 | bool slash_prepended = false; 76 | char cgroup_name[BUFSIZE]; 77 | char *start = buf + buflen - 1; 78 | *start = '\0'; //null terminate the end 79 | 80 | /* Get cgroup->kn */ 81 | readmem(cgroup + MEMBER_OFFSET("cgroup", "kn"), KVADDR, &kernfs_node, sizeof(void *), 82 | "cgroup->kn", FAULT_ON_ERROR); 83 | 84 | do { 85 | /* Get kn->name */ 86 | readmem(kernfs_node + MEMBER_OFFSET("kernfs_node", "name"), KVADDR, &cgroup_name_ptr, sizeof(void *), 87 | "kernfs_node->name", FAULT_ON_ERROR); 88 | /* Get kn->parent */ 89 | readmem(kernfs_node + MEMBER_OFFSET("kernfs_node", "parent"), KVADDR, &kernfs_parent, sizeof(void *), 90 | "kernfs_node->parent", FAULT_ON_ERROR); 91 | 92 | if (kernfs_parent != 0) { 93 | read_string(cgroup_name_ptr, cgroup_name, BUFSIZE-1); 94 | prepend_string(buf, &start, cgroup_name); 95 | slash_prepended = true; 96 | } else if (!slash_prepended) { 97 | if (--start < buf) { 98 | error(FATAL, "Cgroup too long to parse\n"); 99 | } 100 | *start = '/'; 101 | } 102 | 103 | kernfs_node = kernfs_parent; 104 | 105 | } while(kernfs_parent); 106 | 107 | memmove(buf, start, buf + buflen - start); 108 | } 109 | 110 | /* For pre-3.15 kernels */ 111 | static void get_cgroup_name_old(ulong cgroup, char *buf, size_t buflen) 112 | { 113 | ulong cgroup_name_ptr; 114 | ulong cgroup_parent_ptr; 115 | char cgroup_name[BUFSIZE]; 116 | char *start = buf + buflen - 1; 117 | *start = '\0'; //null terminate the end 118 | bool slash_prepended = false; 119 | 120 | do { 121 | /* Get cgroup->name */ 122 | readmem(cgroup + MEMBER_OFFSET("cgroup", "name"), KVADDR, &cgroup_name_ptr, sizeof(void *), 123 | "cgroup->name", FAULT_ON_ERROR); 124 | /* Get cgroup->parent */ 125 | readmem(cgroup + MEMBER_OFFSET("cgroup", "parent"), KVADDR, &cgroup_parent_ptr, sizeof(void *), 126 | "cgroup->parent", FAULT_ON_ERROR); 127 | 128 | read_string(cgroup_name_ptr + MEMBER_OFFSET("cgroup_name", "name"), cgroup_name, BUFSIZE-1); 129 | 130 | if (cgroup_parent_ptr) { 131 | prepend_string(buf, &start, cgroup_name); 132 | slash_prepended = true; 133 | } else if (!slash_prepended) { 134 | if (--start < buf) 135 | break; 136 | *start = '/'; 137 | } 138 | 139 | cgroup = cgroup_parent_ptr; 140 | 141 | } while(cgroup_parent_ptr); 142 | 143 | memmove(buf, start, buf + buflen - start); 144 | } 145 | 146 | static void get_subsys_name(ulong subsys, char *buf, size_t buflen) 147 | { 148 | ulong subsys_name_ptr; 149 | ulong cgroup_subsys_ptr; 150 | 151 | /* Get cgroup->kn */ 152 | readmem(subsys + MEMBER_OFFSET("cgroup_subsys_state", "ss"), KVADDR, &cgroup_subsys_ptr, sizeof(void *), 153 | "cgroup_subsys_state->ss", FAULT_ON_ERROR); 154 | 155 | readmem(cgroup_subsys_ptr + MEMBER_OFFSET("cgroup_subsys", "name"), KVADDR, &subsys_name_ptr, sizeof(void *), 156 | "cgroup_subsys->name", FAULT_ON_ERROR); 157 | read_string(subsys_name_ptr, buf, buflen-1); 158 | } 159 | 160 | static void get_cgroup_name(ulong cgroup, ulong subsys) 161 | { 162 | char *cgroup_path = GETBUF(MAX_CGROUP_PATH); 163 | char subsys_name[BUFSIZE]; 164 | 165 | /* Handle the 2 cases of cgroup_name and the kernfs one */ 166 | if (MEMBER_EXISTS("cgroup", "kn")) { 167 | get_cgroup_name_kn(cgroup, cgroup_path, MAX_CGROUP_PATH); 168 | } else if (MEMBER_EXISTS("cgroup", "name")) { 169 | get_cgroup_name_old(cgroup, cgroup_path, MAX_CGROUP_PATH); 170 | } 171 | 172 | /* pre-3.12 cgroup_subsys_state doesn't contain 'ss' member */ 173 | if (have_ss_member) { 174 | get_subsys_name(subsys, subsys_name, BUFSIZE); 175 | fprintf(fp, "subsys: %-20s cgroup: %s\n", subsys_name, cgroup_path); 176 | } else { 177 | fprintf(fp, "cgroup: %s\n", cgroup_path); 178 | } 179 | 180 | 181 | FREEBUF(cgroup_path); 182 | } 183 | 184 | 185 | void show_proc_cgroups(ulong task_ctx) { 186 | int en_subsys_cnt; 187 | int i; 188 | ulong *cgroup_subsys_arr; 189 | ulong subsys_base_ptr; 190 | ulong cgroups_subsys_ptr = 0; 191 | 192 | 193 | /* Get address of task_struct->cgroups */ 194 | readmem(task_ctx + MEMBER_OFFSET("task_struct", "cgroups"), 195 | KVADDR, &cgroups_subsys_ptr, sizeof(void *), 196 | "task_struct->cgroups", FAULT_ON_ERROR); 197 | 198 | subsys_base_ptr = cgroups_subsys_ptr + MEMBER_OFFSET("css_set", "subsys"); 199 | en_subsys_cnt = MEMBER_SIZE("css_set", "subsys") / sizeof(void *); 200 | cgroup_subsys_arr = (ulong *)GETBUF(en_subsys_cnt * sizeof(ulong)); 201 | 202 | /* Get the contents of the css_set->subsys array */ 203 | readmem(subsys_base_ptr, KVADDR, cgroup_subsys_arr, sizeof(ulong) * en_subsys_cnt, 204 | "css_set->subsys", FAULT_ON_ERROR); 205 | 206 | for (i = 0; i < en_subsys_cnt; i++) { 207 | ulong cgroup; 208 | 209 | /* Generally the subsys_array is not NULL-terminated, however 210 | * a particular fedora kernel was NULL-terminated 211 | */ 212 | if (!cgroup_subsys_arr[i]) 213 | continue; 214 | 215 | /* Get cgroup_subsys_state -> cgroup */ 216 | readmem(cgroup_subsys_arr[i] + MEMBER_OFFSET("cgroup_subsys_state", "cgroup"), 217 | KVADDR, &cgroup, sizeof(void *), "cgroup_subsys_state->cgroup", FAULT_ON_ERROR); 218 | 219 | get_cgroup_name(cgroup, cgroup_subsys_arr[i]); 220 | } 221 | 222 | FREEBUF(cgroup_subsys_arr); 223 | } 224 | 225 | 226 | static void showcgrp(void) { 227 | 228 | ulong value; 229 | struct task_context *tc; 230 | ulong task_struct_ptr = 0; 231 | 232 | while (args[++optind]) { 233 | if (IS_A_NUMBER(args[optind])) { 234 | switch (str_to_context(args[optind], &value, &tc)) 235 | { 236 | case STR_PID: 237 | task_struct_ptr = tc->task; 238 | ++optind; 239 | break; 240 | 241 | case STR_TASK: 242 | task_struct_ptr = value; 243 | ++optind; 244 | break; 245 | 246 | case STR_INVALID: 247 | error(FATAL, "invalid task or pid value: %s\n\n", 248 | args[optind]); 249 | break; 250 | } 251 | } else { 252 | if (argcnt > 1) 253 | error(FATAL, "invalid task or pid value: %s\n",args[optind]); 254 | else 255 | break; 256 | } 257 | } 258 | 259 | if (!task_struct_ptr) { 260 | task_struct_ptr = CURRENT_TASK(); 261 | } 262 | 263 | show_proc_cgroups(task_struct_ptr); 264 | } 265 | 266 | char *help_proc_cgroups[] = { 267 | "showcg", 268 | "Show which cgroups is a process member of", 269 | " [task | pid]", 270 | 271 | " This command prints the cgroup for each subsys that a process is a member of", 272 | "\nExample", 273 | " Show the cgroup for the currently active process:\n", 274 | " crash> showcg", 275 | " subsys: cpuset cgroup: /user.slice/user-1000.slice/session-c1.scope", 276 | " subsys: cpu cgroup: /user.slice/user-1000.slice/session-c1.scope", 277 | " subsys: cpuacct cgroup: /user.slice/user-1000.slice/session-c1.scope", 278 | " subsys: blkio cgroup: /user.slice/user-1000.slice/session-c1.scope", 279 | " subsys: memory cgroup: /user.slice/user-1000.slice/session-c1.scope", 280 | " subsys: devices cgroup: /user.slice/user-1000.slice/session-c1.scope", 281 | " subsys: freezer cgroup: /user.slice/user-1000.slice/session-c1.scope", 282 | " subsys: net_cls cgroup: /user.slice/user-1000.slice/session-c1.scope", 283 | " subsys: perf_event cgroup: /user.slice/user-1000.slice/session-c1.scope", 284 | " subsys: net_prio cgroup: /user.slice/user-1000.slice/session-c1.scope", 285 | " subsys: hugetlb cgroup: /user.slice/user-1000.slice/session-c1.scope", 286 | "\n Alternatively you can pass either a pid or a task pointer to show the cgroup the", 287 | " respective process is a member of e.g:\n", 288 | " crash> showcg 1064\n OR", 289 | " crash> showcg ffff880405711b80", 290 | NULL 291 | }; 292 | 293 | 294 | -------------------------------------------------------------------------------- /pstruct.c: -------------------------------------------------------------------------------- 1 | /* pstruct.c - print structure's member 2 | * 3 | * Copyright (C) 2012 FUJITSU LIMITED 4 | * Author: Qiao Nuohan 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #include "defs.h" /* From the crash source top-level directory */ 18 | 19 | #define STRUCTURE_CACHE_MAX_SIZE 10 20 | 21 | struct struct_cache { 22 | char name[100]; 23 | char member[100]; 24 | long type; 25 | long unsigned_type; 26 | long length; 27 | long offset; 28 | long bitpos; 29 | long bitsize; 30 | }; 31 | 32 | int struct_cache_size = -1; 33 | struct struct_cache struct_cache[STRUCTURE_CACHE_MAX_SIZE]; 34 | 35 | void pstruct_init(void); 36 | void pstruct_fini(void); 37 | 38 | void cmd_pstruct(void); /* Declare the commands and their help data. */ 39 | char *help_pstruct[]; 40 | 41 | static struct struct_cache *get_struct_cache(char *, char *); 42 | static void get_bitfield_data(ulong *, int, int); 43 | 44 | static struct command_table_entry command_table[] = { 45 | { "pstruct", cmd_pstruct, help_pstruct, 0 }, /* One or more commands, */ 46 | { NULL } /* terminated by NULL, */ 47 | }; 48 | 49 | void __attribute__((constructor)) 50 | pstruct_init(void) /* Register the command set. */ 51 | { 52 | register_extension(command_table); 53 | } 54 | 55 | /* 56 | * The pstruct_fini() function is called if the shared object is unloaded. 57 | * If desired, perform any cleanups here. 58 | */ 59 | void __attribute__((destructor)) 60 | pstruct_fini(void) { } 61 | 62 | 63 | char *help_pstruct[] = { 64 | "pstruct", /* command name */ 65 | "print structure member's data in one line", /* short description */ 66 | "struct_name.member[.member...,member...] [-d|-x] [-l offset]\n" 67 | " [address|symbol]", /* argument synopsis, or " " if none */ 68 | 69 | " This command displays the contents of a structure's members in one", 70 | " line.\n", 71 | " The arguments are as follows:\n", 72 | " struct_name name of a C-code structure used by the kernel.", 73 | " .member... name of a structure member; to display multiple members", 74 | " of a structure, use a comma-separated list of members.", 75 | " -l offset if the address argument is a pointer to a structure", 76 | " member that is contained by the target data structure,", 77 | " typically a pointer to an embedded list_head, the offset", 78 | " to the embedded member may be entered in either of the", 79 | " following manners:", 80 | " 1. in \"structure.member\" format.", 81 | " 2. a number of bytes. ", 82 | " -x override default output format with hexadecimal format.", 83 | " -d override default output format with decimal format.", 84 | " ", 85 | "\nEXAMPLE", 86 | " Display the page's member private, _count.counter, inuse at address ", 87 | " 0xffffea00000308f0:\n", 88 | " %s> pstruct page.private,_count.counter,inuse 0xffffea00000308f0", 89 | " 0 198896 59904", 90 | " ", 91 | " Display the page's member mapping, index at address 0xffffea00000308f0", 92 | " in hexadecimal format:\n", 93 | " %s> pstruct page.mapping,index ffffea000004c778 -x", 94 | " 0xffff88004b6412b8 0x100167", 95 | NULL 96 | }; 97 | 98 | /* 99 | * Arguments are passed to the command functions in the global args[argcnt] 100 | * array. See getopt(3) for info on dash arguments. Check out defs.h and 101 | * other crash commands for usage of the myriad of utility routines available 102 | * to accomplish what your task. 103 | */ 104 | void 105 | cmd_pstruct(void) 106 | { 107 | int c, i; 108 | ulong addr; 109 | struct syment *sp; 110 | ulong list_head_offset; 111 | int argc_members; 112 | unsigned int radix; 113 | struct datatype_member datatype_member, *dm; 114 | char *structname, *members; 115 | char *separator; 116 | char *memberlist[MAXARGS]; 117 | char outputbuf[BUFSIZE]; 118 | long outputindex; 119 | ulong tmpvalue; 120 | struct struct_cache *struct_cache; 121 | 122 | argc_members = 0; 123 | dm = &datatype_member; 124 | list_head_offset = 0; 125 | structname = separator = members = NULL; 126 | outputindex = 0; 127 | 128 | while ((c = getopt(argcnt, args, "dxl:")) != EOF) { 129 | switch(c) 130 | { 131 | case 'd': 132 | if (radix == 16) 133 | error(FATAL, 134 | "-d and -x are mutually exclusive\n"); 135 | radix = 10; 136 | break; 137 | 138 | case 'x': 139 | if (radix == 10) 140 | error(FATAL, 141 | "-d and -x are mutually exclusive\n"); 142 | radix = 16; 143 | break; 144 | 145 | case 'l': 146 | if (IS_A_NUMBER(optarg)) 147 | list_head_offset = stol(optarg, 148 | FAULT_ON_ERROR, NULL); 149 | else if (arg_to_datatype(optarg, 150 | dm, RETURN_ON_ERROR) > 1) 151 | list_head_offset = dm->member_offset; 152 | else 153 | error(FATAL, "invalid -l option: %s\n", 154 | optarg); 155 | break; 156 | 157 | default: 158 | argerrs++; 159 | break; 160 | } 161 | } 162 | 163 | if (argerrs || !args[optind] || !args[optind+1] || args[optind+2]) 164 | cmd_usage(pc->curcmd, SYNOPSIS); 165 | 166 | if ((count_chars(args[optind], ',')+1) > MAXARGS) 167 | error(FATAL, "too many members in comma-separated list!\n"); 168 | 169 | if ((LASTCHAR(args[optind]) == ',') || (LASTCHAR(args[optind]) == '.')) 170 | error(FATAL, "invalid format: %s\n", args[optind]); 171 | 172 | if (count_chars(args[optind], '.') < 1) 173 | error(FATAL, "no member format is invalid: %s\n", args[optind]); 174 | 175 | /* 176 | * Handle struct.member[,member] argument format. 177 | */ 178 | structname = GETBUF(strlen(args[optind])+1); 179 | strcpy(structname, args[optind]); 180 | separator = strstr(structname, "."); 181 | 182 | members = GETBUF(strlen(args[optind])+1); 183 | strcpy(members, separator+1); 184 | replace_string(members, ",", ' '); 185 | argc_members = parse_line(members, memberlist); 186 | 187 | *separator = NULLCHAR; 188 | 189 | /* 190 | * Handle address 191 | */ 192 | if (clean_arg() && IS_A_NUMBER(args[optind+1])) 193 | addr = htol(args[optind+1], FAULT_ON_ERROR, NULL); 194 | else if ((sp = symbol_search(args[optind+1]))) 195 | addr = sp->value; 196 | else { 197 | fprintf(fp, "symbol not found: %s\n", args[optind+1]); 198 | fprintf(fp, "possible alternatives:\n"); 199 | if (!symbol_query(args[optind], " ", NULL)) 200 | fprintf(fp, " (none found)\n"); 201 | goto freebuf; 202 | } 203 | 204 | if (list_head_offset) 205 | addr -= list_head_offset; 206 | 207 | i = 0; 208 | outputindex = 0; 209 | 210 | do { 211 | tmpvalue = 0; 212 | struct_cache = get_struct_cache(structname, memberlist[i]); 213 | 214 | switch (struct_cache->type) 215 | { 216 | case TYPE_CODE_PTR: 217 | readmem(addr+struct_cache->offset, KVADDR, &tmpvalue, 218 | struct_cache->length, "tmpvalue", FAULT_ON_ERROR); 219 | outputindex += sprintf(outputbuf + outputindex, "0x%lx\t", 220 | tmpvalue); 221 | break; 222 | 223 | case TYPE_CODE_INT: 224 | readmem(addr+struct_cache->offset, KVADDR, &tmpvalue, 225 | struct_cache->length, "tmpvalue", FAULT_ON_ERROR); 226 | get_bitfield_data(&tmpvalue, struct_cache->bitpos, 227 | struct_cache->bitsize); 228 | 229 | if (radix == 16 || (radix == 0 && *gdb_output_radix == 16)) 230 | outputindex += sprintf(outputbuf + outputindex, 231 | "0x%lx\t", tmpvalue); 232 | else if (struct_cache->unsigned_type || 233 | struct_cache->length == sizeof(ulonglong)) 234 | outputindex += sprintf(outputbuf + outputindex, 235 | "%lu\t", tmpvalue); 236 | else 237 | outputindex += sprintf(outputbuf + outputindex, 238 | "%d\t", (int)tmpvalue); 239 | break; 240 | 241 | default: 242 | error(FATAL, "invalid data structure reference %s.%s\n", 243 | struct_cache->name, struct_cache->member); 244 | break; 245 | } 246 | 247 | } while (++i < argc_members); 248 | 249 | fprintf(fp, "%s\n", outputbuf); 250 | 251 | freebuf: 252 | if (structname) 253 | FREEBUF(structname); 254 | if (members) 255 | FREEBUF(members); 256 | } 257 | 258 | static struct struct_cache * 259 | get_struct_cache(char *structname, char *member) 260 | { 261 | int index = 0; 262 | char buf[BUFSIZE]; 263 | char *printmlist[MAXARGS]; 264 | 265 | while (index <= struct_cache_size && index <= STRUCTURE_CACHE_MAX_SIZE) { 266 | if (!strcmp(struct_cache[index].name, structname) && 267 | !strcmp(struct_cache[index].member, member)) 268 | return &struct_cache[index]; 269 | 270 | index++; 271 | } 272 | 273 | struct_cache_size++; 274 | index = struct_cache_size % STRUCTURE_CACHE_MAX_SIZE; 275 | 276 | open_tmpfile(); 277 | 278 | sprintf(buf, "printm ((struct %s *)0x0).%s", structname, member); 279 | 280 | if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) { 281 | rewind(fp); 282 | sprintf(buf, "printm ((union %s *)0x0).%s", structname, member); 283 | if (!gdb_pass_through(buf, pc->tmpfile2, GNU_RETURN_ON_ERROR)) 284 | error(FATAL, "invalid data structure reference %s.%s\n", 285 | structname, member); 286 | } 287 | 288 | rewind(fp); 289 | if (fgets(buf, BUFSIZE, fp)) 290 | { 291 | parse_line(buf, printmlist); 292 | } 293 | 294 | sprintf(struct_cache[index].name, "%s", structname); 295 | sprintf(struct_cache[index].member, "%s", member); 296 | struct_cache[index].type = dtol(printmlist[0], RETURN_ON_ERROR, NULL); 297 | struct_cache[index].unsigned_type = dtol(printmlist[1], 298 | RETURN_ON_ERROR, NULL); 299 | struct_cache[index].length = dtol(printmlist[2], RETURN_ON_ERROR, NULL); 300 | struct_cache[index].offset = dtol(printmlist[3], RETURN_ON_ERROR, NULL); 301 | struct_cache[index].bitpos = dtol(printmlist[4], RETURN_ON_ERROR, NULL); 302 | struct_cache[index].bitsize = dtol(printmlist[5], RETURN_ON_ERROR, NULL); 303 | 304 | close_tmpfile(); 305 | 306 | return &struct_cache[index]; 307 | } 308 | 309 | static void 310 | get_bitfield_data(ulong *value, int pos, int size) 311 | { 312 | if (pos == 0 && size == 0) 313 | return; 314 | 315 | ulong tmpvalue = *value; 316 | ulong mask; 317 | 318 | tmpvalue = tmpvalue >> pos; 319 | mask = (1UL << size) - 1; 320 | tmpvalue &= mask; 321 | 322 | *value = tmpvalue; 323 | } 324 | -------------------------------------------------------------------------------- /ptdump-1.0.7.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crash-utility/crash-extensions/88604fab8c91dc3072b71ae22e60015fc5ec26cf/ptdump-1.0.7.tar.gz -------------------------------------------------------------------------------- /qemu-vtop.c: -------------------------------------------------------------------------------- 1 | /* qemu-vtop.c - qemu-vtop extension module for crash 2 | * 3 | * Copyright (C) 2011, 2012 FUJITSU LIMITED 4 | * Auther: Qiao Nuohan 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | */ 11 | 12 | #include "defs.h" /* From the crash source top-level directory */ 13 | 14 | #define PHYSICAL_ADDR (0x1) 15 | #define USER_SPECIFY_TASK (0x4) 16 | 17 | #define MAXTOKENS (100) 18 | 19 | struct qemu_vtop_offset_table { 20 | long file_private_data; 21 | long kvm_memslots; 22 | long kvm_memslots_nmemslots; 23 | long kvm_memslots_memslots; 24 | long kvm_memory_slot_base_gfn; 25 | long kvm_memory_slot_npages; 26 | long kvm_memory_slot_userspace_addr; 27 | }; 28 | 29 | struct qemu_vtop_size_table { 30 | long kvm_memory_slot; 31 | }; 32 | 33 | int _init(void); 34 | int _fini(void); 35 | 36 | void cmd_qemu_vtop(void); 37 | char *help_qemu_vtop[]; 38 | static void qemu_vtop_init(); 39 | static void do_qemu_vtop_physical(ulong, struct task_context *); 40 | static ulong get_file_ref(ulong, struct reference *); 41 | static ulong parse_for_file(char *); 42 | static ulong gpa_to_hva(ulong , ulong ); 43 | static void print_vtop(ulong, ulong, struct task_context *); 44 | 45 | static struct command_table_entry command_table[] = { 46 | { "qemu-vtop", cmd_qemu_vtop, help_qemu_vtop, 0 }, /* One or more commands, */ 47 | { NULL } /* terminated by NULL, */ 48 | }; 49 | 50 | static struct qemu_vtop_offset_table qemu_vtop_offset_table = { 0 }; 51 | static struct qemu_vtop_size_table qemu_vtop_size_table = { 0 }; 52 | 53 | #define QEMU_VTOP_MEMBER_OFFSET_INIT(X, Y, Z) (qemu_vtop_offset_table.X = MEMBER_OFFSET(Y, Z)) 54 | #define QEMU_VTOP_ANON_MEMBER_OFFSET_INIT(X, Y, Z) (qemu_vtop_offset_table.X = ANON_MEMBER_OFFSET(Y, Z)) 55 | #define QEMU_VTOP_STRUCT_SIZE_INIT(X, Y) (qemu_vtop_size_table.X = STRUCT_SIZE(Y)) 56 | 57 | #define QEMU_VTOP_OFFSET(X) (OFFSET_verify(qemu_vtop_offset_table.X, (char *)__FUNCTION__, __FILE__, __LINE__, #X)) 58 | #define QEMU_VTOP_SIZE(X) (SIZE_verify(qemu_vtop_size_table.X, (char *)__FUNCTION__, __FILE__, __LINE__, #X)) 59 | 60 | int 61 | _init(void) /* Register the command set. */ 62 | { 63 | register_extension(command_table); 64 | return 1; 65 | } 66 | 67 | /* 68 | * The _fini() function is called if the shared object is unloaded. 69 | * If desired, perform any cleanups here. 70 | */ 71 | int 72 | _fini(void) 73 | { 74 | return 1; 75 | } 76 | 77 | void 78 | cmd_qemu_vtop(void) 79 | { 80 | int c; 81 | int other; 82 | ulong vaddr, context; 83 | struct task_context *tc; 84 | ulong qemu_vtop_flag; 85 | 86 | tc = NULL; 87 | 88 | while ((c = getopt(argcnt, args, "g:p")) != EOF) { 89 | switch(c) 90 | { 91 | case 'g': 92 | switch (str_to_context(optarg, &context, &tc)) { 93 | case STR_PID: 94 | case STR_TASK: 95 | qemu_vtop_flag |= USER_SPECIFY_TASK; 96 | break; 97 | case STR_INVALID: 98 | error(FATAL, "invalid task or pid value: %s\n", 99 | optarg); 100 | break; 101 | } 102 | break; 103 | case 'p': 104 | qemu_vtop_flag |= PHYSICAL_ADDR; 105 | break; 106 | default: 107 | argerrs++; 108 | break; 109 | } 110 | } 111 | 112 | if (argerrs || !args[optind]) 113 | cmd_usage(pc->curcmd, SYNOPSIS); 114 | 115 | if (!(qemu_vtop_flag & USER_SPECIFY_TASK)) 116 | error(FATAL, "-g [pid | taskp] must be specified\n"); 117 | 118 | if ((qemu_vtop_flag & PHYSICAL_ADDR) == 0) 119 | error(FATAL, "-p should be specified\n"); 120 | 121 | qemu_vtop_init(); 122 | 123 | other = 0; 124 | while (args[optind]) { 125 | vaddr = htol(args[optind], FAULT_ON_ERROR, NULL); 126 | 127 | if (other++) 128 | fprintf(fp, "\n"); 129 | 130 | if (qemu_vtop_flag & PHYSICAL_ADDR) 131 | do_qemu_vtop_physical(vaddr, tc); 132 | 133 | optind++; 134 | } 135 | 136 | fprintf(fp, "\n"); 137 | } 138 | 139 | /* 140 | * The optional help data is simply an array of strings in a defined format. 141 | * For example, the "help qemu-vtop" command will use the help_qemu_vtop[] string 142 | * array below to create a help page that looks like this: 143 | * 144 | * NAME 145 | * qemu-vtop - KVM guest's address to host's address 146 | * 147 | * SYNOPSIS 148 | * qemu-vtop -g [pid | taskp] -p address ... 149 | * 150 | * DESCRIPTION 151 | * This command translates a guest's address on a KVM to its host's physical 152 | * address. It first gets the host's virtual address related to the guest's 153 | * address, then display the information of "vtop ". 154 | * 155 | * -p address The address is a physical address of a guest 156 | * -g [pid | taskp] Translate the guest's address of the KVM specified by 157 | * PID(pid) or hexadecimal task_struct pointer(taskp). 158 | * 159 | * EXAMPLE 160 | * Translate guest(pid:191579)'s physical address 34de5840: 161 | * 162 | * crash> qemu-vtop -g 191579 -p 34de5840 163 | * GUEST PHYSICAL HOST VIRTUAL HOST PHYSICAL 164 | * 34de5840 7f060cbe5840 611de5840 165 | * 166 | * PML: 72dbb67f0 => 875afa067 167 | * PUD: 875afa0c0 => 736871067 168 | * PMD: 736871328 => 8000000611c000e7 169 | * PAGE: 611c00000 (2MB) 170 | * 171 | * PTE PHYSICAL FLAGS 172 | * 8000000611c000e7 611c00000 (PRESENT|RW|USER|ACCESSED|DIRTY|PSE|NX) 173 | * 174 | * VMA START END FLAGS FILE 175 | * ffff8806edca49e0 7f05d7e00000 7f0697e00000 80120073 176 | * 177 | * PAGE PHYSICAL MAPPING INDEX CNT FLAGS 178 | * ffffea00153e8a18 611de5000 0 7f9e743e5 0 c0000000008000 179 | * 180 | */ 181 | 182 | char *help_qemu_vtop[] = { 183 | "qemu-vtop", /* command name */ 184 | "KVM guest's address to host's address", /* short description */ 185 | "-g [pid | taskp] -p address ...", /* argument synopsis */ 186 | 187 | " This command translates a guest's address on a KVM to its host's physical", 188 | " address. It first gets the host's virtual address related to the guest's", 189 | " address, then display the information of \"vtop \".", 190 | " ", 191 | " -p address The address is a physical address of a guest", 192 | " -g [pid | taskp] Translate the guest's address of the KVM specified by", 193 | " PID(pid) or hexadecimal task_struct pointer(taskp).", 194 | "\nEXAMPLE", 195 | " Translate guest(pid:191579)'s physical address 34de5840:", 196 | " ", 197 | " crash> qemu-vtop -g 191579 -p 34de5840", 198 | " GUEST PHYSICAL HOST VIRTUAL HOST PHYSICAL", 199 | " 34de5840 7f060cbe5840 611de5840", 200 | " ", 201 | " PML: 72dbb67f0 => 875afa067", 202 | " PUD: 875afa0c0 => 736871067", 203 | " PMD: 736871328 => 8000000611c000e7", 204 | " PAGE: 611c00000 (2MB)", 205 | " ", 206 | " PTE PHYSICAL FLAGS", 207 | " 8000000611c000e7 611c00000 (PRESENT|RW|USER|ACCESSED|DIRTY|PSE|NX)", 208 | " ", 209 | " VMA START END FLAGS FILE", 210 | " ffff8806edca49e0 7f05d7e00000 7f0697e00000 80120073", 211 | " ", 212 | " PAGE PHYSICAL MAPPING INDEX CNT FLAGS", 213 | " ffffea00153e8a18 611de5000 0 7f9e743e5 0 c0000000008000", 214 | NULL 215 | }; 216 | 217 | /* 218 | * init some offsets and sizes 219 | */ 220 | static void qemu_vtop_init() 221 | { 222 | QEMU_VTOP_MEMBER_OFFSET_INIT(file_private_data, "file","private_data"); 223 | QEMU_VTOP_MEMBER_OFFSET_INIT(kvm_memslots, "kvm", "memslots"); 224 | QEMU_VTOP_MEMBER_OFFSET_INIT(kvm_memslots_nmemslots, "kvm_memslots", "nmemslots"); 225 | QEMU_VTOP_MEMBER_OFFSET_INIT(kvm_memslots_memslots, "kvm_memslots", "memslots"); 226 | QEMU_VTOP_MEMBER_OFFSET_INIT(kvm_memory_slot_base_gfn, "kvm_memory_slot", "base_gfn"); 227 | QEMU_VTOP_MEMBER_OFFSET_INIT(kvm_memory_slot_npages, "kvm_memory_slot", "npages"); 228 | QEMU_VTOP_MEMBER_OFFSET_INIT(kvm_memory_slot_userspace_addr, "kvm_memory_slot", "userspace_addr"); 229 | 230 | QEMU_VTOP_STRUCT_SIZE_INIT(kvm_memory_slot, "kvm_memory_slot"); 231 | } 232 | 233 | static void 234 | do_qemu_vtop_physical(ulong gpa, struct task_context *tc) 235 | { 236 | struct reference reference, *ref; 237 | ulong filep, private_data, memslots, nmemslots, hva; 238 | filep = 0; 239 | private_data = 0; 240 | memslots = 0; 241 | nmemslots = 0; 242 | 243 | ref = &reference; 244 | BZERO(ref, sizeof(struct reference)); 245 | ref->str = "anon_inode:/kvm-vm"; 246 | 247 | filep = get_file_ref(tc->task, ref); 248 | if (!filep) { 249 | error(INFO, "task(pid:%ld task:%lx) is not a qemu process\n", tc->pid, tc->task); 250 | return; 251 | } 252 | 253 | readmem(filep + QEMU_VTOP_OFFSET(file_private_data), 254 | KVADDR, &private_data, sizeof(ulong), 255 | "file private_data", FAULT_ON_ERROR); 256 | if (!private_data) { 257 | error(INFO, "failed to get information of qemu process(pid:%ld task:%lx)\n", 258 | tc->pid, tc->task); 259 | return; 260 | } 261 | 262 | readmem(private_data + QEMU_VTOP_OFFSET(kvm_memslots), 263 | KVADDR, &memslots, sizeof(ulong), 264 | "kvm memslots", FAULT_ON_ERROR); 265 | if (!memslots) { 266 | error(INFO, "failed to get information of KVM, please check " 267 | "whether KVM module is loaded\n"); 268 | return; 269 | } 270 | 271 | hva = gpa_to_hva(gpa, memslots); 272 | if (!hva) { 273 | error(INFO, "%lx has not been allocated\n", gpa); 274 | return; 275 | } 276 | 277 | print_vtop(gpa, hva, tc); 278 | } 279 | 280 | /* 281 | * get the pointer to the file struct of a file descriptor, ref->str 282 | * is used to search for the specified file descriptor. 283 | */ 284 | static ulong 285 | get_file_ref(ulong task, struct reference *ref) 286 | { 287 | ulong file = 0; 288 | open_tmpfile(); 289 | open_files_dump(task, 0, NULL); 290 | file = parse_for_file(ref->str); 291 | close_tmpfile(); 292 | return file; 293 | } 294 | 295 | static ulong 296 | parse_for_file(char *str) 297 | { 298 | char buf[BUFSIZE]; 299 | char *tokens[MAXTOKENS]; 300 | ulong file = 0; 301 | 302 | rewind(pc->tmpfile); 303 | 304 | while (fgets(buf, BUFSIZE, pc->tmpfile)) { 305 | if (strstr(buf, str)) { 306 | parse_line(buf, tokens); 307 | file = htol(tokens[1], FAULT_ON_ERROR, NULL); 308 | break; 309 | } 310 | } 311 | 312 | return file; 313 | } 314 | 315 | static ulong 316 | gpa_to_hva(ulong gpa, ulong memslots) 317 | { 318 | ulong nmemslots, kvm_memory_slot_size, memslots_offset; 319 | ulong base_gfn_offset, npages_offset, userspace_addr_offset; 320 | ulong hva = 0; 321 | ulong gfn = gpa >> PAGE_SHIFT; 322 | 323 | readmem(memslots + QEMU_VTOP_OFFSET(kvm_memslots_nmemslots), 324 | KVADDR, &nmemslots, sizeof(ulong), 325 | "kvm_memslots nmemslots", FAULT_ON_ERROR); 326 | kvm_memory_slot_size = QEMU_VTOP_SIZE(kvm_memory_slot); 327 | memslots_offset = QEMU_VTOP_OFFSET(kvm_memslots_memslots); 328 | 329 | base_gfn_offset = QEMU_VTOP_OFFSET(kvm_memory_slot_base_gfn); 330 | npages_offset = QEMU_VTOP_OFFSET(kvm_memory_slot_npages); 331 | userspace_addr_offset = QEMU_VTOP_OFFSET(kvm_memory_slot_userspace_addr); 332 | 333 | int i = 0; 334 | int found = 0; 335 | ulong hfn; 336 | ulong base_gfn, npages, userspace_addr; 337 | 338 | /* 339 | * search every kvm_memory_slot to find which one the gfn is located in. 340 | */ 341 | while (i<=nmemslots) { 342 | readmem(memslots + memslots_offset + i*kvm_memory_slot_size + 343 | QEMU_VTOP_OFFSET(kvm_memory_slot_base_gfn), 344 | KVADDR, &base_gfn, sizeof(ulong), 345 | "kvm_memory_slot base_gfn", FAULT_ON_ERROR); 346 | readmem(memslots + memslots_offset + i*kvm_memory_slot_size + 347 | QEMU_VTOP_OFFSET(kvm_memory_slot_npages), 348 | KVADDR, &npages, sizeof(ulong), 349 | "kvm_memory_slot npages", FAULT_ON_ERROR); 350 | if (gfn>=base_gfn && gfn tmpfile); 389 | 390 | linenum = 0; 391 | glen = 16; 392 | hlen = VADDR_PRLEN; 393 | 394 | while (fgets(buf, BUFSIZE, pc->tmpfile)) { 395 | if (linenum==0) { 396 | fprintf(pc->saved_fp, "%s %s %s\n", 397 | mkstring(buf1, glen, LJUST, "GUEST PHYSICAL"), 398 | mkstring(buf2, hlen, LJUST, "HOST VIRTUAL"), 399 | mkstring(buf3, hlen, LJUST, "HOST PHYSICAL")); 400 | } 401 | else if(linenum==1) { 402 | char *host_physical; 403 | host_physical = strstr(buf, " "); 404 | while (!strncmp(host_physical, " ", 1)) { 405 | host_physical++; 406 | } 407 | fprintf(pc->saved_fp, "%*lx %*lx %s", -glen, gpa, -hlen, hva, host_physical); 408 | } 409 | else 410 | fprintf(pc->saved_fp, "%s", buf); 411 | 412 | linenum++; 413 | } 414 | close_tmpfile(); 415 | } 416 | -------------------------------------------------------------------------------- /snap.c: -------------------------------------------------------------------------------- 1 | /* snap.c - capture live memory into a kdump or netdump dumpfile 2 | * 3 | * Copyright (C) 2009, 2013, 2014, 2017 David Anderson 4 | * Copyright (C) 2009, 2013, 2014, 2017 Red Hat, Inc. All rights reserved. 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #include "defs.h" 18 | #include 19 | #include 20 | #include 21 | 22 | void snap_init(void); 23 | void snap_fini(void); 24 | 25 | void cmd_snap(void); 26 | char *help_snap[]; 27 | 28 | static struct command_table_entry command_table[] = { 29 | { "snap", cmd_snap, help_snap, 0 }, 30 | { NULL } 31 | }; 32 | 33 | static char *generate_elf_header(int, int, char *); 34 | static int verify_paddr(physaddr_t); 35 | static void init_ram_segments(void); 36 | static int print_progress(const char *, ulong); 37 | 38 | #if defined(X86) || defined(X86_64) || defined(IA64) || defined(PPC64) || defined(ARM64) 39 | int supported = TRUE; 40 | #else 41 | int supported = FALSE; 42 | #endif 43 | 44 | void __attribute__((constructor)) 45 | snap_init(void) /* Register the command set. */ 46 | { 47 | register_extension(command_table); 48 | } 49 | 50 | void __attribute__((destructor)) 51 | snap_fini(void) 52 | { 53 | } 54 | 55 | 56 | /* 57 | * Just pass in an unused filename. 58 | */ 59 | void 60 | cmd_snap(void) 61 | { 62 | int c, fd, n; 63 | physaddr_t paddr; 64 | size_t offset; 65 | char *buf; 66 | char *filename; 67 | struct node_table *nt; 68 | int type; 69 | char *elf_header; 70 | Elf64_Phdr *load; 71 | int load_index; 72 | 73 | if (!supported) 74 | error(FATAL, "command not supported on the %s architecture\n", 75 | pc->machine_type); 76 | 77 | filename = NULL; 78 | buf = GETBUF(PAGESIZE()); 79 | type = KDUMP_ELF64; 80 | 81 | while ((c = getopt(argcnt, args, "n")) != EOF) { 82 | switch(c) 83 | { 84 | case 'n': 85 | if (machine_type("X86_64")) 86 | option_not_supported('n'); 87 | else 88 | type = NETDUMP_ELF64; 89 | break; 90 | default: 91 | argerrs++; 92 | break; 93 | } 94 | } 95 | 96 | if (argerrs || !args[optind]) 97 | cmd_usage(pc->curcmd, SYNOPSIS); 98 | 99 | while (args[optind]) { 100 | if (filename) 101 | cmd_usage(pc->curcmd, SYNOPSIS); 102 | 103 | if (file_exists(args[optind], NULL)) 104 | error(FATAL, "%s: file already exists\n", args[optind]); 105 | else if ((fd = open(args[optind], O_RDWR|O_CREAT, 0644)) < 0) 106 | error(FATAL, args[optind]); 107 | 108 | filename = args[optind]; 109 | optind++; 110 | } 111 | 112 | if (!filename) 113 | cmd_usage(pc->curcmd, SYNOPSIS); 114 | 115 | init_ram_segments(); 116 | 117 | if (!(elf_header = generate_elf_header(type, fd, filename))) 118 | error(FATAL, "cannot generate ELF header\n"); 119 | 120 | load = (Elf64_Phdr *)(elf_header + sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr)); 121 | load_index = machine_type("X86_64") || machine_type("IA64") ? 1 : 0; 122 | 123 | for (n = 0; n < vt->numnodes; n++) { 124 | nt = &vt->node_table[n]; 125 | paddr = nt->start_paddr; 126 | offset = load[load_index + n].p_offset; 127 | 128 | for (c = 0; c < nt->size; c++, paddr += PAGESIZE()) { 129 | if (!verify_paddr(paddr)) 130 | continue; 131 | if (!readmem(paddr, PHYSADDR, &buf[0], PAGESIZE(), 132 | "memory page", QUIET|RETURN_ON_ERROR)) 133 | continue; 134 | 135 | lseek(fd, (off_t)(paddr + offset - nt->start_paddr), SEEK_SET); 136 | if (write(fd, &buf[0], PAGESIZE()) != PAGESIZE()) 137 | error(FATAL, "write to dumpfile failed\n"); 138 | 139 | if (!print_progress(filename, BTOP(paddr))) 140 | return; 141 | } 142 | } 143 | 144 | fprintf(stderr, "\r%s: [100%%] ", filename); 145 | fprintf(fp, "\n"); 146 | sprintf(buf, "/bin/ls -l %s\n", filename); 147 | system(buf); 148 | 149 | FREEBUF(elf_header); 150 | FREEBUF(buf); 151 | } 152 | 153 | 154 | char *help_snap[] = { 155 | "snap", /* command name */ 156 | "take a memory snapshot", /* short description */ 157 | "[-n] dumpfile", /* filename */ 158 | 159 | " This command takes a snapshot of physical memory and creates an ELF vmcore.", 160 | " The default vmcore is a kdump-style dumpfile. Supported on x86, x86_64,", 161 | " ia64 and ppc64 architectures only.", 162 | " ", 163 | " -n create a netdump-style vmcore (n/a on x86_64).", 164 | NULL 165 | }; 166 | 167 | /* 168 | * Architecture-specific and -generic ELF header data borrowed from the 169 | * netdump.h file in the netdump package, modified slightly to also create 170 | * a kdump-style vmcore. 171 | */ 172 | 173 | /****************************************************************************** 174 | * Elf core dumping * 175 | ******************************************************************************/ 176 | 177 | /* 178 | * Host-platform independent data 179 | */ 180 | #define ELF_PRARGSZ (80) /* Number of chars for args */ 181 | struct elf_prpsinfo_64 182 | { 183 | char pr_state; /* numeric process state */ 184 | char pr_sname; /* char for pr_state */ 185 | char pr_zomb; /* zombie */ 186 | char pr_nice; /* nice val */ 187 | __u64 pr_flag; /* flags */ 188 | __u32 pr_uid; 189 | __u32 pr_gid; 190 | __u32 pr_pid, pr_ppid, pr_pgrp, pr_sid; 191 | /* Lots missing */ 192 | char pr_fname[16]; /* filename of executable */ 193 | char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ 194 | }; 195 | 196 | /* 197 | * i386 specific 198 | */ 199 | struct user_regs_struct_i386 { 200 | __u32 ebx, ecx, edx, esi, edi, ebp, eax; 201 | __u16 ds, __ds, es, __es; 202 | __u16 fs, __fs, gs, __gs; 203 | __u32 orig_eax, eip; 204 | __u16 cs, __cs; 205 | __u32 eflags, esp; 206 | __u16 ss, __ss; 207 | }; 208 | 209 | #define ELF_NGREG_I386 (sizeof (struct user_regs_struct_i386) / sizeof(__u32)) 210 | typedef __u32 elf_gregset_i386_t[ELF_NGREG_I386]; 211 | 212 | struct elf_prstatus_i386 { 213 | char pad[72]; 214 | elf_gregset_i386_t pr_reg; /* GP registers */ 215 | __u32 pr_fpvalid; /* True if math co-processor being used. */ 216 | }; 217 | 218 | /* 219 | * x86_64 specific 220 | */ 221 | struct user_regs_struct_x86_64 { 222 | __u64 r15,r14,r13,r12,rbp,rbx,r11,r10; 223 | __u64 r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax; 224 | __u64 rip,cs,eflags; 225 | __u64 rsp,ss; 226 | __u64 fs_base, gs_base; 227 | __u64 ds,es,fs,gs; 228 | }; 229 | 230 | #define ELF_NGREG_X86_64 (sizeof (struct user_regs_struct_x86_64) / sizeof(__u64)) 231 | typedef __u64 elf_gregset_x86_64_t[ELF_NGREG_X86_64]; 232 | 233 | struct elf_prstatus_x86_64 { 234 | char pad[112]; 235 | elf_gregset_x86_64_t pr_reg; /* GP registers */ 236 | __u32 pr_fpvalid; /* True if math co-processor being used. */ 237 | }; 238 | 239 | /* 240 | * ppc64 specific 241 | */ 242 | struct user_regs_struct_ppc64 { 243 | __u64 gpr[32]; 244 | __u64 nip; 245 | __u64 msr; 246 | __u64 orig_gpr3; 247 | __u64 ctr; 248 | __u64 link; 249 | __u64 xer; 250 | __u64 ccr; 251 | __u64 softe; 252 | __u64 trap; 253 | __u64 dar; 254 | __u64 dsisr; 255 | __u64 result; 256 | }; 257 | 258 | #define ELF_NGREG_PPC64 (sizeof (struct user_regs_struct_ppc64) / sizeof(__u64)) 259 | typedef __u64 elf_gregset_ppc64_t[ELF_NGREG_PPC64]; 260 | 261 | struct elf_prstatus_ppc64 { 262 | char pad[112]; 263 | elf_gregset_ppc64_t pr_reg; /* GP registers */ 264 | __u32 pr_fpvalid; /* True if math co-processor being used. */ 265 | }; 266 | 267 | /* 268 | * ia64 specific 269 | */ 270 | struct _ia64_fpreg { 271 | union { 272 | __u64 bits[2]; 273 | } u; 274 | } __attribute__ ((aligned (16))); 275 | 276 | struct user_regs_struct_ia64 { 277 | /* The following registers are saved by SAVE_MIN: */ 278 | __u64 b6; /* scratch */ 279 | __u64 b7; /* scratch */ 280 | 281 | __u64 ar_csd; /* used by cmp8xchg16 (scratch) */ 282 | __u64 ar_ssd; /* reserved for future use (scratch) */ 283 | 284 | __u64 r8; /* scratch (return value register 0) */ 285 | __u64 r9; /* scratch (return value register 1) */ 286 | __u64 r10; /* scratch (return value register 2) */ 287 | __u64 r11; /* scratch (return value register 3) */ 288 | 289 | __u64 cr_ipsr; /* interrupted task's psr */ 290 | __u64 cr_iip; /* interrupted task's instruction pointer */ 291 | __u64 cr_ifs; /* interrupted task's function state */ 292 | 293 | __u64 ar_unat; /* interrupted task's NaT register (preserved) */ 294 | __u64 ar_pfs; /* prev function state */ 295 | __u64 ar_rsc; /* RSE configuration */ 296 | /* The following two are valid only if cr_ipsr.cpl > 0: */ 297 | __u64 ar_rnat; /* RSE NaT */ 298 | __u64 ar_bspstore; /* RSE bspstore */ 299 | 300 | __u64 pr; /* 64 predicate registers (1 bit each) */ 301 | __u64 b0; /* return pointer (bp) */ 302 | __u64 loadrs; /* size of dirty partition << 16 */ 303 | 304 | __u64 r1; /* the gp pointer */ 305 | __u64 r12; /* interrupted task's memory stack pointer */ 306 | __u64 r13; /* thread pointer */ 307 | 308 | __u64 ar_fpsr; /* floating point status (preserved) */ 309 | __u64 r15; /* scratch */ 310 | 311 | /* The remaining registers are NOT saved for system calls. */ 312 | 313 | __u64 r14; /* scratch */ 314 | __u64 r2; /* scratch */ 315 | __u64 r3; /* scratch */ 316 | 317 | /* The following registers are saved by SAVE_REST: */ 318 | __u64 r16; /* scratch */ 319 | __u64 r17; /* scratch */ 320 | __u64 r18; /* scratch */ 321 | __u64 r19; /* scratch */ 322 | __u64 r20; /* scratch */ 323 | __u64 r21; /* scratch */ 324 | __u64 r22; /* scratch */ 325 | __u64 r23; /* scratch */ 326 | __u64 r24; /* scratch */ 327 | __u64 r25; /* scratch */ 328 | __u64 r26; /* scratch */ 329 | __u64 r27; /* scratch */ 330 | __u64 r28; /* scratch */ 331 | __u64 r29; /* scratch */ 332 | __u64 r30; /* scratch */ 333 | __u64 r31; /* scratch */ 334 | 335 | __u64 ar_ccv; /* compare/exchange value (scratch) */ 336 | 337 | /* 338 | * Floating point registers that the kernel considers scratch: 339 | */ 340 | struct _ia64_fpreg f6; /* scratch */ 341 | struct _ia64_fpreg f7; /* scratch */ 342 | struct _ia64_fpreg f8; /* scratch */ 343 | struct _ia64_fpreg f9; /* scratch */ 344 | struct _ia64_fpreg f10; /* scratch */ 345 | struct _ia64_fpreg f11; /* scratch */ 346 | }; 347 | 348 | #define ELF_NGREG_IA64 (sizeof (struct user_regs_struct_ia64) / sizeof(__u64)) 349 | typedef __u64 elf_gregset_ia64_t[ELF_NGREG_IA64]; 350 | 351 | struct elf_prstatus_ia64 { 352 | char pad[112]; 353 | elf_gregset_ia64_t pr_reg; /* GP registers */ 354 | __u32 pr_fpvalid; /* True if math co-processor being used. */ 355 | }; 356 | 357 | /* 358 | * arm64 specific 359 | */ 360 | 361 | struct user_pt_regs_arm64 { 362 | __u64 regs[31]; 363 | __u64 sp; 364 | __u64 pc; 365 | __u64 pstate; 366 | }; 367 | 368 | #define ELF_NGREG_ARM64 (sizeof (struct user_pt_regs_arm64) / sizeof(elf_greg_t)) 369 | #ifndef elf_greg_t 370 | typedef unsigned long elf_greg_t; 371 | #endif 372 | typedef elf_greg_t elf_gregset_arm64_t[ELF_NGREG_ARM64]; 373 | 374 | struct elf_prstatus_arm64 { 375 | char pad[112]; 376 | elf_gregset_arm64_t pr_reg; 377 | int pr_fpvalid; 378 | }; 379 | 380 | 381 | union prstatus { 382 | struct elf_prstatus_i386 x86; 383 | struct elf_prstatus_x86_64 x86_64; 384 | struct elf_prstatus_ppc64 ppc64; 385 | struct elf_prstatus_ia64 ia64; 386 | struct elf_prstatus_arm64 arm64; 387 | }; 388 | 389 | static size_t 390 | dump_elf_note(char *buf, Elf64_Word type, char *name, char *desc, int d_len) 391 | { 392 | Elf64_Nhdr *note; 393 | size_t len; 394 | 395 | note = (Elf64_Nhdr *)buf; 396 | note->n_namesz = strlen(name); 397 | note->n_descsz = d_len; 398 | note->n_type = type; 399 | len = sizeof(Elf64_Nhdr); 400 | 401 | memcpy(buf + len, name, note->n_namesz); 402 | len = roundup(len + note->n_namesz, 4); 403 | 404 | memcpy(buf + len, desc, note->n_descsz); 405 | len = roundup(len + note->n_descsz, 4); 406 | 407 | return len; 408 | } 409 | 410 | char * 411 | generate_elf_header(int type, int fd, char *filename) 412 | { 413 | int i, n; 414 | char *buffer, *ptr; 415 | Elf64_Ehdr *elf; 416 | Elf64_Phdr *notes; 417 | Elf64_Phdr *load; 418 | size_t offset, len, l_offset; 419 | size_t data_offset; 420 | struct elf_prpsinfo_64 prpsinfo; 421 | union prstatus prstatus; 422 | int prstatus_len; 423 | ushort e_machine; 424 | int num_segments; 425 | struct node_table *nt; 426 | struct SNAP_info { 427 | ulonglong task_struct; 428 | ulonglong arch_data1; 429 | ulonglong arch_data2; 430 | } SNAP_info; 431 | 432 | num_segments = vt->numnodes; 433 | 434 | if (machine_type("X86_64")) { 435 | e_machine = EM_X86_64; 436 | prstatus_len = sizeof(prstatus.x86_64); 437 | num_segments += 1; /* mapped kernel section for phys_base */ 438 | } else if (machine_type("X86")) { 439 | e_machine = EM_386; 440 | prstatus_len = sizeof(prstatus.x86); 441 | } else if (machine_type("IA64")) { 442 | e_machine = EM_IA_64; 443 | prstatus_len = sizeof(prstatus.ia64); 444 | num_segments += 1; /* mapped kernel section for phys_start */ 445 | } else if (machine_type("PPC64")) { 446 | e_machine = EM_PPC64; 447 | prstatus_len = sizeof(prstatus.ppc64); 448 | } else if (machine_type("ARM64")) { 449 | e_machine = EM_AARCH64; 450 | prstatus_len = sizeof(prstatus.arm64); 451 | } else 452 | return NULL; 453 | 454 | /* should be enought for the notes + roundup + two blocks */ 455 | buffer = (char *)GETBUF(sizeof(Elf64_Ehdr) + 456 | num_segments * sizeof(Elf64_Phdr) + PAGESIZE() * 2); 457 | offset = 0; 458 | ptr = buffer; 459 | 460 | /* Elf header */ 461 | elf = (Elf64_Ehdr *)ptr; 462 | memcpy(elf->e_ident, ELFMAG, SELFMAG); 463 | elf->e_ident[EI_CLASS] = ELFCLASS64; 464 | #if __BYTE_ORDER == __BIG_ENDIAN 465 | elf->e_ident[EI_DATA] = ELFDATA2MSB; 466 | #else 467 | elf->e_ident[EI_DATA] = ELFDATA2LSB; 468 | #endif 469 | elf->e_ident[EI_VERSION] = EV_CURRENT; 470 | elf->e_ident[EI_OSABI] = ELFOSABI_SYSV; 471 | elf->e_ident[EI_ABIVERSION] = 0; 472 | memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); 473 | 474 | elf->e_type = ET_CORE; 475 | elf->e_machine = e_machine; 476 | elf->e_version = EV_CURRENT; 477 | elf->e_entry = 0; 478 | elf->e_phoff = sizeof(Elf64_Ehdr); 479 | elf->e_shoff = 0; 480 | elf->e_flags = 0; 481 | elf->e_ehsize = sizeof(Elf64_Ehdr); 482 | elf->e_phentsize = sizeof(Elf64_Phdr); 483 | elf->e_phnum = 1 + num_segments; 484 | elf->e_shentsize = 0; 485 | elf->e_shnum = 0; 486 | elf->e_shstrndx = 0; 487 | 488 | offset += sizeof(Elf64_Ehdr); 489 | ptr += sizeof(Elf64_Ehdr); 490 | 491 | /* PT_NOTE */ 492 | notes = (Elf64_Phdr *)ptr; 493 | notes->p_type = PT_NOTE; 494 | notes->p_offset = 0; /* TO BE FILLED IN */ 495 | notes->p_vaddr = 0; 496 | notes->p_paddr = 0; 497 | notes->p_filesz = 0; /* TO BE FILLED IN */ 498 | notes->p_memsz = 0; 499 | notes->p_flags = 0; 500 | notes->p_align = 0; 501 | 502 | offset += sizeof(Elf64_Phdr); 503 | ptr += sizeof(Elf64_Phdr); 504 | 505 | /* PT_LOAD */ 506 | load = (Elf64_Phdr *)ptr; 507 | for (i = n = 0; i < num_segments; i++) { 508 | load[i].p_type = PT_LOAD; 509 | load[i].p_offset = 0; /* TO BE FILLED IN */ 510 | 511 | switch (e_machine) 512 | { 513 | case EM_X86_64: 514 | nt = &vt->node_table[n]; 515 | if (i == 0) { 516 | #ifdef X86_64 517 | load[i].p_vaddr = __START_KERNEL_map; 518 | load[i].p_paddr = machdep->machspec->phys_base; 519 | #endif 520 | load[i].p_filesz = 0; 521 | load[i].p_memsz = load[i].p_filesz; 522 | } else { 523 | load[i].p_vaddr = PTOV(nt->start_paddr); 524 | load[i].p_paddr = nt->start_paddr; 525 | load[i].p_filesz = nt->size * PAGESIZE(); 526 | load[i].p_memsz = load[i].p_filesz; 527 | n++; 528 | } 529 | load[i].p_flags = PF_R | PF_W | PF_X; 530 | load[i].p_align = 0; 531 | break; 532 | 533 | case EM_386: 534 | nt = &vt->node_table[n++]; 535 | load[i].p_vaddr = 0; 536 | load[i].p_paddr = nt->start_paddr; 537 | load[i].p_filesz = nt->size * PAGESIZE(); 538 | load[i].p_memsz = load[i].p_filesz; 539 | load[i].p_flags = PF_R | PF_W | PF_X; 540 | load[i].p_align = (type == NETDUMP_ELF64) ? PAGESIZE() : 0; 541 | break; 542 | 543 | case EM_IA_64: 544 | nt = &vt->node_table[n]; 545 | if (i == 0) { 546 | #ifdef IA64 547 | load[i].p_vaddr = machdep->machspec->kernel_start; 548 | load[i].p_paddr = machdep->machspec->phys_start; 549 | #endif 550 | load[i].p_filesz = 0; 551 | load[i].p_memsz = load[i].p_filesz; 552 | } else { 553 | load[i].p_vaddr = PTOV(nt->start_paddr); 554 | load[i].p_paddr = nt->start_paddr; 555 | load[i].p_filesz = nt->size * PAGESIZE(); 556 | load[i].p_memsz = load[i].p_filesz; 557 | n++; 558 | } 559 | load[i].p_flags = PF_R | PF_W | PF_X; 560 | load[i].p_align = (type == NETDUMP_ELF64) ? PAGESIZE() : 0; 561 | break; 562 | 563 | case EM_PPC64: 564 | nt = &vt->node_table[n++]; 565 | load[i].p_vaddr = PTOV(nt->start_paddr); 566 | load[i].p_paddr = nt->start_paddr; 567 | load[i].p_filesz = nt->size * PAGESIZE(); 568 | load[i].p_memsz = load[i].p_filesz; 569 | load[i].p_flags = PF_R | PF_W | PF_X; 570 | load[i].p_align = (type == NETDUMP_ELF64) ? PAGESIZE() : 0; 571 | break; 572 | 573 | case EM_AARCH64: 574 | nt = &vt->node_table[n++]; 575 | load[i].p_vaddr = PTOV(nt->start_paddr); 576 | load[i].p_paddr = nt->start_paddr; 577 | load[i].p_filesz = nt->size * PAGESIZE(); 578 | load[i].p_memsz = load[i].p_filesz; 579 | load[i].p_flags = PF_R | PF_W | PF_X; 580 | load[i].p_align = (type == NETDUMP_ELF64) ? PAGESIZE() : 0; 581 | break; 582 | } 583 | 584 | // l_offset += load[i].p_filesz; 585 | offset += sizeof(Elf64_Phdr); 586 | ptr += sizeof(Elf64_Phdr); 587 | } 588 | notes->p_offset = offset; 589 | 590 | /* NT_PRSTATUS note */ 591 | memset(&prstatus, 0, sizeof(prstatus)); 592 | len = dump_elf_note(ptr, NT_PRSTATUS, "CORE", 593 | (char *)&prstatus, prstatus_len); 594 | offset += len; 595 | ptr += len; 596 | notes->p_filesz += len; 597 | 598 | /* NT_PRPSINFO note */ 599 | memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo_64)); 600 | prpsinfo.pr_state = 0; 601 | prpsinfo.pr_sname = 'R'; 602 | prpsinfo.pr_zomb = 0; 603 | strcpy(prpsinfo.pr_fname, "vmlinux"); 604 | 605 | len = dump_elf_note(ptr, NT_PRPSINFO, "CORE", 606 | (char *)&prpsinfo, sizeof(prpsinfo)); 607 | 608 | offset += len; 609 | ptr += len; 610 | notes->p_filesz += len; 611 | 612 | /* NT_TASKSTRUCT note */ 613 | SNAP_info.task_struct = CURRENT_TASK(); 614 | #ifdef X86_64 615 | SNAP_info.arch_data1 = kt->relocate; 616 | SNAP_info.arch_data2 = 0; 617 | #elif ARM64 618 | SNAP_info.arch_data1 = machdep->machspec->kimage_voffset; 619 | SNAP_info.arch_data2 = (machdep->machspec->VA_BITS_ACTUAL << 32) | 620 | machdep->machspec->CONFIG_ARM64_VA_BITS; 621 | #else 622 | SNAP_info.arch_data1 = 0; 623 | SNAP_info.arch_data2 = 0; 624 | #endif 625 | len = dump_elf_note (ptr, NT_TASKSTRUCT, "SNAP", 626 | (char *)&SNAP_info, sizeof(struct SNAP_info)); 627 | offset += len; 628 | ptr += len; 629 | notes->p_filesz += len; 630 | 631 | if (type == NETDUMP_ELF64) 632 | offset = roundup (offset, PAGESIZE()); 633 | 634 | l_offset = offset; 635 | for (i = 0; i < num_segments; i++) { 636 | load[i].p_offset = l_offset; 637 | l_offset += load[i].p_filesz; 638 | } 639 | data_offset = offset; 640 | 641 | while (offset > 0) { 642 | len = write(fd, buffer + (data_offset - offset), offset); 643 | if (len < 0) { 644 | perror(filename); 645 | FREEBUF(buffer); 646 | return NULL; 647 | } 648 | 649 | offset -= len; 650 | } 651 | 652 | return buffer; 653 | } 654 | 655 | struct ram_segments { 656 | physaddr_t start; 657 | physaddr_t end; 658 | }; 659 | 660 | static struct ram_segments *ram_segments = NULL; 661 | static int nr_segments = 0; 662 | 663 | static void 664 | init_ram_segments(void) 665 | { 666 | int i, errflag; 667 | FILE *iomem; 668 | char buf[BUFSIZE], *p1, *p2; 669 | physaddr_t start, end; 670 | 671 | if ((iomem = fopen("/proc/iomem", "r")) == NULL) 672 | goto fail_iomem; 673 | 674 | while (fgets(buf, BUFSIZE, iomem)) { 675 | if (strstr(buf, "System RAM")) { 676 | console(buf); 677 | nr_segments++; 678 | } 679 | } 680 | if (!nr_segments) 681 | goto fail_iomem; 682 | 683 | ram_segments = (struct ram_segments *) 684 | GETBUF(sizeof(struct ram_segments) * nr_segments); 685 | 686 | rewind(iomem); 687 | i = 0; 688 | while (fgets(buf, BUFSIZE, iomem)) { 689 | if (strstr(buf, "System RAM")) { 690 | if (!(p1 = strstr(buf, ":"))) 691 | goto fail_iomem; 692 | *p1 = NULLCHAR; 693 | clean_line(buf); 694 | if (strstr(buf, " ")) 695 | goto fail_iomem; 696 | p1 = buf; 697 | if (!(p2 = strstr(buf, "-"))) 698 | goto fail_iomem; 699 | *p2 = NULLCHAR; 700 | p2++; 701 | errflag = 0; 702 | start = htoll(p1, RETURN_ON_ERROR|QUIET, &errflag); 703 | end = htoll(p2, RETURN_ON_ERROR|QUIET, &errflag); 704 | if (errflag) 705 | goto fail_iomem; 706 | ram_segments[i].start = PHYSPAGEBASE(start); 707 | if (PAGEOFFSET(start)) 708 | ram_segments[i].start += PAGESIZE(); 709 | ram_segments[i].end = PHYSPAGEBASE(end); 710 | if (PAGEOFFSET(end) == (PAGESIZE()-1)) 711 | ram_segments[i].end += PAGESIZE(); 712 | console("ram_segments[%d]: %016llx %016llx [%s-%s]\n", i, 713 | (ulonglong)ram_segments[i].start, 714 | (ulonglong)ram_segments[i].end, p1, p2); 715 | i++; 716 | } 717 | } 718 | 719 | fclose(iomem); 720 | return; 721 | 722 | fail_iomem: 723 | fclose(iomem); 724 | nr_segments = 0; 725 | if (ram_segments) 726 | FREEBUF(ram_segments); 727 | 728 | return; 729 | } 730 | 731 | static int 732 | verify_paddr(physaddr_t paddr) 733 | { 734 | int i, ok; 735 | 736 | if (!machdep->verify_paddr(paddr)) 737 | return FALSE; 738 | 739 | if (!nr_segments) 740 | return TRUE; 741 | 742 | for (i = ok = 0; i < nr_segments; i++) { 743 | if ((paddr >= ram_segments[i].start) && 744 | (paddr < ram_segments[i].end)) { 745 | ok++; 746 | break; 747 | } 748 | } 749 | 750 | /* 751 | * Pre-2.6.13 x86_64 /proc/iomem was restricted to 4GB, 752 | * so just accept it. 753 | */ 754 | if ((paddr >= 0x100000000ULL) && 755 | machine_type("X86_64") && 756 | (THIS_KERNEL_VERSION < LINUX(2,6,13))) 757 | ok++; 758 | 759 | if (!ok) { 760 | if (CRASHDEBUG(1)) 761 | console("reject: %llx\n", (ulonglong)paddr); 762 | return FALSE; 763 | } 764 | 765 | return TRUE; 766 | } 767 | 768 | /* 769 | * Borrowed from makedumpfile, prints a percentage-done value 770 | * once per second. 771 | */ 772 | static int 773 | print_progress(const char *filename, ulong current) 774 | { 775 | int n, progress; 776 | time_t tm; 777 | struct node_table *nt; 778 | static time_t last_time = 0; 779 | static ulong total_pages = 0; 780 | static ulong written_pages = 0; 781 | 782 | if (!total_pages) { 783 | for (n = 0; n < vt->numnodes; n++) { 784 | nt = &vt->node_table[n]; 785 | total_pages += nt->size; 786 | } 787 | } 788 | 789 | if (received_SIGINT()) { 790 | fprintf(stderr, "\n\n"); 791 | return FALSE; 792 | } 793 | 794 | if (++written_pages < total_pages) { 795 | tm = time(NULL); 796 | if (tm - last_time < 1) 797 | return TRUE; 798 | last_time = tm; 799 | progress = written_pages * 100 / total_pages; 800 | } else 801 | progress = 100; 802 | 803 | fprintf(stderr, "\r%s: [%2d%%] ", filename, progress); 804 | 805 | return TRUE; 806 | } 807 | -------------------------------------------------------------------------------- /snap.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2009, 2011, 2013 David Anderson 3 | # Copyright (C) 2009, 2011, 2013 Red Hat, Inc. All rights reserved. 4 | # 5 | # This program is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 2 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | 16 | ifeq ($(shell arch), i686) 17 | TARGET=X86 18 | TARGET_CFLAGS=-D_FILE_OFFSET_BITS=64 19 | endif 20 | ifeq ($(shell arch), ppc64) 21 | TARGET=PPC64 22 | TARGET_CFLAGS=-m64 23 | endif 24 | ifeq ($(shell arch), ppc64le) 25 | TARGET=PPC64 26 | TARGET_CFLAGS=-m64 27 | endif 28 | ifeq ($(shell arch), ia64) 29 | TARGET=IA64 30 | TARGET_CFLAGS= 31 | endif 32 | ifeq ($(shell arch), x86_64) 33 | TARGET=X86_64 34 | TARGET_CFLAGS= 35 | endif 36 | 37 | ifeq ($(shell /bin/ls /usr/include/crash/defs.h 2>/dev/null), /usr/include/crash/defs.h) 38 | INCDIR=/usr/include/crash 39 | endif 40 | ifeq ($(shell /bin/ls ../defs.h 2> /dev/null), ../defs.h) 41 | INCDIR=.. 42 | endif 43 | ifeq ($(shell /bin/ls ./defs.h 2> /dev/null), ./defs.h) 44 | INCDIR=. 45 | endif 46 | 47 | all: snap.so 48 | 49 | snap.so: $(INCDIR)/defs.h snap.c 50 | gcc -Wall -g -I$(INCDIR) -shared -rdynamic -o snap.so snap.c -fPIC -D$(TARGET) $(TARGET_CFLAGS) $(GDB_FLAGS) 51 | -------------------------------------------------------------------------------- /sockq.c: -------------------------------------------------------------------------------- 1 | /* sockq.c - sockq extension module for crash 2 | * 3 | * Copyright (C) 2014 FUJITSU LIMITED 4 | * Author: Qiao Nuohan 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #include "defs.h" /* From the crash source top-level directory */ 18 | 19 | void sockq_init(void); /* constructor function */ 20 | void sockq_fini(void); /* destructor function (optional) */ 21 | 22 | void cmd_sockq(void); /* Declare the commands and their help data. */ 23 | char *help_sockq[]; 24 | 25 | static struct command_table_entry command_table[] = { 26 | { "sockq", cmd_sockq, help_sockq, 0}, /* One or more commands, */ 27 | { NULL }, /* terminated by NULL, */ 28 | }; 29 | 30 | char *help_sockq[] = { 31 | "sockq", /* command name */ 32 | "get socket receive queue into a file", /* short description */ 33 | "file_address outfile", /* argument synopsis */ 34 | 35 | " This command gets the data from the socket receive queue.", 36 | " ", 37 | " file_address A hexadecimal value of socket's file structure address.", 38 | " outfile A name of output file. If the file already exists,", 39 | " it is overwritten.", 40 | NULL 41 | }; 42 | 43 | void __attribute__((constructor)) 44 | sockq_init(void) /* Register the command set. */ 45 | { 46 | register_extension(command_table); 47 | } 48 | 49 | /* 50 | * This function is called if the shared object is unloaded. 51 | * If desired, perform any cleanups here. 52 | */ 53 | void __attribute__((destructor)) 54 | sockq_fini(void) { } 55 | 56 | static int 57 | get_member_data(ulonglong addr, char *name, char *member, void* buf) 58 | { 59 | ulong member_offset; 60 | 61 | member_offset = MEMBER_OFFSET(name, member); 62 | 63 | if (!readmem(addr + member_offset, KVADDR, buf, 64 | MEMBER_SIZE(name, member), name, FAULT_ON_ERROR)) 65 | return FALSE; 66 | 67 | return TRUE; 68 | } 69 | 70 | /* 71 | * write receive data in the specified file 72 | */ 73 | static int 74 | write_data(int fd, char *buf, ulong addr, ulong size) 75 | { 76 | ulong wsize; 77 | 78 | while (size > 0) { 79 | /* size of the buffer is pagesize */ 80 | wsize = (size > PAGESIZE()) ? PAGESIZE() : size; 81 | 82 | if (!readmem(addr, KVADDR, buf, wsize, "vaddr", FAULT_ON_ERROR)) { 83 | fprintf(fp, "cannot read data from packet buffer\n"); 84 | return 1; 85 | } 86 | 87 | if (write(fd, buf, wsize) < 0) { 88 | fprintf(fp, "cannot write data in a file\n"); 89 | return 1; 90 | } 91 | 92 | addr += wsize; 93 | size -= wsize; 94 | } 95 | 96 | return 0; 97 | } 98 | 99 | int 100 | do_sockq(ulong file_addr, char *output_file, int fd) 101 | { 102 | int rc = 1; 103 | ulong pd, sk; 104 | uint qlen; 105 | char *buf = NULL; 106 | ulong next, head; 107 | unsigned int len; 108 | ulong wnext; 109 | 110 | if (!get_member_data(file_addr, "file", "private_data", &pd)) { 111 | fprintf(fp, "cannot get private_data of file structure\n"); 112 | goto cleanup; 113 | } 114 | 115 | if (!get_member_data(pd, "socket", "sk", &sk)) { 116 | fprintf(fp, "cannot get sk of socket structure\n"); 117 | goto cleanup; 118 | } 119 | 120 | if (!get_member_data(sk + MEMBER_OFFSET("sock", "sk_receive_queue"), 121 | "sk_buff_head", "next", &next)) { 122 | fprintf(fp, "cannot get the first queue of sock structure\n"); 123 | goto cleanup; 124 | } 125 | 126 | if (!get_member_data(sk + MEMBER_OFFSET("sock", "sk_receive_queue"), 127 | "sk_buff_head", "qlen", &qlen)) { 128 | fprintf(fp, "cannot get the number of queue list\n"); 129 | goto cleanup; 130 | } 131 | 132 | /* create a output file */ 133 | if (output_file != NULL && 134 | (fd = open(output_file, 135 | O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR)) < 0) { 136 | fprintf(fp, "cannot create %s\n", output_file); 137 | goto cleanup; 138 | } 139 | 140 | if (qlen == 0) { 141 | /* receive queue is empty */ 142 | rc = 0; 143 | goto cleanup; 144 | } 145 | 146 | /* get work area */ 147 | buf = GETBUF(PAGESIZE()); 148 | 149 | while (qlen-- > 0) { 150 | /* get packet buffer are info */ 151 | if (!get_member_data(next, "sk_buff", "head", &head)) { 152 | fprintf(fp, "cannot head of sk_buff structure\n"); 153 | goto cleanup; 154 | } 155 | 156 | if (!get_member_data(next, "sk_buff", "len", &len)) { 157 | fprintf(fp, "cannot tail of sk_buff structure\n"); 158 | goto cleanup; 159 | } 160 | 161 | /* write data in the output file */ 162 | if (write_data(fd, buf, head, len)) 163 | goto cleanup; 164 | 165 | /* next receive queue */ 166 | wnext = next; 167 | if (!get_member_data(wnext, "sk_buff", "next", &next)) { 168 | fprintf(fp, "cannot get next of sk_buff structure\n"); 169 | goto cleanup; 170 | } 171 | } 172 | 173 | /* all process normally ends */ 174 | rc = 0; 175 | 176 | cleanup: 177 | if (output_file != NULL) 178 | close(fd); 179 | if (buf) 180 | FREEBUF(buf); 181 | 182 | return rc; 183 | } 184 | 185 | void 186 | cmd_sockq(void) 187 | { 188 | ulong file_addr; 189 | 190 | if (argcnt != 3) 191 | cmd_usage(pc->curcmd, SYNOPSIS); 192 | 193 | optind++; 194 | file_addr = htol(args[optind], FAULT_ON_ERROR, NULL); 195 | 196 | optind++; 197 | if (strlen(args[optind]) > PATH_MAX) { 198 | fprintf(fp, "cannot create specified output file\n"); 199 | return; 200 | } 201 | 202 | do_sockq(file_addr, args[optind], -1); 203 | } 204 | -------------------------------------------------------------------------------- /spu.c: -------------------------------------------------------------------------------- 1 | /* spu.c - commands for viewing Cell/B.E. SPUs data 2 | * 3 | * (C) Copyright 2007 IBM Corp. 4 | * 5 | * Author: Lucio Correia 6 | * 7 | * This program is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | */ 17 | 18 | #include "defs.h" 19 | 20 | #define NR_SPUS (16) /* Enough for current hardware */ 21 | #define MAX_PRIO (140) 22 | #define MAX_PROPERTY_NAME (64) 23 | #define STR_SPU_INVALID (0x0) 24 | #define STR_SPU_ID (0x1) 25 | #define STR_SPU_PID (0x2) 26 | #define STR_SPU_CTX_ADDR (0x8) 27 | 28 | #define SPUCTX_CMD_NAME "spuctx" 29 | #define SPUS_CMD_NAME "spus" 30 | #define SPURQ_CMD_NAME "spurq" 31 | 32 | struct cbe_size_table; 33 | struct cbe_offset_table; 34 | void init_cbe_size_table(void); 35 | void init_cbe_offset_table(void); 36 | ulong get_spu_addr(ulong spu_info); 37 | 38 | void cmd_spus(void); 39 | void cmd_spurq(void); 40 | void cmd_spuctx(void); 41 | char *help_spus[]; 42 | char *help_spurq[]; 43 | void show_spu_state(ulong spu); 44 | void dump_spu_runq(ulong k_prio_array); 45 | char *help_spuctx[]; 46 | void show_ctx_info(ulong ctx_addr); 47 | void print_ctx_info(char *ctx_data, char *spu_data, int info); 48 | void show_ctx_info_all(void); 49 | 50 | 51 | static struct command_table_entry command_table[] = { 52 | SPUCTX_CMD_NAME, cmd_spuctx, help_spuctx, 0, 53 | SPUS_CMD_NAME, cmd_spus, help_spus, 0, 54 | SPURQ_CMD_NAME, cmd_spurq, help_spurq, 0, 55 | NULL 56 | }; 57 | 58 | struct cbe_size_table { 59 | long crash_spu_info; 60 | long spu; 61 | long spu_context; 62 | long spu_prio_array; 63 | long list_head; 64 | } cbe_size_table; 65 | 66 | struct cbe_offset_table { 67 | long crash_spu_info_spu; 68 | long crash_spu_info_saved_mfc_sr1_RW; 69 | long crash_spu_info_saved_mfc_dar; 70 | long crash_spu_info_saved_mfc_dsisr; 71 | long crash_spu_info_saved_spu_runcntl_RW; 72 | long crash_spu_info_saved_spu_status_R; 73 | long crash_spu_info_saved_spu_npc_RW; 74 | 75 | long spu_node; 76 | long spu_number; 77 | long spu_ctx; 78 | long spu_pid; 79 | long spu_name; 80 | long spu_slb_replace; 81 | long spu_mm; 82 | long spu_timestamp; 83 | long spu_class_0_pending; 84 | long spu_problem; 85 | long spu_priv2; 86 | long spu_flags; 87 | 88 | long spu_context_spu; 89 | long spu_context_state; 90 | long spu_context_prio; 91 | long spu_context_local_store; 92 | long spu_context_rq; 93 | 94 | long spu_prio_array_runq; 95 | } cbe_offset_table; 96 | 97 | #define CBE_SIZE(X) (cbe_size_table.X) 98 | #define CBE_OFFSET(X, Y) (cbe_offset_table.X##_##Y) 99 | 100 | #define CBE_SIZE_INIT(X, Y) \ 101 | do { \ 102 | cbe_size_table.X = STRUCT_SIZE(Y); \ 103 | if (cbe_size_table.X == -1) \ 104 | error(FATAL, "Couldn't get %s size.\n", Y); \ 105 | } while(0) 106 | 107 | #define CBE_OFFSET_INIT(X, Y, Z) \ 108 | do { \ 109 | cbe_offset_table.X = MEMBER_OFFSET(Y, Z); \ 110 | if (cbe_offset_table.X == -1) \ 111 | error(FATAL, "Couldn't get %s.%s offset.\n", Y, Z); \ 112 | } while(0) 113 | 114 | ulong spu[NR_SPUS]; 115 | 116 | /***************************************************************************** 117 | * INIT FUNCTIONS 118 | */ 119 | 120 | /* 121 | * Read kernel virtual addresses of crash_spu_info data stored by kdump 122 | */ 123 | 124 | void init_cbe_size_table(void) 125 | { 126 | CBE_SIZE_INIT(crash_spu_info, "crash_spu_info"); 127 | CBE_SIZE_INIT(spu, "spu"); 128 | CBE_SIZE_INIT(spu_context, "spu_context"); 129 | CBE_SIZE_INIT(spu_prio_array, "spu_prio_array"); 130 | CBE_SIZE_INIT(list_head, "list_head"); 131 | } 132 | 133 | void init_cbe_offset_table(void) 134 | { 135 | CBE_OFFSET_INIT(crash_spu_info_spu, "crash_spu_info", "spu"); 136 | CBE_OFFSET_INIT(crash_spu_info_saved_mfc_sr1_RW, "crash_spu_info", 137 | "saved_mfc_sr1_RW"); 138 | CBE_OFFSET_INIT(crash_spu_info_saved_mfc_dar, "crash_spu_info", 139 | "saved_mfc_dar"); 140 | CBE_OFFSET_INIT(crash_spu_info_saved_mfc_dsisr, "crash_spu_info", 141 | "saved_mfc_dsisr"); 142 | CBE_OFFSET_INIT(crash_spu_info_saved_spu_runcntl_RW, "crash_spu_info", 143 | "saved_spu_runcntl_RW"); 144 | CBE_OFFSET_INIT(crash_spu_info_saved_spu_status_R, "crash_spu_info", 145 | "saved_spu_status_R"); 146 | CBE_OFFSET_INIT(crash_spu_info_saved_spu_npc_RW, "crash_spu_info", 147 | "saved_spu_npc_RW"); 148 | 149 | CBE_OFFSET_INIT(spu_node, "spu", "node"); 150 | CBE_OFFSET_INIT(spu_number, "spu", "number"); 151 | CBE_OFFSET_INIT(spu_ctx, "spu", "ctx"); 152 | CBE_OFFSET_INIT(spu_pid, "spu", "pid"); 153 | CBE_OFFSET_INIT(spu_name, "spu", "name"); 154 | CBE_OFFSET_INIT(spu_slb_replace, "spu", "slb_replace"); 155 | CBE_OFFSET_INIT(spu_mm, "spu", "mm"); 156 | CBE_OFFSET_INIT(spu_timestamp, "spu", "timestamp"); 157 | CBE_OFFSET_INIT(spu_class_0_pending, "spu", "class_0_pending"); 158 | CBE_OFFSET_INIT(spu_problem, "spu", "problem"); 159 | CBE_OFFSET_INIT(spu_priv2, "spu", "priv2"); 160 | CBE_OFFSET_INIT(spu_flags, "spu", "flags"); 161 | 162 | CBE_OFFSET_INIT(spu_context_spu, "spu_context", "spu"); 163 | CBE_OFFSET_INIT(spu_context_state, "spu_context", "state"); 164 | CBE_OFFSET_INIT(spu_context_prio, "spu_context", "prio"); 165 | CBE_OFFSET_INIT(spu_context_local_store, "spu_context", "local_store"); 166 | CBE_OFFSET_INIT(spu_context_rq, "spu_context", "rq"); 167 | 168 | CBE_OFFSET_INIT(spu_prio_array_runq, "spu_prio_array", "runq"); 169 | } 170 | 171 | void get_crash_spu_info(void) 172 | { 173 | int i; 174 | ulong addr; 175 | long struct_size; 176 | 177 | addr = symbol_value("crash_spu_info"); 178 | struct_size = CBE_SIZE(crash_spu_info); 179 | 180 | for (i = 0; i < NR_SPUS; i++) 181 | spu[i] = addr + (i * struct_size); 182 | } 183 | 184 | _init() 185 | { 186 | int i, n_registered; 187 | struct command_table_entry *cte; 188 | 189 | init_cbe_size_table(); 190 | init_cbe_offset_table(); 191 | 192 | for (i = 0; i < NR_SPUS; i++) 193 | spu[i] = 0; 194 | 195 | register_extension(command_table); 196 | 197 | get_crash_spu_info(); 198 | } 199 | 200 | 201 | _fini() { } 202 | 203 | 204 | 205 | /***************************************************************************** 206 | * BASIC FUNCTIONS 207 | */ 208 | 209 | 210 | /* 211 | * Returns a pointer to the requested SPU field 212 | */ 213 | ulong get_spu_addr(ulong spu_info) 214 | { 215 | ulong spu_addr; 216 | 217 | readmem(spu_info + CBE_OFFSET(crash_spu_info, spu), KVADDR, &spu_addr, 218 | sizeof(spu_addr), "get_spu_addr", FAULT_ON_ERROR); 219 | 220 | return spu_addr; 221 | } 222 | 223 | 224 | /***************************************************************************** 225 | * SPUCTX COMMAND 226 | */ 227 | 228 | #define DUMP_WIDTH 23 229 | #define DUMP_SPU_NAME \ 230 | do { \ 231 | fprintf(fp, " %-*s = %s\n", DUMP_WIDTH, "name", name_str); \ 232 | } while(0) 233 | 234 | #define DUMP_SPU_FIELD(format, field, cast) \ 235 | do { \ 236 | offset = CBE_OFFSET(spu, field); \ 237 | fprintf(fp, " %-*s = "format"\n", DUMP_WIDTH, #field, \ 238 | cast(spu_data + offset)); \ 239 | } while(0) 240 | 241 | #define DUMP_CTX_FIELD(format, field, cast) \ 242 | do { \ 243 | offset = CBE_OFFSET(spu_context, field); \ 244 | fprintf(fp, " %-*s = "format"\n", DUMP_WIDTH, #field, \ 245 | cast(ctx_data + offset)); \ 246 | } while(0) 247 | 248 | #define DUMP_DBG_FIELD(format, field, cast) \ 249 | do { \ 250 | offset = CBE_OFFSET(crash_spu_info, field); \ 251 | fprintf(fp, " %-*s = "format"\n", DUMP_WIDTH, #field, \ 252 | cast(debug_data + offset)); \ 253 | } while(0) 254 | 255 | /* 256 | * Print the spu and spu_context structs fields. Some SPU memory-mapped IO 257 | * registers are taken directly from crash_spu_info. 258 | */ 259 | void print_ctx_info(char *ctx_data, char *spu_data, int info) 260 | { 261 | long offset, size; 262 | char *name_str, *debug_data; 263 | 264 | DUMP_CTX_FIELD("%d", state, *(int *)); 265 | DUMP_CTX_FIELD("%d", prio, *(int *)); 266 | DUMP_CTX_FIELD("%p", local_store, *(ulong *)); 267 | DUMP_CTX_FIELD("%p", rq, *(ulong *)); 268 | 269 | if (spu_data) { 270 | offset = CBE_OFFSET(spu, name); 271 | size = MAX_PROPERTY_NAME * sizeof(char); 272 | name_str = (char *)GETBUF(size); 273 | readmem(*(ulong *)(spu_data + offset), KVADDR, name_str, size, 274 | "name_str", FAULT_ON_ERROR); 275 | DUMP_SPU_NAME; 276 | FREEBUF(name_str); 277 | 278 | DUMP_SPU_FIELD("%d", node, *(int *)); 279 | DUMP_SPU_FIELD("%d", number, *(int *)); 280 | DUMP_SPU_FIELD("%d", pid, *(int *)); 281 | DUMP_SPU_FIELD("0x%x", slb_replace, *(unsigned int *)); 282 | DUMP_SPU_FIELD("%p", mm, *(ulong *)); 283 | DUMP_SPU_FIELD("%p", timestamp, *(long long *)); 284 | DUMP_SPU_FIELD("%d", class_0_pending, *(int *)); 285 | DUMP_SPU_FIELD("%p", problem, *(ulong *)); 286 | DUMP_SPU_FIELD("%p", priv2, *(ulong *)); 287 | DUMP_SPU_FIELD("0x%lx", flags, *(ulong *)); 288 | 289 | size = CBE_SIZE(crash_spu_info); 290 | debug_data = (char *)GETBUF(size); 291 | readmem(spu[info], KVADDR, debug_data, size, "debug_data", 292 | FAULT_ON_ERROR); 293 | 294 | DUMP_DBG_FIELD("0x%lx", saved_mfc_sr1_RW, *(ulong *)); 295 | DUMP_DBG_FIELD("0x%lx", saved_mfc_dar, *(ulong *)); 296 | DUMP_DBG_FIELD("0x%lx", saved_mfc_dsisr, *(ulong *)); 297 | DUMP_DBG_FIELD("0x%x", saved_spu_runcntl_RW, *(uint *)); 298 | DUMP_DBG_FIELD("0x%x", saved_spu_status_R, *(uint *)); 299 | DUMP_DBG_FIELD("0x%x", saved_spu_npc_RW, *(uint *)); 300 | 301 | FREEBUF(debug_data); 302 | } 303 | } 304 | 305 | 306 | /* 307 | * Pass ctx and respective spu data to print_ctx_info for the contexts in 308 | * ctx_addr list (chosen contexts). 309 | */ 310 | void show_ctx_info(ulong ctx_addr) 311 | { 312 | int number, info, i; 313 | char *ctx_data, *spu_data; 314 | long size, offset; 315 | ulong spu_addr, addr; 316 | 317 | if (!ctx_addr) 318 | return; 319 | 320 | spu_data = NULL; 321 | info = 0; 322 | 323 | size = CBE_SIZE(spu_context); 324 | ctx_data = GETBUF(size); 325 | if (!ctx_data) 326 | error(FATAL, "Couldn't allocate memory for ctx.\n"); 327 | readmem(ctx_addr, KVADDR, ctx_data, size, "show_ctx_info ctx", 328 | FAULT_ON_ERROR); 329 | 330 | spu_addr = *(ulong *)(ctx_data + CBE_OFFSET(spu_context, spu)); 331 | 332 | if (spu_addr) { 333 | size = CBE_SIZE(spu); 334 | spu_data = GETBUF(size); 335 | if (!spu_data) 336 | error(FATAL, "Couldn't allocate memory for spu.\n"); 337 | readmem(spu_addr, KVADDR, spu_data, size, "show_ctx_info spu", 338 | FAULT_ON_ERROR); 339 | 340 | for (i = 0; i < NR_SPUS; i++) { 341 | readmem(spu[i], KVADDR, &addr, sizeof(addr), "spu addr", 342 | FAULT_ON_ERROR); 343 | if (addr == spu_addr) 344 | info = i; 345 | } 346 | } 347 | 348 | fprintf(fp,"\nDumping context fields for spu_context %lx:\n", ctx_addr); 349 | print_ctx_info(ctx_data, spu_data, info); 350 | 351 | FREEBUF(ctx_data); 352 | if (spu_addr) 353 | FREEBUF(spu_data); 354 | } 355 | 356 | /* 357 | * Pass ctx and respective spu data to show_ctx_info for all the contexts 358 | * running and on the runqueue. 359 | */ 360 | void show_ctx_info_all(void) 361 | { 362 | int i, j, cnt; 363 | long prio_size, prio_runq_off, ctx_rq_off, jump, offset, ctxs_size; 364 | char *u_spu_prio; 365 | ulong spu_prio_addr, k_spu_prio, kvaddr, uvaddr, spu_addr, ctx_addr; 366 | ulong *ctxs; 367 | ulong list_head[2]; 368 | struct list_data list_data, *ld; 369 | 370 | /* Walking SPUs */ 371 | for (i = 0; i < NR_SPUS; i++) { 372 | spu_addr = get_spu_addr(spu[i]) + CBE_OFFSET(spu, ctx); 373 | readmem(spu_addr, KVADDR, &ctx_addr, sizeof(ctx_addr), 374 | "show_ctx_info_all", FAULT_ON_ERROR); 375 | if (ctx_addr) 376 | show_ctx_info(ctx_addr); 377 | } 378 | 379 | /* Walking SPU runqueue */ 380 | if (symbol_exists("spu_prio")) { 381 | spu_prio_addr = symbol_value("spu_prio"); 382 | readmem(spu_prio_addr, KVADDR, &k_spu_prio, sizeof(k_spu_prio), 383 | "runq_array", FAULT_ON_ERROR); 384 | } 385 | else 386 | error(FATAL, "Could not get SPU run queue data.\n"); 387 | 388 | jump = CBE_SIZE(list_head); 389 | prio_runq_off = CBE_OFFSET(spu_prio_array, runq); 390 | ctx_rq_off = CBE_OFFSET(spu_context, rq); 391 | prio_size = CBE_SIZE(spu_prio_array); 392 | 393 | u_spu_prio = (char *)GETBUF(prio_size); 394 | readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, "get_runq_ctxs", 395 | FAULT_ON_ERROR); 396 | 397 | for (i = 0; i < MAX_PRIO; i++) { 398 | offset = prio_runq_off + i * jump; 399 | kvaddr = k_spu_prio + offset; 400 | uvaddr = (ulong)u_spu_prio + offset; 401 | 402 | BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2); 403 | 404 | if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr)) 405 | continue; 406 | 407 | ld = &list_data; 408 | 409 | BZERO(ld, sizeof(struct list_data)); 410 | ld->start = list_head[0]; 411 | ld->list_head_offset = ctx_rq_off; 412 | ld->flags |= RETURN_ON_LIST_ERROR; 413 | ld->end = kvaddr; 414 | 415 | hq_open(); 416 | cnt = do_list(ld); 417 | if (cnt == -1) { 418 | hq_close(); 419 | FREEBUF(u_spu_prio); 420 | error(FATAL, "Couldn't walk the list.\n"); 421 | } 422 | 423 | ctxs_size = cnt * sizeof(ulong); 424 | ctxs = (ulong *)GETBUF(ctxs_size); 425 | 426 | BZERO(ctxs, ctxs_size); 427 | cnt = retrieve_list(ctxs, cnt); 428 | hq_close(); 429 | 430 | for (j = 0; j < cnt; j++) 431 | show_ctx_info(ctxs[j]); 432 | 433 | FREEBUF(ctxs); 434 | } 435 | 436 | FREEBUF(u_spu_prio); 437 | } 438 | 439 | /* 440 | * Tries to discover the meaning of string and to find the referred context 441 | */ 442 | int str_to_spuctx(char *string, ulong *value, ulong *spu_ctx) 443 | { 444 | char *s, *u_spu_prio; 445 | ulong dvalue, hvalue, addr, ctx; 446 | ulong k_spu_prio, spu_prio_addr, kvaddr, uvaddr; 447 | int type, pid, i, j, cnt; 448 | long prio_size, prio_runq_off, ctx_rq_off, jump, offset, ctxs_size; 449 | ulong *ctxs; 450 | ulong list_head[2]; 451 | struct list_data list_data, *ld; 452 | 453 | if (string == NULL) { 454 | error(INFO, "%s: received NULL string.\n", __FUNCTION__); 455 | return STR_SPU_INVALID; 456 | } 457 | 458 | s = string; 459 | dvalue = hvalue = BADADDR; 460 | 461 | if (decimal(s, 0)) 462 | dvalue = dtol(s, RETURN_ON_ERROR, NULL); 463 | 464 | if (hexadecimal(s, 0)) { 465 | if (STRNEQ(s, "0x") || STRNEQ(s, "0X")) 466 | s += 2; 467 | if (strlen(s) <= MAX_HEXADDR_STRLEN) 468 | hvalue = htol(s, RETURN_ON_ERROR, NULL); 469 | } 470 | 471 | type = STR_SPU_INVALID; 472 | 473 | if (dvalue != BADADDR) { 474 | /* Testing for SPU ID */ 475 | if ((dvalue >= 0) && (dvalue < NR_SPUS)) { 476 | addr = get_spu_addr(spu[dvalue]) + CBE_OFFSET(spu, ctx); 477 | readmem(addr, KVADDR, &ctx, sizeof(ctx), 478 | "str_to_spuctx ID", FAULT_ON_ERROR); 479 | 480 | type = STR_SPU_ID; 481 | *value = dvalue; 482 | *spu_ctx = ctx; 483 | return type; 484 | } 485 | else { 486 | /* Testing for PID */ 487 | for (i = 0; i < NR_SPUS; i++) { 488 | addr = get_spu_addr(spu[i]) + 489 | CBE_OFFSET(spu, pid); 490 | readmem(addr, KVADDR, &pid, sizeof(pid), 491 | "str_to_spuctx PID", FAULT_ON_ERROR); 492 | 493 | if (dvalue == pid) { 494 | addr = get_spu_addr(spu[i]) + 495 | CBE_OFFSET(spu, ctx); 496 | readmem(addr, KVADDR, &ctx, sizeof(ctx), 497 | "str_to_spuctx PID ctx", 498 | FAULT_ON_ERROR); 499 | 500 | type = STR_SPU_PID; 501 | *value = dvalue; 502 | *spu_ctx = ctx; 503 | return type; 504 | } 505 | } 506 | } 507 | } 508 | 509 | if (hvalue != BADADDR) { 510 | /* Testing for spuctx address on SPUs */ 511 | for (i = 0; i < NR_SPUS; i++) { 512 | addr = get_spu_addr(spu[i]) + CBE_OFFSET(spu, ctx); 513 | readmem(addr, KVADDR, &ctx, sizeof(ctx), 514 | "str_to_spuctx CTX", FAULT_ON_ERROR); 515 | 516 | if (hvalue == ctx) { 517 | type = STR_SPU_CTX_ADDR; 518 | *value = hvalue; 519 | *spu_ctx = ctx; 520 | return type; 521 | } 522 | } 523 | 524 | /* Testing for spuctx address on SPU runqueue */ 525 | if (symbol_exists("spu_prio")) { 526 | spu_prio_addr = symbol_value("spu_prio"); 527 | readmem(spu_prio_addr, KVADDR, &k_spu_prio, 528 | sizeof(k_spu_prio), "runq_array", FAULT_ON_ERROR); 529 | } 530 | else 531 | error(FATAL, "Could not get SPU run queue data.\n"); 532 | 533 | jump = CBE_SIZE(list_head); 534 | prio_runq_off = CBE_OFFSET(spu_prio_array, runq); 535 | ctx_rq_off = CBE_OFFSET(spu_context, rq); 536 | prio_size = CBE_SIZE(spu_prio_array); 537 | 538 | u_spu_prio = (char *)GETBUF(prio_size); 539 | readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, 540 | "get_runq_ctxs", FAULT_ON_ERROR); 541 | 542 | for (i = 0; i < MAX_PRIO; i++) { 543 | offset = prio_runq_off + i * jump; 544 | kvaddr = k_spu_prio + offset; 545 | uvaddr = (ulong)u_spu_prio + offset; 546 | 547 | BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2); 548 | 549 | if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr)) 550 | continue; 551 | 552 | ld = &list_data; 553 | 554 | BZERO(ld, sizeof(struct list_data)); 555 | ld->start = list_head[0]; 556 | ld->list_head_offset = ctx_rq_off; 557 | ld->flags |= RETURN_ON_LIST_ERROR; 558 | ld->end = kvaddr; 559 | 560 | hq_open(); 561 | cnt = do_list(ld); 562 | if (cnt == -1) { 563 | hq_close(); 564 | FREEBUF(u_spu_prio); 565 | error(FATAL, "Couldn't walk the list.\n"); 566 | } 567 | 568 | ctxs_size = cnt * sizeof(ulong); 569 | ctxs = (ulong *)GETBUF(ctxs_size); 570 | 571 | BZERO(ctxs, ctxs_size); 572 | cnt = retrieve_list(ctxs, cnt); 573 | hq_close(); 574 | 575 | for (j = 0; j < cnt; j++) 576 | if (hvalue == ctxs[j]) { 577 | type = STR_SPU_CTX_ADDR; 578 | *value = hvalue; 579 | *spu_ctx = ctxs[j]; 580 | FREEBUF(u_spu_prio); 581 | FREEBUF(ctxs); 582 | return type; 583 | } 584 | 585 | FREEBUF(ctxs); 586 | } 587 | 588 | FREEBUF(u_spu_prio); 589 | } 590 | 591 | return type; 592 | } 593 | 594 | /* 595 | * spuctx command stands for "spu context" and shows the context fields 596 | * for the spu or respective struct address passed as an argument 597 | */ 598 | void cmd_spuctx() 599 | { 600 | int i, c, cnt; 601 | ulong value, ctx; 602 | ulong *ctxlist; 603 | 604 | while ((c = getopt(argcnt, args, "")) != EOF) { 605 | switch(c) 606 | { 607 | default: 608 | argerrs++; 609 | break; 610 | } 611 | } 612 | 613 | if (argerrs) 614 | cmd_usage(pc->curcmd, SYNOPSIS); 615 | 616 | if (!args[optind]) { 617 | show_ctx_info_all(); 618 | return; 619 | } 620 | 621 | cnt = 0; 622 | ctxlist = (ulong *)GETBUF((MAXARGS+NR_CPUS)*sizeof(ctx)); 623 | 624 | while (args[optind]) { 625 | if (IS_A_NUMBER(args[optind])) { 626 | switch (str_to_spuctx(args[optind], &value, &ctx)) 627 | { 628 | case STR_SPU_ID: 629 | case STR_SPU_PID: 630 | case STR_SPU_CTX_ADDR: 631 | ctxlist[cnt++] = ctx; 632 | break; 633 | 634 | case STR_SPU_INVALID: 635 | error(INFO, "Invalid SPU reference: %s\n", 636 | args[optind]); 637 | break; 638 | } 639 | } 640 | else 641 | error(INFO, "Invalid SPU reference: %s\n", 642 | args[optind]); 643 | optind++; 644 | } 645 | 646 | if (cnt == 0) 647 | error(INFO, "No valid ID, PID or context address.\n"); 648 | else 649 | for (i = 0; i < cnt; i++) 650 | show_ctx_info(ctxlist[i]); 651 | 652 | FREEBUF(ctxlist); 653 | } 654 | 655 | 656 | /***************************************************************************** 657 | * SPUS COMMAND 658 | */ 659 | 660 | void print_spu_header(ulong spu_info) 661 | { 662 | int id, pid, size, state; 663 | uint status; 664 | ulong ctx_addr, spu_addr; 665 | char *spu_data; 666 | const char *state_str; 667 | 668 | if (spu_info) { 669 | readmem(spu_info + CBE_OFFSET(crash_spu_info, 670 | saved_spu_status_R), KVADDR, &status, sizeof(status), 671 | "print_spu_header: get status", FAULT_ON_ERROR); 672 | 673 | size = CBE_SIZE(spu); 674 | spu_data = GETBUF(size); 675 | spu_addr = get_spu_addr(spu_info); 676 | readmem(spu_addr, KVADDR, spu_data, size, "SPU struct", 677 | FAULT_ON_ERROR); 678 | 679 | id = *(int *)(spu_data + CBE_OFFSET(spu, number)); 680 | pid = *(int *)(spu_data + CBE_OFFSET(spu, pid)); 681 | ctx_addr = *(ulong *)(spu_data + CBE_OFFSET(spu, ctx)); 682 | 683 | if (ctx_addr) { 684 | readmem(ctx_addr + CBE_OFFSET(spu_context, state), 685 | KVADDR, &state, sizeof(state), 686 | "print_spu_header get ctxstate", FAULT_ON_ERROR); 687 | 688 | switch (state) { 689 | case 0: /* SPU_STATE_RUNNABLE */ 690 | state_str = "RUNNABLE"; 691 | break; 692 | 693 | case 1: /* SPU_STATE_SAVED */ 694 | state_str = " SAVED "; 695 | break; 696 | 697 | default: 698 | state_str = "UNKNOWN "; 699 | } 700 | } 701 | else { 702 | state_str = " - "; 703 | } 704 | 705 | fprintf(fp, "%2d %16lx %s %16lx %s %5d\n", id, 706 | spu_addr, 707 | status % 2 ? "RUNNING" : (ctx_addr ? "STOPPED" : " IDLE "), 708 | ctx_addr, state_str, pid); 709 | 710 | FREEBUF(spu_data); 711 | } 712 | } 713 | 714 | void print_node_header(int node) 715 | { 716 | fprintf(fp, "\n"); 717 | fprintf(fp, "NODE %i:\n", node); 718 | fprintf(fp, "ID SPUADDR SPUSTATUS CTXADDR \ 719 | CTXSTATE PID \n"); 720 | } 721 | 722 | void show_spus() 723 | { 724 | int i, j, nr_cpus, show_header, node; 725 | ulong spu_addr, addr; 726 | long offset; 727 | 728 | nr_cpus = kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS; 729 | 730 | for (i = 0; i < nr_cpus; i++) { 731 | show_header = TRUE; 732 | 733 | for (j = 0; j < NR_SPUS; j++) { 734 | addr = spu[j] + CBE_OFFSET(crash_spu_info, spu); 735 | readmem(addr, KVADDR, &spu_addr, sizeof(spu_addr), 736 | "show_spus spu_addr", FAULT_ON_ERROR); 737 | 738 | offset = CBE_OFFSET(spu, node); 739 | if (offset == -1) 740 | error(FATAL, "Couldn't get spu.node offset.\n"); 741 | 742 | spu_addr += offset; 743 | readmem(spu_addr, KVADDR, &node, sizeof(node), 744 | "show_spus node", FAULT_ON_ERROR); 745 | 746 | if (node == i) { 747 | if (show_header) { 748 | print_node_header(node); 749 | show_header = FALSE; 750 | } 751 | 752 | print_spu_header(spu[j]); 753 | } 754 | } 755 | } 756 | } 757 | 758 | /* 759 | * spus stands for "spu state" and shows what contexts are running in what 760 | * SPU. 761 | */ 762 | void cmd_spus() 763 | { 764 | int c; 765 | 766 | while ((c = getopt(argcnt, args, "")) != EOF) { 767 | switch(c) 768 | { 769 | default: 770 | argerrs++; 771 | break; 772 | } 773 | } 774 | 775 | if (argerrs || args[optind]) 776 | cmd_usage(pc->curcmd, SYNOPSIS); 777 | else 778 | show_spus(); 779 | } 780 | 781 | 782 | /***************************************************************************** 783 | * SPURQ COMMAND 784 | */ 785 | 786 | /* 787 | * Prints the addresses of SPU contexts on the SPU runqueue. 788 | */ 789 | void dump_spu_runq(ulong k_spu_prio) 790 | { 791 | int i, cnt; 792 | long prio_size, prio_runq_off, ctx_rq_off, jump, offset; 793 | char *u_spu_prio; 794 | ulong kvaddr, uvaddr; 795 | ulong list_head[2]; 796 | struct list_data list_data, *ld; 797 | 798 | prio_runq_off = CBE_OFFSET(spu_prio_array, runq); 799 | jump = CBE_SIZE(list_head); 800 | ctx_rq_off = CBE_OFFSET(spu_context, rq); 801 | prio_size = CBE_SIZE(spu_prio_array); 802 | 803 | u_spu_prio = (char *)GETBUF(prio_size); 804 | readmem(k_spu_prio, KVADDR, u_spu_prio, prio_size, "get_runq_ctxs", 805 | FAULT_ON_ERROR); 806 | 807 | for (i = 0; i < MAX_PRIO; i++) { 808 | offset = prio_runq_off + (i * jump); 809 | kvaddr = k_spu_prio + offset; 810 | uvaddr = (ulong)u_spu_prio + offset; 811 | 812 | BCOPY((char *)uvaddr, (char *)&list_head[0], sizeof(ulong)*2); 813 | 814 | if ((list_head[0] == kvaddr) && (list_head[1] == kvaddr)) 815 | continue; 816 | 817 | fprintf(fp, "PRIO[%i]:\n", i); 818 | 819 | ld = &list_data; 820 | 821 | BZERO(ld, sizeof(struct list_data)); 822 | ld->start = list_head[0]; 823 | ld->list_head_offset = ctx_rq_off; 824 | ld->flags |= VERBOSE; 825 | ld->end = kvaddr; 826 | 827 | hq_open(); 828 | cnt = do_list(ld); 829 | hq_close(); 830 | 831 | if (cnt == -1) { 832 | FREEBUF(u_spu_prio); 833 | error(FATAL, "Couldn't walk runqueue[%i].\n", i); 834 | } 835 | } 836 | 837 | FREEBUF(u_spu_prio); 838 | } 839 | 840 | /* 841 | * spurq stands for "spu run queue" and shows info about the contexts 842 | * that are on the SPU run queue 843 | */ 844 | void cmd_spurq() 845 | { 846 | int c; 847 | ulong spu_prio_addr, spu_prio; 848 | long size; 849 | 850 | while ((c = getopt(argcnt, args, "")) != EOF) { 851 | switch(c) 852 | { 853 | default: 854 | argerrs++; 855 | break; 856 | } 857 | } 858 | 859 | if (argerrs || args[optind]) 860 | cmd_usage(pc->curcmd, SYNOPSIS); 861 | else { 862 | if (symbol_exists("spu_prio")) { 863 | spu_prio_addr = symbol_value("spu_prio"); 864 | readmem(spu_prio_addr, KVADDR, &spu_prio, 865 | sizeof(spu_prio), "runq_array", FAULT_ON_ERROR); 866 | dump_spu_runq(spu_prio); 867 | } else 868 | error(FATAL, "Could not get SPU run queue data.\n"); 869 | } 870 | } 871 | 872 | /********************************************************************************** 873 | * HELP TEXTS 874 | */ 875 | 876 | char *help_spuctx[] = { 877 | SPUCTX_CMD_NAME, 878 | "shows complete info about a SPU context", 879 | "[ID | PID | CTXADDR] ...", 880 | 881 | " This command shows the fields of spu and spu_context structs for a ", 882 | "SPU context, including debug info specially saved by kdump after a ", 883 | "crash.", 884 | " By default, it shows info about all the contexts created by the ", 885 | "system, including ones in the runqueue. To specify the contexts of ", 886 | "interest, the PID of the controller task, ID of the SPU which the ", 887 | "context is bound to or the address of spu_context struct can be used ", 888 | "as parameters.", 889 | "\nEXAMPLES", 890 | "\n Show info about contexts bound to SPUs 0 and 7, and the one ", 891 | "controlled by thread whose PID is 1524:", 892 | "\n crash> spuctx 0 7 1524", 893 | "\n Dumping context fields for spu_context c00000003dcbdd80:", 894 | " state = 0", 895 | " prio = 120", 896 | " local_store = 0xc000000039055840", 897 | " rq = 0xc00000003dcbe720", 898 | " node = 0", 899 | " number = 0", 900 | " pid = 1524", 901 | " name = spe", 902 | " slb_replace = 0", 903 | " mm = 0xc0000000005dd700", 904 | " timestamp = 0x10000566f", 905 | " class_0_pending = 0", 906 | " problem = 0xd000080080210000", 907 | " priv2 = 0xd000080080230000", 908 | " flags = 0", 909 | " saved_mfc_sr1_RW = 59", 910 | " saved_mfc_dar = 14987979559889612800", 911 | " saved_mfc_dsisr = 0", 912 | " saved_spu_runcntl_RW = 1", 913 | " saved_spu_status_R = 1", 914 | " saved_spu_npc_RW = 0", 915 | "\n Dumping context fields for spu_context c00000003dec4e80:", 916 | " state = 0", 917 | " prio = 120", 918 | " local_store = 0xc00000003b1cea40", 919 | " rq = 0xc00000003dec5820", 920 | " node = 0", 921 | " number = 7", 922 | " pid = 1538", 923 | " name = spe", 924 | " slb_replace = 0", 925 | " mm = 0xc0000000005d2b80", 926 | " timestamp = 0x10000566f", 927 | " class_0_pending = 0", 928 | " problem = 0xd000080080600000", 929 | " priv2 = 0xd000080080620000", 930 | " flags = 0", 931 | " saved_mfc_sr1_RW = 59", 932 | " saved_mfc_dar = 14987979559896297472", 933 | " saved_mfc_dsisr = 0", 934 | " saved_spu_runcntl_RW = 1", 935 | " saved_spu_status_R = 1", 936 | " saved_spu_npc_RW = 0", 937 | "\n Dumping context fields for spu_context c00000003dcbdd80:", 938 | " state = 0", 939 | " prio = 120", 940 | " local_store = 0xc000000039055840", 941 | " rq = 0xc00000003dcbe720", 942 | " node = 0", 943 | " number = 0", 944 | " pid = 1524", 945 | " name = spe", 946 | " slb_replace = 0", 947 | " mm = 0xc0000000005dd700", 948 | " timestamp = 0x10000566f", 949 | " class_0_pending = 0", 950 | " problem = 0xd000080080210000", 951 | " priv2 = 0xd000080080230000", 952 | " flags = 0", 953 | " saved_mfc_sr1_RW = 59", 954 | " saved_mfc_dar = 14987979559889612800", 955 | " saved_mfc_dsisr = 0", 956 | " saved_spu_runcntl_RW = 1", 957 | " saved_spu_status_R = 1", 958 | " saved_spu_npc_RW = 0", 959 | 960 | "\n Show info about the context whose struct spu_context address is ", 961 | "0xc00000003dcbed80:\n", 962 | "crash> spuctx 0x00000003dcbed80", 963 | " ...", 964 | NULL 965 | }; 966 | 967 | 968 | char *help_spus[] = { 969 | SPUS_CMD_NAME, 970 | "shows how contexts are scheduled in the SPUs", 971 | " ", 972 | " This command shows how the contexts are scheduled in the SPUs of ", 973 | "each node. It provides info about the spu address, SPU status, the ", 974 | "spu_context address, context state and spu_context addresses and the ", 975 | "PID of controller thread for each SPU.", 976 | "\nEXAMPLE", 977 | " Show SPU contexts:", 978 | "\n crash> spus", 979 | " NODE 0:", 980 | " ID SPUADDR SPUSTATUS CTXADDR CTXSTATE PID ", 981 | " 0 c000000001fac880 RUNNING c00000003dcbdd80 RUNNABLE 1524", 982 | " 1 c000000001faca80 RUNNING c00000003bf34e00 RUNNABLE 1528", 983 | " 2 c000000001facc80 RUNNING c00000003bf30e00 RUNNABLE 1525", 984 | " 3 c000000001face80 RUNNING c000000039421d00 RUNNABLE 1533", 985 | " 4 c00000003ee29080 RUNNING c00000003dec3e80 RUNNABLE 1534", 986 | " 5 c00000003ee28e80 RUNNING c00000003bf32e00 RUNNABLE 1526", 987 | " 6 c00000003ee28c80 STOPPED c000000039e5e700 SAVED 1522", 988 | " 7 c00000003ee2e080 RUNNING c00000003dec4e80 RUNNABLE 1538", 989 | "\n NODE 1:", 990 | " ID SPUADDR SPUSTATUS CTXADDR CTXSTATE PID ", 991 | " 8 c00000003ee2de80 RUNNING c00000003dcbed80 RUNNABLE 1529", 992 | " 9 c00000003ee2dc80 RUNNING c00000003bf39e00 RUNNABLE 1535", 993 | " 10 c00000003ee2da80 RUNNING c00000003bf3be00 RUNNABLE 1521", 994 | " 11 c000000001fad080 RUNNING c000000039420d00 RUNNABLE 1532", 995 | " 12 c000000001fad280 RUNNING c00000003bf3ee00 RUNNABLE 1536", 996 | " 13 c000000001fad480 RUNNING c00000003dec2e80 RUNNABLE 1539", 997 | " 14 c000000001fad680 RUNNING c00000003bf3ce00 RUNNABLE 1537", 998 | " 15 c000000001fad880 RUNNING c00000003dec6e80 RUNNABLE 1540", 999 | NULL 1000 | }; 1001 | 1002 | 1003 | char *help_spurq[] = { 1004 | SPURQ_CMD_NAME, 1005 | "shows contexts on the SPU runqueue", 1006 | " ", 1007 | " This command shows info about all contexts waiting for execution ", 1008 | "in the SPU runqueue. No parameter is needed.", 1009 | "\nEXAMPLE", 1010 | " Show SPU runqueue:", 1011 | "\n crash> spurq", 1012 | " PRIO[120]:", 1013 | " c000000000fd7380", 1014 | " c00000003bf31e00", 1015 | " PRIO[125]:", 1016 | " c000000039422d00", 1017 | " c00000000181eb80", 1018 | NULL 1019 | }; 1020 | 1021 | -------------------------------------------------------------------------------- /swap_usage.c: -------------------------------------------------------------------------------- 1 | 2 | /* swap-usage.c - Check actual swap consumption for each process 3 | * 4 | * Aaron Tomlin 5 | * 6 | * Copyright (C) 2013 Red Hat, Inc. All rights reserved. 7 | * 8 | * This program is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2 of the License, or 11 | * (at your option) any later version. 12 | * 13 | * This program is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | */ 18 | 19 | #include "defs.h" 20 | 21 | #define DISPLAY_KB (0x2) 22 | #define DISPLAY_TG (0x4) 23 | 24 | #ifdef ARM 25 | #define _PAGE_FILE (1 << 2) 26 | #endif /* ARM */ 27 | 28 | #ifdef X86 29 | #define _PAGE_FILE (1 << 6) /* _PAGE_BIT_DIRTY */ 30 | #endif /* X86 */ 31 | 32 | #ifdef X86_64 33 | /* set in defs.h already */ 34 | #define _PAGE_FILE 0x040 35 | #endif /* X86_64 */ 36 | 37 | #ifdef ALPHA 38 | #define _PAGE_FILE 0x80000 /* set:pagecache, unset:swap */ 39 | #endif /* ALPHA */ 40 | 41 | #ifdef IA64 42 | #define _PAGE_FILE (1 << 1) /* see swap & file pte remarks below */ 43 | #endif /* IA64 */ 44 | 45 | #ifdef S390 46 | #define _PAGE_FILE 0x601 47 | #endif /* S390 */ 48 | 49 | #define pte_file_present(pte) (pte & _PAGE_FILE) 50 | 51 | #define MEMBER_FOUND 1 52 | #define MEMBER_NOT_FOUND 0 53 | #define PRINT_HEADER() \ 54 | fprintf(fp, \ 55 | "PID SWAP COMM\n"); 56 | 57 | int _init(void); 58 | int _fini(void); 59 | 60 | void cmd_pswap(void); 61 | char *help_pswap[]; 62 | 63 | static unsigned int swap_usage_offset; 64 | 65 | static struct command_table_entry command_table[] = { 66 | { "pswap", cmd_pswap, help_pswap, 0 }, 67 | { NULL } 68 | }; 69 | 70 | int 71 | _init(void) 72 | { 73 | register_extension(command_table); 74 | return 1; 75 | } 76 | 77 | int 78 | _fini(void) 79 | { 80 | return 1; 81 | } 82 | 83 | void 84 | show_swap_usage(struct task_context *tc, ulong exists, ulong flag) 85 | { 86 | struct task_mem_usage task_mem_usage, *tm; 87 | tm = &task_mem_usage; 88 | get_task_mem_usage(tc->task, tm); 89 | physaddr_t paddr; 90 | ulong mm; 91 | ulong vma; 92 | ulong vm_start; 93 | ulong vm_end; 94 | ulong vm_next; 95 | ulong swap_usage = 0; 96 | 97 | readmem(tc->task + OFFSET(task_struct_mm), KVADDR, &mm, 98 | sizeof(void *), "mm_struct mm", FAULT_ON_ERROR); 99 | 100 | if (!mm) 101 | return; 102 | 103 | switch (exists) { 104 | case MEMBER_FOUND: 105 | 106 | readmem((mm + swap_usage_offset), KVADDR, &swap_usage, 107 | sizeof(void *), "mm_counter_t", FAULT_ON_ERROR); 108 | 109 | break; 110 | 111 | case MEMBER_NOT_FOUND: 112 | default: 113 | 114 | readmem(mm + OFFSET(mm_struct_mmap), KVADDR, &vma, 115 | sizeof(void *), "mm_struct mmap", FAULT_ON_ERROR); 116 | 117 | for (; vma; vma = vm_next) { 118 | 119 | readmem(vma + OFFSET(vm_area_struct_vm_start), KVADDR, &vm_start, 120 | sizeof(void *), "vm_area_struct vm_start", FAULT_ON_ERROR); 121 | 122 | readmem(vma + OFFSET(vm_area_struct_vm_end), KVADDR, &vm_end, 123 | sizeof(void *), "vm_area_struct vm_end", FAULT_ON_ERROR); 124 | 125 | readmem(vma + OFFSET(vm_area_struct_vm_next), KVADDR, &vm_next, 126 | sizeof(void *), "vm_area_struct vm_next", FAULT_ON_ERROR); 127 | 128 | while (vm_start < vm_end) { 129 | if (!uvtop(tc, vm_start, &paddr, 0)) { 130 | 131 | if (paddr && !(pte_file_present(paddr))) { 132 | swap_usage++; 133 | } 134 | } 135 | vm_start += PAGESIZE(); 136 | } 137 | } 138 | } 139 | if (flag & DISPLAY_KB) 140 | swap_usage <<= (PAGESHIFT()-10); 141 | 142 | fprintf(fp, "%3ld %6ld%s%5s\n", tc->pid, swap_usage, 143 | (flag & DISPLAY_KB) ? "k\t" : "\t", tc->comm); 144 | } 145 | 146 | 147 | void 148 | cmd_pswap(void) 149 | { 150 | struct task_context *tc; 151 | int i; 152 | int c; 153 | ulong value; 154 | ulong flag = 0; 155 | ulong tgid; 156 | int subsequent = 0; 157 | ulong exists = MEMBER_NOT_FOUND; 158 | 159 | if (MEMBER_EXISTS("mm_struct", "_swap_usage")) { 160 | swap_usage_offset = MEMBER_OFFSET("mm_struct", "_swap_usage"); 161 | exists = MEMBER_FOUND; 162 | } 163 | 164 | while ((c = getopt(argcnt, args, "kG")) != EOF) { 165 | switch (c) { 166 | case 'k': 167 | flag |= DISPLAY_KB; 168 | break; 169 | case 'G': 170 | flag |= DISPLAY_TG; 171 | break; 172 | default: 173 | argerrs++; 174 | break; 175 | } 176 | } 177 | 178 | if (argerrs) 179 | cmd_usage(pc->curcmd, SYNOPSIS); 180 | 181 | if (!args[optind]) { 182 | PRINT_HEADER(); 183 | tc = FIRST_CONTEXT(); 184 | for (i = 0; i < RUNNING_TASKS(); i++, tc++) { 185 | if (!is_kernel_thread(tc->task)) { 186 | if (flag & DISPLAY_TG) { 187 | tgid = task_tgid(tc->task); 188 | if (tc->pid != tgid) 189 | continue; 190 | tc = tgid_to_context(tgid); 191 | } 192 | show_swap_usage(tc, exists, flag); 193 | } 194 | } 195 | return; 196 | } 197 | 198 | PRINT_HEADER(); 199 | while (args[optind]) { 200 | switch (str_to_context(args[optind], &value, &tc)) { 201 | case STR_PID: 202 | for (tc = pid_to_context(value); tc; tc = tc->tc_next) { 203 | if (!is_kernel_thread(tc->task)) { 204 | if (flag & DISPLAY_TG) { 205 | tgid = task_tgid(tc->task); 206 | if (tc->pid != tgid) 207 | continue; 208 | tc = tgid_to_context(tgid); 209 | } 210 | show_swap_usage(tc, exists, flag); 211 | } else { 212 | error(INFO, "only specify a user task or pid: %s\n", 213 | args[optind]); 214 | } 215 | } 216 | break; 217 | 218 | case STR_TASK: 219 | for (; tc; tc = tc->tc_next) { 220 | if (!is_kernel_thread(tc->task)) { 221 | if (flag & DISPLAY_TG) { 222 | tgid = task_tgid(tc->task); 223 | if (tc->pid != tgid) 224 | continue; 225 | tc = tgid_to_context(tgid); 226 | } 227 | show_swap_usage(tc, exists, flag); 228 | } else { 229 | error(INFO, "only specify a user task or pid: %s\n", 230 | args[optind]); 231 | } 232 | } 233 | break; 234 | 235 | case STR_INVALID: 236 | error(INFO, "invalid task or pid value: %s\n", 237 | args[optind]); 238 | break; 239 | } 240 | 241 | subsequent++; 242 | optind++; 243 | } 244 | } 245 | 246 | char *help_pswap[] = { 247 | "pswap", 248 | "Returns the actual swap consumption of a user process", 249 | "[-k -G] [pid | taskp]", 250 | 251 | " This command obtains the swap consumption (in pages) of a user process.", 252 | " The process list may be restricted with the following options:\n", 253 | " -k print in kilobytes.\n" 254 | " -G show only the thread group leader in a thread group.\n" 255 | " ", 256 | " If no arguments are specified, every user process will be checked.", 257 | " Supported on ARM, X86, X86_64, ALPHA, IA64 and S390 only.", 258 | "\nEXAMPLE", 259 | " Show the swap consumption for pid 1232, 1353 and 2275:\n", 260 | " crash> pswap 1232 1353 2275", 261 | " PID SWAP COMM", 262 | " 1232 34 auditd", 263 | " 1353 526 vi", 264 | " 2275 30237 gnome-shell", 265 | " crash>", 266 | " ", 267 | " Show the swap consumption for thread group leaders only:\n", 268 | " crash> pswap -G", 269 | " PID SWAP COMM", 270 | " 469 71 zsh", 271 | " 599 37 systemd-journal", 272 | " 608 298 lvmetad", 273 | " 637 428 systemd-udevd", 274 | " 822 77 auditd", 275 | " 836 26 audispd", 276 | " 838 39 sedispatch", 277 | " 842 23 alsactl", 278 | " 844 44 bluetoothd", 279 | " 851 46 rtkit-daemon", 280 | " 852 59 accounts-daemon", 281 | " 855 23 avahi-daemon", 282 | " 857 96 rsyslogd", 283 | " 858 179 restorecond", 284 | " 859 144 smartd", 285 | " 862 33 irqbalance", 286 | " 867 41 systemd-logind", 287 | " 868 37 dbus-daemon", 288 | " crash>", 289 | NULL 290 | }; 291 | -------------------------------------------------------------------------------- /vz.c: -------------------------------------------------------------------------------- 1 | /* vz.c - crash extension for OpenVZ containers 2 | * 3 | * Copyright (C) 2015 Vasily Averin 4 | * Copyright (C) 2015 Parallels IP Holdings GmbH. 5 | * 6 | * This program is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 2 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | */ 16 | 17 | #include "defs.h" 18 | 19 | #define VZ_MEMBER_OFFSET_INIT(X,Y,Z) (vz_offset_table.X=MEMBER_OFFSET(Y,Z)) 20 | #define VZ_OFFSET(X) (OFFSET_verify(vz_offset_table.X, (char *)__FUNCTION__, __FILE__, __LINE__, #X)) 21 | #define VZ_INVALID_MEMBER(X) (vz_offset_table.X == INVALID_OFFSET) 22 | #define VZ_VALID_MEMBER(X) (vz_offset_table.X >= 0) 23 | #define VZ_INIT 0x1 24 | 25 | struct vz_offset_table { 26 | long task_veinfo; 27 | long veinfo_ve; 28 | long task_ve; 29 | long ve_velist; 30 | long ve_veid; 31 | long ve_nsproxy; 32 | long nsproxy_pidns; 33 | long pidns_init; 34 | }; 35 | 36 | static struct vz_offset_table vz_offset_table = { 0 }; 37 | static int init_flags = 0; 38 | static int id_size; 39 | 40 | void vz_init(void); 41 | void vz_fini(void); 42 | 43 | static ulong 44 | ve_to_task(ulong ve) 45 | { 46 | ulong ns, pidns, task; 47 | 48 | readmem(ve + VZ_OFFSET(ve_nsproxy), KVADDR, &ns, 49 | sizeof(ulong), "ve_struct.ve_ns", FAULT_ON_ERROR); 50 | readmem(ns + VZ_OFFSET(nsproxy_pidns), KVADDR, &pidns, 51 | sizeof(ulong), "nsproxy.ve_pidns", FAULT_ON_ERROR); 52 | readmem(pidns + VZ_OFFSET(pidns_init), KVADDR, &task, 53 | sizeof(ulong), "pid_namespace.child_reaper", FAULT_ON_ERROR); 54 | 55 | return task; 56 | } 57 | 58 | #define VZLIST_HEADER \ 59 | " CTID VE_STRUCT TASK PID COMM\n" 60 | 61 | static void 62 | show_container(ulong ve, ulong ctid, ulong flag) 63 | { 64 | ulong task; 65 | struct task_context *tc; 66 | 67 | task = ve_to_task(ve); 68 | tc = task_to_context(task); 69 | 70 | if (!(flag & PS_NO_HEADER)) 71 | fprintf(fp, VZLIST_HEADER); 72 | 73 | fprintf(fp, "%9ld %16lx %16lx %6ld %s\n", 74 | ctid, ve, tc->task, tc->pid, tc->comm); 75 | return; 76 | } 77 | 78 | static void 79 | show_containers(ulong ctid) 80 | { 81 | struct list_data list_data, *ld; 82 | ulong ve, id, flag = 0; 83 | int i, cnt; 84 | 85 | ld = &list_data; 86 | BZERO(ld, sizeof(struct list_data)); 87 | ld->flags |= LIST_ALLOCATE; 88 | 89 | ld->start = ld->end = symbol_value("ve_list_head"); 90 | ld->list_head_offset = 0; 91 | 92 | cnt = do_list(ld); 93 | 94 | for (i = 1; i < cnt; i++) { 95 | id = 0; 96 | ve = ld->list_ptr[i] - VZ_OFFSET(ve_velist); 97 | readmem(ve + VZ_OFFSET(ve_veid), KVADDR, &id, 98 | id_size, "ve_struct.veid", FAULT_ON_ERROR); 99 | if ((ctid == -1) || ctid == id) { 100 | show_container(ve, id, flag); 101 | flag = PS_NO_HEADER; 102 | } 103 | } 104 | return; 105 | } 106 | 107 | void 108 | cmd_vzlist(void) 109 | { 110 | int c; 111 | ulong ctid = -1; 112 | 113 | while ((c = getopt(argcnt, args, "E:")) != EOF) { 114 | switch(c) 115 | { 116 | case 'E': 117 | ctid = stol(optarg, FAULT_ON_ERROR, NULL); 118 | break; 119 | default: 120 | argerrs++; 121 | break; 122 | } 123 | } 124 | if (argerrs) { 125 | cmd_usage(pc->curcmd, SYNOPSIS); 126 | return; 127 | } 128 | 129 | show_containers(ctid); 130 | return; 131 | } 132 | 133 | char *help_vzlist[] = { 134 | "vzlist", 135 | "shows list of runnig OpenVZ containers", 136 | "[-E CTID]", 137 | "If no argument is entered, command shows IDs of all running containers\n", 138 | " -E Container ID", 139 | "\nEXAMPLES", 140 | "%s> vzlist", 141 | " CTID VE_STRUCT TASK PID COMM", 142 | " 121 ffff8801491e7000 ffff8801493d0ff0 95990 init", 143 | " 123 ffff880135a37000 ffff8803fb0a3470 95924 init", 144 | " 321 ffff88045a778000 ffff880400616300 95923 init", 145 | " 700 ffff88019ddae000 ffff88019ddd4fb0 95882 init", 146 | " 503 ffff88045a84e800 ffff8803c3c782c0 95902 init", 147 | " 122 ffff8804004ea000 ffff88045612afb0 95886 init", 148 | " 600 ffff88016e467000 ffff880459d653f0 95885 init", 149 | " 0 ffffffff81aaa220 ffff88045e530b30 1 init", 150 | NULL 151 | }; 152 | 153 | static ulong 154 | task_to_ctid(ulong task) 155 | { 156 | ulong veinfo, ve, ctid = 0; 157 | 158 | if (VZ_VALID_MEMBER(task_veinfo)) { 159 | veinfo = task + VZ_OFFSET(task_veinfo); 160 | readmem(veinfo + VZ_OFFSET(veinfo_ve), KVADDR, &ve, 161 | sizeof(ulong), "ve_task_info.exec_env", FAULT_ON_ERROR); 162 | } else if (VZ_VALID_MEMBER(task_ve)) { 163 | readmem(task + VZ_OFFSET(task_ve), KVADDR, &ve, 164 | sizeof(ulong), "task_struct.task_ve", FAULT_ON_ERROR); 165 | } else 166 | return 0; 167 | 168 | readmem(ve + VZ_OFFSET(ve_veid), KVADDR, &ctid, 169 | id_size, "ve_struct.veid", FAULT_ON_ERROR); 170 | 171 | return ctid; 172 | } 173 | 174 | static void 175 | show_ctid(struct task_context *tc, ulong flag) 176 | { 177 | ulong ctid; 178 | 179 | ctid = task_to_ctid(tc->task); 180 | if (!(flag & PS_NO_HEADER)) 181 | fprintf(fp, " CTID PID TASK COMM\n"); 182 | 183 | fprintf(fp, "%9ld %6ld %16lx %s\n", 184 | ctid, tc->pid, tc->task, tc->comm); 185 | return; 186 | } 187 | 188 | void 189 | cmd_ctid(void) 190 | { 191 | ulong value, flag = 0; 192 | struct task_context *tc; 193 | int c; 194 | 195 | while ((c = getopt(argcnt, args, "")) != EOF) { 196 | switch(c) 197 | { 198 | default: 199 | cmd_usage(pc->curcmd, SYNOPSIS); 200 | return; 201 | } 202 | } 203 | 204 | if (!args[optind]) { 205 | tc = task_to_context(CURRENT_TASK()); 206 | show_ctid(tc, flag); 207 | } 208 | while (args[optind]) { 209 | switch (str_to_context(args[optind], &value, &tc)) 210 | { 211 | case STR_PID: 212 | case STR_TASK: 213 | break; 214 | case STR_INVALID: 215 | error(INFO, "invalid task or pid value: %s\n", 216 | args[optind]); 217 | default: 218 | argerrs++; 219 | break; 220 | } 221 | if (argerrs) 222 | break; 223 | show_ctid(tc, flag); 224 | flag = PS_NO_HEADER; 225 | optind++; 226 | } 227 | if (argerrs) 228 | cmd_usage(pc->curcmd, SYNOPSIS); 229 | 230 | return; 231 | } 232 | 233 | char *help_ctid[] = { 234 | "ctid", 235 | "shows Container ID of given tasks", 236 | "[task|pid]", 237 | " If no argument is entered, command shows CTID of current task", 238 | "\nEXAMPLES", 239 | "%s> ctid 99583", 240 | " CTID PID TASK COMM", 241 | " 121 99583 ffff880203e56f30 httpd", 242 | NULL 243 | }; 244 | 245 | static void 246 | show_vzps(ulong ctid) 247 | { 248 | struct task_context *tc; 249 | ulong flag; 250 | int i; 251 | 252 | tc = FIRST_CONTEXT(); 253 | for (i = 0; i < RUNNING_TASKS(); i++, tc++) { 254 | ulong id = task_to_ctid(tc->task); 255 | if ((ctid == -1) || (ctid == id)) { 256 | show_ctid(tc, flag); 257 | flag = PS_NO_HEADER; 258 | } 259 | } 260 | return; 261 | } 262 | 263 | void 264 | cmd_vzps(void) 265 | { 266 | ulong ctid = -1; 267 | int c; 268 | 269 | while ((c = getopt(argcnt, args, "E:")) != EOF) { 270 | switch(c) 271 | { 272 | case 'E': 273 | ctid = stol(optarg, FAULT_ON_ERROR, NULL); 274 | break; 275 | default: 276 | argerrs++; 277 | break; 278 | } 279 | } 280 | if (argerrs) 281 | cmd_usage(pc->curcmd, SYNOPSIS); 282 | 283 | show_vzps(ctid); 284 | return; 285 | } 286 | 287 | char *help_vzps[] = { 288 | "vzps", 289 | "shows list of tasks related to specified CTID", 290 | " [ -E CTID]", 291 | " If no argument is entered, command shows CTID for all processes\n", 292 | "\nEXAMPLES", 293 | "%s> vzps -E 121", 294 | " CTID PID TASK COMM", 295 | " 121 95990 ffff8801493d0ff0 init", 296 | " 121 95996 ffff8803c3e3b0f0 kthreadd/121", 297 | " 121 95997 ffff8803cd3aacb0 khelper/121", 298 | " 121 97267 ffff880405e4b2f0 udevd", 299 | " 121 99341 ffff8803c3fd2440 syslogd", 300 | " 121 99404 ffff880405e0c2c0 klogd", 301 | " 121 99424 ffff8803fb0f68c0 sshd", 302 | " 121 99445 ffff8801493d0500 xinetd", 303 | " 121 99557 ffff8804599f9230 sendmail", 304 | " 121 99568 ffff8804591b00c0 sendmail", 305 | " 121 99583 ffff880203e56f30 httpd", 306 | " 121 99594 ffff88016e4e01c0 crond", 307 | " 121 99614 ffff8803fb26cf70 xfs", 308 | " 121 99624 ffff88045a6ce2c0 saslauthd", 309 | " 121 99625 ffff8801ce134ff0 saslauthd", 310 | " 121 248691 ffff88040e2ee9c0 httpd", 311 | NULL 312 | }; 313 | 314 | static struct command_table_entry command_table[] = { 315 | { "vzlist", cmd_vzlist, help_vzlist, 0}, 316 | { "ctid", cmd_ctid, help_ctid, 0}, 317 | { "vzps", cmd_vzps, help_vzps, 0}, 318 | { NULL }, 319 | }; 320 | 321 | void __attribute__((constructor)) 322 | vz_init(void) 323 | { 324 | if (init_flags & VZ_INIT) 325 | return; 326 | 327 | if (!symbol_exists("ve_list_head")) { 328 | fprintf(fp, "vz commands only work on OpenVZ kernels\n"); 329 | return; 330 | } 331 | init_flags |= VZ_INIT; 332 | 333 | BNEG(&vz_offset_table, sizeof(vz_offset_table)); 334 | 335 | if (STRUCT_EXISTS("ve_task_info")) { 336 | /* RHEL6-based OpenVZ */ 337 | VZ_MEMBER_OFFSET_INIT(task_veinfo, 338 | "task_struct", "ve_task_info"); 339 | VZ_MEMBER_OFFSET_INIT(veinfo_ve, "ve_task_info", "exec_env"); 340 | } else { 341 | /* RHEL7-based OpenVZ */ 342 | VZ_MEMBER_OFFSET_INIT(task_ve, "task_struct", "task_ve"); 343 | } 344 | if (STRUCT_EXISTS("ve_struct")) { 345 | VZ_MEMBER_OFFSET_INIT(ve_velist, "ve_struct", "ve_list"); 346 | VZ_MEMBER_OFFSET_INIT(ve_veid, "ve_struct", "veid"); 347 | VZ_MEMBER_OFFSET_INIT(ve_nsproxy, "ve_struct", "ve_ns"); 348 | id_size = MEMBER_SIZE("ve_struct", "veid"); 349 | } 350 | if (STRUCT_EXISTS("nsproxy")) { 351 | VZ_MEMBER_OFFSET_INIT(nsproxy_pidns, "nsproxy", "pid_ns"); 352 | } 353 | if (STRUCT_EXISTS("pid_namespace")) 354 | VZ_MEMBER_OFFSET_INIT(pidns_init, 355 | "pid_namespace", "child_reaper"); 356 | 357 | register_extension(command_table); 358 | } 359 | 360 | void __attribute__((destructor)) 361 | vz_fini(void) { } 362 | --------------------------------------------------------------------------------