forked from enlightenment/efl
efl_debug (client): rewrite using efl_net.
Rewrite and greatly improve error checking, allow multiple commands to be given in series, etc. This also leverages on Efl.Io.Queue to buffer data for us, then all we need to do is peek at its contents, when there is a full message we use and then ask the queue to discard it. The CLST processing was also rewritten to not allocate memory. Since memory may be unaligned we still iterate over the buffer and memcpy() to an integer to avoid alignment errors.
This commit is contained in:
parent
d0b7d876a7
commit
b79f03c916
|
@ -16,150 +16,360 @@
|
|||
* if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define EFL_BETA_API_SUPPORT 1
|
||||
#define EFL_EO_API_SUPPORT 1
|
||||
#include "efl_debug_common.h"
|
||||
|
||||
static unsigned char *buf;
|
||||
static unsigned int buf_size;
|
||||
static Eo *dialer;
|
||||
static Eo *input;
|
||||
static Eo *output;
|
||||
static Eo *send_copier;
|
||||
static Eo *recv_copier;
|
||||
|
||||
static int my_argc;
|
||||
static char **my_argv;
|
||||
static const char *expect = NULL;
|
||||
static Eina_List *waiting;
|
||||
|
||||
static Ecore_Con_Server *svr;
|
||||
static int retval = EXIT_SUCCESS;
|
||||
|
||||
static const char CLST[4] = "CLST";
|
||||
|
||||
static void
|
||||
_do(char *op, unsigned char *d, int size)
|
||||
_process_reply(const char op[static 4], const Eina_Slice payload)
|
||||
{
|
||||
if (!strcmp(op, "CLST"))
|
||||
{
|
||||
int i, n;
|
||||
#define IS_OP(x) memcmp(op, x, 4) == 0
|
||||
|
||||
n = (size) / sizeof(int);
|
||||
if (n < 10000)
|
||||
if (IS_OP(CLST))
|
||||
{
|
||||
int mypid = getpid();
|
||||
size_t offset;
|
||||
|
||||
waiting = eina_list_remove(waiting, CLST);
|
||||
|
||||
for (offset = 0; offset + sizeof(int) <= payload.len; offset += sizeof(int))
|
||||
{
|
||||
int *pids = malloc(n * sizeof(int));
|
||||
if (pids)
|
||||
{
|
||||
int mypid = getpid();
|
||||
memcpy(pids, d, n * sizeof(int));
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (pids[i] == mypid) continue;
|
||||
if (pids[i] > 0) printf("%i\n", pids[i]);
|
||||
}
|
||||
free(pids);
|
||||
}
|
||||
int p;
|
||||
|
||||
memcpy(&p, payload.bytes + offset, sizeof(int));
|
||||
|
||||
if (p == mypid) continue;
|
||||
if (p > 0) printf("%i\n", p);
|
||||
}
|
||||
}
|
||||
if ((expect) && (!strcmp(op, expect))) ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_server_add(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Add *ev EINA_UNUSED)
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < my_argc; i++)
|
||||
else
|
||||
{
|
||||
if (!strcmp(my_argv[i], "list"))
|
||||
{
|
||||
send_svr(svr, "LIST", NULL, 0);
|
||||
expect = "CLST";
|
||||
}
|
||||
else if ((!strcmp(my_argv[i], "pon")) &&
|
||||
(i < (my_argc - 2)))
|
||||
{
|
||||
unsigned char tmp[8];
|
||||
int pid = atoi(my_argv[i + 1]);
|
||||
unsigned int freq = atoi(my_argv[i + 2]);
|
||||
i += 2;
|
||||
store_val(tmp, 0, pid);
|
||||
store_val(tmp, 4, freq);
|
||||
send_svr(svr, "PLON", tmp, sizeof(tmp));
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
else if ((!strcmp(my_argv[i], "poff")) &&
|
||||
(i < (my_argc - 1)))
|
||||
{
|
||||
unsigned char tmp[4];
|
||||
int pid = atoi(my_argv[i + 1]);
|
||||
i++;
|
||||
store_val(tmp, 0, pid);
|
||||
send_svr(svr, "PLOF", tmp, sizeof(tmp));
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
else if ((!strcmp(my_argv[i], "evlogon")) &&
|
||||
(i < (my_argc - 1)))
|
||||
{
|
||||
unsigned char tmp[4];
|
||||
int pid = atoi(my_argv[i + 1]);
|
||||
i++;
|
||||
store_val(tmp, 0, pid);
|
||||
send_svr(svr, "EVON", tmp, sizeof(tmp));
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
else if ((!strcmp(my_argv[i], "evlogoff")) &&
|
||||
(i < (my_argc - 1)))
|
||||
{
|
||||
unsigned char tmp[4];
|
||||
int pid = atoi(my_argv[i + 1]);
|
||||
i++;
|
||||
store_val(tmp, 0, pid);
|
||||
send_svr(svr, "EVOF", tmp, sizeof(tmp));
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
fprintf(stderr, "ERROR: unexpected server reply: %.4s\n", op);
|
||||
retval = EXIT_FAILURE;
|
||||
}
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
|
||||
if (!waiting) ecore_main_loop_quit();
|
||||
|
||||
#undef IS_OP
|
||||
}
|
||||
|
||||
Eina_Bool
|
||||
_server_del(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Del *ev EINA_UNUSED)
|
||||
static void
|
||||
_on_data(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
|
||||
{
|
||||
ecore_main_loop_quit();
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
Eina_Slice slice, payload;
|
||||
Efl_Debug_Message_Header msgheader;
|
||||
|
||||
if (!efl_io_queue_slice_get(output, &slice))
|
||||
return;
|
||||
|
||||
if (slice.len < sizeof(msgheader))
|
||||
return;
|
||||
|
||||
memcpy(&msgheader, slice.mem, sizeof(msgheader));
|
||||
if (msgheader.size < 4) /* must contain at last 4 byte opcode */
|
||||
{
|
||||
fprintf(stderr, "ERROR: invalid message header, size=%u\n", msgheader.size);
|
||||
retval = EXIT_FAILURE;
|
||||
ecore_main_loop_quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msgheader.size + 4 > slice.len)
|
||||
return;
|
||||
|
||||
payload.bytes = slice.bytes + sizeof(msgheader);
|
||||
payload.len = msgheader.size - 4;
|
||||
|
||||
_process_reply(msgheader.op, payload);
|
||||
|
||||
efl_io_queue_discard(output, sizeof(msgheader) + payload.len);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_server_data(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Con_Event_Server_Data *ev)
|
||||
_command_send(const char op[static 4], const void *data, unsigned int len)
|
||||
{
|
||||
char op[5];
|
||||
unsigned char *d = NULL;
|
||||
int size;
|
||||
Eina_Error err;
|
||||
Efl_Debug_Message_Header msghdr = {
|
||||
.size = 4 + len,
|
||||
};
|
||||
Eina_Slice s, r;
|
||||
|
||||
_protocol_collect(&(buf), &(buf_size), ev->data, ev->size);
|
||||
while ((size = _proto_read(&(buf), &(buf_size), op, &d)) >= 0)
|
||||
memcpy(msghdr.op, op, 4);
|
||||
|
||||
s.mem = &msghdr;
|
||||
s.len = sizeof(msghdr);
|
||||
|
||||
err = efl_io_writer_write(input, &s, &r);
|
||||
if (err || r.len) goto end;
|
||||
|
||||
if (!len) goto end;
|
||||
|
||||
s.mem = data;
|
||||
s.len = len;
|
||||
err = efl_io_writer_write(input, &s, &r);
|
||||
|
||||
end:
|
||||
if (err)
|
||||
{
|
||||
_do(op, d, size);
|
||||
free(d);
|
||||
d = NULL;
|
||||
fprintf(stderr, "ERROR: could not queue message '%.4s': %s\n", op, eina_error_msg_get(err));
|
||||
retval = EXIT_FAILURE;
|
||||
return EINA_FALSE;
|
||||
}
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
|
||||
if (r.len)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not queue message '%.4s': out of memory\n", op);
|
||||
retval = EXIT_FAILURE;
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_dialer_eos(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
|
||||
{
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
static void
|
||||
_dialer_error(void *data EINA_UNUSED, const Efl_Event *event)
|
||||
{
|
||||
Eina_Error *perr = event->info;
|
||||
|
||||
fprintf(stderr, "ERROR: error communicating to %s: %s\n",
|
||||
efl_net_dialer_address_dial_get(dialer),
|
||||
eina_error_msg_get(*perr));
|
||||
retval = EINA_TRUE;
|
||||
ecore_main_loop_quit();
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
Eo *loop;
|
||||
char *path;
|
||||
Eina_Error err;
|
||||
int i;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
fprintf(stderr, "ERROR: missing argument.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if ((strcmp(argv[i], "-h") != 0) &&
|
||||
(strcmp(argv[i], "--help") != 0))
|
||||
continue;
|
||||
|
||||
printf("Usage:\n"
|
||||
"\n"
|
||||
"\t%s <command> [arguments]\n"
|
||||
"\n"
|
||||
"where <command> is one of:\n"
|
||||
"\tlist list connected process (pid)\n"
|
||||
"\tpon <pid> <freq> enable profiling for <pid> at frequency <freq> in microseconds.\n"
|
||||
"\tpoff <pid> disable profiling for <pid>\n"
|
||||
"\tevlogon <pid> start logging events to ~/efl_debug_evlog-<pid>.log\n"
|
||||
"\tevlogoff <pid> stop logging events from <pid>\n",
|
||||
argv[0]);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
ecore_app_no_system_modules();
|
||||
|
||||
eina_init();
|
||||
ecore_init();
|
||||
ecore_con_init();
|
||||
|
||||
my_argc = argc;
|
||||
my_argv = argv;
|
||||
|
||||
svr = ecore_con_server_connect(ECORE_CON_LOCAL_USER, "efl_debug", 0, NULL);
|
||||
if (!svr)
|
||||
path = ecore_con_local_path_new(EINA_FALSE, "efl_debug", 0);
|
||||
if (!path)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Cannot connect to debug daemon.\n");
|
||||
return -1;
|
||||
fprintf(stderr, "ERROR: could not get local communication path\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, (Ecore_Event_Handler_Cb)_server_add, NULL);
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL, (Ecore_Event_Handler_Cb)_server_del, NULL);
|
||||
ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DATA, (Ecore_Event_Handler_Cb)_server_data, NULL);
|
||||
loop = ecore_main_loop_get();
|
||||
|
||||
#ifdef EFL_NET_DIALER_UNIX_CLASS
|
||||
dialer = efl_add(EFL_NET_DIALER_UNIX_CLASS, loop);
|
||||
#else
|
||||
/* TODO: maybe start a TCP using locahost:12345?
|
||||
* Right now eina_debug_monitor is only for AF_UNIX, so not an issue.
|
||||
*/
|
||||
fprintf(stderr, "ERROR: your platform doesn't support Efl.Net.Dialer.Unix\n");
|
||||
#endif
|
||||
if (!dialer)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not create communication dialer\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
efl_event_callback_add(dialer, EFL_NET_DIALER_EVENT_ERROR, _dialer_error, NULL);
|
||||
efl_event_callback_add(dialer, EFL_IO_READER_EVENT_EOS, _dialer_eos, NULL);
|
||||
|
||||
input = efl_add(EFL_IO_QUEUE_CLASS, loop);
|
||||
if (!input)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not create input queue\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
output = efl_add(EFL_IO_QUEUE_CLASS, loop,
|
||||
efl_event_callback_add(efl_added, EFL_IO_QUEUE_EVENT_SLICE_CHANGED, _on_data, NULL));
|
||||
if (!output)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not create output queue\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
send_copier = efl_add(EFL_IO_COPIER_CLASS, loop,
|
||||
efl_io_copier_source_set(efl_added, input),
|
||||
efl_io_copier_destination_set(efl_added, dialer),
|
||||
efl_io_closer_close_on_destructor_set(efl_added, EINA_FALSE));
|
||||
if (!send_copier)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not create send copier\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
recv_copier = efl_add(EFL_IO_COPIER_CLASS, loop,
|
||||
efl_io_copier_source_set(efl_added, dialer),
|
||||
efl_io_copier_destination_set(efl_added, output),
|
||||
efl_io_closer_close_on_destructor_set(efl_added, EINA_FALSE));
|
||||
if (!recv_copier)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not create receive copier\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
const char *cmd = argv[i];
|
||||
|
||||
if (strcmp(cmd, "list") == 0)
|
||||
{
|
||||
if (!_command_send("LIST", NULL, 0))
|
||||
goto end;
|
||||
waiting = eina_list_append(waiting, CLST);
|
||||
}
|
||||
else if (strcmp(cmd, "pon") == 0)
|
||||
{
|
||||
if (i + 2 >= argc)
|
||||
{
|
||||
fprintf(stderr, "ERROR: missing argument: pon <pid> <freq>\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data[2] = {atoi(argv[i + 1]), atoi(argv[1 + 2])};
|
||||
if (!_command_send("PLON", data, sizeof(data)))
|
||||
goto end;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "poff") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf(stderr, "ERROR: missing argument: poff <pid>\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data[1] = {atoi(argv[i + 1])};
|
||||
if (!_command_send("PLOFF", data, sizeof(data)))
|
||||
goto end;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "evlogon") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf(stderr, "ERROR: missing argument: evlogon <pid>\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data[1] = {atoi(argv[i + 1])};
|
||||
if (!_command_send("EVON", data, sizeof(data)))
|
||||
goto end;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else if (strcmp(cmd, "evlogoff") == 0)
|
||||
{
|
||||
if (i + 1 >= argc)
|
||||
{
|
||||
fprintf(stderr, "ERROR: missing argument: evlogoff <pid>\n");
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data[1] = {atoi(argv[i + 1])};
|
||||
if (!_command_send("EVOF", data, sizeof(data)))
|
||||
goto end;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: unknown command: %s\n", argv[i]);
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
efl_io_queue_eos_mark(input);
|
||||
|
||||
err = efl_net_dialer_dial(dialer, path);
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "ERROR: could not connect '%s': %s\n", path, eina_error_msg_get(err));
|
||||
retval = EXIT_FAILURE;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ecore_main_loop_begin();
|
||||
ecore_con_server_flush(svr);
|
||||
|
||||
while ((!efl_io_closer_closed_get(dialer)) &&
|
||||
efl_io_queue_usage_get(input))
|
||||
efl_io_copier_flush(send_copier);
|
||||
|
||||
end:
|
||||
eina_list_free(waiting);
|
||||
efl_del(input);
|
||||
efl_del(output);
|
||||
efl_del(dialer);
|
||||
efl_del(send_copier);
|
||||
efl_del(recv_copier);
|
||||
free(path);
|
||||
|
||||
ecore_con_shutdown();
|
||||
ecore_shutdown();
|
||||
eina_shutdown();
|
||||
return retval;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,11 @@
|
|||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct _Efl_Debug_Message_Header {
|
||||
unsigned int size;
|
||||
char op[4];
|
||||
} Efl_Debug_Message_Header;
|
||||
|
||||
void _protocol_collect(unsigned char **buf, unsigned int *buf_size,
|
||||
void *data, int size);
|
||||
int _proto_read(unsigned char **buf, unsigned int *buf_size,
|
||||
|
@ -36,16 +41,6 @@ int _proto_read(unsigned char **buf, unsigned int *buf_size,
|
|||
memcpy(&dst, ((unsigned char *)buf) + off, sizeof(dst))
|
||||
#define store_val(buf, off, src) \
|
||||
memcpy(buf + off, &src, sizeof(src))
|
||||
#define send_svr(svr, op, data, size) \
|
||||
do { \
|
||||
unsigned char head[8]; \
|
||||
char *op2 = op; \
|
||||
int size2 = size + 4; \
|
||||
memcpy(head + 0, &size2, 4); \
|
||||
memcpy(head + 4, op2, 4); \
|
||||
ecore_con_server_send(svr, head, 8); \
|
||||
if (size > 0) ecore_con_server_send(svr, data, size); \
|
||||
} while (0)
|
||||
#define send_cli(cli, op, data, size) \
|
||||
do { \
|
||||
unsigned char head[8]; \
|
||||
|
|
Loading…
Reference in New Issue