Coverage Report

Created: 2025-12-07 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libunwind/src/os-linux.h
Line
Count
Source
1
/* libunwind - a platform-independent unwind library
2
   Copyright (C) 2003-2004 Hewlett-Packard Co
3
   Copyright (C) 2007 David Mosberger-Tang
4
        Contributed by David Mosberger-Tang <dmosberger@gmail.com>
5
6
This file is part of libunwind.
7
8
Permission is hereby granted, free of charge, to any person obtaining
9
a copy of this software and associated documentation files (the
10
"Software"), to deal in the Software without restriction, including
11
without limitation the rights to use, copy, modify, merge, publish,
12
distribute, sublicense, and/or sell copies of the Software, and to
13
permit persons to whom the Software is furnished to do so, subject to
14
the following conditions:
15
16
The above copyright notice and this permission notice shall be
17
included in all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
26
27
#ifndef os_linux_h
28
#define os_linux_h
29
30
struct map_iterator
31
  {
32
    off_t offset;
33
    int fd;
34
    size_t buf_size;
35
    char *buf;
36
    char *buf_end;
37
    char *path;
38
  };
39
40
static inline char *
41
unw_ltoa (char *buf, long val)
42
50.9k
{
43
50.9k
  char *cp = buf, tmp;
44
50.9k
  ssize_t i, len;
45
46
50.9k
  do
47
101k
    {
48
101k
      *cp++ = '0' + (val % 10);
49
101k
      val /= 10;
50
101k
    }
51
101k
  while (val);
52
53
  /* reverse the order of the digits: */
54
50.9k
  len = cp - buf;
55
50.9k
  --cp;
56
101k
  for (i = 0; i < len / 2; ++i)
57
50.9k
    {
58
50.9k
      tmp = buf[i];
59
50.9k
      buf[i] = cp[-i];
60
50.9k
      cp[-i] = tmp;
61
50.9k
    }
62
50.9k
  return buf + len;
63
50.9k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:unw_ltoa
os-linux.c:unw_ltoa
Line
Count
Source
42
50.9k
{
43
50.9k
  char *cp = buf, tmp;
44
50.9k
  ssize_t i, len;
45
46
50.9k
  do
47
101k
    {
48
101k
      *cp++ = '0' + (val % 10);
49
101k
      val /= 10;
50
101k
    }
51
101k
  while (val);
52
53
  /* reverse the order of the digits: */
54
50.9k
  len = cp - buf;
55
50.9k
  --cp;
56
101k
  for (i = 0; i < len / 2; ++i)
57
50.9k
    {
58
50.9k
      tmp = buf[i];
59
50.9k
      buf[i] = cp[-i];
60
50.9k
      cp[-i] = tmp;
61
50.9k
    }
62
50.9k
  return buf + len;
63
50.9k
}
64
65
static inline int
66
maps_init (struct map_iterator *mi, pid_t pid)
67
25.4k
{
68
25.4k
  char path[sizeof ("/proc/0123456789/maps")], *cp;
69
70
25.4k
  memcpy (path, "/proc/", 6);
71
25.4k
  cp = unw_ltoa (path + 6, pid);
72
25.4k
  assert (cp + 6 < path + sizeof (path));
73
25.4k
  memcpy (cp, "/maps", 6);
74
75
25.4k
  mi->fd = open (path, O_RDONLY);
76
25.4k
  if (mi->fd >= 0)
77
25.4k
    {
78
      /* Try to allocate a page-sized buffer.  */
79
25.4k
      mi->buf_size = getpagesize ();
80
25.4k
      GET_MEMORY (cp, mi->buf_size);
81
25.4k
      if (!cp)
82
0
        {
83
0
          close(mi->fd);
84
0
          mi->fd = -1;
85
0
          return -1;
86
0
        }
87
25.4k
      else
88
25.4k
        {
89
25.4k
          mi->offset = 0;
90
25.4k
          mi->buf = mi->buf_end = cp + mi->buf_size;
91
25.4k
          return 0;
92
25.4k
        }
93
25.4k
    }
94
0
  return -1;
95
25.4k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:maps_init
os-linux.c:maps_init
Line
Count
Source
67
25.4k
{
68
25.4k
  char path[sizeof ("/proc/0123456789/maps")], *cp;
69
70
25.4k
  memcpy (path, "/proc/", 6);
71
25.4k
  cp = unw_ltoa (path + 6, pid);
72
25.4k
  assert (cp + 6 < path + sizeof (path));
73
25.4k
  memcpy (cp, "/maps", 6);
74
75
25.4k
  mi->fd = open (path, O_RDONLY);
76
25.4k
  if (mi->fd >= 0)
77
25.4k
    {
78
      /* Try to allocate a page-sized buffer.  */
79
25.4k
      mi->buf_size = getpagesize ();
80
25.4k
      GET_MEMORY (cp, mi->buf_size);
81
25.4k
      if (!cp)
82
0
        {
83
0
          close(mi->fd);
84
0
          mi->fd = -1;
85
0
          return -1;
86
0
        }
87
25.4k
      else
88
25.4k
        {
89
25.4k
          mi->offset = 0;
90
25.4k
          mi->buf = mi->buf_end = cp + mi->buf_size;
91
25.4k
          return 0;
92
25.4k
        }
93
25.4k
    }
94
0
  return -1;
95
25.4k
}
96
97
static inline char *
98
skip_whitespace (char *cp)
99
802k
{
100
802k
  if (!cp)
101
0
    return NULL;
102
103
2.59M
  while (*cp == ' ' || *cp == '\t')
104
1.79M
    ++cp;
105
802k
  return cp;
106
802k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:skip_whitespace
os-linux.c:skip_whitespace
Line
Count
Source
99
802k
{
100
802k
  if (!cp)
101
0
    return NULL;
102
103
2.59M
  while (*cp == ' ' || *cp == '\t')
104
1.79M
    ++cp;
105
802k
  return cp;
106
802k
}
107
108
static inline char *
109
scan_hex (char *cp, unsigned long *valp)
110
445k
{
111
445k
  unsigned long num_digits = 0, digit, val = 0;
112
113
445k
  cp = skip_whitespace (cp);
114
445k
  if (!cp)
115
0
    return NULL;
116
117
3.65M
  while (1)
118
3.65M
    {
119
3.65M
      digit = *cp;
120
3.65M
      if ((digit - '0') <= 9)
121
2.58M
        digit -= '0';
122
1.07M
      else if ((digit - 'A') < 6)
123
0
        digit -= 'A' - 10;
124
1.07M
      else if ((digit - 'a') < 6)
125
627k
        digit -= 'a' - 10;
126
445k
      else
127
445k
        break;
128
3.20M
      val = (val << 4) | digit;
129
3.20M
      ++num_digits;
130
3.20M
      ++cp;
131
3.20M
    }
132
445k
  if (!num_digits)
133
0
    return NULL;
134
445k
  *valp = val;
135
445k
  return cp;
136
445k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:scan_hex
os-linux.c:scan_hex
Line
Count
Source
110
445k
{
111
445k
  unsigned long num_digits = 0, digit, val = 0;
112
113
445k
  cp = skip_whitespace (cp);
114
445k
  if (!cp)
115
0
    return NULL;
116
117
3.65M
  while (1)
118
3.65M
    {
119
3.65M
      digit = *cp;
120
3.65M
      if ((digit - '0') <= 9)
121
2.58M
        digit -= '0';
122
1.07M
      else if ((digit - 'A') < 6)
123
0
        digit -= 'A' - 10;
124
1.07M
      else if ((digit - 'a') < 6)
125
627k
        digit -= 'a' - 10;
126
445k
      else
127
445k
        break;
128
3.20M
      val = (val << 4) | digit;
129
3.20M
      ++num_digits;
130
3.20M
      ++cp;
131
3.20M
    }
132
445k
  if (!num_digits)
133
0
    return NULL;
134
445k
  *valp = val;
135
445k
  return cp;
136
445k
}
137
138
static inline char *
139
scan_dec (char *cp, unsigned long *valp)
140
89.1k
{
141
89.1k
  unsigned long num_digits = 0, digit, val = 0;
142
143
89.1k
  if (!(cp = skip_whitespace (cp)))
144
0
    return NULL;
145
146
579k
  while (1)
147
579k
    {
148
579k
      digit = *cp;
149
579k
      if ((digit - '0') <= 9)
150
490k
        {
151
490k
          digit -= '0';
152
490k
          ++cp;
153
490k
        }
154
89.1k
      else
155
89.1k
        break;
156
490k
      val = (10 * val) + digit;
157
490k
      ++num_digits;
158
490k
    }
159
89.1k
  if (!num_digits)
160
0
    return NULL;
161
89.1k
  *valp = val;
162
89.1k
  return cp;
163
89.1k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:scan_dec
os-linux.c:scan_dec
Line
Count
Source
140
89.1k
{
141
89.1k
  unsigned long num_digits = 0, digit, val = 0;
142
143
89.1k
  if (!(cp = skip_whitespace (cp)))
144
0
    return NULL;
145
146
579k
  while (1)
147
579k
    {
148
579k
      digit = *cp;
149
579k
      if ((digit - '0') <= 9)
150
490k
        {
151
490k
          digit -= '0';
152
490k
          ++cp;
153
490k
        }
154
89.1k
      else
155
89.1k
        break;
156
490k
      val = (10 * val) + digit;
157
490k
      ++num_digits;
158
490k
    }
159
89.1k
  if (!num_digits)
160
0
    return NULL;
161
89.1k
  *valp = val;
162
89.1k
  return cp;
163
89.1k
}
164
165
static inline char *
166
scan_char (char *cp, char *valp)
167
178k
{
168
178k
  if (!cp)
169
0
    return NULL;
170
171
178k
  *valp = *cp;
172
173
  /* don't step over NUL terminator */
174
178k
  if (*cp)
175
178k
    ++cp;
176
178k
  return cp;
177
178k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:scan_char
os-linux.c:scan_char
Line
Count
Source
167
178k
{
168
178k
  if (!cp)
169
0
    return NULL;
170
171
178k
  *valp = *cp;
172
173
  /* don't step over NUL terminator */
174
178k
  if (*cp)
175
178k
    ++cp;
176
178k
  return cp;
177
178k
}
178
179
/* Scan a string delimited by white-space.  Fails on empty string or
180
   if string is doesn't fit in the specified buffer.  */
181
static inline char *
182
scan_string (char *cp, char *valp, size_t buf_size)
183
178k
{
184
178k
  size_t i = 0;
185
186
178k
  if (!(cp = skip_whitespace (cp)))
187
0
    return NULL;
188
189
4.12M
  while (*cp != ' ' && *cp != '\t' && *cp != '\0')
190
3.94M
    {
191
3.94M
      if ((valp != NULL) && (i < buf_size - 1))
192
356k
        valp[i++] = *cp;
193
3.94M
      ++cp;
194
3.94M
    }
195
178k
  if (i == 0 || i >= buf_size)
196
89.1k
    return NULL;
197
89.1k
  valp[i] = '\0';
198
89.1k
  return cp;
199
178k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:scan_string
os-linux.c:scan_string
Line
Count
Source
183
178k
{
184
178k
  size_t i = 0;
185
186
178k
  if (!(cp = skip_whitespace (cp)))
187
0
    return NULL;
188
189
4.12M
  while (*cp != ' ' && *cp != '\t' && *cp != '\0')
190
3.94M
    {
191
3.94M
      if ((valp != NULL) && (i < buf_size - 1))
192
356k
        valp[i++] = *cp;
193
3.94M
      ++cp;
194
3.94M
    }
195
178k
  if (i == 0 || i >= buf_size)
196
89.1k
    return NULL;
197
89.1k
  valp[i] = '\0';
198
89.1k
  return cp;
199
178k
}
200
201
static inline int
202
maps_next (struct map_iterator *mi,
203
           unsigned long *low, unsigned long *high, unsigned long *offset,
204
           unsigned long *flags)
205
89.1k
{
206
89.1k
  char perm[16], dash = 0, colon = 0, *cp;
207
89.1k
  unsigned long major, minor, inum;
208
89.1k
  ssize_t i, nread;
209
210
89.1k
  if (mi->fd < 0)
211
0
    return 0;
212
213
89.1k
  while (1)
214
89.1k
    {
215
89.1k
      ssize_t bytes_left = mi->buf_end - mi->buf;
216
89.1k
      char *eol = NULL;
217
218
6.44M
      for (i = 0; i < bytes_left; ++i)
219
6.42M
        {
220
6.42M
          if (mi->buf[i] == '\n')
221
63.6k
            {
222
63.6k
              eol = mi->buf + i;
223
63.6k
              break;
224
63.6k
            }
225
6.35M
          else if (mi->buf[i] == '\0')
226
0
            break;
227
6.42M
        }
228
89.1k
      if (!eol)
229
25.4k
        {
230
          /* copy down the remaining bytes, if any */
231
25.4k
          if (bytes_left > 0)
232
0
            memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
233
234
25.4k
          mi->buf = mi->buf_end - mi->buf_size;
235
25.4k
          nread = read (mi->fd, mi->buf + bytes_left,
236
25.4k
                        mi->buf_size - bytes_left);
237
25.4k
          if (nread <= 0)
238
0
            return 0;
239
25.4k
          else if ((size_t) (nread + bytes_left) < mi->buf_size)
240
25.4k
            {
241
              /* Move contents to the end of the buffer so we
242
                 maintain the invariant that all bytes between
243
                 mi->buf and mi->buf_end are valid.  */
244
25.4k
              memmove (mi->buf_end - nread - bytes_left, mi->buf,
245
25.4k
                       nread + bytes_left);
246
25.4k
              mi->buf = mi->buf_end - nread - bytes_left;
247
25.4k
            }
248
249
25.4k
          eol = mi->buf + bytes_left + nread - 1;
250
251
3.28M
          for (i = bytes_left; i < bytes_left + nread; ++i)
252
3.28M
            if (mi->buf[i] == '\n')
253
25.4k
              {
254
25.4k
                eol = mi->buf + i;
255
25.4k
                break;
256
25.4k
              }
257
25.4k
        }
258
89.1k
      cp = mi->buf;
259
89.1k
      mi->buf = eol + 1;
260
89.1k
      *eol = '\0';
261
262
      /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
263
89.1k
      cp = scan_hex (cp, low);
264
89.1k
      cp = scan_char (cp, &dash);
265
89.1k
      cp = scan_hex (cp, high);
266
89.1k
      cp = scan_string (cp, perm, sizeof (perm));
267
89.1k
      cp = scan_hex (cp, offset);
268
89.1k
      cp = scan_hex (cp, &major);
269
89.1k
      cp = scan_char (cp, &colon);
270
89.1k
      cp = scan_hex (cp, &minor);
271
89.1k
      cp = scan_dec (cp, &inum);
272
89.1k
      cp = mi->path = skip_whitespace (cp);
273
89.1k
      if (!cp)
274
0
        continue;
275
89.1k
      cp = scan_string (cp, NULL, 0);
276
89.1k
      if (dash != '-' || colon != ':')
277
0
        continue;       /* skip line with unknown or bad format */
278
89.1k
      if (flags)
279
0
        {
280
0
          *flags = 0;
281
0
          if (perm[0] == 'r')
282
0
            {
283
0
              *flags |= PROT_READ;
284
0
            }
285
0
          if (perm[1] == 'w')
286
0
            {
287
0
              *flags |= PROT_WRITE;
288
0
            }
289
0
          if (perm[2] == 'x')
290
0
            {
291
0
              *flags |= PROT_EXEC;
292
0
            }
293
0
        }
294
89.1k
      return 1;
295
89.1k
    }
296
0
  return 0;
297
89.1k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:maps_next
os-linux.c:maps_next
Line
Count
Source
205
89.1k
{
206
89.1k
  char perm[16], dash = 0, colon = 0, *cp;
207
89.1k
  unsigned long major, minor, inum;
208
89.1k
  ssize_t i, nread;
209
210
89.1k
  if (mi->fd < 0)
211
0
    return 0;
212
213
89.1k
  while (1)
214
89.1k
    {
215
89.1k
      ssize_t bytes_left = mi->buf_end - mi->buf;
216
89.1k
      char *eol = NULL;
217
218
6.44M
      for (i = 0; i < bytes_left; ++i)
219
6.42M
        {
220
6.42M
          if (mi->buf[i] == '\n')
221
63.6k
            {
222
63.6k
              eol = mi->buf + i;
223
63.6k
              break;
224
63.6k
            }
225
6.35M
          else if (mi->buf[i] == '\0')
226
0
            break;
227
6.42M
        }
228
89.1k
      if (!eol)
229
25.4k
        {
230
          /* copy down the remaining bytes, if any */
231
25.4k
          if (bytes_left > 0)
232
0
            memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
233
234
25.4k
          mi->buf = mi->buf_end - mi->buf_size;
235
25.4k
          nread = read (mi->fd, mi->buf + bytes_left,
236
25.4k
                        mi->buf_size - bytes_left);
237
25.4k
          if (nread <= 0)
238
0
            return 0;
239
25.4k
          else if ((size_t) (nread + bytes_left) < mi->buf_size)
240
25.4k
            {
241
              /* Move contents to the end of the buffer so we
242
                 maintain the invariant that all bytes between
243
                 mi->buf and mi->buf_end are valid.  */
244
25.4k
              memmove (mi->buf_end - nread - bytes_left, mi->buf,
245
25.4k
                       nread + bytes_left);
246
25.4k
              mi->buf = mi->buf_end - nread - bytes_left;
247
25.4k
            }
248
249
25.4k
          eol = mi->buf + bytes_left + nread - 1;
250
251
3.28M
          for (i = bytes_left; i < bytes_left + nread; ++i)
252
3.28M
            if (mi->buf[i] == '\n')
253
25.4k
              {
254
25.4k
                eol = mi->buf + i;
255
25.4k
                break;
256
25.4k
              }
257
25.4k
        }
258
89.1k
      cp = mi->buf;
259
89.1k
      mi->buf = eol + 1;
260
89.1k
      *eol = '\0';
261
262
      /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
263
89.1k
      cp = scan_hex (cp, low);
264
89.1k
      cp = scan_char (cp, &dash);
265
89.1k
      cp = scan_hex (cp, high);
266
89.1k
      cp = scan_string (cp, perm, sizeof (perm));
267
89.1k
      cp = scan_hex (cp, offset);
268
89.1k
      cp = scan_hex (cp, &major);
269
89.1k
      cp = scan_char (cp, &colon);
270
89.1k
      cp = scan_hex (cp, &minor);
271
89.1k
      cp = scan_dec (cp, &inum);
272
89.1k
      cp = mi->path = skip_whitespace (cp);
273
89.1k
      if (!cp)
274
0
        continue;
275
89.1k
      cp = scan_string (cp, NULL, 0);
276
89.1k
      if (dash != '-' || colon != ':')
277
0
        continue;       /* skip line with unknown or bad format */
278
89.1k
      if (flags)
279
0
        {
280
0
          *flags = 0;
281
0
          if (perm[0] == 'r')
282
0
            {
283
0
              *flags |= PROT_READ;
284
0
            }
285
0
          if (perm[1] == 'w')
286
0
            {
287
0
              *flags |= PROT_WRITE;
288
0
            }
289
0
          if (perm[2] == 'x')
290
0
            {
291
0
              *flags |= PROT_EXEC;
292
0
            }
293
0
        }
294
89.1k
      return 1;
295
89.1k
    }
296
0
  return 0;
297
89.1k
}
298
299
static inline void
300
maps_close (struct map_iterator *mi)
301
25.4k
{
302
25.4k
  if (mi->fd < 0)
303
0
    return;
304
25.4k
  close (mi->fd);
305
25.4k
  mi->fd = -1;
306
25.4k
  if (mi->buf)
307
25.4k
    {
308
25.4k
      mi_munmap (mi->buf_end - mi->buf_size, mi->buf_size);
309
      mi->buf = mi->buf_end = NULL;
310
25.4k
    }
311
25.4k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:maps_close
os-linux.c:maps_close
Line
Count
Source
301
25.4k
{
302
25.4k
  if (mi->fd < 0)
303
0
    return;
304
25.4k
  close (mi->fd);
305
25.4k
  mi->fd = -1;
306
25.4k
  if (mi->buf)
307
25.4k
    {
308
25.4k
      mi_munmap (mi->buf_end - mi->buf_size, mi->buf_size);
309
      mi->buf = mi->buf_end = NULL;
310
25.4k
    }
311
25.4k
}
312
313
#endif /* os_linux_h */