#include <ltt/type.h>
#include <stdio.h>
+#define PREALLOCATED_EXECUTION_STACK 10
+
LttvExecutionMode
LTTV_STATE_MODE_UNKNOWN,
LTTV_STATE_USER_MODE,
LTTV_STATE_WAIT_FORK,
LTTV_STATE_WAIT_CPU,
LTTV_STATE_EXIT,
+ LTTV_STATE_ZOMBIE,
LTTV_STATE_WAIT,
LTTV_STATE_RUN;
guint process_hash(gconstpointer key)
{
- return ((LttvProcessState *)key)->pid;
+ guint pid = ((const LttvProcessState *)key)->pid;
+ return (pid>>8 ^ pid>>4 ^ pid>>2 ^ pid) ;
}
+/* If the hash table hash function is well distributed,
+ * the process_equal should compare different pid */
gboolean process_equal(gconstpointer a, gconstpointer b)
{
- LttvProcessState *process_a, *process_b;
-
- process_a = (LttvProcessState *)a;
- process_b = (LttvProcessState *)b;
+ const LttvProcessState *process_a, *process_b;
+ gboolean ret = TRUE;
+
+ process_a = (const LttvProcessState *)a;
+ process_b = (const LttvProcessState *)b;
+
+ if(likely(process_a->pid != process_b->pid)) ret = FALSE;
+ else if(likely(process_a->pid == 0 &&
+ process_a->last_cpu != process_b->last_cpu)) ret = FALSE;
- if(process_a->pid != process_b->pid) return FALSE;
- if(process_a->pid == 0 &&
- process_a->last_cpu != process_b->last_cpu) return FALSE;
- return TRUE;
+ return ret;
}
LttvTracefileState *tfcs;
- LttTime null_time = {0,0};
-
if(self->processes != NULL) lttv_state_free_process_table(self->processes);
self->processes = g_hash_table_new(process_hash, process_equal);
self->nb_event = 0;
for(i = 0 ; i < nb_tracefile ; i++) {
tfcs = LTTV_TRACEFILE_STATE(self->parent.tracefiles[i]);
- tfcs->parent.timestamp = null_time;
+ ltt_trace_time_span_get(self->parent.t, &tfcs->parent.timestamp, NULL);
tfcs->saved_position = 0;
tfcs->process = lttv_state_create_process(tfcs, NULL,0);
tfcs->process->state->s = LTTV_STATE_RUN;
tfcs->process->last_cpu = tfcs->cpu_name;
+ tfcs->process->last_cpu_index = ((LttvTracefileContext*)tfcs)->index;
}
}
static void
fini(LttvTracesetState *self)
{
- guint i, j, nb_trace;
+ guint i, nb_trace;
LttvTraceState *tcs;
tcs = (LttvTraceState *)(LTTV_TRACESET_CONTEXT(self)->traces[i]);
lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_TRACE_STATE_USE_COUNT,
LTTV_UINT, &v);
+
+ g_assert(*(v.v_uint) != 0);
(*v.v_uint)--;
- g_assert(*(v.v_uint) >= 0);
if(*(v.v_uint) == 0) {
free_name_tables(tcs);
free_max_time(tcs);
process = (LttvProcessState *)value;
new_process = g_new(LttvProcessState, 1);
*new_process = *process;
- new_process->execution_stack = g_array_new(FALSE, FALSE,
- sizeof(LttvExecutionState));
+ new_process->execution_stack = g_array_sized_new(FALSE, FALSE,
+ sizeof(LttvExecutionState), PREALLOCATED_EXECUTION_STACK);
g_array_set_size(new_process->execution_stack,process->execution_stack->len);
for(i = 0 ; i < process->execution_stack->len; i++) {
g_array_index(new_process->execution_stack, LttvExecutionState, i) =
guint depth = process->execution_stack->len;
if(process->state->t != t){
- g_info("Different execution mode type (%d.%09d): ignore it\n",
+ g_info("Different execution mode type (%lu.%09lu): ignore it\n",
tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec);
g_info("process state has %s when pop_int is %s\n",
g_quark_to_string(process->state->t),
}
if(depth == 1){
- g_info("Trying to pop last state on stack (%d.%09d): ignore it\n",
+ g_info("Trying to pop last state on stack (%lu.%09lu): ignore it\n",
tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec);
return;
}
process->pid = pid;
process->last_cpu = tfs->cpu_name;
+ process->last_cpu_index = ((LttvTracefileContext*)tfs)->index;
g_warning("Process %u, core %p", process->pid, process);
g_hash_table_insert(tcs->processes, process, process);
process->creation_time.tv_nsec);
process->pid_time = g_quark_from_string(buffer);
process->last_cpu = tfs->cpu_name;
- process->execution_stack = g_array_new(FALSE, FALSE,
- sizeof(LttvExecutionState));
+ process->last_cpu_index = ((LttvTracefileContext*)tfs)->index;
+ process->execution_stack = g_array_sized_new(FALSE, FALSE,
+ sizeof(LttvExecutionState), PREALLOCATED_EXECUTION_STACK);
g_array_set_size(process->execution_stack, 1);
es = process->state = &g_array_index(process->execution_stack,
LttvExecutionState, 0);
es->t = LTTV_STATE_USER_MODE;
es->n = LTTV_STATE_SUBMODE_NONE;
es->entry = tfs->parent.timestamp;
+ g_assert(tfs->parent.timestamp.tv_sec != 0);
es->change = tfs->parent.timestamp;
es->s = LTTV_STATE_WAIT_FORK;
return process;
}
-LttvProcessState *
-lttv_state_find_process_from_trace(LttvTraceState *ts, GQuark cpu, guint pid)
+LttvProcessState *lttv_state_find_process(LttvTracefileState *tfs,
+ guint pid)
{
LttvProcessState key;
LttvProcessState *process;
+ LttvTraceState* ts = (LttvTraceState*)tfs->parent.t_context;
+
key.pid = pid;
- key.last_cpu = cpu;
+ key.last_cpu = tfs->cpu_name;
process = g_hash_table_lookup(ts->processes, &key);
return process;
}
-
-LttvProcessState *lttv_state_find_process(LttvTracefileState *tfs,
- guint pid)
-{
- LttvTraceState *ts =(LttvTraceState *)tfs->parent.t_context;
- return lttv_state_find_process_from_trace(ts, tfs->cpu_name, pid);
-}
-
-
LttvProcessState *
lttv_state_find_process_or_create(LttvTracefileState *tfs, guint pid)
{
LttvProcessState *process = lttv_state_find_process(tfs, pid);
- if(process == NULL) process = lttv_state_create_process(tfs, NULL, pid);
+ if(unlikely(process == NULL)) process = lttv_state_create_process(tfs, NULL, pid);
return process;
}
-
+/* FIXME : this function should be called when we receive an event telling that
+ * release_task has been called in the kernel. In happens generally when
+ * the parent waits for its child terminaison, but may also happen in special
+ * cases in the child's exit : when the parent ignores its children SIGCCHLD or
+ * has the flag SA_NOCLDWAIT. It can also happen when the child is part
+ * of a killed thread ground, but isn't the leader.
+ */
static void exit_process(LttvTracefileState *tfs, LttvProcessState *process)
{
LttvTraceState *ts = LTTV_TRACE_STATE(tfs->parent.t_context);
pid_out = ltt_event_get_unsigned(s->parent.e, h->f2);
state_out = ltt_event_get_unsigned(s->parent.e, h->f3);
- if(s->process != NULL) {
+ if(likely(s->process != NULL)) {
/* We could not know but it was not the idle process executing.
This should only happen at the beginning, before the first schedule
is missing. It is not obvious how we could, after the fact, compensate
the wrongly attributed statistics. */
- if(s->process->pid != pid_out) {
+ if(unlikely(s->process->pid != pid_out)) {
g_assert(s->process->pid == 0);
}
- if(state_out == 0) s->process->state->s = LTTV_STATE_WAIT_CPU;
- else if(s->process->state->s == LTTV_STATE_EXIT)
- exit_process(s, s->process);
- else s->process->state->s = LTTV_STATE_WAIT;
+ if(unlikely(s->process->state->s == LTTV_STATE_EXIT)) {
+ s->process->state->s = LTTV_STATE_ZOMBIE;
+ } else {
+ if(unlikely(state_out == 0)) s->process->state->s = LTTV_STATE_WAIT_CPU;
+ else s->process->state->s = LTTV_STATE_WAIT;
+ } /* FIXME : we do not remove process here, because the kernel
+ * still has them : they may be zombies. We need to know
+ * exactly when release_task is executed on the PID to
+ * know when the zombie is destroyed.
+ */
+ //else
+ // exit_process(s, s->process);
s->process->state->change = s->parent.timestamp;
}
s->process = lttv_state_find_process_or_create(s, pid_in);
s->process->state->s = LTTV_STATE_RUN;
s->process->last_cpu = s->cpu_name;
+ s->process->last_cpu_index = ((LttvTracefileContext*)s)->index;
s->process->state->change = s->parent.timestamp;
return FALSE;
}
{
LttField *f;
guint child_pid;
+ LttvProcessState *zombie_process;
/* Child PID */
f = trace_hook->f2;
child_pid = ltt_event_get_unsigned(s->parent.e, f);
- lttv_state_create_process(s, s->process, child_pid);
-
- return FALSE;
-#if 0
- LttField *f = ((LttvTraceHook *)hook_data)->f1;
-
- LttvTracefileState *s = (LttvTracefileState *)call_data;
-
- guint child_pid;
+ zombie_process = lttv_state_find_process(s, child_pid);
- child_pid = ltt_event_get_unsigned(s->parent.e, f);
+ if(unlikely(zombie_process != NULL)) {
+ /* Reutilisation of PID. Only now we are sure that the old PID
+ * has been released. FIXME : sould know when release_task happens instead.
+ */
+ exit_process(s, zombie_process);
+ }
+ g_assert(s->process->pid != child_pid);
lttv_state_create_process(s, s->process, child_pid);
+
return FALSE;
-#endif //0
}
static gboolean process_exit(LttvTraceHook *trace_hook, LttvTracefileState *s)
{
- if(s->process != NULL) {
+ if(likely(s->process != NULL)) {
s->process->state->s = LTTV_STATE_EXIT;
}
return FALSE;
-
-#if 0
- LttvTracefileState *s = (LttvTracefileState *)call_data;
+}
- if(s->process != NULL) {
- s->process->state->s = LTTV_STATE_EXIT;
+static gboolean process_release(LttvTraceHook *trace_hook,
+ LttvTracefileState *s)
+{
+ LttField *f;
+ guint release_pid;
+ LttvProcessState *process;
+
+ /* PID of the process to release */
+ f = trace_hook->f2;
+ release_pid = ltt_event_get_unsigned(s->parent.e, f);
+
+ process = lttv_state_find_process(s, release_pid);
+
+ if(likely(process != NULL)) {
+ /* release_task is happening at kernel level : we can now safely release
+ * the data structure of the process */
+ exit_process(s, process);
}
+
return FALSE;
-#endif //0
}
gboolean process(void *hook_data, void *call_data)
return process_fork(trace_hook, s);
} else if(sub_id == 3) {
return process_exit(trace_hook, s);
+ } else if(sub_id == 7) {
+ return process_release(trace_hook, s);
}
return 0;
}
+gint lttv_state_hook_add_event_hooks(void *hook_data, void *call_data)
+{
+ LttvTracesetState *tss = (LttvTracesetState*)(call_data);
+
+ lttv_state_add_event_hooks(tss);
+
+ return 0;
+}
void lttv_state_add_event_hooks(LttvTracesetState *self)
{
}
}
+gint lttv_state_hook_remove_event_hooks(void *hook_data, void *call_data)
+{
+ LttvTracesetState *tss = (LttvTracesetState*)(call_data);
+
+ lttv_state_remove_event_hooks(tss);
+
+ return 0;
+}
void lttv_state_remove_event_hooks(LttvTracesetState *self)
{
self->saved_position = 0;
*(tcs->max_time_state_recomputed_in_seek) = self->parent.timestamp;
g_free(ep);
+
+ return FALSE;
}
{
LttvTraceset *traceset = self->parent.ts;
- guint i, j, k, nb_trace, nb_tracefile;
+ guint i, j, nb_trace, nb_tracefile;
LttvTraceState *ts;
{
LttvTraceset *traceset = self->parent.ts;
- guint i, j, k, nb_trace, nb_tracefile;
+ guint i, j, nb_trace, nb_tracefile;
LttvTraceState *ts;
{
LttvTraceset *traceset = self->parent.ts;
- guint i, j, nb_trace, nb_saved_state;
+ guint i, nb_trace;
int min_pos, mid_pos, max_pos;
NULL, /* class_data */
sizeof (LttvTracesetState),
0, /* n_preallocs */
- (GInstanceInitFunc) traceset_state_instance_init /* instance_init */
+ (GInstanceInitFunc) traceset_state_instance_init, /* instance_init */
+ NULL /* value handling */
};
type = g_type_register_static (LTTV_TRACESET_CONTEXT_TYPE, "LttvTracesetStateType",
NULL, /* class_data */
sizeof (LttvTraceState),
0, /* n_preallocs */
- (GInstanceInitFunc) trace_state_instance_init /* instance_init */
+ (GInstanceInitFunc) trace_state_instance_init, /* instance_init */
+ NULL /* value handling */
};
type = g_type_register_static (LTTV_TRACE_CONTEXT_TYPE,
NULL, /* class_data */
sizeof (LttvTracefileState),
0, /* n_preallocs */
- (GInstanceInitFunc) tracefile_state_instance_init /* instance_init */
+ (GInstanceInitFunc) tracefile_state_instance_init, /* instance_init */
+ NULL /* value handling */
};
type = g_type_register_static (LTTV_TRACEFILE_CONTEXT_TYPE,
LTTV_STATE_SUBMODE_NONE = g_quark_from_string("(no submode)");
LTTV_STATE_WAIT_CPU = g_quark_from_string("wait for cpu");
LTTV_STATE_EXIT = g_quark_from_string("exiting");
+ LTTV_STATE_ZOMBIE = g_quark_from_string("zombie");
LTTV_STATE_WAIT = g_quark_from_string("wait for I/O");
LTTV_STATE_RUN = g_quark_from_string("running");
LTTV_STATE_TRACEFILES = g_quark_from_string("tracefiles");