r50306 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r50305‎ | r50306 | r50307 >
Date:14:09, 7 May 2009
Author:river
Status:deferred
Tags:
Comment:
split linux-specific code into proc_linux.cc
Modified paths:
  • /trunk/tools/slayerd/Makefile (modified) (history)
  • /trunk/tools/slayerd/debian/files (modified) (history)
  • /trunk/tools/slayerd/proc_linux.cc (added) (history)
  • /trunk/tools/slayerd/process.cc (added) (history)
  • /trunk/tools/slayerd/process.h (added) (history)
  • /trunk/tools/slayerd/slayerd.cc (modified) (history)

Diff [purge]

Index: trunk/tools/slayerd/Makefile
@@ -7,13 +7,13 @@
88
99 CXX = g++
1010 CXXFLAGS = -O2 -g3 -ggdb
11 -CPPFLAGS = -DETCDIR=\"$(CONFDIR)\" -DPREFIX=\"$(PREFIX)\"
12 -LDFLAGS =
13 -SRCS = slayerd.cc
 11+CPPFLAGS = -DETCDIR=\"$(CONFDIR)\" -DPREFIX=\"$(PREFIX)\" -I/usr/local/include
 12+LDFLAGS = -L/usr/local/lib
 13+SRCS = slayerd.cc process.cc
1414 OBJS = $(SRCS:.cc=.o)
1515
1616 slayerd: $(OBJS)
17 - $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ -lboost_filesystem
 17+ $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJS) -o $@ -lboost_filesystem
1818
1919 install: slayerd
2020 install -d $(DESTDIR)$(SBINDIR)
Index: trunk/tools/slayerd/debian/files
@@ -1,2 +1,3 @@
22 slayerd_1.5_amd64.deb admin extra
33 slayerd_1.6_amd64.deb admin extra
 4+slayerd_1.6_amd64.deb admin extra
Index: trunk/tools/slayerd/process.cc
@@ -0,0 +1,13 @@
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+#ifdef __linux__
 11+# include "proc_linux.cc"
 12+#else
 13+# error dont know how to enumerate processes on this platform
 14+#endif
Index: trunk/tools/slayerd/process.h
@@ -0,0 +1,31 @@
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#ifndef PROCESS_H
 13+#define PROCESS_H
 14+
 15+#include <vector>
 16+
 17+struct process {
 18+ typedef boost::shared_ptr<process> pointer;
 19+
 20+ virtual ~process() {};
 21+
 22+ virtual pid_t pid() const = 0;
 23+ virtual std::string command() const = 0;
 24+ virtual std::string cmdline() const = 0;
 25+ virtual std::size_t rss() const = 0;
 26+ virtual std::size_t vsize() const = 0;
 27+ virtual uid_t uid() const = 0;
 28+};
 29+
 30+std::vector<process::pointer> enumerate_processes();
 31+
 32+#endif /* !PROCESS_H */
Index: trunk/tools/slayerd/proc_linux.cc
@@ -0,0 +1,151 @@
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
 3+/*
 4+ * Permission is granted to anyone to use this software for any purpose,
 5+ * including commercial applications, and to alter it and redistribute it
 6+ * freely. This software is provided 'as-is', without any express or implied
 7+ * warranty.
 8+ */
 9+
 10+/* $Id$ */
 11+
 12+#include <sys/stat.h>
 13+#include <unistd.h>
 14+
 15+#include <string>
 16+#include <fstream>
 17+#include <boost/shared_ptr.hpp>
 18+#include <boost/filesystem.hpp>
 19+#include <boost/lexical_cast.hpp>
 20+
 21+#include "process.h"
 22+
 23+namespace fs = boost::filesystem;
 24+
 25+namespace {
 26+ std::string PATH_PROC = "/proc";
 27+}
 28+
 29+struct process_linux : process {
 30+ process_linux(fs::path const &pth);
 31+
 32+ pid_t pid() const { return _pid; }
 33+ std::string command() const { return _comm; }
 34+ std::string cmdline() const { return _fullcomm; }
 35+ std::size_t rss() const { return _mres; }
 36+ std::size_t vsize() const { return _vsize; }
 37+ uid_t uid() const { return _uid; }
 38+
 39+ pid_t _pid;
 40+ /* short command from /proc/{pid}/stat */
 41+ std::string _comm;
 42+ /* fulll command from /proc/{pid}/cmdline */
 43+ std::string _fullcomm;
 44+ unsigned long _vsize;
 45+ long _rss;
 46+ uid_t _uid;
 47+ int _mres;
 48+
 49+ void _read_proc_data(fs::path const &);
 50+};
 51+
 52+process_linux::process_linux(fs::path const &pth)
 53+ : _pid(boost::lexical_cast<pid_t>(pth.leaf()))
 54+{
 55+ struct stat st;
 56+ if (::stat(pth.native_directory_string().c_str(), &st) == -1)
 57+ throw std::runtime_error("could not stat proc dir");
 58+ _uid = st.st_uid;
 59+
 60+ _read_proc_data(pth);
 61+}
 62+
 63+void
 64+process_linux::_read_proc_data(fs::path const &pth)
 65+{
 66+ /* Everything that we can collect, but that doesn't belong in process. */
 67+ unsigned long _flags, _minflt, _cminflt, _majflt, _cmajflt, _utime, _stime;
 68+ long _cutime, _cstime, _priority, _itrealvalue, _starttime;
 69+ unsigned long _rlim, _startcode, _endcode, _stackstart, _kstkesp, _kstkeip;
 70+ unsigned long _signal, _blocked, _sigignore, _sigcatch, _wchan, _nswap, _cnswap;
 71+ int _exit_signal, _processor;
 72+ unsigned long _rt_priority, _policy;
 73+ int _mshare, _mtext, _mlib, _mdata;
 74+ pid_t _ppid, _pgrp, _sid, _tty, _tpgid, _state, _msize, _nice;
 75+
 76+ {
 77+ std::ifstream f((pth / "stat").native_file_string().c_str());
 78+
 79+ if (!f)
 80+ throw std::runtime_error("could not read line from stat");
 81+
 82+ long dummy;
 83+ if (!(f >> _pid >> _comm >> _state >> _ppid >> _pgrp >> _sid >> _tty >> _tpgid
 84+ >> _flags >> _minflt >> _cminflt >> _majflt >> _cmajflt >> _utime
 85+ >> _stime >> _cutime >> _cstime >> _priority >> _nice >> dummy >> _itrealvalue
 86+ >> _starttime >> _vsize >> _rss >> _rlim >> _startcode >> _endcode
 87+ >> _stackstart >> _kstkesp >> _kstkeip >> _signal >> _blocked >> _sigignore
 88+ >> _sigcatch >> _wchan >> _nswap >> _cnswap >> _exit_signal >> _processor
 89+ >> _rt_priority >> _policy
 90+ ))
 91+ throw std::runtime_error("could not parse stat line");
 92+ }
 93+
 94+ {
 95+ std::ifstream f((pth / "statm").native_file_string().c_str());
 96+
 97+ if (!f)
 98+ throw std::runtime_error("could not read line from stat");
 99+
 100+ if (!(f >> _msize >> _mres >> _mshare >> _mtext >> _mlib >> _mdata))
 101+ throw std::runtime_error("could not parse statm line");
 102+ }
 103+
 104+ {
 105+ std::ifstream f((pth / "cmdline").native_file_string().c_str());
 106+
 107+ if (!f)
 108+ throw std::runtime_error("could not read line from cmdline");
 109+
 110+ if (!std::getline(f, _fullcomm))
 111+ throw std::runtime_error("could not parse cmdline");
 112+ }
 113+}
 114+
 115+template<typename C>
 116+struct directory_enumerator {
 117+ C &list;
 118+
 119+ directory_enumerator(C &list) : list(list) {}
 120+
 121+ void operator() (fs::path const &pth) const {
 122+ /*
 123+ * Ensure it is actually a pid.
 124+ */
 125+ try {
 126+ boost::lexical_cast<pid_t>(pth.leaf());
 127+ } catch (boost::bad_lexical_cast const &) {
 128+ return;
 129+ }
 130+
 131+ try {
 132+ list.push_back(process::pointer(new process_linux(pth)));
 133+ } catch (...) {}
 134+ }
 135+};
 136+
 137+template<typename C>
 138+directory_enumerator<C>
 139+enumerate_directory(C &list) {
 140+ return directory_enumerator<C>(list);
 141+}
 142+
 143+std::vector<process::pointer>
 144+enumerate_processes()
 145+{
 146+ std::vector<process::pointer> processes;
 147+ fs::path proc(PATH_PROC);
 148+
 149+ std::for_each(fs::directory_iterator(proc), fs::directory_iterator(),
 150+ enumerate_directory(processes));
 151+ return processes;
 152+}
Index: trunk/tools/slayerd/slayerd.cc
@@ -1,4 +1,4 @@
2 -/* Copyright (c) 2007 River Tarnell <river@attenuate.org>. */
 2+/* Copyright (c) 2007-2009 River Tarnell <river@loreley.flyingparchment.org.uk>. */
33 /*
44 * Permission is granted to anyone to use this software for any purpose,
55 * including commercial applications, and to alter it and redistribute it
@@ -12,35 +12,37 @@
1313 * slayerd: monitor user activity and regulate users using too much RAM.
1414 */
1515
16 -#include <string>
17 -#include <iostream>
18 -#include <fstream>
19 -#include <algorithm>
20 -#include <stdexcept>
21 -#include <vector>
22 -#include <map>
23 -#include <set>
24 -#include <cerrno>
25 -#include <cstring>
 16+#include <string>
 17+#include <iostream>
 18+#include <fstream>
 19+#include <algorithm>
 20+#include <stdexcept>
 21+#include <vector>
 22+#include <map>
 23+#include <set>
 24+#include <cerrno>
 25+#include <cstring>
2626
27 -#include <sys/types.h>
28 -#include <sys/mman.h>
29 -#include <sys/stat.h>
30 -#include <sys/wait.h>
31 -#include <unistd.h>
32 -#include <pwd.h>
33 -#include <signal.h>
34 -#include <syslog.h>
 27+#include <sys/types.h>
 28+#include <sys/mman.h>
 29+#include <sys/stat.h>
 30+#include <sys/wait.h>
 31+#include <unistd.h>
 32+#include <pwd.h>
 33+#include <signal.h>
 34+#include <syslog.h>
3535
36 -#include <boost/filesystem/path.hpp>
37 -#include <boost/filesystem/operations.hpp>
38 -#include <boost/lexical_cast.hpp>
39 -#include <boost/format.hpp>
 36+#include <boost/filesystem/path.hpp>
 37+#include <boost/filesystem/operations.hpp>
 38+#include <boost/lexical_cast.hpp>
 39+#include <boost/format.hpp>
 40+#include <boost/shared_ptr.hpp>
4041
 42+#include "process.h"
 43+
4144 namespace fs = boost::filesystem;
4245
4346 namespace {
44 - std::string PATH_PROC = "/proc";
4547 std::string CONFFILE = "/etc/slayerd/slayerd.conf";
4648 }
4749
@@ -67,116 +69,6 @@
6870 {}
6971 } config;
7072
71 -struct process {
72 - process(fs::path const &pth);
73 -
74 - pid_t _pid;
75 - /* short command from /proc/{pid}/stat */
76 - std::string _comm;
77 - /* fulll command from /proc/{pid}/cmdline */
78 - std::string _fullcomm;
79 - char _state;
80 - pid_t _ppid;
81 - pid_t _pgrp;
82 - pid_t _sid;
83 - int _tty;
84 - pid_t _tpgid;
85 - unsigned long _flags;
86 - unsigned long _minflt;
87 - unsigned long _cminflt;
88 - unsigned long _majflt;
89 - unsigned long _cmajflt;
90 - unsigned long _utime;
91 - unsigned long _stime;
92 - long _cutime;
93 - long _cstime;
94 - long _priority;
95 - long _itrealvalue;
96 - long _starttime;
97 - unsigned long _vsize;
98 - long _rss;
99 - unsigned long _rlim;
100 - unsigned long _startcode;
101 - unsigned long _endcode;
102 - unsigned long _stackstart;
103 - unsigned long _kstkesp;
104 - unsigned long _kstkeip;
105 - unsigned long _signal;
106 - unsigned long _blocked;
107 - unsigned long _sigignore;
108 - unsigned long _sigcatch;
109 - unsigned long _wchan;
110 - unsigned long _nswap;
111 - unsigned long _cnswap;
112 - int _exit_signal;
113 - int _processor;
114 - unsigned long _rt_priority;
115 - unsigned long _policy;
116 - long _nice;
117 - uid_t _uid;
118 - int _msize;
119 - int _mres;
120 - int _mshare;
121 - int _mtext;
122 - int _mlib;
123 - int _mdata;
124 -
125 - void _read_proc_data(fs::path const &);
126 -};
127 -
128 -process::process(fs::path const &pth)
129 - : _pid(boost::lexical_cast<pid_t>(pth.leaf()))
130 -{
131 - struct stat st;
132 - if (::stat(pth.native_directory_string().c_str(), &st) == -1)
133 - throw std::runtime_error("could not stat proc dir");
134 - _uid = st.st_uid;
135 -
136 - _read_proc_data(pth);
137 -}
138 -
139 -void
140 -process::_read_proc_data(fs::path const &pth)
141 -{
142 - {
143 - std::ifstream f((pth / "stat").native_file_string().c_str());
144 -
145 - if (!f)
146 - throw std::runtime_error("could not read line from stat");
147 -
148 - long dummy;
149 - if (!(f >> _pid >> _comm >> _state >> _ppid >> _pgrp >> _sid >> _tty >> _tpgid
150 - >> _flags >> _minflt >> _cminflt >> _majflt >> _cmajflt >> _utime
151 - >> _stime >> _cutime >> _cstime >> _priority >> _nice >> dummy >> _itrealvalue
152 - >> _starttime >> _vsize >> _rss >> _rlim >> _startcode >> _endcode
153 - >> _stackstart >> _kstkesp >> _kstkeip >> _signal >> _blocked >> _sigignore
154 - >> _sigcatch >> _wchan >> _nswap >> _cnswap >> _exit_signal >> _processor
155 - >> _rt_priority >> _policy
156 - ))
157 - throw std::runtime_error("could not parse stat line");
158 - }
159 -
160 - {
161 - std::ifstream f((pth / "statm").native_file_string().c_str());
162 -
163 - if (!f)
164 - throw std::runtime_error("could not read line from stat");
165 -
166 - if (!(f >> _msize >> _mres >> _mshare >> _mtext >> _mlib >> _mdata))
167 - throw std::runtime_error("could not parse statm line");
168 - }
169 -
170 - {
171 - std::ifstream f((pth / "cmdline").native_file_string().c_str());
172 -
173 - if (!f)
174 - throw std::runtime_error("could not read line from cmdline");
175 -
176 - if (!std::getline(f, _fullcomm))
177 - throw std::runtime_error("could not parse cmdline");
178 - }
179 -}
180 -
18173 std::string
18274 username(uid_t uid)
18375 {
@@ -195,40 +87,12 @@
19688 return p->pw_uid;
19789 }
19890
199 -template<typename C>
200 -struct directory_enumerator {
201 - C &list;
202 -
203 - directory_enumerator(C &list) : list(list) {}
204 -
205 - void operator() (fs::path const &pth) const {
206 - /*
207 - * Ensure it is actually a pid.
208 - */
209 - try {
210 - boost::lexical_cast<pid_t>(pth.leaf());
211 - } catch (boost::bad_lexical_cast const &) {
212 - return;
213 - }
214 -
215 - try {
216 - list.push_back(process(pth));
217 - } catch (...) {}
218 - }
219 -};
220 -
221 -template<typename C>
222 -directory_enumerator<C>
223 -enumerate_directory(C &list) {
224 - return directory_enumerator<C>(list);
225 -}
226 -
22791 struct user {
22892 user() : uid(-1), rss(0) {}
22993
23094 uid_t uid;
231 - unsigned long rss;
232 - std::vector<process> processes;
 95+ std::size_t rss;
 96+ std::vector<process::pointer> processes;
23397 };
23498
23599 /*
@@ -241,6 +105,13 @@
242106 return b.*F < a.*F;
243107 }
244108
 109+template<typename S, typename T, T (S::*F)(void) const>
 110+bool
 111+ptmf_comparator(boost::shared_ptr<S> const &a, boost::shared_ptr<S> const &b)
 112+{
 113+ return ((*b).*F)() < ((*a).*F)();
 114+}
 115+
245116 void
246117 version(void) {
247118 std::cerr << "slayerd " << VERSION << "\n";
@@ -580,22 +451,18 @@
581452 % config.delay % (config.limit / 1024 / 1024) % (config.thresh / 1024 / 1024)));
582453
583454 for (;;) {
584 - fs::path proc(PATH_PROC);
585 - std::vector<process> processes;
 455+ std::vector<process::pointer> processes(enumerate_processes());
586456
587 - std::for_each(fs::directory_iterator(proc), fs::directory_iterator(),
588 - enumerate_directory(processes));
589 -
590457 /*
591458 * Aggregate the processes by user.
592459 */
593460 std::vector<user> users;
594461 for (std::size_t i = 0, end = processes.size(); i < end; ++i) {
595 - process &p = processes[i];
 462+ process::pointer p = processes[i];
596463 user *u = 0;
597464
598465 for (std::size_t ui = 0, uend = users.size(); ui != uend; ++ui)
599 - if (users[ui].uid == p._uid) {
 466+ if (users[ui].uid == p->uid()) {
600467 u = &users[ui];
601468 break;
602469 }
@@ -603,18 +470,18 @@
604471 if (u == 0) {
605472 std::size_t n = users.size();
606473 users.resize(n + 1);
607 - users[n].uid = p._uid;
 474+ users[n].uid = p->uid();
608475 u = &users[n];
609476 }
610477
611 - u->rss += p._mres;
 478+ u->rss += p->rss();
612479 u->processes.push_back(p);
613480 }
614481
615482 /*
616483 * Sort user by RSS.
617484 */
618 - std::sort(users.begin(), users.end(), field_comparator<user, unsigned long, &user::rss>);
 485+ std::sort(users.begin(), users.end(), field_comparator<user, std::size_t, &user::rss>);
619486
620487 for (std::size_t i = 0, end = users.size(); i < end; ++i) {
621488 user &u = users[i];
@@ -637,31 +504,32 @@
638505 % (bytes / 1024 / 1024)
639506 % (config.limit / 1024 / 1024)));
640507
641 - std::sort(u.processes.begin(), u.processes.end(), field_comparator<process, int, &process::_mres>);
 508+ std::sort(u.processes.begin(), u.processes.end(), ptmf_comparator<process, std::size_t, &process::rss>);
642509
643510 while (bytes >= config.thresh && !u.processes.empty()) {
644 - process &p = u.processes[0];
 511+ process::pointer p = u.processes[0];
645512
646513 /* command is (%s) formatted, strip parentheses. */
647 - std::string comm = p._comm.substr(1);
 514+ std::string comm = p->command().substr(1);
648515 comm.resize(comm.size() - 1);
649516
650517 /* arguments are \0 separated, use spaces for display */
651 - std::replace(p._fullcomm.begin(), p._fullcomm.end(), '\0', ' ');
 518+ std::string cm = p->cmdline();
 519+ std::replace(cm.begin(), cm.end(), '\0', ' ');
652520
653521 if (!config.debug)
654 - kill(p._pid, SIGKILL);
 522+ kill(p->pid(), SIGKILL);
655523
656 - std::size_t thissize = std::size_t(p._mres) * pagesize;
 524+ std::size_t thissize = std::size_t(p->rss()) * pagesize;
657525
658526 log(str(boost::format(" killed process \"%s\" (pid %d) using %dM, usage now %dM")
659 - % comm % p._pid
 527+ % comm % p->pid()
660528 % (thissize / 1024 / 1024)
661529 % ((bytes - thissize) / 1024 / 1024)));
662530
663531 process_list += str(boost::format(" %s (pid %d), using %d megabyte(s).\n"
664 - " command: \"%s\"\n")
665 - % comm % p._pid % (thissize / 1024 / 1024) % p._fullcomm);
 532+ " command: %s\n")
 533+ % comm % p->pid() % (thissize / 1024 / 1024) % p->cmdline());
666534
667535 bytes -= thissize;
668536 u.processes.erase(u.processes.begin());

Status & tagging log