not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / libs / ksysguard / processui / KMonitorProcessIO.cpp
blob5dd187082698bdb5c3f7c1c795d575ae487a4eed
1 /*
2 KSysGuard, the KDE System Guard
4 Copyright (C) 2007 Trent Waddington <trent.waddington@gmail.com>
5 Copyright (c) 2008 John Tapsell <tapsell@kde.org>
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This library 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 GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public License
18 aint with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
25 #include "../config-ksysguard.h"
27 #include <klocale.h>
28 #include <kdebug.h>
29 #include <QTimer>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <stdio.h>
34 #ifdef HAVE_SYS_PTRACE_H
35 #include <sys/ptrace.h>
36 #endif
37 #include <sys/types.h>
38 #include <sys/wait.h>
39 #include <sys/syscall.h>
40 #include <sys/user.h>
41 #include <ctype.h>
43 #ifdef __i386__
44 #define REG_ORIG_ACCUM(regs) regs.orig_eax
45 #define REG_ACCUM(regs) regs.eax
46 #define REG_PARAM1(regs) regs.ebx
47 #define REG_PARAM2(regs) regs.ecx
48 #define REG_PARAM3(regs) regs.edx
49 #endif
50 #ifdef __amd64__
51 #define REG_ORIG_ACCUM(regs) regs.orig_rax
52 #define REG_ACCUM(regs) regs.rax
53 #define REG_PARAM1(regs) regs.rdi
54 #define REG_PARAM2(regs) regs.rsi
55 #define REG_PARAM3(regs) regs.rdx
56 #endif
57 #if defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__PPC__) || defined(powerpc)
58 #define REG_ORIG_ACCUM(regs) regs.gpr[0]
59 #define REG_ACCUM(regs) regs.gpr[3]
60 #define REG_PARAM1(regs) regs.orig_gpr3
61 #define REG_PARAM2(regs) regs.gpr[4]
62 #define REG_PARAM3(regs) regs.gpr[5]
63 #ifndef PT_ORIG_R3
64 #define PT_ORIG_R3 34
65 #endif
66 #endif
67 #ifdef __ia64__
68 #undef slots
69 #include <sys/rse.h>
70 #define REG_ORIG_ACCUM(regs) regs.pt.gr[15]
71 #define REG_ACCUM(regs) (regs.pt.gr[10] ? -regs.pt.gr[8] : regs.pt.gr[8])
72 #define REG_PARAM1(regs) regs.arg[0]
73 #define REG_PARAM2(regs) regs.arg[1]
74 #define REG_PARAM3(regs) regs.arg[2]
75 #endif
77 #include "KMonitorProcessIO.h"
79 #include "KMonitorProcessIO.moc"
81 KMonitorProcessIO::KMonitorProcessIO(QWidget* parent, int pid)
82 : KTextEditVT( parent ), mPid(pid)
84 mIncludeChildProcesses = true;
85 remove_duplicates = false;
87 mUpdateInterval = 20;
88 mTimer.setSingleShot(false);
89 connect(&mTimer, SIGNAL(timeout()), this, SLOT(update()));
91 lastdir = 3; //an invalid direction, so that the color gets set the first time
93 setReadOnly(true);
94 setParseAnsiEscapeCodes(true);
95 document()->setMaximumBlockCount(100);
96 mCursor = textCursor();
99 if(pid == -1)
100 return;
101 attach(mPid);
104 KMonitorProcessIO::~KMonitorProcessIO() {
105 detach();
108 int KMonitorProcessIO::updateInterval() const {
109 return mUpdateInterval;
112 void KMonitorProcessIO::setUpdateInterval(int msecs) {
113 mUpdateInterval = msecs;
114 if(mTimer.isActive()) {
115 mTimer.stop();
116 mTimer.start(msecs); //Start with the new interval time
121 void KMonitorProcessIO::detach() {
122 foreach(int pid, attached_pids) {
123 detach(pid);
127 int KMonitorProcessIO::attachedPid() const {
128 return mPid;
130 void KMonitorProcessIO::detach(int pid) {
131 int status;
132 #ifdef HAVE_SYS_PTRACE_H
133 if(!ptrace(PTRACE_DETACH, pid, 0, 0)) {
134 //successfully detached
135 } else if(kill(pid, 0) < 0) {
136 if(errno != ESRCH)
137 kDebug() << "Something seriously strange when trying to detach.";
138 } else if (kill(pid, SIGSTOP) < 0) {
139 if (errno != ESRCH)
140 kDebug() << "Something seriously strange when trying to detach and then trying to stop the process";
141 } else {
142 for (;;) {
143 if (waitpid(pid, &status, 0) < 0) {
144 if (errno != ECHILD)
145 kDebug() << "Something seriously strange when trying to detach and waiting for process to stop";
146 break;
148 if (!WIFSTOPPED(status)) {
149 /* Au revoir, mon ami. */
150 break;
152 if (WSTOPSIG(status) == SIGSTOP) {
153 //Okay process is now stopped. Lets try detaching again. Silly linux.
154 if (ptrace(PTRACE_DETACH,pid, 0, 0) < 0) {
155 if (errno != ESRCH)
156 kDebug() << "Something seriously strange when trying to detach the second time.";
157 /* I died trying. */
159 break;
161 // we didn't manage to stop the process. Lets try continuing it and the stopping it
162 if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
163 if (errno != ESRCH)
164 kDebug() << "Something seriously strange when trying to detach and continue";
165 break;
169 #endif
170 attached_pids.removeAll(pid);
172 if(attached_pids.isEmpty()) {
173 mTimer.stop();
177 bool KMonitorProcessIO::reattach() {
178 if(mPid == -1)
179 return false;
180 return attach(mPid);
183 bool KMonitorProcessIO::attach(int pid) {
184 if(pid == -1) {
185 //Indicates to detach all
186 detach();
187 return false;
189 #ifdef HAVE_SYS_PTRACE_H
190 if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
191 kDebug() << "Failed to attach to process " << pid;
192 if(attached_pids.isEmpty()) {
193 mTimer.stop();
194 insertHtml(i18n("<br/><i><font color=\"gray\">Failed to attach to process %1</font></i><br/>", pid));
195 return false;
197 } else {
198 if(attached_pids.isEmpty()) {
199 //First process added. Automatically start timer
200 ptrace(PTRACE_SYSCALL, pid, 0, 0);
201 mTimer.start(mUpdateInterval);
202 if(mPid == -1)
203 mPid = pid;
205 attached_pids.append(pid);
207 return true;
208 #else
209 return false;
210 #endif
213 void KMonitorProcessIO::update(bool modified)
215 #ifdef HAVE_SYS_PTRACE_H
216 static QColor writeColor = QColor(255,0,0);
217 static QColor readColor = QColor(0,0,255);
219 int status;
220 int pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED);
221 if (pid == -1 || !WIFSTOPPED(status)) {
222 if(modified)
223 ensureCursorVisible();
224 return;
226 #if defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__PPC__) || defined(powerpc)
227 struct pt_regs regs;
228 regs.gpr[0] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R0, 0);
229 regs.gpr[3] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R3, 0);
230 regs.gpr[4] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R4, 0);
231 regs.gpr[5] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R5, 0);
232 regs.orig_gpr3 = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_ORIG_R3, 0);
233 #endif
234 #ifdef __ia64__
235 struct {
236 struct pt_all_user_regs pt;
237 unsigned long arg[3];
238 } regs;
239 ptrace(PTRACE_GETREGS, pid, 0, &regs.pt);
240 if (REG_ORIG_ACCUM(regs) >= 0) {
241 unsigned long *out0 = ia64_rse_skip_regs((unsigned long *)regs.pt.ar[17], -(regs.pt.cfm & 0x7f) + ((regs.pt.cfm >> 7) & 0x7f));
242 regs.arg[0] = ptrace(PTRACE_PEEKDATA, pid, ia64_rse_skip_regs(out0, 0), 0);
243 regs.arg[1] = ptrace(PTRACE_PEEKDATA, pid, ia64_rse_skip_regs(out0, 1), 0);
244 regs.arg[2] = ptrace(PTRACE_PEEKDATA, pid, ia64_rse_skip_regs(out0, 2), 0);
246 #endif
247 #if defined __i386__ || defined __amd64__
248 struct user_regs_struct regs;
249 ptrace(PTRACE_GETREGS, pid, 0, &regs);
250 #endif
251 /*unsigned int b = ptrace(PTRACE_PEEKTEXT, pid, regs.eip, 0);*/
252 if (mIncludeChildProcesses && (
253 #ifdef SYS_fork
254 REG_ORIG_ACCUM(regs) == SYS_fork ||
255 #endif
256 #ifdef SYS_clone
257 REG_ORIG_ACCUM(regs) == SYS_clone ||
258 #endif
259 #ifdef SYS_clone2
260 REG_ORIG_ACCUM(regs) == SYS_clone2 ||
261 #endif
262 0)) {
263 if (REG_ACCUM(regs) > 0)
264 attach(REG_ACCUM(regs));
266 if ((REG_ORIG_ACCUM(regs) == SYS_read || REG_ORIG_ACCUM(regs) == SYS_write) && (REG_PARAM3(regs) == REG_ACCUM(regs))) {
267 for (unsigned long i = 0; i < REG_PARAM3(regs); i++) {
268 union {
269 unsigned long l;
270 unsigned char c[sizeof(long)];
271 } a;
272 a.l = ptrace(PTRACE_PEEKDATA, pid, REG_PARAM2(regs) + i, 0);
273 if(!modified) {
274 //Before we add text or change the color, make sure we are at the end
275 moveCursor(QTextCursor::End);
277 if(REG_ORIG_ACCUM(regs) != lastdir) {
278 if(REG_ORIG_ACCUM(regs) == SYS_read)
279 setTextColor(readColor);
280 else
281 setTextColor(writeColor);
282 lastdir = REG_ORIG_ACCUM(regs);
284 for (unsigned j = 0; j < sizeof(a.c) && i < REG_PARAM3(regs); i++, j++) {
285 unsigned char c = a.c[j];
286 /** Use the KTextEditVT specific function to parse the character 'c' */
287 insertVTChar(QChar(c));
290 modified = true;
292 ptrace(PTRACE_SYSCALL, pid, 0, 0);
293 update(modified);
294 #endif
297 void KMonitorProcessIO::setIncludeChildProcesses(bool include) {
298 mIncludeChildProcesses = include;
301 bool KMonitorProcessIO::includeChildProcesses() const {
302 return mIncludeChildProcesses;
306 KMonitorProcessIO::State KMonitorProcessIO::state() const {
307 if(attached_pids.isEmpty())
308 return Detached;
309 if(mTimer.isActive())
310 return AttachedRunning;
311 return AttachedPaused;
314 void KMonitorProcessIO::pauseProcesses() {
315 if(state() == AttachedRunning) {
316 mTimer.stop();
319 void KMonitorProcessIO::resumeProcesses() {
320 if(state() == AttachedPaused)
321 mTimer.start(mUpdateInterval);
324 void KMonitorProcessIO::setState(State new_state) {
325 if(new_state == AttachedPaused) pauseProcesses();
326 if(new_state == AttachedRunning) resumeProcesses();
327 if(new_state == Detached) detach();