clouseau/src/lib/extensions/evlog/main.c

1623 lines
44 KiB
C

#include <Eina.h>
#include <Elementary.h>
#include "../../Clouseau.h"
#define _EET_ENTRY "config"
#define RES 500000.0
#define LEN 5.0
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define SWAP_64(x) x
#define SWAP_32(x) x
#define SWAP_16(x) x
#define SWAP_DBL(x) x
#else
#define SWAP_64(x) eina_swap64(x)
#define SWAP_32(x) eina_swap32(x)
#define SWAP_16(x) eina_swap16(x)
#define SWAP_DBL(x) SWAP_64(x)
#endif
typedef struct _Evlog Evlog;
typedef struct _Evlog_Thread Evlog_Thread;
typedef struct _Evlog_Event Evlog_Event;
typedef struct _Evlog_Cpu_Use Evlog_Cpu_Use;
typedef struct _Evlog_Cpu_Freq Evlog_Cpu_Freq;
struct _Evlog
{
double first_timestamp;
double last_timestamp;
int state_uniq;
int state_num;
int cpucores;
int cpumhzmax;
int thread_num;
int cpufreq_num;
Evlog_Event *states;
Evlog_Thread *threads;
Evlog_Cpu_Freq *cpufreqs;
char **state_uniq_str;
int cpumhzlast[64];
};
struct _Evlog_Event
{
const char *event;
const char *detail;
double timestamp;
double latency;
};
struct _Evlog_Cpu_Use
{
int usage;
double timestamp;
};
struct _Evlog_Cpu_Freq
{
int core;
int mhz;
double timestamp;
};
struct _Evlog_Thread
{
unsigned long long id;
int event_num;
int cpuused_num;
Evlog_Event *events;
Evlog_Cpu_Use *cpuused;
};
typedef struct
{
void *src;
char *event;
char *detail;
Evas_Object *obj;
double t0, t1;
int n, n2;
int slot;
Eina_Bool nuke : 1;
Eina_Bool cpu_use : 1;
} Event;
typedef struct
{
Evlog *evlog;
Eo *main;
Eo *zoom_slider;
Eo *scroller;
Eo *table;
Eo *record_icon;
Eo *record_button;
Ecore_Timer *record_get_timer;
Eo *refresh_interval_entry;
struct {
Evas_Object *state;
Evas_Object *cpufreq;
Evas_Object **thread;
Evas_Object *over;
} grid;
Eina_List *objs;
struct {
Ecore_Job *job;
Ecore_Thread *thread;
double t0, t1, tmin;
volatile Eina_Bool redo;
Eina_List *remobjs;
} update;
} Inf;
#define ROUND_AMOUNT 1024
#define ROUND(x) ((x + (ROUND_AMOUNT - 1)) / ROUND_AMOUNT) * ROUND_AMOUNT;
static void _fill_begin(Inf *inf);
static int _record_on_op = EINA_DEBUG_OPCODE_INVALID;
static int _record_off_op = EINA_DEBUG_OPCODE_INVALID;
static int _record_get_op = EINA_DEBUG_OPCODE_INVALID;
static Eina_Bool _record_get_cb(Eina_Debug_Session *, int, void *, int);
EINA_DEBUG_OPCODES_ARRAY_DEFINE(_ops,
{"CPU/Freq/on", &_record_on_op, NULL},
{"CPU/Freq/off", &_record_off_op, NULL},
{"EvLog/get", &_record_get_op, &_record_get_cb},
{NULL, NULL, NULL}
);
static void
_evlog_state_event_register(Evlog *evlog, Evlog_Event *ev)
{
int n0, n, j;
for (j = 0; j < evlog->state_uniq; j++)
{
if (!strcmp(evlog->state_uniq_str[j], ev->event + 1)) break;
}
if (j == evlog->state_uniq)
{
evlog->state_uniq++;
evlog->state_uniq_str = realloc(evlog->state_uniq_str,
evlog->state_uniq * sizeof(char *));
evlog->state_uniq_str[evlog->state_uniq - 1] = strdup(ev->event + 1);
}
n0 = evlog->state_num;
evlog->state_num++;
n = evlog->state_num;
n0 = ROUND(n0);
n = ROUND(n);
if (n != n0)
{
Evlog_Event *tmp;
tmp = realloc(evlog->states, n * sizeof(Evlog_Event));
if (!tmp)
{
eina_stringshare_del(ev->event);
eina_stringshare_del(ev->detail);
return;
}
evlog->states = tmp;
}
evlog->states[evlog->state_num - 1] = *ev;
}
static void
_evlog_thread_event_register(Evlog_Thread *th, Evlog_Event *ev)
{
int n0, n;
n0 = th->event_num;
th->event_num++;
n = th->event_num;
n0 = ROUND(n0);
n = ROUND(n);
if (n != n0)
{
Evlog_Event *tmp;
tmp = realloc(th->events, n * sizeof(Evlog_Event));
if (!tmp)
{
eina_stringshare_del(ev->event);
eina_stringshare_del(ev->detail);
return;
}
th->events = tmp;
}
th->events[th->event_num - 1] = *ev;
}
static void
_evlog_thread_cpu_freq(Evlog *evlog, double timestamp, int core, int mhz)
{
int n0, n;
n0 = evlog->cpufreq_num;
evlog->cpufreq_num++;
n = evlog->cpufreq_num;
n0 = ROUND(n0);
n = ROUND(n);
if (n != n0)
{
Evlog_Cpu_Freq *tmp;
tmp = realloc(evlog->cpufreqs, n * sizeof(Evlog_Cpu_Freq));
if (!tmp) return;
evlog->cpufreqs = tmp;
}
evlog->cpufreqs[evlog->cpufreq_num - 1].core = core;
evlog->cpufreqs[evlog->cpufreq_num - 1].mhz = mhz;
evlog->cpufreqs[evlog->cpufreq_num - 1].timestamp = timestamp;
if (evlog->cpucores <= core) evlog->cpucores = core + 1;
if (mhz > evlog->cpumhzmax) evlog->cpumhzmax = mhz;
evlog->cpumhzlast[core] = mhz;
}
static void
_evlog_thread_cpu_use(Evlog_Thread *th, double timestamp, int cpu)
{
int n0, n;
n0 = th->cpuused_num;
th->cpuused_num++;
n = th->cpuused_num;
n0 = ROUND(n0);
n = ROUND(n);
if (n != n0)
{
Evlog_Cpu_Use *tmp;
tmp = realloc(th->cpuused, n * sizeof(Evlog_Cpu_Use));
if (!tmp) return;
th->cpuused = tmp;
}
th->cpuused[th->cpuused_num - 1].usage = cpu;
th->cpuused[th->cpuused_num - 1].timestamp = timestamp;
}
static Eina_Bool
_evlog_thread_push(Evlog *evlog, int slot, unsigned long long thread)
{
Evlog_Thread *tmp;
if (slot < evlog->thread_num) return EINA_TRUE;
evlog->thread_num = slot + 1;
tmp = realloc(evlog->threads, evlog->thread_num * sizeof(Evlog_Thread));
if (!tmp) return EINA_FALSE;
evlog->threads = tmp;
evlog->threads[slot].id = thread;
evlog->threads[slot].event_num = 0;
evlog->threads[slot].events = NULL;
evlog->threads[slot].cpuused_num = 0;
evlog->threads[slot].cpuused = NULL;
return EINA_TRUE;
}
static int
_evlog_thread_slot_find(Evlog *evlog, unsigned long long thread)
{
int i;
for (i = 0; i < evlog->thread_num; i++)
{
if (evlog->threads[i].id == thread) return i;
}
return i;
}
static void
_evlog_event_register(Evlog *evlog, unsigned long long thread, Evlog_Event *ev)
{
int i;
if ((ev->event[0] == '<') || (ev->event[0] == '>'))
{
_evlog_state_event_register(evlog, ev);
}
else if (ev->event[0] == '*')
{
if ((!strncmp(ev->event, "*CPUFREQ ", 9)) && (ev->detail))
{
int cpu = atoi(ev->event + 9);
int freq = atoi(ev->detail);
_evlog_thread_cpu_freq(evlog, ev->timestamp, cpu, freq);
}
else if ((!strncmp(ev->event, "*CPUUSED ", 9)) && (ev->detail))
{
unsigned long long thcpu = atoll(ev->event + 9);
int cpu = atoi(ev->detail);
i = _evlog_thread_slot_find(evlog, thcpu);
if (!_evlog_thread_push(evlog, i, thcpu)) return;
_evlog_thread_cpu_use(&(evlog->threads[i]), ev->timestamp, cpu);
}
eina_stringshare_del(ev->event);
eina_stringshare_del(ev->detail);
}
else
{
i = _evlog_thread_slot_find(evlog, thread);
if (!_evlog_thread_push(evlog, i, thread))
{
eina_stringshare_del(ev->event);
eina_stringshare_del(ev->detail);
return;
}
_evlog_thread_event_register(&(evlog->threads[i]), ev);
}
}
static void *
_evlog_event_read(Evlog *evlog, void *ptr, void *end)
{
char *data = ptr;
char *dataend = end;
const char *eventstr = NULL, *detailstr = NULL;
Eina_Evlog_Item item;
if ((unsigned int)(dataend - data) < sizeof(Eina_Evlog_Item)) return NULL;
memcpy(&item, data, sizeof(Eina_Evlog_Item));
item.tim = SWAP_DBL(item.tim);
item.srctim = SWAP_DBL(item.srctim);
item.thread = SWAP_64(item.thread);
item.obj = SWAP_64(item.obj);
item.event_offset = SWAP_16(item.event_offset);
item.detail_offset = SWAP_16(item.detail_offset);
item.event_next = SWAP_16(item.event_next);
if (item.event_offset >= sizeof(Eina_Evlog_Item))
eventstr = data + item.event_offset;
if (item.detail_offset >= sizeof(Eina_Evlog_Item))
detailstr = data + item.detail_offset;
if (eventstr)
{
Evlog_Event ev;
ev.event = eina_stringshare_add(eventstr);
if (detailstr)
ev.detail = eina_stringshare_add(detailstr);
else
ev.detail = NULL;
ev.timestamp = (item.srctim == 0.0) ? item.tim : item.srctim;
ev.latency = (item.srctim != 0.0) ? item.tim - item.srctim : 0.0;
if (evlog->first_timestamp == 0.0)
evlog->first_timestamp = ev.timestamp;
if (ev.timestamp > evlog->last_timestamp)
evlog->last_timestamp = ev.timestamp;
_evlog_event_register(evlog, item.thread, &ev);
}
data += item.event_next;
if (data >= dataend) return NULL;
return data;
}
static Eina_Bool
_evlog_block_read(Evlog *evlog, char *buffer, int size)
{
char *ptr, *end;
int i;
/* The first 4 bytes correspond to the overflow.
* It is not used so we pass it. */
if (size < 4) return EINA_FALSE;
size -= 4;
ptr = buffer + 4;
end = ptr + size;
while ((ptr = _evlog_event_read(evlog, ptr, end)));
for (i = 0; i < evlog->cpucores; i++)
{
_evlog_thread_cpu_freq(evlog, evlog->last_timestamp,
i, evlog->cpumhzlast[i]);
}
return EINA_TRUE;
}
static Eina_Bool
_can_see(double t0, double t1, double tmin, double t0in, double t1in)
{
if (t1in >= 0.0)
{
if ((t0in <= t1) &&
(t1in >= t0) &&
((t1in - t0in) >= tmin))
return EINA_TRUE;
}
else
{
if ((t0in <= t1) &&
(t0in >= t0))
return EINA_TRUE;
}
return EINA_FALSE;
}
static void
_create_log_states(Inf *inf)
{
Evas_Object *o;
int h = 1;
double res = elm_slider_value_get(inf->zoom_slider);
o = elm_grid_add(inf->main);
inf->grid.state = o;
elm_grid_size_set(o, LEN * RES, h);
res = res * res;
evas_object_size_hint_min_set(o, LEN * res, h * 20);
}
static void
_fill_log_states(Inf *inf)
{
Evas_Object *o, *oo;
Evlog *evlog = inf->evlog;
int i, j;
int h = 0;
Eina_List *events = NULL, *l;
Event *ev;
Evlog_Event *e;
double t;
double len = evlog->last_timestamp - evlog->first_timestamp;
o = inf->grid.state;
for (i = 0; i < evlog->state_num; i++)
{
e = &(evlog->states[i]);
t = e->timestamp - evlog->first_timestamp;
if (e->event[0] == '>')
{
ev = calloc(1, sizeof(Event));
if (ev)
{
ev->src = e;
ev->event = strdup(e->event + 1);
if (e->detail) ev->detail = strdup(e->detail);
ev->t0 = t;
events = eina_list_append(events, ev);
for (j = 0; j < evlog->state_uniq; j++)
{
if (!strcmp(evlog->state_uniq_str[j], ev->event))
{
ev->n = j;
break;
}
}
if ((ev->n + 1) > h) h = ev->n + 1;
}
}
else if (e->event[0] == '<')
{
EINA_LIST_FOREACH(events, l, ev)
{
if (!strcmp(ev->event, e->event + 1))
{
ev->t1 = t;
free(ev->event);
free(ev->detail);
free(ev);
events = eina_list_remove_list(events, l);
break;
}
}
}
}
t = evlog->last_timestamp - evlog->first_timestamp;
EINA_LIST_FREE(events, ev)
{
ev->t1 = t;
free(ev->event);
free(ev->detail);
free(ev);
}
if (h < 1) h = 1;
elm_grid_size_set(o, len * RES, h);
double res = elm_slider_value_get(inf->zoom_slider);
res = res * res;
evas_object_size_hint_min_set(o, len * res, h * 20);
oo = evas_object_rectangle_add(evas_object_evas_get(inf->main));
evas_object_color_set(oo, 24, 24, 24, 255);
elm_grid_pack(o, oo, 0, 0, len * RES, h);
evas_object_show(oo);
}
static void
_add_log_state(Inf *inf, Event *ev)
{
Eina_List *l;
Event *ev2;
EINA_LIST_FOREACH(inf->objs, l, ev2)
{
if (ev2->src == ev->src)
{
free(ev->event);
free(ev->detail);
free(ev);
return;
}
}
inf->objs = eina_list_append(inf->objs, ev);
}
static void
_add_log_event(Inf *inf, Event *ev)
{
Eina_List *l;
Event *ev2;
EINA_LIST_FOREACH(inf->objs, l, ev2)
{
if (ev2->src == ev->src)
{
free(ev->event);
free(ev->detail);
free(ev);
return;
}
}
inf->objs = eina_list_append(inf->objs, ev);
}
static void
_create_cpufreq_states(Inf *inf)
{
int cpucores = 1;
inf->grid.cpufreq = elm_grid_add(inf->main);
elm_grid_size_set(inf->grid.cpufreq, LEN * RES, cpucores);
}
static void
_fill_cpufreq_states(Inf *inf)
{
Evas_Object *o, *oo;
Evlog *evlog = inf->evlog;
double len = evlog->last_timestamp - evlog->first_timestamp;
o = inf->grid.cpufreq;
elm_grid_size_set(o, len * RES, evlog->cpucores);
evas_object_size_hint_min_set(o, 1, evlog->cpucores * 10);
oo = evas_object_rectangle_add(evas_object_evas_get(inf->main));
evas_object_color_set(oo, 16, 16, 16, 255);
elm_grid_pack(o, oo, 0, 0, len * RES, evlog->cpucores * 4);
evas_object_show(oo);
}
static void
_fill_timed_log_states(Inf *inf, Evlog *evlog, double t0, double t1, double tmin)
{
int i, j;
Eina_List *events = NULL, *l;
Event *ev;
char buf[256];
Evlog_Event *e;
double t;
double *ct;
for (i = 0; i < evlog->state_num; i++)
{
e = &(evlog->states[i]);
t = e->timestamp - evlog->first_timestamp;
if (e->event[0] == '>')
{
ev = calloc(1, sizeof(Event));
if (ev)
{
ev->src = e;
ev->event = strdup(e->event + 1);
if (e->detail) ev->detail = strdup(e->detail);
ev->t0 = t;
events = eina_list_append(events, ev);
for (j = 0; j < evlog->state_uniq; j++)
{
if (!strcmp(evlog->state_uniq_str[j], ev->event))
{
ev->n = j;
break;
}
}
ev->slot = 0;
}
}
else if (e->event[0] == '<')
{
EINA_LIST_FOREACH(events, l, ev)
{
if (!strcmp(ev->event, e->event + 1))
{
ev->t1 = t;
if (_can_see(t0, t1, tmin, ev->t0, ev->t1))
_add_log_state(inf, ev);
else
{
free(ev->event);
free(ev->detail);
free(ev);
}
events = eina_list_remove_list(events, l);
break;
}
}
}
}
ct = calloc(evlog->cpucores, sizeof(double));
for (i = 0; i < evlog->cpufreq_num; i++)
{
Evlog_Cpu_Freq *f = &(evlog->cpufreqs[i]);
double tt = f->timestamp - evlog->first_timestamp;
if (_can_see(t0, t1, tmin, ct[f->core], tt))
{
ev = calloc(1, sizeof(Event));
if (ev)
{
ev->src = f;
ev->event = strdup("*CPUFREQ");
snprintf(buf, sizeof(buf), "%iMHz", f->mhz);
ev->detail = strdup(buf);
ev->n = f->core;
ev->n2 = f->mhz;
ev->t0 = ct[f->core];
ev->t1 = tt;
ev->slot = -2;
_add_log_event(inf, ev);
}
}
ct[f->core] = tt;
}
free(ct);
t = evlog->last_timestamp - evlog->first_timestamp;
EINA_LIST_FREE(events, ev)
{
ev->t1 = t;
if (_can_see(t0, t1, tmin, ev->t0, ev->t1))
_add_log_state(inf, ev);
else
{
free(ev->event);
free(ev->detail);
free(ev);
}
}
}
static Evas_Object *
_create_log_thread(Inf *inf, Evlog *evlog, Evlog_Thread *th, int slot)
{
Evas_Object *o, *oo;
double len = evlog->last_timestamp - evlog->first_timestamp;
int i, c;
int h = 0;
Eina_List *stack = NULL, *l;
Event *ev;
Evlog_Event *e;
double t;
o = elm_grid_add(inf->main);
for (i = 0; i < th->event_num; i++)
{
e = &(th->events[i]);
t = e->timestamp - evlog->first_timestamp;
if (e->event[0] == '+')
{
ev = calloc(1, sizeof(Event));
if (ev)
{
ev->src = e;
stack = eina_list_append(stack, ev);
ev->event = strdup(e->event + 1);
if (e->detail) ev->detail = strdup(e->detail);
ev->t0 = t;
ev->n = eina_list_count(stack) - 1;
if ((ev->n + 1) > h) h = ev->n + 1;
}
}
else if (e->event[0] == '-')
{
l = eina_list_last(stack);
if (l)
{
ev = l->data;
ev->t1 = t;
free(ev->event);
free(ev);
stack = eina_list_remove_list(stack, l);
}
}
else if (e->event[0] == '!')
{
}
else if (e->event[0] == '*')
{
// XXX: handle:
// 'CPUFREQ [X]' (CPU core) | [N] (Mhz)
// 'CPUUSED [X]' (Thread ID) | [N] (percent CPU used)
}
}
t = evlog->last_timestamp - evlog->first_timestamp;
EINA_LIST_FREE(stack, ev)
{
ev->t1 = t;
free(ev->event);
free(ev->detail);
free(ev);
}
if (h < 1) h = 1;
elm_grid_size_set(o, len * RES, (h * 2) + 1);
evas_object_size_hint_min_set(o, 1, (h * 20) + 10);
oo = evas_object_rectangle_add(evas_object_evas_get(inf->main));
c = 32 + ((slot % 2) * 16);
evas_object_color_set(oo, c, c, c, 255);
elm_grid_pack(o, oo, 0, 0, len * RES, (h * 2) + 1);
evas_object_show(oo);
return o;
}
static void
_fill_log_thread(Inf *inf, Evlog *evlog, Evlog_Thread *th, int slot, double t0, double t1, double tmin)
{
Eina_List *stack = NULL, *l;
Event *ev;
int i;
int h = 0;
Evlog_Event *e;
double t;
char buf[256];
for (i = 0; i < th->event_num; i++)
{
e = &(th->events[i]);
t = e->timestamp - evlog->first_timestamp;
if (e->event[0] == '+')
{
ev = calloc(1, sizeof(Event));
if (ev)
{
ev->src = e;
stack = eina_list_append(stack, ev);
ev->event = strdup(e->event + 1);
if (e->detail) ev->detail = strdup(e->detail);
ev->t0 = t;
ev->n = eina_list_count(stack) - 1;
ev->slot = slot;
if ((ev->n + 1) > h) h = ev->n + 1;
}
}
else if (e->event[0] == '-')
{
l = eina_list_last(stack);
if (l)
{
ev = l->data;
ev->t1 = t;
if (_can_see(t0, t1, tmin, ev->t0, ev->t1))
_add_log_event(inf, ev);
else
{
free(ev->event);
free(ev->detail);
free(ev);
}
stack = eina_list_remove_list(stack, l);
}
}
else if (e->event[0] == '!')
{
ev = calloc(1, sizeof(Event));
if (ev)
{
ev->src = e;
ev->event = strdup(e->event + 1);
if (e->detail) ev->detail = strdup(e->detail);
ev->t0 = t;
ev->t1 = -1.0;
ev->n = 0;
ev->slot = -1;
if (_can_see(t0, t1, tmin, ev->t0, ev->t1))
_add_log_event(inf, ev);
else
{
free(ev->event);
free(ev->detail);
free(ev);
}
}
}
// else if (e->event[0] == '*')
// {
// XXX: handle:
// 'CPUFREQ [X]' (CPU core) | [N] (Mhz)
// 'CPUUSED [X]' (Thread ID) | [N] (percent CPU used)
// }
}
t = evlog->last_timestamp - evlog->first_timestamp;
EINA_LIST_FREE(stack, ev)
{
ev->t1 = t;
if (_can_see(t0, t1, tmin, ev->t0, ev->t1))
_add_log_event(inf, ev);
else
{
free(ev->event);
free(ev->detail);
free(ev);
}
}
t = 0.0;
for (i = 0; i < th->cpuused_num; i++)
{
double tt = th->cpuused[i].timestamp - evlog->first_timestamp;
if (_can_see(t0, t1, tmin, t, tt))
{
ev = calloc(1, sizeof(Event));
if (ev)
{
ev->src = &(th->cpuused[i]);
ev->event = strdup("*CPUUSED");
snprintf(buf, sizeof(buf), "%i%%", th->cpuused[i].usage);
ev->detail = strdup(buf);
ev->t0 = t;
ev->t1 = tt;
ev->n = th->cpuused[i].usage;
ev->slot = slot;
ev->cpu_use = 1;
_add_log_event(inf, ev);
}
}
t = tt;
}
}
static void
_fill_event_clean(Inf *inf, double t0, double t1, double tmin)
{
Eina_List *l;
Event *ev;
EINA_LIST_FOREACH(inf->objs, l, ev)
{
if (_can_see(t0, t1, tmin, ev->t0, ev->t1))
{
if (ev->nuke)
{
inf->update.remobjs = eina_list_remove(inf->update.remobjs, ev);
ev->nuke = 0;
}
}
else
{
if (!ev->nuke)
{
inf->update.remobjs = eina_list_append(inf->update.remobjs, ev);
ev->nuke = 1;
}
}
}
}
static void
_create_log_table(Inf *inf)
{
Evas_Object *o;
int y = 0;
o = elm_label_add(inf->main);
elm_object_text_set(o, "<b>CPU FREQ</b>");
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_table_pack(inf->table, o, 0, y, 1, 1);
evas_object_show(o);
_create_cpufreq_states(inf);
o = inf->grid.cpufreq;
evas_object_size_hint_weight_set(o, 0.0, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_table_pack(inf->table, o, 2, y++, 1, 1);
evas_object_show(o);
o = elm_label_add(inf->main);
elm_object_text_set(o, "<b>STATES</b>");
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_table_pack(inf->table, o, 0, y, 1, 1);
evas_object_show(o);
_create_log_states(inf);
o = inf->grid.state;
evas_object_size_hint_weight_set(o, 0.0, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_table_pack(inf->table, o, 2, y++, 1, 1);
evas_object_show(o);
}
static void
_fill_log_table(Inf *inf)
{
Evlog *evlog = inf->evlog;
Eo *o;
char buf[256];
int i, y = 2;
_fill_cpufreq_states(inf);
_fill_log_states(inf);
inf->grid.thread = calloc(1, evlog->thread_num * sizeof(Evas_Object *));
for (i = 0; i < evlog->thread_num; i++)
{
snprintf(buf, sizeof(buf), "<b>%i</b>", i + 1);
o = elm_label_add(inf->main);
elm_object_text_set(o, buf);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_table_pack(inf->table, o, 0, y, 1, 1);
evas_object_show(o);
o = _create_log_thread(inf, evlog, &(evlog->threads[i]), i);
inf->grid.thread[i] = o;
evas_object_size_hint_weight_set(o, 0.0, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.5);
elm_table_pack(inf->table, o, 2, y++, 1, 1);
evas_object_show(o);
}
o = elm_separator_add(inf->main);
evas_object_size_hint_weight_set(o, 0.0, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(o, 0.5, EVAS_HINT_FILL);
elm_table_pack(inf->table, o, 1, 0, 1, y);
evas_object_show(o);
o = elm_grid_add(inf->main);
inf->grid.over = o;
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_grid_size_set(o, (evlog->last_timestamp - evlog->first_timestamp) * RES, 1);
elm_table_pack(inf->table, o, 2, 0, 1, y);
evas_object_show(o);
}
static void
_cb_fill_blocking(void *data, Ecore_Thread *thread EINA_UNUSED)
{
Inf *inf = data;
int i;
_fill_event_clean(inf,
inf->update.t0 - inf->evlog->first_timestamp,
inf->update.t1 - inf->evlog->first_timestamp,
inf->update.tmin);
_fill_timed_log_states(inf, inf->evlog,
inf->update.t0 - inf->evlog->first_timestamp,
inf->update.t1 - inf->evlog->first_timestamp,
inf->update.tmin);
for (i = 0; i < inf->evlog->thread_num; i++)
{
_fill_log_thread(inf, inf->evlog, &(inf->evlog->threads[i]), i + 1,
inf->update.t0 - inf->evlog->first_timestamp,
inf->update.t1 - inf->evlog->first_timestamp,
inf->update.tmin);
}
}
static Evas_Object *
_add_log_state_object(Evas_Object *main, Evas_Object *grid, Event *ev)
{
Evas_Object *o, *oe;
unsigned char col[4] = {255, 255, 255, 255};
int i;
char *s;
char buf[512];
o = elm_layout_add(main);
oe = elm_layout_edje_get(o);
elm_layout_file_set(o, EVLOG_EDJ, "state");
i = 0;
for (s = ev->event; *s; s++)
{
col[i % 3] ^= *s;
i++;
}
if (ev->detail)
{
for (s = ev->detail; *s; s++)
{
col[i % 3] ^= ((*s << 3) | (i << 1));
i++;
}
}
edje_object_color_class_set(oe, "state",
col[0] / 2, col[1] / 2, col[2] / 2, col[3],
255, 255, 255, 255,
255, 255, 255, 255);
if (ev->detail)
{
snprintf(buf, sizeof(buf), "%s (%s)", ev->event, ev->detail);
edje_object_part_text_set(oe, "text", buf);
}
else
edje_object_part_text_set(oe, "text", ev->event);
elm_grid_pack(grid, o, ev->t0 * RES, ev->n, (ev->t1 - ev->t0) * RES, 1);
if (ev->detail)
snprintf(buf, sizeof(buf), "%s (%s) - %1.5fms [%1.5fms]", ev->event, ev->detail, ev->t0 * 1000.0, (ev->t1 - ev->t0) * 1000.0);
else
snprintf(buf, sizeof(buf), "%s - %1.5fms [%1.5fms]", ev->event, ev->t0 * 1000.0, (ev->t1 - ev->t0) * 1000.0);
elm_object_tooltip_text_set(o, buf);
evas_object_show(o);
return o;
}
static Evas_Object *
_add_log_event_object(Evas_Object *main, Evas_Object *grid, Event *ev)
{
Evas_Object *o, *oe;
int col[4] = {255, 255, 255, 255}, i;
char *s;
char buf[512];
o = elm_layout_add(main);
oe = elm_layout_edje_get(o);
elm_layout_file_set(o, EVLOG_EDJ, "range");
i = 0;
for (s = ev->event; *s; s++)
{
col[i % 3] ^= *s;
i++;
}
edje_object_color_class_set(oe, "range",
col[0] / 2, col[1] / 2, col[2] / 2, col[3],
255, 255, 255, 255,
255, 255, 255, 255);
if (ev->detail)
{
snprintf(buf, sizeof(buf), "%s (%s)", ev->event, ev->detail);
edje_object_part_text_set(oe, "text", buf);
}
else
edje_object_part_text_set(oe, "text", ev->event);
elm_grid_pack(grid, o, ev->t0 * RES, 1 + (ev->n * 2), (ev->t1 - ev->t0) * RES, 2);
if (ev->detail)
snprintf(buf, sizeof(buf), "%s (%s) - %1.5fms [%1.5fms]", ev->event, ev->detail, ev->t0 * 1000.0, (ev->t1 - ev->t0) * 1000.0);
else
snprintf(buf, sizeof(buf), "%s - %1.5fms [%1.5fms]", ev->event, ev->t0 * 1000.0, (ev->t1 - ev->t0) * 1000.0);
elm_object_tooltip_text_set(o, buf);
evas_object_show(o);
return o;
}
static Evas_Object *
_add_log_cpuused_object(Evas_Object *main, Evas_Object *grid, Event *ev)
{
Evas_Object *o, *oe;
int col[4] = {0, 0, 0, 255};
char buf[512];
o = elm_layout_add(main);
oe = elm_layout_edje_get(o);
elm_layout_file_set(o, EVLOG_EDJ, "cpuused");
if (ev->n <= 33)
{
col[0] = (ev->n * 255) / 33;
}
else if (ev->n <= 67)
{
col[0] = 255;
col[1] = ((ev->n - 33) * 255) / 24;
}
else
{
col[0] = 255;
col[1] = 255;
col[2] = ((ev->n - 67) * 255) / 33;
}
edje_object_color_class_set(oe, "range",
col[0], col[1], col[2], col[3],
255, 255, 255, 255,
255, 255, 255, 255);
elm_grid_pack(grid, o, ev->t0 * RES, 0, (ev->t1 - ev->t0) * RES, 1);
if (ev->detail)
snprintf(buf, sizeof(buf), "%i%% - %1.5fms [%1.5fms]", ev->n, ev->t0 * 1000.0, (ev->t1 - ev->t0) * 1000.0);
elm_object_tooltip_text_set(o, buf);
evas_object_show(o);
return o;
}
static Evas_Object *
_add_log_frame_object(Evas_Object *main, Evas_Object *grid, Event *ev)
{
Evas_Object *o;
char buf[512];
o = elm_layout_add(main);
elm_layout_file_set(o, EVLOG_EDJ, "frame");
elm_grid_pack(grid, o, ev->t0 * RES, ev->n, 0, 1);
snprintf(buf, sizeof(buf), "%s - %1.5fms", ev->event, ev->t0 * 1000.0);
elm_object_tooltip_text_set(o, buf);
evas_object_show(o);
return o;
}
static Evas_Object *
_add_log_event_event_object(Evas_Object *main, Evas_Object *grid, Event *ev)
{
Evas_Object *o, *oe;
int col[4] = {255, 255, 255, 255}, i, max;
char *s;
char buf[512];
o = elm_layout_add(main);
oe = elm_layout_edje_get(o);
elm_layout_file_set(o, EVLOG_EDJ, "event");
i = 0;
max = 0;
for (s = ev->event; *s; s++)
{
col[i % 3] ^= *s;
if (col[i % 3] > max) max = col[i % 3];
i++;
}
if (max > 0)
{
for (i = 0; i < 3; i++)
{
col[i] = (col[i] * 255) / max;
}
}
edje_object_color_class_set(oe, "event",
(3 * col[0]) / 4, (3 * col[1]) / 4, (3 * col[2]) / 4, (3 * col[3]) / 4,
255, 255, 255, 255,
255, 255, 255, 255);
if (ev->detail)
{
snprintf(buf, sizeof(buf), "%s (%s)", ev->event, ev->detail);
edje_object_part_text_set(oe, "text", buf);
}
else
edje_object_part_text_set(oe, "text", ev->event);
elm_grid_pack(grid, o, ev->t0 * RES, ev->n, 0, 1);
if (ev->detail)
snprintf(buf, sizeof(buf), "%s (%s) - %1.5fms", ev->event, ev->detail, ev->t0 * 1000.0);
else
snprintf(buf, sizeof(buf), "%s - %1.5fms", ev->event, ev->t0 * 1000.0);
elm_object_tooltip_text_set(o, buf);
evas_object_show(o);
return o;
}
static Evas_Object *
_add_log_cpufreq_object(Evas_Object *parent, Evas_Object *grid, Event *ev, int mhzmax)
{
Evas_Object *o, *oe;
int col[4] = {0, 0, 0, 255}, n;
char buf[512];
o = elm_layout_add(parent);
oe = elm_layout_edje_get(o);
elm_layout_file_set(o, EVLOG_EDJ, "cpufreq");
n = (ev->n2 * 100) / mhzmax;
if (n <= 33)
{
col[0] = (n * 255) / 33;
}
else if (n <= 67)
{
col[0] = 255;
col[1] = ((n - 33) * 255) / 24;
}
else
{
col[0] = 255;
col[1] = 255;
col[2] = ((n - 67) * 255) / 33;
}
edje_object_color_class_set(oe, "range",
col[0], col[1], col[2], col[3],
255, 255, 255, 255,
255, 255, 255, 255);
elm_grid_pack(grid, o, ev->t0 * RES, ev->n, (ev->t1 - ev->t0) * RES, 1);
if (ev->detail)
snprintf(buf, sizeof(buf), "%iMHz - %1.5fms [%1.5fms]", ev->n2, ev->t0 * 1000.0, (ev->t1 - ev->t0) * 1000.0);
elm_object_tooltip_text_set(o, buf);
evas_object_show(o);
return o;
}
static void
_cb_fill_end(void *data, Ecore_Thread *thread EINA_UNUSED)
{
Inf *inf = data;
Event *ev;
Evas_Object *o;
Eina_List *l;
inf->update.thread = NULL;
EINA_LIST_FOREACH(inf->objs, l, ev)
{
if ((!ev->obj) && (!ev->nuke))
{
if (ev->slot == 0) // state
{
o = _add_log_state_object(inf->main,
inf->grid.state,
ev);
ev->obj = o;
}
else if (ev->slot > 0) // thread
{
if (!strcmp(ev->event, "*CPUUSED"))
o = _add_log_cpuused_object(inf->main,
inf->grid.thread[ev->slot - 1],
ev);
else
o = _add_log_event_object(inf->main,
inf->grid.thread[ev->slot - 1],
ev);
ev->obj = o;
}
else if (ev->slot == -1) // frames
{
if (!strcmp(ev->event, "FRAME"))
{
o = _add_log_frame_object(inf->main,
inf->grid.over,
ev);
ev->obj = o;
}
else
{
o = _add_log_event_event_object(inf->main,
inf->grid.over,
ev);
ev->obj = o;
}
}
else if (ev->slot == -2) // cpufreq
{
if (!strcmp(ev->event, "*CPUFREQ"))
{
o = _add_log_cpufreq_object(inf->main,
inf->grid.cpufreq,
ev,
inf->evlog->cpumhzmax);
ev->obj = o;
}
}
}
}
EINA_LIST_FREE(inf->update.remobjs, ev)
{
inf->objs = eina_list_remove(inf->objs, ev);
if (ev->obj) evas_object_del(ev->obj);
free(ev->event);
free(ev->detail);
free(ev);
}
if (inf->update.redo)
{
inf->update.redo = EINA_FALSE;
_fill_begin(inf);
}
}
static void
_cb_fill_cancel(void *data, Ecore_Thread *thread EINA_UNUSED)
{
Inf *inf = data;
inf->update.thread = NULL;
}
static void
_fill_begin(Inf *inf)
{
Evas_Coord x, w, wx, ww;
double t, t0, t1;
Evas_Coord gw;
if (!inf->evlog) return;
if (inf->update.thread)
{
inf->update.redo = EINA_TRUE;
return;
}
elm_grid_size_get(inf->grid.state, &gw, NULL);
evas_object_geometry_get(inf->grid.state, &x, NULL, &w, NULL);
evas_output_viewport_get(evas_object_evas_get(inf->grid.state),
&wx, NULL, &ww, NULL);
if (ww < 1) ww = 1;
if (w < 1) w = 1;
if (w < ww) w = ww;
if (gw < 1) gw = 1;
t0 = inf->evlog->first_timestamp;
t1 = inf->evlog->last_timestamp;
inf->update.tmin = (3.0 * (double)gw) / ((double)w * RES);
wx -= 100;
ww += 200;
t = ((t1 - t0) * (double)ww) / ((double)w);
t0 = ((wx - x) * (t1 - t0)) / ((double)w);
if (t0 < 0.0) t0 = 0.0;
inf->update.t0 = t0 + inf->evlog->first_timestamp;
inf->update.t1 = inf->update.t0 + t;
inf->update.thread = ecore_thread_run(_cb_fill_blocking,
_cb_fill_end,
_cb_fill_cancel,
inf);
}
static void
_cb_fill_job(void *data)
{
Inf *inf = data;
inf->update.job = NULL;
_fill_begin(inf);
}
static void
_fill_update(Inf *inf)
{
if (inf->update.job) ecore_job_del(inf->update.job);
inf->update.job = ecore_job_add(_cb_fill_job, inf);
}
static void
_cb_zoom(void *data, Evas_Object *obj, void *info EINA_UNUSED)
{
Inf *inf = data;
Evas_Coord w = 0, h = 1;
evas_object_size_hint_min_get(inf->grid.state, &w, &h);
if (w < 1) w = 1;
double len = inf->evlog->last_timestamp - inf->evlog->first_timestamp;
double res = elm_slider_value_get(obj);
Evas_Coord sx, sy, sw, sh;
elm_scroller_region_get(inf->scroller, &sx, &sy, &sw, &sh);
res = res * res;
evas_object_size_hint_min_set(inf->grid.state, len * res, h);
double snx = ((double)(sx + (sw / 2)) * (len * res)) / (double)w;
double snw = ((double)(sw) * (len * res)) / (double)w;
elm_scroller_region_show(inf->scroller, snx - (snw / 2), sy, snw, sh);
}
static void
_cb_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
_fill_update(data);
}
static void
_cb_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
_fill_update(data);
}
static void
_cb_sc_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *info EINA_UNUSED)
{
_fill_update(data);
}
static Evas_Object *
_evlog_view_add(Inf *inf)
{
Evas_Object *sc, *tb;
sc = elm_scroller_add(inf->main);
inf->scroller = sc;
evas_object_event_callback_add(sc, EVAS_CALLBACK_RESIZE, _cb_sc_resize, inf);
tb = elm_table_add(inf->main);
inf->table = tb;
evas_object_event_callback_add(tb, EVAS_CALLBACK_MOVE, _cb_move, inf);
evas_object_event_callback_add(tb, EVAS_CALLBACK_RESIZE, _cb_resize, inf);
_create_log_table(inf);
elm_object_content_set(sc, tb);
evas_object_show(tb);
evas_object_smart_callback_add(inf->zoom_slider, "changed", _cb_zoom, inf);
return sc;
}
static void
_evlog_import(Clouseau_Extension *ext, void *buffer, int size, int version EINA_UNUSED)
{
Inf *inf = ext->data;
Eina_Bool ret;
if (!inf->evlog) inf->evlog = calloc(1, sizeof(Evlog));
ret = _evlog_block_read(inf->evlog, buffer, size);
if (!ret)
{
free(inf->evlog);
inf->evlog = NULL;
}
else _fill_log_table(inf);
}
static Eina_Bool
_record_get_cb(Eina_Debug_Session *session, int cid EINA_UNUSED, void *buffer, int size)
{
Clouseau_Extension *ext = eina_debug_session_data_get(session);
_evlog_import(ext, buffer, size, -1);
return EINA_TRUE;
}
static Eina_Bool
_record_request_cb(void *data)
{
Clouseau_Extension *ext = data;
eina_debug_session_send(ext->session, ext->app_id, _record_get_op, NULL, 0);
return EINA_TRUE;
}
static void
_process_recording(void *data, Evas_Object *bt EINA_UNUSED, void *event_info EINA_UNUSED)
{
Clouseau_Extension *ext = data;
Inf *inf = ext->data;
const char *icon_name = elm_icon_standard_get(inf->record_icon);
if (!strcmp(icon_name, "media-record"))
{
const char *interval_str = elm_entry_entry_get(inf->refresh_interval_entry);
double interval = atof(interval_str);
if (!interval)
{
interval = 0.2;
elm_entry_entry_set(inf->refresh_interval_entry, "0.2");
}
eina_debug_session_send(ext->session, ext->app_id, _record_on_op, NULL, 0);
elm_icon_standard_set(inf->record_icon, "media-playback-stop");
inf->record_get_timer = ecore_timer_add(interval, _record_request_cb, ext);
}
else
{
eina_debug_session_send(ext->session, ext->app_id, _record_off_op, NULL, 0);
elm_icon_standard_set(inf->record_icon, "media-record");
ecore_timer_del(inf->record_get_timer);
inf->record_get_timer = NULL;
}
}
static void
_evlog_clear(void *data, Evas_Object *bt EINA_UNUSED, void *event_info EINA_UNUSED)
{
Clouseau_Extension *ext = data;
Inf *inf = ext->data;
int i;
elm_grid_clear(inf->grid.state, EINA_TRUE);
elm_grid_clear(inf->grid.cpufreq, EINA_TRUE);
for (i = 0; inf->evlog && i < inf->evlog->thread_num; i++)
elm_grid_clear(inf->grid.thread[i], EINA_TRUE);
elm_grid_clear(inf->grid.over, EINA_TRUE);
free(inf->evlog);
inf->evlog = NULL;
}
static Eo *
_ui_get(Clouseau_Extension *ext, Eo *parent)
{
Eo *view, *bar_box, *o, *o2;
Inf *inf = ext->data;
inf->main = o = elm_box_add(parent);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_horizontal_set(o, EINA_FALSE);
evas_object_show(o);
bar_box = o = elm_box_add(inf->main);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0);
elm_box_horizontal_set(o, EINA_TRUE);
elm_box_pack_end(inf->main, o);
evas_object_show(o);
o = elm_icon_add(bar_box);
elm_icon_standard_set(o, "edit-delete");
evas_object_show(o);
o2 = elm_button_add(bar_box);
elm_object_part_content_set(o2, "icon", o);
elm_box_pack_end(bar_box, o2);
efl_gfx_visible_set(o2, EINA_TRUE);
evas_object_smart_callback_add(o2, "clicked", _evlog_clear, ext);
inf->record_icon = o = elm_icon_add(bar_box);
elm_icon_standard_set(o, "media-record");
evas_object_show(o);
inf->record_button = o = elm_button_add(bar_box);
elm_object_part_content_set(o, "icon", inf->record_icon);
elm_box_pack_end(bar_box, o);
efl_gfx_visible_set(o, EINA_TRUE);
evas_object_smart_callback_add(o, "clicked", _process_recording, ext);
o = elm_separator_add(bar_box);
elm_box_pack_end(bar_box, o);
efl_gfx_visible_set(o, EINA_TRUE);
o = elm_label_add(bar_box);
elm_object_text_set(o, "Refresh interval (in seconds):");
elm_box_pack_end(bar_box, o);
efl_gfx_visible_set(o, EINA_TRUE);
inf->refresh_interval_entry = o = elm_entry_add(bar_box);
elm_entry_single_line_set(o, EINA_TRUE);
elm_object_text_set(o, "0.2");
elm_box_pack_end(bar_box, o);
efl_gfx_visible_set(o, EINA_TRUE);
inf->zoom_slider = o = elm_slider_add(inf->main);
evas_object_data_set(o, "inf", inf);
elm_slider_min_max_set(o, 1.0, 1000.0);
elm_slider_step_set(o, 0.1);
elm_slider_value_set(o, 50.0);
elm_object_text_set(o, "Zoom");
elm_slider_unit_format_set(o, "%1.1f");
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, 0.0);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, 0.0);
elm_box_pack_end(inf->main, o);
evas_object_show(o);
view = _evlog_view_add(inf);
evas_object_size_hint_weight_set(view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(view, EVAS_HINT_FILL, EVAS_HINT_FILL);
elm_box_pack_end(inf->main, view);
evas_object_show(view);
return inf->main;
}
static void
_app_changed(Clouseau_Extension *ext)
{
Inf *inf = ext->data;
elm_object_disabled_set(inf->record_button, EINA_FALSE);
_evlog_clear(ext, NULL, NULL);
}
static void
_session_changed(Clouseau_Extension *ext)
{
int i = 0;
Inf *inf = ext->data;
Eina_Debug_Opcode *ops = _ops();
_app_changed(ext);
while (ops[i].opcode_name)
{
if (ops[i].opcode_id) *(ops[i].opcode_id) = EINA_DEBUG_OPCODE_INVALID;
i++;
}
if (ext->session)
{
eina_debug_session_data_set(ext->session, ext);
eina_debug_opcodes_register(ext->session, ops, NULL, NULL);
}
elm_object_disabled_set(inf->record_button, EINA_TRUE);
}
EAPI const char *
extension_name_get()
{
return "Event log";
}
EAPI const char *
extension_nickname_get()
{
return "evlog";
}
EAPI Eina_Bool
extension_start(Clouseau_Extension *ext, Eo *parent)
{
Inf *inf;
eina_init();
inf = calloc(1, sizeof(Inf));
ext->data = inf;
ext->session_changed_cb = _session_changed;
ext->app_changed_cb = _app_changed;
ext->import_data_cb = _evlog_import;
ext->ui_object = _ui_get(ext, parent);
return !!ext->ui_object;
}
EAPI Eina_Bool
extension_stop(Clouseau_Extension *ext)
{
Inf *inf = ext->data;
efl_del(ext->ui_object);
free(inf);
eina_shutdown();
return EINA_TRUE;
}