e59de36c8157ffb6512f6f3a01872978f9fbe098
[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_direct_hash, g_direct_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_direct_hash, g_direct_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, 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_direct_hash, g_direct_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_direct_hash, g_direct_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 {
241 time = (end - start)/NSEC_PER_SEC;
242 new->iostream->ret_read = new->iostream->ret_read/(time);
243 new->iostream->ret_write = new->iostream->ret_write/(time);
244 }
245
246 for (j = 0; j < tmp->process_files_table->len; j++) {
247 tmpfile = g_ptr_array_index(tmp->process_files_table, j);
248 newfile = g_new0(struct files, 1);
249
250 memcpy(newfile, tmpfile, sizeof(struct files));
251
252 newfile->name = strdup(tmpfile->name);
253 newfile->ref = new;
254
255 g_ptr_array_add(new->process_files_table, newfile);
256 g_ptr_array_add(dst->files_table, newfile);
257
258 /*
259 * if the process died during the last period, we remove all
260 * files associated with if after the copy
261 */
262 if (tmp->death > 0 && tmp->death < end) {
263 g_ptr_array_remove(tmp->process_files_table, tmpfile);
264 g_free(tmpfile);
265 }
266 }
267 g_ptr_array_add(dst->process_table, new);
268
269 /*
270 * if the process died during the last period, we remove it from
271 * the current process list after the copy
272 */
273 if (tmp->death > 0 && tmp->death < end) {
274 g_ptr_array_remove(lttngtop.process_table, tmp);
275 g_ptr_array_free(tmp->threads, TRUE);
276 free(tmp->comm);
277 g_ptr_array_free(tmp->process_files_table, TRUE);
278 g_hash_table_destroy(tmp->perf);
279 g_free(tmp);
280 }
281 }
282 rotate_perfcounter();
283
284 for (i = 0; i < lttngtop.cpu_table->len; i++) {
285 tmpcpu = g_ptr_array_index(lttngtop.cpu_table, i);
286 newcpu = g_new0(struct cputime, 1);
287 memcpy(newcpu, tmpcpu, sizeof(struct cputime));
288 newcpu->perf = g_hash_table_new(g_direct_hash, g_direct_equal);
289 g_hash_table_foreach(tmpcpu->perf, copy_perf_counter, newcpu->perf);
290 /*
291 * note : we don't care about the current process pointer in the copy
292 * so the reference is invalid after the memcpy
293 */
294 g_ptr_array_add(dst->cpu_table, newcpu);
295 }
296 /* create the threads index if required */
297 for (i = 0; i < dst->process_table->len; i++) {
298 tmp = g_ptr_array_index(dst->process_table, i);
299 if (tmp->pid == tmp->tid) {
300 for (j = 0; j < dst->process_table->len; j++) {
301 tmp2 = g_ptr_array_index(dst->process_table, j);
302 if (tmp2->pid == tmp->pid) {
303 tmp2->threadparent = tmp;
304 g_ptr_array_add(tmp->threads, tmp2);
305 }
306 }
307 }
308 }
309
310 // update_global_stats(dst);
311 cleanup_processtop();
312
313 return dst;
314 }
315
This page took 0.054677 seconds and 4 git commands to generate.