1/**
2 * @file daemon/opd_kernel.c
3 * Dealing with the kernel and kernel module samples
4 *
5 * @remark Copyright 2002 OProfile authors
6 * @remark Read the file COPYING
7 *
8 * @author John Levon
9 * @author Philippe Elie
10 * Modified by Aravind Menon for Xen
11 * These modifications are:
12 * Copyright (C) 2005 Hewlett-Packard Co.
13 */
14
15#include "opd_kernel.h"
16#include "opd_sfile.h"
17#include "opd_trans.h"
18#include "opd_printf.h"
19#include "opd_stats.h"
20#include "oprofiled.h"
21
22#include "op_fileio.h"
23#include "op_config.h"
24#include "op_libiberty.h"
25
26#include <string.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <assert.h>
30
31static LIST_HEAD(modules);
32
33static struct kernel_image vmlinux_image;
34
35static struct kernel_image xen_image;
36
37void opd_create_vmlinux(char const * name, char const * arg)
38{
39	/* vmlinux is *not* on the list of modules */
40	list_init(&vmlinux_image.list);
41
42	/* for no vmlinux */
43	if (no_vmlinux) {
44		vmlinux_image.name = "no-vmlinux";
45		return;
46	}
47
48	vmlinux_image.name = xstrdup(name);
49
50	sscanf(arg, "%llx,%llx", &vmlinux_image.start, &vmlinux_image.end);
51
52	verbprintf(vmisc, "kernel_start = %llx, kernel_end = %llx\n",
53	           vmlinux_image.start, vmlinux_image.end);
54
55	if (!vmlinux_image.start && !vmlinux_image.end) {
56		fprintf(stderr, "error: mis-parsed kernel range: %llx-%llx\n",
57		        vmlinux_image.start, vmlinux_image.end);
58		exit(EXIT_FAILURE);
59	}
60}
61
62void opd_create_xen(char const * name, char const * arg)
63{
64	/* xen is *not* on the list of modules */
65	list_init(&xen_image.list);
66
67	/* for no xen */
68	if (no_xen) {
69		xen_image.name = "no-xen";
70		return;
71	}
72
73	xen_image.name = xstrdup(name);
74
75	sscanf(arg, "%llx,%llx", &xen_image.start, &xen_image.end);
76
77	verbprintf(vmisc, "xen_start = %llx, xen_end = %llx\n",
78	           xen_image.start, xen_image.end);
79
80	if (!xen_image.start && !xen_image.end) {
81		fprintf(stderr, "error: mis-parsed xen range: %llx-%llx\n",
82		        xen_image.start, xen_image.end);
83		exit(EXIT_FAILURE);
84	}
85}
86
87
88/**
89 * Allocate and initialise a kernel image description
90 * @param name image name
91 * @param start start address
92 * @param end end address
93 */
94static struct kernel_image *
95opd_create_module(char const * name, vma_t start, vma_t end)
96{
97	struct kernel_image * image = xmalloc(sizeof(struct kernel_image));
98
99	image->name = xstrdup(name);
100	image->start = start;
101	image->end = end;
102	list_add(&image->list, &modules);
103
104	return image;
105}
106
107
108/**
109 * Clear and free all kernel image information and reset
110 * values.
111 */
112static void opd_clear_modules(void)
113{
114	struct list_head * pos;
115	struct list_head * pos2;
116	struct kernel_image * image;
117
118	list_for_each_safe(pos, pos2, &modules) {
119		image = list_entry(pos, struct kernel_image, list);
120		if (image->name)
121			free(image->name);
122		free(image);
123	}
124
125	list_init(&modules);
126
127	/* clear out lingering references */
128	sfile_clear_kernel();
129}
130
131
132/*
133 * each line is in the format:
134 *
135 * module_name 16480 1 dependencies Live 0xe091e000
136 *
137 * without any blank space in each field
138 */
139void opd_reread_module_info(void)
140{
141	FILE * fp;
142	char * line;
143	struct kernel_image * image;
144	int module_size;
145	char ref_count[32+1];
146	int ret;
147	char module_name[256+1];
148	char live_info[32+1];
149	char dependencies[4096+1];
150	unsigned long long start_address;
151
152	if (no_vmlinux)
153		return;
154
155	opd_clear_modules();
156
157	printf("Reading module info.\n");
158
159	fp = op_try_open_file("/proc/modules", "r");
160
161	if (!fp) {
162		printf("oprofiled: /proc/modules not readable, "
163			"can't process module samples.\n");
164		return;
165	}
166
167	while (1) {
168		line = op_get_line(fp);
169
170		if (!line)
171			break;
172
173		if (line[0] == '\0') {
174			free(line);
175			continue;
176		}
177
178		ret = sscanf(line, "%256s %u %32s %4096s %32s %llx",
179			     module_name, &module_size, ref_count,
180			     dependencies, live_info, &start_address);
181		if (ret != 6) {
182			printf("bad /proc/modules entry: %s\n", line);
183			free(line);
184			continue;
185		}
186
187		image = opd_create_module(module_name, start_address,
188		                          start_address + module_size);
189
190		verbprintf(vmodule, "module %s start %llx end %llx\n",
191			   image->name, image->start, image->end);
192
193		free(line);
194	}
195
196	op_close_file(fp);
197}
198
199
200/**
201 * find a kernel image by PC value
202 * @param trans holds PC value to look up
203 *
204 * find the kernel image which contains this PC.
205 *
206 * Return %NULL if not found.
207 */
208struct kernel_image * find_kernel_image(struct transient const * trans)
209{
210	struct list_head * pos;
211	struct kernel_image * image = &vmlinux_image;
212
213	if (no_vmlinux)
214		return image;
215
216	if (image->start <= trans->pc && image->end > trans->pc)
217		return image;
218
219	list_for_each(pos, &modules) {
220		image = list_entry(pos, struct kernel_image, list);
221		if (image->start <= trans->pc && image->end > trans->pc)
222			return image;
223	}
224
225	if (xen_image.start <= trans->pc && xen_image.end > trans->pc)
226		return &xen_image;
227
228	return NULL;
229}
230