Add config.h support : will fix the LARGEFILE problem
[lttv.git] / ltt / branches / poly / lttv / lttv / module.c
1
2 /* This file is part of the Linux Trace Toolkit viewer
3 * Copyright (C) 2003-2004 Michel Dagenais
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License Version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
17 * MA 02111-1307, USA.
18 */
19
20
21 /* module.c : Implementation of the module loading/unloading mechanism. */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <lttv/module.h>
28 #include <gmodule.h>
29
30
31 struct _LttvLibrary
32 {
33 LttvLibraryInfo info;
34 GPtrArray *modules;
35 GModule *gm;
36 guint locked_loaded;
37 };
38
39
40 struct _LttvModule
41 {
42 LttvModuleInfo info;
43 char **prerequisites_names;
44 GPtrArray *prerequisites;
45 };
46
47
48 /* Modules are searched by name. However, a library may be loaded which
49 provides a module with the same name as an existing one. A stack of
50 modules is thus maintained for each name.
51
52 Libraries correspond to glib modules. The g_module function is
53 responsible for loading each library only once. */
54
55 static GHashTable *modules_by_name = NULL;
56
57 static GPtrArray *libraries = NULL;
58
59 static GHashTable *libraries_by_g_module = NULL;
60
61 static GPtrArray *library_paths = NULL;
62
63 static gboolean initialized = FALSE;
64
65 static gboolean destroyed = TRUE;
66
67 static struct _LttvModuleDescription *builtin_chain = NULL;
68
69 static struct _LttvModuleDescription *module_chain = NULL;
70
71 static struct _LttvModuleDescription **module_next = &module_chain;
72
73 static GQuark lttv_module_error;
74
75 static void init();
76
77 static void finish_destroy();
78
79 static void module_release(LttvModule *m);
80
81
82 static LttvLibrary *library_add(char *name, char *path, GModule *gm)
83 {
84 LttvLibrary *l;
85
86 LttvModule *m;
87
88 struct _LttvModuleDescription *link;
89
90 GPtrArray *modules;
91
92 l = g_new(LttvLibrary, 1);
93 l->modules = g_ptr_array_new();
94 l->gm = gm;
95 l->locked_loaded = 0;
96 l->info.name = g_strdup(name);
97 l->info.path = g_strdup(path);
98 l->info.load_count = 0;
99
100 g_ptr_array_add(libraries, l);
101 g_hash_table_insert(libraries_by_g_module, gm, l);
102
103 *module_next = NULL;
104 for(link = module_chain; link != NULL; link = link->next) {
105 m = g_new(LttvModule, 1);
106 g_ptr_array_add(l->modules, m);
107
108 modules = g_hash_table_lookup(modules_by_name, link->name);
109 if(modules == NULL) {
110 modules = g_ptr_array_new();
111 g_hash_table_insert(modules_by_name, g_strdup(link->name), modules);
112 }
113 g_ptr_array_add(modules, m);
114
115 m->prerequisites_names = link->prerequisites;
116 m->prerequisites = g_ptr_array_new();
117 m->info.name = link->name;
118 m->info.short_description = link->short_description;
119 m->info.description = link->description;
120 m->info.init = link->init;
121 m->info.destroy = link->destroy;
122 m->info.library = l;
123 m->info.require_count = 0;
124 m->info.use_count = 0;
125 m->info.prerequisites_number = link->prerequisites_number;
126 }
127 return l;
128 }
129
130
131 static void library_remove(LttvLibrary *l)
132 {
133 LttvModule *m;
134
135 GPtrArray *modules;
136 GPtrArray **modules_ptr = &modules; /* for strict aliasing */
137 guint i;
138
139 char *key;
140 char **key_ptr = &key; /* for strict aliasing */
141
142 for(i = 0 ; i < l->modules->len ; i++) {
143 m = (LttvModule *)(l->modules->pdata[i]);
144
145 g_hash_table_lookup_extended(modules_by_name, m->info.name,
146 (gpointer *)key_ptr, (gpointer *)modules_ptr);
147 g_assert(modules != NULL);
148 g_ptr_array_remove(modules, m);
149 if(modules->len == 0) {
150 g_hash_table_remove(modules_by_name, m->info.name);
151 g_ptr_array_free(modules, TRUE);
152 g_free(key);
153 }
154
155 g_ptr_array_free(m->prerequisites, TRUE);
156 g_free(m);
157 }
158
159 g_ptr_array_remove(libraries, l);
160 g_hash_table_remove(libraries_by_g_module, l->gm);
161 g_ptr_array_free(l->modules, TRUE);
162 g_free(l->info.name);
163 g_free(l->info.path);
164 g_free(l);
165 }
166
167
168 static LttvLibrary *library_load(char *name, GError **error)
169 {
170 GModule *gm = NULL;
171
172 int i, nb;
173
174 /* path is always initialized, checked */
175 char *path = NULL, *pathname;
176
177 LttvLibrary *l;
178
179 GString *messages = g_string_new("");
180
181 /* insure that module.c is initialized */
182
183 init();
184
185 /* Try to find the library along all the user specified paths */
186
187 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Load library %s", name);
188 nb = lttv_library_path_number();
189 for(i = 0 ; i <= nb ; i++) {
190 if(i < nb) path = lttv_library_path_get(i);
191 else path = NULL;
192
193 pathname = g_module_build_path(path ,name);
194 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Try path %s", pathname);
195 module_chain = NULL;
196 module_next = &module_chain;
197 gm = g_module_open(pathname,0);
198 g_free(pathname);
199
200 if(gm != NULL) break;
201
202 g_string_append(messages, g_module_error());
203 g_string_append(messages, "\n");
204 g_log(G_LOG_DOMAIN,G_LOG_LEVEL_INFO,"Trial failed, %s", g_module_error());
205 }
206
207 /* Module cannot be found */
208
209 if(gm == NULL) {
210 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Failed to load %s", name);
211 g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND,
212 "Cannot load library %s: %s", name, messages->str);
213 g_string_free(messages, TRUE);
214 return NULL;
215 }
216 g_string_free(messages, TRUE);
217
218 /* Check if the library was already loaded */
219
220 l = g_hash_table_lookup(libraries_by_g_module, gm);
221
222 /* This library was not already loaded */
223
224 if(l == NULL) {
225 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Library %s (%s) loaded", name,
226 g_module_name(gm));
227 l = library_add(name, path, gm);
228 }
229 return l;
230 }
231
232
233 LttvLibrary *lttv_library_load(char *name, GError **error)
234 {
235 LttvLibrary *l = library_load(name, error);
236 if(l != NULL) l->info.load_count++;
237 return l;
238 }
239
240
241 static void library_unload(LttvLibrary *l)
242 {
243 guint i;
244
245 GModule *gm;
246
247 LttvModule *m;
248
249 if(l->locked_loaded > 0) {
250 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: locked loaded",
251 l->info.name);
252 return;
253 }
254
255 if(l->info.load_count > 0) {
256 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: load count %d",
257 l->info.name, l->info.load_count);
258 return;
259 }
260
261 /* Check if all its modules have been released */
262
263 for(i = 0 ; i < l->modules->len ; i++) {
264 m = (LttvModule *)(l->modules->pdata[i]);
265 if(m->info.use_count > 0) {
266 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO,"Unload library %s: module %s used",
267 l->info.name, m->info.name);
268 return;
269 }
270 }
271
272 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Unload library %s: close the GModule",
273 l->info.name);
274 gm = l->gm;
275 library_remove(l);
276 if(gm != NULL) g_module_close(gm);
277
278 /* insure that module.c will be finalized */
279
280 finish_destroy();
281 }
282
283
284 void lttv_library_unload(LttvLibrary *l)
285 {
286 /* In the case where we wait for a module to release, the load count is 0
287 * and should not be decremented. */
288 if(l->info.load_count != 0) l->info.load_count--;
289 library_unload(l);
290 }
291
292
293 static void library_lock_loaded(LttvLibrary *l)
294 {
295 l->locked_loaded++;
296 }
297
298
299 static void library_unlock_loaded(LttvLibrary *l)
300 {
301 l->locked_loaded--;
302 library_unload(l);
303 }
304
305
306 static LttvModule *module_require(char *name, GError **error)
307 {
308 GError *tmp_error = NULL;
309
310 guint i, j;
311
312 LttvModule *m, *required;
313
314 LttvLibrary *l = NULL;
315
316 GPtrArray *modules;
317
318 /* Insure that module.c is initialized */
319
320 init();
321
322 /* Check if the module is already loaded */
323
324 modules = g_hash_table_lookup(modules_by_name, name);
325
326 /* Try to load a library having the module name */
327
328 if(modules == NULL) {
329 l = library_load(name, error);
330 if(l == NULL) return NULL;
331 else library_lock_loaded(l);
332
333 /* A library was found, does it contain the named module */
334
335 modules = g_hash_table_lookup(modules_by_name, name);
336 if(modules == NULL) {
337 g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND,
338 "Module %s not found in library %s", name, l->info.name);
339 library_unlock_loaded(l);
340 return NULL;
341 }
342 }
343 m = (LttvModule *)(modules->pdata[modules->len - 1]);
344
345 /* We have the module */
346
347 m->info.use_count++;
348
349 /* First use of the module. Initialize after getting the prerequisites */
350
351 if(m->info.use_count == 1) {
352 for(i = 0 ; i < m->info.prerequisites_number ; i++) {
353 required = module_require(m->prerequisites_names[i], &tmp_error);
354
355 /* A prerequisite could not be found, undo everything and fail */
356
357 if(required == NULL) {
358 for(j = 0 ; j < m->prerequisites->len ; j++) {
359 module_release((LttvModule *)(m->prerequisites->pdata[j]));
360 }
361 g_ptr_array_set_size(m->prerequisites, 0);
362 if(l != NULL) library_unlock_loaded(l);
363 g_set_error(error, lttv_module_error, LTTV_MODULE_NOT_FOUND,
364 "Cannot find prerequisite for module %s: %s", name,
365 tmp_error->message);
366 g_clear_error(&tmp_error);
367 return NULL;
368 }
369 g_ptr_array_add(m->prerequisites, required);
370 }
371 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Module %s: init()", m->info.name);
372 m->info.init();
373 }
374
375 /* Decrement the load count of the library. It will not really be
376 unloaded since it contains a currently used module. */
377
378 if(l != NULL) library_unlock_loaded(l);
379
380 return(m);
381 }
382
383
384 /* The require_count for a module is the number of explicit calls to
385 lttv_module_require, while the use_count also counts the number of times
386 a module is needed as a prerequisite. */
387
388 LttvModule *lttv_module_require(char *name, GError **error)
389 {
390 LttvModule *m = module_require(name, error);
391 if(m != NULL) m->info.require_count++;
392 return(m);
393 }
394
395
396 static void module_release(LttvModule *m)
397 {
398 guint i;
399
400 library_lock_loaded(m->info.library);
401
402 m->info.use_count--;
403 if(m->info.use_count == 0) {
404 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Module %s: destroy()",m->info.name);
405 m->info.destroy();
406 for(i = 0 ; i < m->prerequisites->len ; i++) {
407 module_release((LttvModule *)(m->prerequisites->pdata[i]));
408 }
409 g_ptr_array_set_size(m->prerequisites, 0);
410 }
411 library_unlock_loaded(m->info.library);
412 }
413
414
415 void lttv_module_release(LttvModule *m)
416 {
417 m->info.require_count--;
418 module_release(m);
419 }
420
421
422 void lttv_module_info(LttvModule *m, LttvModuleInfo *info)
423 {
424 *info = m->info;
425 }
426
427
428 unsigned lttv_module_prerequisite_number(LttvModule *m)
429 {
430 return m->prerequisites->len;
431 }
432
433
434 LttvModule *lttv_module_prerequisite_get(LttvModule *m, unsigned i)
435 {
436 return (LttvModule *)(m->prerequisites->pdata[i]);
437 }
438
439
440 void lttv_library_info(LttvLibrary *l, LttvLibraryInfo *info)
441 {
442 *info = l->info;
443 }
444
445
446 unsigned lttv_library_module_number(LttvLibrary *l)
447 {
448 return l->modules->len;
449 }
450
451
452 LttvModule *lttv_library_module_get(LttvLibrary *l, unsigned i)
453 {
454 return (LttvModule *)(l->modules->pdata[i]);
455 }
456
457
458 unsigned lttv_library_number()
459 {
460 return libraries->len;
461 }
462
463
464 LttvLibrary *lttv_library_get(unsigned i)
465 {
466 return (LttvLibrary *)(libraries->pdata[i]);
467 }
468
469
470 void lttv_library_path_add(const char *name)
471 {
472 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Add library path %s", name);
473 g_ptr_array_add(library_paths,(char*)g_strdup(name));
474 }
475
476
477 void lttv_library_path_remove(const char *name)
478 {
479 guint i;
480
481 for(i = 0 ; i < library_paths->len ; i++) {
482 if(g_str_equal(name, library_paths->pdata[i])) {
483 g_free(library_paths->pdata[i]);
484 g_ptr_array_remove_index(library_paths,i);
485 return;
486 }
487 }
488 }
489
490
491 unsigned lttv_library_path_number()
492 {
493 return library_paths->len;
494 }
495
496
497 char *lttv_library_path_get(unsigned i)
498 {
499 return (char *)(library_paths->pdata[library_paths->len - i - 1]);
500 }
501
502
503 void lttv_module_register(struct _LttvModuleDescription *d)
504 {
505 *module_next = d;
506 module_next = &(d->next);
507 }
508
509
510 static void init()
511 {
512 if(initialized) return;
513 g_assert(destroyed);
514
515 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Init module.c");
516
517 initialized = TRUE;
518 destroyed = FALSE;
519 lttv_module_error = g_quark_from_string("LTTV_MODULE_ERROR");
520 modules_by_name = g_hash_table_new(g_str_hash, g_str_equal);
521 libraries = g_ptr_array_new();
522 libraries_by_g_module = g_hash_table_new(g_direct_hash, g_direct_equal);
523 library_paths = g_ptr_array_new();
524
525 if(builtin_chain == NULL) builtin_chain = module_chain;
526 module_chain = builtin_chain;
527 library_add("builtin", NULL, NULL);
528 }
529
530
531 static void finish_destroy()
532 {
533 guint i;
534
535 if(initialized) return;
536 g_assert(!destroyed);
537
538 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Finish destroy module.c");
539 g_hash_table_destroy(modules_by_name);
540 g_ptr_array_free(libraries, TRUE);
541 g_hash_table_destroy(libraries_by_g_module);
542 for(i = 0 ; i < library_paths->len ; i++) {
543 g_free(library_paths->pdata[i]);
544 }
545 g_ptr_array_free(library_paths, TRUE);
546 destroyed = TRUE;
547 }
548
549
550 static void destroy()
551 {
552 guint i, j, nb;
553
554 LttvLibrary *l, **locked_libraries;
555
556 LttvModule *m;
557
558 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "Destroy module.c");
559
560 /* Unload all libraries */
561
562 nb = libraries->len;
563 locked_libraries = g_new(LttvLibrary *, nb);
564
565 for(i = 0 ; i < nb ; i++) {
566 //g_assert(nb == libraries->len);
567 l = (LttvLibrary *)(libraries->pdata[i]);
568 locked_libraries[i] = l;
569 library_lock_loaded(l);
570 for(j = 0 ; j < l->modules->len ; j++) {
571 m = (LttvModule *)(l->modules->pdata[j]);
572 while(m->info.require_count > 0) lttv_module_release(m);
573 }
574 library_unlock_loaded(l);
575 while(l->info.load_count > 0) lttv_library_unload(l);
576
577 /* If the number of librairies loaded have changed, restart from the
578 * beginning */
579 if(nb != libraries->len) {
580 i = 0;
581 nb = libraries->len;
582 }
583
584 }
585
586 for(i = 0 ; i < nb ; i++) {
587 l = locked_libraries[i];
588 library_unlock_loaded(l);
589 }
590 g_free(locked_libraries);
591
592 /* The library containing module.c may be locked by our caller */
593
594 g_assert(libraries->len <= 1);
595
596 initialized = FALSE;
597 }
598
599 LTTV_MODULE("module", "Modules in libraries", \
600 "Load libraries, list, require and initialize contained modules", \
601 init, destroy)
602
This page took 0.04111 seconds and 4 git commands to generate.