Initial version of headless-server
[platform/core/uifw/headless-server.git] / src / debug / debug.c
1 /*
2 * Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28
29 #include <pepper.h>
30 #include <wayland-server.h>
31 #include <wayland-util.h>
32 #include <pepper-inotify.h>
33 #include <pepper-keyrouter.h>
34 #include <pepper-xkb.h>
35 #include <headless_server.h>
36
37 #define MAX_CMDS        256
38
39 #define STDOUT_REDIR                    "stdout"
40 #define STDERR_REDIR                    "stderr"
41 #define PROTOCOL_TRACE_ON               "protocol_trace_on"
42 #define PROTOCOL_TRACE_OFF              "protocol_trace_off"
43 #define KEYGRAB_STATUS                  "keygrab_status"
44 #define TOPVWINS                        "topvwins"
45 #define CONNECTED_CLIENTS               "connected_clients"
46 #define CLIENT_RESOURCES                "reslist"
47 #define KEYMAP                          "keymap"
48 #define HELP_MSG                        "help"
49
50 typedef struct
51 {
52         pepper_compositor_t *compositor;
53         pepper_inotify_t *inotify;
54
55         pepper_view_t *top_mapped;
56         pepper_view_t *focus;
57 } headless_debug_t;
58
59 typedef void (*headless_debug_action_cb_t)(headless_debug_t *hd, void *data);
60
61 typedef struct
62 {
63    const char *cmds;
64    headless_debug_action_cb_t cb;
65    headless_debug_action_cb_t disable_cb;
66 } headless_debug_action_t;
67
68 const static int KEY_DEBUG = 0xdeaddeb0;
69 extern void wl_debug_server_enable(int enable);
70
71 static void
72 _headless_debug_usage()
73 {
74         fprintf(stdout, "Supported commands:\n\n");
75         fprintf(stdout, "\t %s\n", PROTOCOL_TRACE_ON);
76         fprintf(stdout, "\t %s\n", PROTOCOL_TRACE_OFF);
77         fprintf(stdout, "\t %s\n", STDOUT_REDIR);
78         fprintf(stdout, "\t %s\n", STDERR_REDIR);
79         fprintf(stdout, "\t %s\n", KEYGRAB_STATUS);
80         fprintf(stdout, "\t %s\n", TOPVWINS);
81         fprintf(stdout, "\t %s\n", CONNECTED_CLIENTS);
82         fprintf(stdout, "\t %s\n", CLIENT_RESOURCES);
83         fprintf(stdout, "\t %s\n", KEYMAP);
84         fprintf(stdout, "\t %s\n", HELP_MSG);
85
86         fprintf(stdout, "\nTo execute commands, just create/remove/update a file with the commands above.\n");
87         fprintf(stdout, "Please refer to the following examples.\n\n");
88         fprintf(stdout, "\t # winfo protocol_trace_on\t : enable event trace\n");
89         fprintf(stdout, "\t # winfo event_trace_off\t : disable event trace\n");
90         fprintf(stdout, "\t # winfo stdout\t\t\t : redirect STDOUT\n");
91         fprintf(stdout, "\t # winfo stderr\t\t\t : redirect STDERR\n");
92         fprintf(stdout, "\t # winfo keygrab_status\t\t : display keygrab status\n");
93         fprintf(stdout, "\t # winfo topvwins\t\t : display top/visible window stack\n");
94         fprintf(stdout, "\t # winfo connected_clients\t : display connected clients information\n");
95         fprintf(stdout, "\t # winfo reslist\t\t : display each resources information of connected clients\n");
96         fprintf(stdout, "\t # winfo keymap\t\t : display current xkb keymap\n");
97         fprintf(stdout, "\t # winfo help\t\t\t : display this help message\n");
98 }
99
100 static void
101 _headless_debug_protocol_trace_on(headless_debug_t *hdebug, void *data)
102 {
103         (void) hdebug;
104         (void) data;
105         wl_debug_server_enable(1);
106 }
107
108 static void
109 _headless_debug_protocol_trace_off(headless_debug_t *hdebug, void *data)
110 {
111         (void) hdebug;
112         (void) data;
113         wl_debug_server_enable(0);
114 }
115
116 static void
117 _headless_debug_dummy(headless_debug_t *hdebug, void *data)
118 {
119         (void) hdebug;
120         (void) data;
121         _headless_debug_usage();
122 }
123
124 static enum wl_iterator_result
125 _client_get_resource_itr(struct wl_resource *resource, void *data)
126 {
127         int *n_resources = (int *)data;
128
129         PEPPER_TRACE("\t\t [resource][%d] class=%s, id=%u\n", ++(*n_resources), wl_resource_get_class(resource), wl_resource_get_id(resource));
130
131         return WL_ITERATOR_CONTINUE;
132 }
133
134 static void
135 _headless_debug_connected_clients(headless_debug_t *hdebug, void *data)
136 {
137         pid_t pid;
138         uid_t uid;
139         gid_t gid;
140
141         int client_fd = -1;
142         uint32_t n_clients = 0;
143         int n_resources = 0;
144
145         struct wl_list *clist = NULL;
146         struct wl_client *client_itr = NULL;
147         pepper_bool_t need_reslist = PEPPER_FALSE;
148
149         const char *cmds = (const char *)data;
150
151         /* check if reslist feature is required */
152         if (cmds && !strncmp(cmds, CLIENT_RESOURCES, MAX_CMDS)) {
153                 need_reslist = PEPPER_TRUE;
154         }
155
156         /* get client list which bound wl_compositor global */
157         clist = wl_display_get_client_list(pepper_compositor_get_display(hdebug->compositor));
158         PEPPER_CHECK(clist, return, "Failed to get client list from compositor->display.\n");
159
160         PEPPER_TRACE("========= [Connected clients information] =========\n");
161         wl_client_for_each(client_itr, clist) {
162                 n_clients++;
163                 client_fd = wl_client_get_fd(client_itr);
164                 wl_client_get_credentials(client_itr, &pid, &uid, &gid);
165                 PEPPER_TRACE("\t client[%d]: pid=%d, user=%d, group=%d, socket_fd=%d", n_clients, pid, uid, gid, client_fd);
166
167                 if (PEPPER_FALSE == need_reslist) {
168                         PEPPER_TRACE("\n");
169                         continue;
170                 }
171
172                 PEPPER_TRACE("\n");
173                 wl_client_for_each_resource(client_itr, _client_get_resource_itr, &n_resources);
174                 PEPPER_TRACE("\t\t number of resources = %d\n", n_resources);
175
176                 n_resources = 0;
177         }
178
179         if (!n_clients)
180                 PEPPER_TRACE("============ [No connected clients] ===========\n\n");
181 }
182
183 static void
184 _headless_debug_redir_stdout(headless_debug_t *hdebug, void *data)
185 {
186         (void) hdebug;
187         (void) data;
188
189         int fd = -1;
190         int ret = 0;
191
192         fd = open("/run/pepper/stdout.txt", O_CREAT | O_WRONLY | O_APPEND, S_IWUSR | S_IWGRP);
193
194         if (fd < 0) {
195                 PEPPER_TRACE("Failed to open stdout.txt (errno=%s)\n", strerror(errno));
196                 return;
197         }
198
199         ret = dup2(fd, 1);
200         close(fd);
201         PEPPER_CHECK(ret >= 0, return, "Failed to redirect STDOUT.\n");
202
203         PEPPER_TRACE("STDOUT has been redirected to stdout.txt.\n");
204 }
205
206 static void
207 _headless_debug_redir_stderr(headless_debug_t *hdebug, void *data)
208 {
209         (void) hdebug;
210         (void) data;
211
212         int fd = -1;
213         int ret = 0;
214
215         fd = open("/run/pepper/stderr.txt", O_CREAT | O_WRONLY | O_APPEND, S_IWUSR | S_IWGRP);
216
217         if (fd < 0) {
218                 PEPPER_TRACE("Failed to open stderr.txt (errno=%s)\n", strerror(errno));
219                 return;
220         }
221
222         ret = dup2(fd, 2);
223         close(fd);
224         PEPPER_CHECK(ret >= 0, return, "Failed to redirect STDERR.\n");
225
226         PEPPER_TRACE("STDERR has been redirected to stderr.txt.\n");
227
228 }
229
230 static void
231 _headless_debug_topvwins(headless_debug_t *hdebug, void *data)
232 {
233         (void) data;
234
235         int cnt = 0;
236         int w, h;
237         double x, y;
238         pid_t pid;
239
240         pepper_list_t *l;
241         const pepper_list_t *list;
242         pepper_view_t *view;
243         pepper_surface_t *surface;
244         pepper_view_t *top_visible = NULL;
245
246         PEPPER_CHECK(hdebug, return, "[%s] Invalid headless debug !\n", __FUNCTION__);
247
248         PEPPER_TRACE("No. WinID      RscID       PID     w    h    x    y   Mapped Visible Top Top_Visible Focus\n");
249         PEPPER_TRACE("==========================================================================================\n");
250
251         list = pepper_compositor_get_view_list(hdebug->compositor);
252
253         pepper_list_for_each_list(l,  list) {
254                 view = (pepper_view_t *)l->item;
255                 PEPPER_CHECK(view, continue, "[%s] Invalid object view:%p\n", __FUNCTION__, view);
256
257                 surface = pepper_view_get_surface(view);
258                 PEPPER_CHECK(surface, continue, "[%s] Invalid object surface:%p\n", __FUNCTION__, surface);
259
260                 cnt++;
261                 pepper_view_get_position(view, &x, &y);
262                 pepper_view_get_size(view, &w, &h);
263                 wl_client_get_credentials(wl_resource_get_client(pepper_surface_get_resource(surface)), &pid, NULL, NULL);
264                 if (!top_visible && pepper_surface_get_buffer(surface))
265                         top_visible = view;
266
267                 pepper_log("DEBUG", PEPPER_LOG_LEVEL_DEBUG, "%3d 0x%08x 0x%08x %5d %4d %4d %4.0f %4.0f     %s       %s     %s       %s       %s\n",
268                                         cnt, surface, pepper_surface_get_resource(surface), pid, w, h, x, y,
269                                         pepper_view_is_mapped(view) ? "O" : "X",
270                                         pepper_view_is_visible(view) ? "O" : "X",
271                                         (hdebug->top_mapped == view) ? "O" : "X",
272                                         (top_visible == view) ? "O" : "X",
273                                         (hdebug->focus == view) ? "O" : "X");
274         }
275
276         PEPPER_TRACE("==========================================================================================\n");
277 }
278
279 static void
280 _headless_debug_keygrab_status(headless_debug_t *hdebug, void *data)
281 {
282         pepper_keyrouter_t *keyrouter;
283
284         keyrouter = headless_input_get_keyrouter(hdebug->compositor);
285         pepper_keyrouter_debug_keygrab_status_print(keyrouter);
286 }
287
288 static void
289 _headless_debug_keymap(headless_debug_t *hdebug, void *data)
290 {
291         pepper_xkb_t *xkb;
292
293         int i;
294         int min_keycode, max_keycode, num_mods, num_groups;
295         struct xkb_context *context = NULL;
296         struct xkb_keymap *keymap = NULL;
297         struct xkb_state *state = NULL;
298         xkb_keysym_t sym = XKB_KEY_NoSymbol;
299         char keyname[256] = {0, };
300
301         xkb = headless_input_get_xkb(hdebug->compositor);
302         PEPPER_CHECK(xkb, return, "xkb is not set\n");
303
304         context = pepper_xkb_get_context(xkb);
305         PEPPER_CHECK(context, return, "Current pepper_xkb has no context.\n");
306         keymap = pepper_xkb_get_keymap(xkb);
307         PEPPER_CHECK(keymap, return, "Current pepper_xkb has no keymap.\n");
308         state = pepper_xkb_get_state(xkb);
309         PEPPER_CHECK(state, return, "Current pepper_xkb has no state.\n");
310
311         min_keycode = xkb_keymap_min_keycode(keymap);
312         max_keycode = xkb_keymap_max_keycode(keymap);
313         num_groups = xkb_map_num_groups(keymap);
314         num_mods = xkb_keymap_num_mods(keymap);
315
316         printf("\n");
317         printf("    min keycode: %d\n", min_keycode);
318         printf("    max keycode: %d\n", max_keycode);
319         printf("    num_groups : %d\n", num_groups);
320         printf("    num_mods   : %d\n", num_mods);
321         for (i = 0; i < num_mods; i++) {
322                 printf("        [%2d] mod: %s\n", i, xkb_keymap_mod_get_name(keymap, i));
323         }
324
325         printf("\n\n\tkeycode\t\tkeyname\t\t  keysym\t    repeat\n");
326         printf("    ----------------------------------------------------------------------\n");
327
328         for (i = min_keycode; i < (max_keycode + 1); i++) {
329                 sym = xkb_state_key_get_one_sym(state, i);
330
331                 memset(keyname, 0, sizeof(keyname));
332                 xkb_keysym_get_name(sym, keyname, sizeof(keyname));
333
334                 if (!strncmp(keyname, "NoSymbol", sizeof("NoSymbol")) && sym == 0x0)
335                         continue;
336
337                 printf("\t%4d%-5s%-25s%-20x%-5d\n", i, "", keyname, sym, xkb_keymap_key_repeats(keymap, i));
338         }
339 }
340
341 static const headless_debug_action_t debug_actions[] =
342 {
343         { STDOUT_REDIR,  _headless_debug_redir_stdout, NULL },
344         { STDERR_REDIR,  _headless_debug_redir_stderr, NULL },
345         { PROTOCOL_TRACE_ON,  _headless_debug_protocol_trace_on, _headless_debug_protocol_trace_off },
346         { PROTOCOL_TRACE_OFF, _headless_debug_protocol_trace_off, NULL },
347         { KEYGRAB_STATUS, _headless_debug_keygrab_status, NULL },
348         { TOPVWINS, _headless_debug_topvwins, NULL },
349         { CONNECTED_CLIENTS, _headless_debug_connected_clients, NULL },
350         { CLIENT_RESOURCES, _headless_debug_connected_clients, NULL },
351         { KEYMAP, _headless_debug_keymap, NULL },
352         { HELP_MSG, _headless_debug_dummy, NULL },
353 };
354
355 static void
356 _headless_debug_enable_action(headless_debug_t *hdebug, char *cmds)
357 {
358         int n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]);
359
360         for(int n=0 ; n < n_actions ; n++) {
361                 if (!strncmp(cmds, debug_actions[n].cmds, MAX_CMDS)) {
362                         PEPPER_TRACE("[%s : %s]\n", __FUNCTION__, debug_actions[n].cmds);
363                         debug_actions[n].cb(hdebug, (void *)debug_actions[n].cmds);
364
365                         break;
366                 }
367         }
368 }
369
370 static void
371 _headless_debug_disable_action(headless_debug_t *hdebug, char *cmds)
372 {
373         int n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]);
374
375         for(int n=0 ; n < n_actions ; n++) {
376                 if (!strncmp(cmds, debug_actions[n].cmds, MAX_CMDS)) {
377                         if (debug_actions[n].disable_cb) {
378                                 PEPPER_TRACE("[%s : %s]\n", __FUNCTION__, debug_actions[n].cmds);
379                                 debug_actions[n].disable_cb(hdebug, (void *)debug_actions[n].cmds);
380                         }
381
382                         break;
383                 }
384         }
385 }
386
387 static void
388 _trace_cb_handle_inotify_event(uint32_t type, pepper_inotify_event_t *ev, void *data)
389 {
390         headless_debug_t *hdebug = data;
391         char *file_name = pepper_inotify_event_name_get(ev);
392
393         PEPPER_CHECK(hdebug, return, "Invalid headless debug instance\n");
394
395         switch (type)
396         {
397                 case PEPPER_INOTIFY_EVENT_TYPE_CREATE:
398                         _headless_debug_enable_action(hdebug, file_name);
399                         break;
400                 case PEPPER_INOTIFY_EVENT_TYPE_REMOVE:
401                         _headless_debug_disable_action(hdebug, file_name);
402                         break;
403                 case PEPPER_INOTIFY_EVENT_TYPE_MODIFY:
404                         break;
405                 default:
406                         PEPPER_TRACE("[%s] Unhandled event type (%d)\n", __FUNCTION__, type);
407                         break;
408         }
409 }
410
411 PEPPER_API void
412 headless_debug_set_focus_view(pepper_compositor_t *compositor, pepper_view_t *focus_view)
413 {
414         headless_debug_t *hdebug = NULL;
415
416         hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG);
417         PEPPER_CHECK(hdebug, return, "Invalid headless debug.\n");
418
419         if (hdebug->focus != focus_view) {
420                 PEPPER_TRACE("[DEBUG] Focus view has been changed to 0x%x (from 0x%x)\n", focus_view, hdebug->focus);
421                 hdebug->focus = focus_view;
422         }
423 }
424
425 PEPPER_API void
426 headless_debug_set_top_view(pepper_compositor_t *compositor, pepper_view_t *top_view)
427 {
428         headless_debug_t *hdebug = NULL;
429
430         hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG);
431         PEPPER_CHECK(hdebug, return, "Invalid headless debug.\n");
432
433         if (hdebug->top_mapped != top_view) {
434                 PEPPER_TRACE("[DEBUG] Top view has been changed to 0x%x (from 0x%x)\n", top_view, hdebug->top_mapped);
435                 hdebug->top_mapped = top_view;
436         }
437 }
438
439 PEPPER_API void
440 headless_debug_deinit(pepper_compositor_t * compositor)
441 {
442         headless_debug_t *hdebug = NULL;
443
444         hdebug = (headless_debug_t *)pepper_object_get_user_data((pepper_object_t *) compositor, &KEY_DEBUG);
445         PEPPER_CHECK(hdebug, return, "Failed to get headless debug instance\n");
446
447         /* remove the directory watching already */
448         if (hdebug->inotify)
449                 pepper_inotify_del(hdebug->inotify, "/run/pepper");
450
451         /* remove inotify */
452         pepper_inotify_destroy(hdebug->inotify);
453         hdebug->inotify = NULL;
454
455         pepper_object_set_user_data((pepper_object_t *)hdebug->compositor, &KEY_DEBUG, NULL, NULL);
456         free(hdebug);
457 }
458
459 pepper_bool_t
460 headless_debug_init(pepper_compositor_t *compositor)
461 {
462         int n_actions;
463         headless_debug_t *hdebug = NULL;
464         pepper_inotify_t *inotify = NULL;
465         pepper_bool_t res = PEPPER_FALSE;
466
467         hdebug = (headless_debug_t*)calloc(1, sizeof(headless_debug_t));
468         PEPPER_CHECK(hdebug, goto error, "Failed to alloc for headless debug\n");
469         hdebug->compositor = compositor;
470
471         /* create inotify to watch file(s) for event trace */
472         inotify = pepper_inotify_create(hdebug->compositor, _trace_cb_handle_inotify_event, hdebug);
473         PEPPER_CHECK(inotify, goto error, "Failed to create inotify\n");
474
475         /* add a directory for watching */
476         res = pepper_inotify_add(inotify, "/run/pepper");
477         PEPPER_CHECK(res, goto error, "Failed on pepper_inotify_add()\n");
478
479         hdebug->inotify = inotify;
480         n_actions = sizeof(debug_actions)/sizeof(debug_actions[0]);
481
482         PEPPER_TRACE("[%s] Done (%d actions have been defined.)\n", __FUNCTION__, n_actions);
483
484         pepper_object_set_user_data((pepper_object_t *)compositor, &KEY_DEBUG, hdebug, NULL);
485         return PEPPER_TRUE;
486
487 error:
488         headless_debug_deinit(compositor);
489
490         return PEPPER_FALSE;
491 }