dtpstree.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. // DT PS Tree
  2. //
  3. // Douglas Thrift
  4. //
  5. // dtpstree.cpp
  6. /* Copyright 2010 Douglas Thrift
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. #include <cerrno>
  21. #include <climits>
  22. #include <cstdarg>
  23. #include <cstdio>
  24. #include <cstdlib>
  25. #include <cstring>
  26. #include <iostream>
  27. #include <map>
  28. #include <set>
  29. #include <sstream>
  30. #include <string>
  31. #include <vector>
  32. #ifdef __GLIBC__
  33. #include <bsd/stdlib.h>
  34. #else
  35. #include <libgen.h>
  36. #endif
  37. #include <curses.h>
  38. #include <err.h>
  39. #include <fcntl.h>
  40. #include <getopt.h>
  41. #include <kvm.h>
  42. #include <paths.h>
  43. #include <pwd.h>
  44. #include <sys/param.h>
  45. #include <sys/sysctl.h>
  46. #include <sys/user.h>
  47. #include <term.h>
  48. #include <vis.h>
  49. #include "foreach.hpp"
  50. #define DTPSTREE_PROGRAM "dtpstree"
  51. #define DTPSTREE_VERSION "1.0.1"
  52. class Proc;
  53. typedef std::map<pid_t, Proc *> PidMap;
  54. typedef std::multimap<std::string, Proc *> NameMap;
  55. enum Flags
  56. {
  57. Arguments = 0x0001,
  58. Ascii = 0x0002,
  59. Compact = 0x0004,
  60. Highlight = 0x0008,
  61. Vt100 = 0x0010,
  62. ShowKernel = 0x0020,
  63. Long = 0x0040,
  64. NumericSort = 0x0080,
  65. ShowPids = 0x0100,
  66. ShowTitles = 0x0200,
  67. UidChanges = 0x0400,
  68. Unicode = 0x0800,
  69. Version = 0x1000,
  70. Pid = 0x2000,
  71. User = 0x4000
  72. };
  73. enum Escape { None, BoxDrawing, Bright };
  74. struct Segment
  75. {
  76. size_t width_;
  77. Escape escape_;
  78. char *string_;
  79. inline Segment(size_t width, Escape escape, char *string) : width_(width), escape_(escape), string_(string) {}
  80. };
  81. struct Branch
  82. {
  83. std::string indentation_;
  84. bool done_;
  85. inline Branch(size_t indentation) : indentation_(indentation, ' '), done_(false) {}
  86. };
  87. class Tree
  88. {
  89. const uint16_t &flags_;
  90. bool vt100_;
  91. wchar_t horizontal_, vertical_, upAndRight_, verticalAndRight_, downAndHorizontal_;
  92. size_t maxWidth_, width_;
  93. bool max_, suppress_;
  94. std::vector<Segment> segments_;
  95. std::vector<Branch> branches_;
  96. bool first_, last_;
  97. public:
  98. Tree(const uint16_t &flags) : flags_(flags), vt100_(false), maxWidth_(0), width_(0), max_(false), suppress_(false)
  99. {
  100. bool tty(isatty(1));
  101. if (flags & Ascii)
  102. {
  103. ascii:
  104. horizontal_ = L'-';
  105. vertical_ = L'|';
  106. upAndRight_ = L'`';
  107. verticalAndRight_ = L'|';
  108. downAndHorizontal_ = L'+';
  109. }
  110. else if (flags & Unicode)
  111. {
  112. unicode:
  113. if (!std::setlocale(LC_CTYPE, ""))
  114. goto vt100;
  115. horizontal_ = L'\x2500';
  116. vertical_ = L'\x2502';
  117. upAndRight_ = L'\x2514';
  118. verticalAndRight_ = L'\x251c';
  119. downAndHorizontal_ = L'\x252c';
  120. char *test;
  121. if (asprintf(&test, "%lc%lc%lc%lc%lc", horizontal_, vertical_, upAndRight_, verticalAndRight_, downAndHorizontal_) == -1)
  122. goto vt100;
  123. std::free(test);
  124. }
  125. else if (flags & Vt100)
  126. {
  127. vt100:
  128. vt100_ = true;
  129. horizontal_ = L'\x71';
  130. vertical_ = L'\x78';
  131. upAndRight_ = L'\x6d';
  132. verticalAndRight_ = L'\x74';
  133. downAndHorizontal_ = L'\x77';
  134. }
  135. else if (tty)
  136. goto unicode;
  137. else
  138. goto ascii;
  139. if (!(flags & Long) && tty)
  140. {
  141. int code;
  142. if (setupterm(NULL, 1, &code) == OK)
  143. {
  144. maxWidth_ = tigetnum("cols");
  145. if (tigetflag("am") && !tigetflag("xenl"))
  146. suppress_ = true;
  147. }
  148. else
  149. maxWidth_ = 80;
  150. }
  151. }
  152. void print(const std::string &string, bool highlight)
  153. {
  154. Escape escape(vt100_ ? BoxDrawing : None);
  155. if (!first_ || flags_ & Arguments)
  156. {
  157. size_t last(branches_.size() - 1);
  158. _foreach (std::vector<Branch>, branch, branches_)
  159. {
  160. size_t width(branch->indentation_.size() + 2);
  161. if (_index == last)
  162. {
  163. wchar_t line;
  164. if (last_)
  165. {
  166. branch->done_ = true;
  167. line = upAndRight_;
  168. }
  169. else
  170. line = verticalAndRight_;
  171. print(width, escape, "%s%lc%lc", branch->indentation_.c_str(), line, horizontal_);
  172. }
  173. else
  174. print(width, escape, "%s%lc ", branch->indentation_.c_str(), branch->done_ ? ' ' : vertical_);
  175. }
  176. }
  177. else if (branches_.size())
  178. {
  179. wchar_t line;
  180. if (last_)
  181. {
  182. branches_.back().done_ = true;
  183. line = horizontal_;
  184. }
  185. else
  186. line = downAndHorizontal_;
  187. print(3, escape, "%lc%lc%lc", horizontal_, line, horizontal_);
  188. }
  189. print(string.size(), highlight ? Bright : None, "%s", string.c_str());
  190. branches_.push_back(Branch(!(flags_ & Arguments) ? string.size() + 1 : 2));
  191. }
  192. inline void printArg(const std::string &arg, bool last)
  193. {
  194. if (max_)
  195. return;
  196. size_t width(arg.size() + 1);
  197. width_ += width;
  198. char *string;
  199. if (maxWidth_ && !(flags_ & Long))
  200. if (width_ > maxWidth_ || !last && width_ + 3 >= maxWidth_)
  201. {
  202. width -= width_ - maxWidth_;
  203. width_ = maxWidth_;
  204. max_ = true;
  205. ssize_t size(static_cast<ssize_t>(width) - 4);
  206. asprintf(&string, " %s...", size > 0 ? arg.substr(0, size).c_str() : "");
  207. }
  208. else
  209. goto print;
  210. else
  211. print:
  212. asprintf(&string, " %s", arg.c_str());
  213. segments_.push_back(Segment(width, None, string));
  214. }
  215. inline void pop(bool children)
  216. {
  217. branches_.pop_back();
  218. if (!(flags_ & Arguments) && !children)
  219. done();
  220. }
  221. inline void done()
  222. {
  223. _foreach (std::vector<Segment>, segment, segments_)
  224. {
  225. const char *begin, *end;
  226. switch (segment->escape_)
  227. {
  228. case BoxDrawing:
  229. begin = "\033(0\017";
  230. end = "\033(B\017";
  231. break;
  232. case Bright:
  233. begin = "\033[1m";
  234. end = "\033[22m";
  235. break;
  236. default:
  237. begin = end = ""; break;
  238. }
  239. std::printf("%s%s%s", begin, segment->string_, end);
  240. std::free(segment->string_);
  241. }
  242. segments_.clear();
  243. if (suppress_ && width_ == maxWidth_)
  244. std::fflush(stdout);
  245. else
  246. std::printf("\n");
  247. width_ = 0;
  248. max_ = false;
  249. }
  250. inline Tree &operator()(bool first, bool last)
  251. {
  252. first_ = first;
  253. last_ = last;
  254. return *this;
  255. }
  256. private:
  257. void print(size_t width, Escape escape, const char * format, ...)
  258. {
  259. if (max_)
  260. return;
  261. std::va_list args;
  262. va_start(args, format);
  263. char *string;
  264. vasprintf(&string, format, args);
  265. va_end(args);
  266. width_ += width;
  267. if (maxWidth_ && !(flags_ & Long))
  268. if (width_ > maxWidth_)
  269. {
  270. width -= width_ - maxWidth_;
  271. width_ = maxWidth_;
  272. max_ = true;
  273. bool previous = !width;
  274. if (previous)
  275. {
  276. std::free(string);
  277. const Segment &segment(segments_.back());
  278. width = segment.width_;
  279. string = segment.string_;
  280. }
  281. std::wstring wide(width - 1, '\0');
  282. std::mbstowcs(const_cast<wchar_t *>(wide.data()), string, wide.size());
  283. std::free(string);
  284. asprintf(&string, "%ls+", wide.c_str());
  285. if (previous)
  286. {
  287. segments_.back().string_ = string;
  288. return;
  289. }
  290. }
  291. segments_.push_back(Segment(width, escape, string));
  292. }
  293. };
  294. class Proc
  295. {
  296. const uint16_t &flags_;
  297. kvm_t *kd_;
  298. kinfo_proc *proc_;
  299. mutable std::string name_;
  300. Proc *parent_;
  301. PidMap childrenByPid_;
  302. NameMap childrenByName_;
  303. bool highlight_, root_;
  304. public:
  305. inline Proc(const uint16_t &flags, kvm_t *kd, kinfo_proc *proc) : flags_(flags), kd_(kd), proc_(proc), parent_(NULL), highlight_(false), root_(false) {}
  306. inline std::string name() const
  307. {
  308. if (name_.empty())
  309. name_ = visual(proc_->ki_comm);
  310. return name_;
  311. }
  312. inline pid_t parent() const { return proc_->ki_ppid; }
  313. inline pid_t pid() const { return proc_->ki_pid; }
  314. inline void child(Proc *proc)
  315. {
  316. if (proc == this)
  317. return;
  318. proc->parent_ = this;
  319. childrenByPid_[proc->pid()] = proc;
  320. childrenByName_.insert(NameMap::value_type(proc->name(), proc));
  321. }
  322. inline void highlight()
  323. {
  324. highlight_ = true;
  325. if (parent_)
  326. parent_->highlight();
  327. }
  328. inline bool root(uid_t uid)
  329. {
  330. if (flags_ & User)
  331. {
  332. if (uid == this->uid())
  333. {
  334. Proc *parent(parent_);
  335. while (parent)
  336. {
  337. if (parent->uid() == uid)
  338. return false;
  339. parent = parent->parent_;
  340. }
  341. return root_ = true;
  342. }
  343. return false;
  344. }
  345. return root_ = !parent_;
  346. }
  347. void printByPid(Tree &tree) const
  348. {
  349. print(tree);
  350. size_t last(childrenByPid_.size() - 1);
  351. _foreach (const PidMap, child, childrenByPid_)
  352. child->second->printByPid(tree(!_index, _index == last));
  353. tree.pop(children());
  354. }
  355. void printByName(Tree &tree) const
  356. {
  357. print(tree);
  358. size_t last(childrenByName_.size() - 1);
  359. _foreach (const NameMap, child, childrenByName_)
  360. child->second->printByName(tree(!_index, _index == last));
  361. tree.pop(children());
  362. }
  363. private:
  364. inline std::string visual(const char *string) const
  365. {
  366. std::string visual(std::strlen(string) * 4 + 1, '\0');
  367. visual.resize(strvis(const_cast<char *>(visual.data()), string, VIS_TAB | VIS_NL | VIS_NOSLASH));
  368. return visual;
  369. }
  370. void print(Tree &tree) const
  371. {
  372. bool titles(flags_ & ShowTitles);
  373. char **argv(NULL);
  374. std::ostringstream print;
  375. if (titles)
  376. {
  377. argv = kvm_getargv(kd_, proc_, 0);
  378. if (argv)
  379. print << visual(*argv);
  380. else
  381. print << name();
  382. }
  383. else
  384. print << name();
  385. bool _pid(flags_ & ShowPids), args(flags_ & Arguments);
  386. bool change(flags_ & UidChanges && (root_ ? !(flags_ & User) && uid() : parent_ && uid() != parent_->uid()));
  387. bool parens((_pid || change) && !args);
  388. if (parens)
  389. print << '(';
  390. if (_pid)
  391. {
  392. if (!parens)
  393. print << ',';
  394. print << pid();
  395. }
  396. if (change)
  397. {
  398. if (!parens || _pid)
  399. print << ',';
  400. passwd *user(getpwuid(uid()));
  401. print << user->pw_name;
  402. }
  403. if (parens)
  404. print << ')';
  405. tree.print(print.str(), highlight_);
  406. if (args)
  407. {
  408. if (!titles && !argv)
  409. argv = kvm_getargv(kd_, proc_, 0);
  410. if (argv && *argv)
  411. for (++argv; *argv; ++argv)
  412. tree.printArg(visual(*argv), !*(argv + 1));
  413. tree.done();
  414. }
  415. }
  416. inline bool children() const { return childrenByPid_.size(); }
  417. inline uid_t uid() const { return proc_->ki_ruid; }
  418. };
  419. static void help(const char *program, option options[], int code = 0)
  420. {
  421. std::printf("Usage: %s [options] [PID|USER]\n\nOptions:\n", basename(program));
  422. for (option *option(options); option->name; ++option)
  423. {
  424. std::string name(option->name);
  425. std::ostringstream arguments;
  426. switch (option->val)
  427. {
  428. case 'H':
  429. if (name != "highlight")
  430. continue;
  431. arguments << "-H[PID], --highlight[=PID]"; break;
  432. case 0:
  433. if (name == "pid")
  434. arguments << "PID, --pid=PID";
  435. else if (name == "user")
  436. arguments << "USER, --user=USER";
  437. else
  438. goto argument;
  439. break;
  440. default:
  441. arguments << '-' << static_cast<char>(option->val) << ", ";
  442. argument:
  443. arguments << "--" << name;
  444. }
  445. const char *description("");
  446. switch (option->val)
  447. {
  448. case 'a':
  449. description = "show command line arguments"; break;
  450. case 'A':
  451. description = "use ASCII line drawing characters"; break;
  452. case 'c':
  453. description = "don't compact identical subtrees"; break;
  454. case 'h':
  455. description = "show this help message and exit"; break;
  456. case 'H':
  457. description = "highlight the current process (or PID) and its\n ancestors"; break;
  458. case 'G':
  459. description = "use VT100 line drawing characters"; break;
  460. case 'k':
  461. description = "show kernel processes"; break;
  462. case 'l':
  463. description = "don't truncate long lines"; break;
  464. case 'n':
  465. description = "sort output by PID"; break;
  466. case 'p':
  467. description = "show PIDs; implies -c"; break;
  468. case 't':
  469. description = "show process titles"; break;
  470. case 'u':
  471. description = "show uid transitions"; break;
  472. case 'U':
  473. description = "use Unicode line drawing characters"; break;
  474. case 'V':
  475. description = "show version information and exit"; break;
  476. case 0:
  477. if (name == "pid")
  478. description = "show only the tree rooted at the process PID";
  479. else if (name == "user")
  480. description = "show only trees rooted at processes of USER";
  481. }
  482. std::printf(" %-27s %s\n", arguments.str().c_str(), description);
  483. }
  484. std::exit(code);
  485. }
  486. template <typename Type, long long minimum, long long maximum>
  487. static Type value(char *program, option options[], bool *success = NULL)
  488. {
  489. const char *error;
  490. long long value(strtonum(optarg, minimum, maximum, &error));
  491. if (error)
  492. if (success && errno == EINVAL)
  493. *success = false;
  494. else
  495. {
  496. warnx("Number is %s: \"%s\"", error, optarg);
  497. help(program, options, 1);
  498. }
  499. else if (success)
  500. *success = true;
  501. return value;
  502. }
  503. static uint16_t options(int argc, char *argv[], pid_t &hpid, pid_t &pid, char *&user)
  504. {
  505. option options[] = {
  506. { "arguments", no_argument, NULL, 'a' },
  507. { "ascii", no_argument, NULL, 'A' },
  508. { "compact", no_argument, NULL, 'c' },
  509. { "help", no_argument, NULL, 'h' },
  510. { "highlight", optional_argument, NULL, 'H' },
  511. { "highlight-all", no_argument, NULL, 'H' },
  512. { "highlight-pid", required_argument, NULL, 'H' },
  513. { "vt100", no_argument, NULL, 'G' },
  514. { "show-kernel", no_argument, NULL, 'k' },
  515. { "long", no_argument, NULL, 'l' },
  516. { "numeric-sort", no_argument, NULL, 'n' },
  517. { "show-pids", no_argument, NULL, 'p' },
  518. { "show-titles", no_argument, NULL, 't' },
  519. { "uid-changes", no_argument, NULL, 'u' },
  520. { "unicode", no_argument, NULL, 'U' },
  521. { "version", no_argument, NULL, 'V' },
  522. { "pid", required_argument, NULL, 0 },
  523. { "user", required_argument, NULL, 0 },
  524. { NULL, 0, NULL, 0 }
  525. };
  526. int option, index;
  527. uint16_t flags(0);
  528. char *program(argv[0]);
  529. while ((option = getopt_long(argc, argv, "aAchH::GklnptuUV", options, &index)) != -1)
  530. switch (option)
  531. {
  532. case 'a':
  533. flags |= Arguments; break;
  534. case 'A':
  535. flags |= Ascii;
  536. flags &= ~Vt100;
  537. flags &= ~Unicode;
  538. break;
  539. case 'c':
  540. flags |= Compact; break;
  541. case 'h':
  542. help(program, options);
  543. case 'H':
  544. hpid = optarg ? value<pid_t, 0, INT_MAX>(program, options) : getpid();
  545. flags |= Highlight;
  546. break;
  547. case 'G':
  548. flags |= Vt100;
  549. flags &= ~Ascii;
  550. flags &= ~Unicode;
  551. break;
  552. case 'k':
  553. flags |= ShowKernel; break;
  554. case 'l':
  555. flags |= Long; break;
  556. case 'n':
  557. flags |= NumericSort; break;
  558. case 'p':
  559. flags |= Compact | ShowPids; break;
  560. case 't':
  561. flags |= ShowTitles; break;
  562. case 'u':
  563. flags |= UidChanges; break;
  564. case 'U':
  565. flags |= Unicode;
  566. flags &= ~Ascii;
  567. flags &= ~Vt100;
  568. break;
  569. case 'V':
  570. flags |= Version; break;
  571. case 0:
  572. {
  573. std::string option(options[index].name);
  574. if (option == "pid")
  575. {
  576. pid = value<pid_t, 0, INT_MAX>(program, options);
  577. flags |= Pid;
  578. flags &= ~User;
  579. }
  580. else if (option == "user")
  581. {
  582. std::free(user);
  583. user = strdup(optarg);
  584. flags |= User;
  585. flags &= ~Pid;
  586. }
  587. }
  588. break;
  589. case '?':
  590. help(program, options, 1);
  591. }
  592. _forall (int, index, optind, argc)
  593. {
  594. bool success;
  595. optarg = argv[index];
  596. pid = value<pid_t, 0, INT_MAX>(program, options, &success);
  597. if (success)
  598. {
  599. flags |= Pid;
  600. flags &= ~User;
  601. }
  602. else
  603. {
  604. std::free(user);
  605. user = strdup(optarg);
  606. flags |= User;
  607. flags &= ~Pid;
  608. }
  609. }
  610. return flags;
  611. }
  612. int main(int argc, char *argv[])
  613. {
  614. pid_t hpid(-1), pid(-1);
  615. char *user(NULL);
  616. uint16_t flags(options(argc, argv, hpid, pid, user));
  617. if (flags & Version)
  618. {
  619. std::printf(DTPSTREE_PROGRAM " " DTPSTREE_VERSION "\n");
  620. return 0;
  621. }
  622. uid_t uid(-1);
  623. if (flags & User)
  624. {
  625. errno = 0;
  626. passwd *_user(getpwnam(user));
  627. if (!_user)
  628. errno ? err(1, NULL) : errx(1, "Unknown user: \"%s\"", user);
  629. uid = _user->pw_uid;
  630. }
  631. char error[_POSIX2_LINE_MAX];
  632. kvm_t *kd(kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, error));
  633. if (!kd)
  634. errx(1, "%s", error);
  635. typedef kinfo_proc *InfoProc;
  636. int count;
  637. InfoProc procs(kvm_getprocs(kd, KERN_PROC_PROC, 0, &count));
  638. if (!procs)
  639. errx(1, "%s", kvm_geterr(kd));
  640. PidMap pids;
  641. _forall (InfoProc, proc, procs, procs + count)
  642. if (flags & ShowKernel || proc->ki_ppid != 0 || proc->ki_pid == 1)
  643. pids[proc->ki_pid] = new Proc(flags, kd, proc);
  644. enum { PidSort, NameSort } sort(flags & NumericSort ? PidSort : NameSort);
  645. _foreach (PidMap, pid, pids)
  646. {
  647. Proc *proc(pid->second);
  648. PidMap::iterator parent(pids.find(proc->parent()));
  649. if (parent != pids.end())
  650. parent->second->child(proc);
  651. }
  652. if (flags & Highlight)
  653. {
  654. PidMap::iterator pid(pids.find(hpid));
  655. if (pid != pids.end())
  656. pid->second->highlight();
  657. }
  658. Tree tree(flags);
  659. if (flags & Pid)
  660. {
  661. PidMap::iterator _pid(pids.find(pid));
  662. if (_pid != pids.end())
  663. {
  664. Proc *proc(_pid->second);
  665. switch (sort)
  666. {
  667. case PidSort:
  668. proc->printByPid(tree);
  669. break;
  670. case NameSort:
  671. proc->printByName(tree);
  672. }
  673. }
  674. return 0;
  675. }
  676. NameMap names;
  677. _foreach (PidMap, pid, pids)
  678. {
  679. Proc *proc(pid->second);
  680. if (proc->root(uid))
  681. switch (sort)
  682. {
  683. case PidSort:
  684. proc->printByPid(tree);
  685. break;
  686. case NameSort:
  687. names.insert(NameMap::value_type(proc->name(), proc));
  688. }
  689. }
  690. switch (sort)
  691. {
  692. case NameSort:
  693. _foreach (NameMap, name, names)
  694. name->second->printByName(tree);
  695. default:
  696. return 0;
  697. }
  698. }
  699. // display a tree of processes