]> sourceware.org Git - systemtap.git/blame - runtime/vma.c
update copyrights
[systemtap.git] / runtime / vma.c
CommitLineData
7d8aa2ee
MW
1/* -*- linux-c -*-
2 * VMA tracking and lookup functions.
3 *
ef36f781 4 * Copyright (C) 2005-2014 Red Hat Inc.
7d8aa2ee
MW
5 * Copyright (C) 2006 Intel Corporation.
6 *
7 * This file is part of systemtap, and is free software. You can
8 * redistribute it and/or modify it under the terms of the GNU General
9 * Public License (GPL); either version 2, or (at your option) any
10 * later version.
11 */
12
13#ifndef _STP_VMA_C_
14#define _STP_VMA_C_
15
16#include "sym.h"
2466bf70 17#include "stp_string.c"
7d8aa2ee
MW
18#include "task_finder_vma.c"
19
18da5887
MW
20#include <asm/uaccess.h>
21
22static void _stp_vma_match_vdso(struct task_struct *tsk)
23{
24/* vdso is arch specific */
3b62d74b 25#if defined(STAPCONF_MM_CONTEXT_VDSO) || defined(STAPCONF_MM_CONTEXT_VDSO_BASE)
18da5887
MW
26 int i, j;
27 if (tsk->mm)
28 {
29 struct _stp_module *found = NULL;
3b62d74b
MW
30
31#ifdef STAPCONF_MM_CONTEXT_VDSO
18da5887 32 unsigned long vdso_addr = (unsigned long) tsk->mm->context.vdso;
3b62d74b
MW
33#else
34 unsigned long vdso_addr = tsk->mm->context.vdso_base;
35#endif
36
a2b0b5c8 37 dbug_task_vma(1,"tsk: %d vdso: 0x%lx\n", tsk->pid, vdso_addr);
3b62d74b 38
18da5887
MW
39 for (i = 0; i < _stp_num_modules && found == NULL; i++) {
40 struct _stp_module *m = _stp_modules[i];
41 if (m->path[0] == '/'
42 && m->num_sections == 1
43 && strncmp(m->name, "vdso", 4) == 0)
44 {
45 unsigned long notes_addr;
46 int all_ok = 1;
31d50137 47 notes_addr = vdso_addr + m->build_id_offset;
8ca8aa7d 48 dbug_task_vma(1,"notes_addr %s: 0x%lx + 0x%lx = 0x%lx (len: %x)\n", m->path,
31d50137 49 vdso_addr, m->build_id_offset, notes_addr, m->build_id_len);
18da5887
MW
50 for (j = 0; j < m->build_id_len; j++)
51 {
52 int rc;
53 unsigned char b;
414b89b8
DS
54
55 /*
56 * Why check CONFIG_UTRACE here? If we're using real
57 * in-kernel utrace, we can always just call
58 * get_user() (since tsk == current).
59 *
f1410a8a
DS
60 * Since we're only reading here, we can call
61 * __access_process_vm_noflush(), which only calls
62 * things that are exported.
414b89b8
DS
63 */
64#ifdef CONFIG_UTRACE
65 rc = copy_from_user(&b, (void*)(notes_addr + j), 1);
66#else
c0456b6f
DS
67 if (tsk == current)
68 {
69 rc = copy_from_user(&b, (void*)(notes_addr + j), 1);
70 }
71 else
72 {
f1410a8a
DS
73 rc = (__access_process_vm_noflush(tsk, (notes_addr + j),
74 &b, 1, 0) != 1);
c0456b6f 75 }
414b89b8 76#endif
18da5887
MW
77 if (rc || b != m->build_id_bits[j])
78 {
a2b0b5c8 79 dbug_task_vma(1,"darn, not equal (rc=%d) at %d (0x%x != 0x%x)\n",
18da5887 80 rc, j, b, m->build_id_bits[j]);
18da5887
MW
81 all_ok = 0;
82 break;
83 }
84 }
85 if (all_ok)
86 found = m;
87 }
88 }
89 if (found != NULL)
90 {
91 stap_add_vma_map_info(tsk, vdso_addr,
92 vdso_addr + found->sections[0].size,
e4d6479f 93 "vdso", found);
a2b0b5c8 94 dbug_task_vma(1,"found vdso: %s\n", found->path);
18da5887
MW
95 }
96 }
97#endif /* STAPCONF_MM_CONTEXT_VDSO */
98}
99
c3c8589d 100#ifdef HAVE_TASK_FINDER
18da5887
MW
101/* exec callback, will try to match vdso for new process,
102 will drop all vma maps for a process that disappears. */
7d8aa2ee
MW
103static int _stp_vma_exec_cb(struct stap_task_finder_target *tgt,
104 struct task_struct *tsk,
105 int register_p,
106 int process_p)
107{
a2b0b5c8 108 dbug_task_vma(1,
7d8aa2ee
MW
109 "tsk %d:%d , register_p: %d, process_p: %d\n",
110 tsk->pid, tsk->tgid, register_p, process_p);
18da5887
MW
111 if (process_p)
112 {
113 if (register_p)
114 _stp_vma_match_vdso(tsk);
115 else
116 stap_drop_vma_maps(tsk);
117 }
7d8aa2ee
MW
118
119 return 0;
120}
121
122/* mmap callback, will match new vma with _stp_module or register vma name. */
123static int _stp_vma_mmap_cb(struct stap_task_finder_target *tgt,
124 struct task_struct *tsk,
125 char *path, struct dentry *dentry,
126 unsigned long addr,
127 unsigned long length,
128 unsigned long offset,
129 unsigned long vm_flags)
130{
131 int i, res;
132 struct _stp_module *module = NULL;
295de711
DS
133 const char *name = ((dentry != NULL) ? (char *)dentry->d_name.name
134 : NULL);
35f71b69
FCE
135
136 if (path == NULL || *path == '\0') /* unknown? */
137 path = (char *)name; /* we'll copy this soon, in ..._add_vma_... */
7d8aa2ee 138
a2b0b5c8 139 dbug_task_vma(1,
7d8aa2ee
MW
140 "mmap_cb: tsk %d:%d path %s, addr 0x%08lx, length 0x%08lx, offset 0x%lx, flags 0x%lx\n",
141 tsk->pid, tsk->tgid, path, addr, length, offset, vm_flags);
7d8aa2ee
MW
142 // We are only interested in the first load of the whole module that
143 // is executable. We register whether or not we know the module,
144 // so we can later lookup the name given an address for this task.
bde34231
MW
145 if (path != NULL && offset == 0 && (vm_flags & VM_EXEC)
146 && stap_find_vma_map_info(tsk, addr, NULL, NULL, NULL, NULL) != 0) {
7d8aa2ee
MW
147 for (i = 0; i < _stp_num_modules; i++) {
148 if (strcmp(path, _stp_modules[i]->path) == 0)
149 {
76783a24
MW
150 unsigned long vm_start = 0;
151 unsigned long vm_end = 0;
a2b0b5c8 152 dbug_task_vma(1,
7d8aa2ee
MW
153 "vm_cb: matched path %s to module (sec: %s)\n",
154 path, _stp_modules[i]->sections[0].name);
7d8aa2ee 155 module = _stp_modules[i];
76783a24
MW
156 /* Make sure we really don't know about this module
157 yet. If we do know, we might want to extend
158 the coverage. */
159 res = stap_find_vma_map_info_user(tsk->group_leader,
160 module,
161 &vm_start, &vm_end,
162 NULL);
163 if (res == -ESRCH)
164 res = stap_add_vma_map_info(tsk->group_leader,
165 addr, addr + length,
166 path, module);
167 else if (res == 0 && vm_end + 1 == addr)
168 res = stap_extend_vma_map_info(tsk->group_leader,
169 vm_start,
170 addr + length);
c2537ee6
MW
171 /* VMA entries are allocated dynamically, this is fine,
172 * since we are in a task_finder callback, which is in
173 * user context. */
a6af96f5 174 if (res != 0) {
c2537ee6 175 _stp_error ("Couldn't register module '%s' for pid %d (%d)\n", _stp_modules[i]->path, tsk->group_leader->pid, res);
a6af96f5 176 }
7d8aa2ee
MW
177 return 0;
178 }
179 }
180
181 /* None of the tracked modules matched, register without,
182 * to make sure we can lookup the name later. Ignore errors,
183 * we will just report unknown when asked and tables were
184 * full. Restrict to target process when given to preserve
185 * vma_map entry slots. */
186 if (_stp_target == 0
187 || _stp_target == tsk->group_leader->pid)
188 {
189 res = stap_add_vma_map_info(tsk->group_leader, addr,
35f71b69 190 addr + length, path, NULL);
a2b0b5c8 191 dbug_task_vma(1,
b100897f 192 "registered '%s' for %d (res:%d) [%lx-%lx]\n",
35f71b69 193 path, tsk->group_leader->pid,
b100897f 194 res, addr, addr + length);
7d8aa2ee
MW
195 }
196
b100897f
MW
197 } else if (path != NULL) {
198 // Once registered, we may want to extend an earlier
199 // registered region. A segment might be mapped with
200 // different flags for different offsets. If so we want
201 // to record the extended range so we can address more
202 // precisely to module names and symbols.
203 res = stap_extend_vma_map_info(tsk->group_leader,
204 addr, addr + length);
a2b0b5c8 205 dbug_task_vma(1,
b100897f
MW
206 "extended '%s' for %d (res:%d) [%lx-%lx]\n",
207 path, tsk->group_leader->pid,
208 res, addr, addr + length);
7d8aa2ee
MW
209 }
210 return 0;
211}
212
213/* munmap callback, removes vma map info. */
214static int _stp_vma_munmap_cb(struct stap_task_finder_target *tgt,
215 struct task_struct *tsk,
216 unsigned long addr,
217 unsigned long length)
218{
219 /* Unconditionally remove vm map info, ignore if not present. */
220 stap_remove_vma_map_info(tsk->group_leader, addr);
221 return 0;
222}
223
c3a9ae38
SM
224#endif
225
7d8aa2ee
MW
226/* Initializes the vma tracker. */
227static int _stp_vma_init(void)
228{
229 int rc = 0;
a915ecd6 230#ifdef HAVE_TASK_FINDER
7d8aa2ee
MW
231 static struct stap_task_finder_target vmcb = {
232 // NB: no .pid, no .procname filters here.
233 // This means that we get a system-wide mmap monitoring
234 // widget while the script is running. (The
235 // system-wideness may be restricted by stap -c or
236 // -x.) But this seems to be necessary if we want to
237 // to stack tracebacks through arbitrary shared libraries.
238 //
239 // XXX: There may be an optimization opportunity
240 // for executables (for which the main task-finder
241 // callback should be sufficient).
242 .pid = 0,
243 .procname = NULL,
1af100fc 244 .purpose = "vma tracking",
7d8aa2ee
MW
245 .callback = &_stp_vma_exec_cb,
246 .mmap_callback = &_stp_vma_mmap_cb,
247 .munmap_callback = &_stp_vma_munmap_cb,
248 .mprotect_callback = NULL
249 };
14fc9001
MW
250 rc = stap_initialize_vma_map ();
251 if (rc != 0) {
252 _stp_error("Couldn't initialize vma map: %d\n", rc);
253 return rc;
254 }
a2b0b5c8 255 dbug_task_vma(1,
7d8aa2ee 256 "registering vmcb (_stap_target: %d)\n", _stp_target);
7d8aa2ee
MW
257 rc = stap_register_task_finder_target (& vmcb);
258 if (rc != 0)
259 _stp_error("Couldn't register task finder target: %d\n", rc);
260#endif
261 return rc;
262}
263
14fc9001
MW
264/* Get rid of the vma tracker (memory). */
265static void _stp_vma_done(void)
266{
267#if defined(CONFIG_UTRACE)
268 stap_destroy_vma_map();
269#endif
270}
271
7d8aa2ee 272#endif /* _STP_VMA_C_ */
This page took 0.12248 seconds and 5 git commands to generate.