Doc
[lttngtop.git] / src / common.c
1 /*
2 * Copyright (C) 2011 Julien Desfossez
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
16 * MA 02111-1307, USA.
17 */
18
19 #include <stdlib.h>
20 #include <string.h>
21 #include "common.h"
22
23 struct processtop *find_process_tid(struct lttngtop *ctx, int tid, char *comm)
24 {
25 gint i;
26 struct processtop *tmp;
27
28 for (i = 0; i < ctx->process_table->len; i++) {
29 tmp = g_ptr_array_index(ctx->process_table, i);
30 if (tmp && tmp->tid == tid)
31 return tmp;
32 }
33 return NULL;
34 }
35
36 struct processtop* add_proc(struct lttngtop *ctx, int tid, char *comm,
37 unsigned long timestamp)
38 {
39 struct processtop *newproc;
40
41 /* if the PID already exists, we just rename the process */
42 /* FIXME : need to integrate with clone/fork/exit to be accurate */
43 newproc = find_process_tid(ctx, tid, comm);
44 if (!newproc) {
45 newproc = g_new0(struct processtop, 1);
46 newproc->tid = tid;
47 newproc->birth = timestamp;
48 newproc->process_files_table = g_ptr_array_new();
49 newproc->threads = g_ptr_array_new();
50 newproc->perf = g_hash_table_new(g_str_hash, g_str_equal);
51 newproc->iostream = g_new0(struct iostream, 1);
52 newproc->iostream->ret_read = 0;
53 newproc->iostream->ret_write = 0;
54 newproc->iostream->ret_total = 0;
55 newproc->iostream->syscall_info = NULL;
56 g_ptr_array_add(ctx->process_table, newproc);
57 }
58 newproc->comm = strdup(comm);
59
60 return newproc;
61 }
62
63 struct processtop* update_proc(struct processtop* proc, int pid, int tid,
64 int ppid, char *comm)
65 {
66 if (proc) {
67 proc->pid = pid;
68 proc->tid = tid;
69 proc->ppid = ppid;
70 if (strcmp(proc->comm, comm) != 0) {
71 free(proc->comm);
72 proc->comm = strdup(comm);
73 }
74 }
75 return proc;
76 }
77
78 /*
79 * This function just sets the time of death of a process.
80 * When we rotate the cputime we remove it from the process list.
81 */
82 void death_proc(struct lttngtop *ctx, int tid, char *comm,
83 unsigned long timestamp)
84 {
85 struct processtop *tmp;
86 tmp = find_process_tid(ctx, tid, comm);
87 if (tmp && strcmp(tmp->comm, comm) == 0)
88 tmp->death = timestamp;
89 }
90
91 struct processtop* get_proc(struct lttngtop *ctx, int tid, char *comm,
92 unsigned long timestamp)
93 {
94 struct processtop *tmp;
95 tmp = find_process_tid(ctx, tid, comm);
96 if (tmp && strcmp(tmp->comm, comm) == 0)
97 return tmp;
98 return add_proc(ctx, tid, comm, timestamp);
99 }
100
101 void add_thread(struct processtop *parent, struct processtop *thread)
102 {
103 gint i;
104 struct processtop *tmp;
105
106 for (i = 0; i < parent->threads->len; i++) {
107 tmp = g_ptr_array_index(parent->threads, i);
108 if (tmp == thread)
109 return;
110 }
111 g_ptr_array_add(parent->threads, thread);
112 }
113
114 struct cputime* add_cpu(int cpu)
115 {
116 struct cputime *newcpu;
117
118 newcpu = g_new0(struct cputime, 1);
119 newcpu->id = cpu;
120 newcpu->current_task = NULL;
121 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
122
123 g_ptr_array_add(lttngtop.cpu_table, newcpu);
124
125 return newcpu;
126 }
127 struct cputime* get_cpu(int cpu)
128 {
129 gint i;
130 struct cputime *tmp;
131
132 for (i = 0; i < lttngtop.cpu_table->len; i++) {
133 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
134 if (tmp->id == cpu)
135 return tmp;
136 }
137
138 return add_cpu(cpu);
139 }
140
141 /*
142 * At the end of a sampling period, we need to display the cpu time for each
143 * process and to reset it to zero for the next period
144 */
145 void rotate_cputime(unsigned long end)
146 {
147 gint i;
148 struct cputime *tmp;
149 unsigned long elapsed;
150
151 for (i = 0; i < lttngtop.cpu_table->len; i++) {
152 tmp = g_ptr_array_index(lttngtop.cpu_table, i);
153 elapsed = end - tmp->task_start;
154 if (tmp->current_task) {
155 tmp->current_task->totalcpunsec += elapsed;
156 tmp->current_task->threadstotalcpunsec += elapsed;
157 if (tmp->current_task->pid != tmp->current_task->tid &&
158 tmp->current_task->threadparent) {
159 tmp->current_task->threadparent->threadstotalcpunsec += elapsed;
160 }
161 }
162 tmp->task_start = end;
163 }
164 }
165
166 void reset_perf_counter(gpointer key, gpointer value, gpointer user_data)
167 {
168 ((struct perfcounter*) value)->count = 0;
169 }
170
171 void copy_perf_counter(gpointer key, gpointer value, gpointer new_table)
172 {
173 struct perfcounter *newperf;
174
175 newperf = g_new0(struct perfcounter, 1);
176 newperf->count = ((struct perfcounter *) value)->count;
177 newperf->visible = ((struct perfcounter *) value)->visible;
178 newperf->sort = ((struct perfcounter *) value)->sort;
179 g_hash_table_insert((GHashTable *) new_table, strdup(key), newperf);
180 }
181
182 void rotate_perfcounter() {
183 int i;
184 struct processtop *tmp;
185 for (i = 0; i < lttngtop.process_table->len; i++) {
186 tmp = g_ptr_array_index(lttngtop.process_table, i);
187 g_hash_table_foreach(tmp->perf, reset_perf_counter, NULL);
188 }
189 }
190
191 void cleanup_processtop()
192 {
193 gint i;
194 struct processtop *tmp;
195
196 for (i = 0; i < lttngtop.process_table->len; i++) {
197 tmp = g_ptr_array_index(lttngtop.process_table, i);
198 tmp->totalcpunsec = 0;
199 tmp->threadstotalcpunsec = 0;
200 tmp->iostream->ret_read = 0;
201 tmp->iostream->ret_write = 0;
202 }
203 }
204
205 struct lttngtop* get_copy_lttngtop(unsigned long start, unsigned long end)
206 {
207 gint i, j;
208 unsigned long time;
209 struct lttngtop *dst;
210 struct processtop *tmp, *tmp2, *new;
211 struct cputime *tmpcpu, *newcpu;
212 struct files *tmpfile, *newfile;
213
214 dst = g_new0(struct lttngtop, 1);
215 dst->start = start;
216 dst->end = end;
217 dst->process_table = g_ptr_array_new();
218 dst->files_table = g_ptr_array_new();
219 dst->cpu_table = g_ptr_array_new();
220 dst->perf_list = g_hash_table_new(g_str_hash, g_str_equal);
221
222 rotate_cputime(end);
223
224 g_hash_table_foreach(lttngtop.perf_list, copy_perf_counter, dst->perf_list);
225 for (i = 0; i < lttngtop.process_table->len; i++) {
226 tmp = g_ptr_array_index(lttngtop.process_table, i);
227 new = g_new0(struct processtop, 1);
228
229 memcpy(new, tmp, sizeof(struct processtop));
230 new->threads = g_ptr_array_new();
231 new->comm = strdup(tmp->comm);
232 new->process_files_table = g_ptr_array_new();
233 new->perf = g_hash_table_new(g_str_hash, g_str_equal);
234 g_hash_table_foreach(tmp->perf, copy_perf_counter, new->perf);
235
236 new->iostream = g_new0(struct iostream, 1);
237 memcpy(new->iostream, tmp->iostream, sizeof(struct iostream));
238 /* compute the stream speed */
239 if (end - start != 0) {
240 time = (end - start) / NSEC_PER_SEC;
241 new->iostream->ret_read = new->iostream->ret_read / time;
242 new->iostream->ret_write = new->iostream->ret_write / time;
243 }
244
245 for (j = 0; j < tmp->process_files_table->len; j++) {
246 tmpfile = g_ptr_array_index(tmp->process_files_table, j);
247 newfile = g_new0(struct files, 1);
248
249 memcpy(newfile, tmpfile, sizeof(struct files));
250
251 newfile->name = strdup(tmpfile->name);
252 newfile->ref = new;
253
254 g_ptr_array_add(new->process_files_table, newfile);
255 g_ptr_array_add(dst->files_table, newfile);
256
257 /*
258 * if the process died during the last period, we remove all
259 * files associated with if after the copy
260 */
261 if (tmp->death > 0 && tmp->death < end) {
262 g_ptr_array_remove(tmp->process_files_table, tmpfile);
263 g_free(tmpfile);
264 }
265 }
266 g_ptr_array_add(dst->process_table, new);
267
268 /*
269 * if the process died during the last period, we remove it from
270 * the current process list after the copy
271 */
272 if (tmp->death > 0 && tmp->death < end) {
273 g_ptr_array_remove(lttngtop.process_table, tmp);
274 /* FIXME : TRUE does not mean clears the object in it */
275 g_ptr_array_free(tmp->threads, TRUE);
276 free(tmp->comm);
277 g_ptr_array_free(tmp->process_files_table, TRUE);
278 /* FIXME : clear elements */
279 g_hash_table_destroy(tmp->perf);
280 g_free(tmp);
281 }
282 }
283 rotate_perfcounter();
284
285 for (i = 0; i < lttngtop.cpu_table->len; i++) {
286 tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
287 newcpu = g_new0(struct cputime, 1);
288 memcpy(newcpu, tmpcpu, sizeof(struct cputime));
289 newcpu->perf = g_hash_table_new(g_str_hash, g_str_equal);
290 g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
291 /*
292 * note : we don't care about the current process pointer in the copy
293 * so the reference is invalid after the memcpy
294 */
295 g_ptr_array_add(dst->cpu_table, newcpu);
296 }
297 /* FIXME : better algo */
298 /* create the threads index if required */
299 for (i = 0; i < dst->process_table->len; i++) {
300 tmp = g_ptr_array_index(dst->process_table, i);
301 if (tmp->pid == tmp->tid) {
302 for (j = 0; j < dst->process_table->len; j++) {
303 tmp2 = g_ptr_array_index(dst->process_table, j);
304 if (tmp2->pid == tmp->pid) {
305 tmp2->threadparent = tmp;
306 g_ptr_array_add(tmp->threads, tmp2);
307 }
308 }
309 }
310 }
311
312 // update_global_stats(dst);
313 cleanup_processtop();
314
315 return dst;
316 }
317
This page took 0.050852 seconds and 4 git commands to generate.