Coverage Report

Created: 2026-05-30 06:04

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
74.4k
{
43
74.4k
  char *cp = buf, tmp;
44
74.4k
  ssize_t i, len;
45
46
74.4k
  do
47
148k
    {
48
148k
      *cp++ = '0' + (val % 10);
49
148k
      val /= 10;
50
148k
    }
51
148k
  while (val);
52
53
  /* reverse the order of the digits: */
54
74.4k
  len = cp - buf;
55
74.4k
  --cp;
56
148k
  for (i = 0; i < len / 2; ++i)
57
74.4k
    {
58
74.4k
      tmp = buf[i];
59
74.4k
      buf[i] = cp[-i];
60
74.4k
      cp[-i] = tmp;
61
74.4k
    }
62
74.4k
  return buf + len;
63
74.4k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:unw_ltoa
os-linux.c:unw_ltoa
Line
Count
Source
42
74.4k
{
43
74.4k
  char *cp = buf, tmp;
44
74.4k
  ssize_t i, len;
45
46
74.4k
  do
47
148k
    {
48
148k
      *cp++ = '0' + (val % 10);
49
148k
      val /= 10;
50
148k
    }
51
148k
  while (val);
52
53
  /* reverse the order of the digits: */
54
74.4k
  len = cp - buf;
55
74.4k
  --cp;
56
148k
  for (i = 0; i < len / 2; ++i)
57
74.4k
    {
58
74.4k
      tmp = buf[i];
59
74.4k
      buf[i] = cp[-i];
60
74.4k
      cp[-i] = tmp;
61
74.4k
    }
62
74.4k
  return buf + len;
63
74.4k
}
64
65
static inline int
66
maps_init (struct map_iterator *mi, pid_t pid)
67
37.2k
{
68
37.2k
  char path[sizeof ("/proc/0123456789/maps")], *cp;
69
70
37.2k
  memcpy (path, "/proc/", 6);
71
37.2k
  cp = unw_ltoa (path + 6, pid);
72
37.2k
  assert (cp + 6 < path + sizeof (path));
73
37.2k
  memcpy (cp, "/maps", 6);
74
75
37.2k
  mi->fd = open (path, O_RDONLY);
76
37.2k
  if (mi->fd >= 0)
77
37.2k
    {
78
      /* Try to allocate a page-sized buffer.  */
79
37.2k
      mi->buf_size = getpagesize ();
80
37.2k
      GET_MEMORY (cp, mi->buf_size);
81
37.2k
      if (!cp)
82
0
        {
83
0
          close(mi->fd);
84
0
          mi->fd = -1;
85
0
          return -1;
86
0
        }
87
37.2k
      else
88
37.2k
        {
89
37.2k
          mi->offset = 0;
90
37.2k
          mi->buf = mi->buf_end = cp + mi->buf_size;
91
37.2k
          return 0;
92
37.2k
        }
93
37.2k
    }
94
0
  return -1;
95
37.2k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:maps_init
os-linux.c:maps_init
Line
Count
Source
67
37.2k
{
68
37.2k
  char path[sizeof ("/proc/0123456789/maps")], *cp;
69
70
37.2k
  memcpy (path, "/proc/", 6);
71
37.2k
  cp = unw_ltoa (path + 6, pid);
72
37.2k
  assert (cp + 6 < path + sizeof (path));
73
37.2k
  memcpy (cp, "/maps", 6);
74
75
37.2k
  mi->fd = open (path, O_RDONLY);
76
37.2k
  if (mi->fd >= 0)
77
37.2k
    {
78
      /* Try to allocate a page-sized buffer.  */
79
37.2k
      mi->buf_size = getpagesize ();
80
37.2k
      GET_MEMORY (cp, mi->buf_size);
81
37.2k
      if (!cp)
82
0
        {
83
0
          close(mi->fd);
84
0
          mi->fd = -1;
85
0
          return -1;
86
0
        }
87
37.2k
      else
88
37.2k
        {
89
37.2k
          mi->offset = 0;
90
37.2k
          mi->buf = mi->buf_end = cp + mi->buf_size;
91
37.2k
          return 0;
92
37.2k
        }
93
37.2k
    }
94
0
  return -1;
95
37.2k
}
96
97
static inline char *
98
skip_whitespace (char *cp)
99
1.17M
{
100
1.17M
  if (!cp)
101
0
    return NULL;
102
103
3.79M
  while (*cp == ' ' || *cp == '\t')
104
2.62M
    ++cp;
105
1.17M
  return cp;
106
1.17M
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:skip_whitespace
os-linux.c:skip_whitespace
Line
Count
Source
99
1.17M
{
100
1.17M
  if (!cp)
101
0
    return NULL;
102
103
3.79M
  while (*cp == ' ' || *cp == '\t')
104
2.62M
    ++cp;
105
1.17M
  return cp;
106
1.17M
}
107
108
static inline char *
109
scan_hex (char *cp, unsigned long *valp)
110
651k
{
111
651k
  unsigned long num_digits = 0, digit, val = 0;
112
113
651k
  cp = skip_whitespace (cp);
114
651k
  if (!cp)
115
0
    return NULL;
116
117
5.33M
  while (1)
118
5.33M
    {
119
5.33M
      digit = *cp;
120
5.33M
      if ((digit - '0') <= 9)
121
3.40M
        digit -= '0';
122
1.92M
      else if ((digit - 'A') < 6)
123
0
        digit -= 'A' - 10;
124
1.92M
      else if ((digit - 'a') < 6)
125
1.27M
        digit -= 'a' - 10;
126
651k
      else
127
651k
        break;
128
4.68M
      val = (val << 4) | digit;
129
4.68M
      ++num_digits;
130
4.68M
      ++cp;
131
4.68M
    }
132
651k
  if (!num_digits)
133
0
    return NULL;
134
651k
  *valp = val;
135
651k
  return cp;
136
651k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:scan_hex
os-linux.c:scan_hex
Line
Count
Source
110
651k
{
111
651k
  unsigned long num_digits = 0, digit, val = 0;
112
113
651k
  cp = skip_whitespace (cp);
114
651k
  if (!cp)
115
0
    return NULL;
116
117
5.33M
  while (1)
118
5.33M
    {
119
5.33M
      digit = *cp;
120
5.33M
      if ((digit - '0') <= 9)
121
3.40M
        digit -= '0';
122
1.92M
      else if ((digit - 'A') < 6)
123
0
        digit -= 'A' - 10;
124
1.92M
      else if ((digit - 'a') < 6)
125
1.27M
        digit -= 'a' - 10;
126
651k
      else
127
651k
        break;
128
4.68M
      val = (val << 4) | digit;
129
4.68M
      ++num_digits;
130
4.68M
      ++cp;
131
4.68M
    }
132
651k
  if (!num_digits)
133
0
    return NULL;
134
651k
  *valp = val;
135
651k
  return cp;
136
651k
}
137
138
static inline char *
139
scan_dec (char *cp, unsigned long *valp)
140
130k
{
141
130k
  unsigned long num_digits = 0, digit, val = 0;
142
143
130k
  if (!(cp = skip_whitespace (cp)))
144
0
    return NULL;
145
146
846k
  while (1)
147
846k
    {
148
846k
      digit = *cp;
149
846k
      if ((digit - '0') <= 9)
150
716k
        {
151
716k
          digit -= '0';
152
716k
          ++cp;
153
716k
        }
154
130k
      else
155
130k
        break;
156
716k
      val = (10 * val) + digit;
157
716k
      ++num_digits;
158
716k
    }
159
130k
  if (!num_digits)
160
0
    return NULL;
161
130k
  *valp = val;
162
130k
  return cp;
163
130k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:scan_dec
os-linux.c:scan_dec
Line
Count
Source
140
130k
{
141
130k
  unsigned long num_digits = 0, digit, val = 0;
142
143
130k
  if (!(cp = skip_whitespace (cp)))
144
0
    return NULL;
145
146
846k
  while (1)
147
846k
    {
148
846k
      digit = *cp;
149
846k
      if ((digit - '0') <= 9)
150
716k
        {
151
716k
          digit -= '0';
152
716k
          ++cp;
153
716k
        }
154
130k
      else
155
130k
        break;
156
716k
      val = (10 * val) + digit;
157
716k
      ++num_digits;
158
716k
    }
159
130k
  if (!num_digits)
160
0
    return NULL;
161
130k
  *valp = val;
162
130k
  return cp;
163
130k
}
164
165
static inline char *
166
scan_char (char *cp, char *valp)
167
260k
{
168
260k
  if (!cp)
169
0
    return NULL;
170
171
260k
  *valp = *cp;
172
173
  /* don't step over NUL terminator */
174
260k
  if (*cp)
175
260k
    ++cp;
176
260k
  return cp;
177
260k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:scan_char
os-linux.c:scan_char
Line
Count
Source
167
260k
{
168
260k
  if (!cp)
169
0
    return NULL;
170
171
260k
  *valp = *cp;
172
173
  /* don't step over NUL terminator */
174
260k
  if (*cp)
175
260k
    ++cp;
176
260k
  return cp;
177
260k
}
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
260k
{
184
260k
  size_t i = 0;
185
186
260k
  if (!(cp = skip_whitespace (cp)))
187
0
    return NULL;
188
189
6.02M
  while (*cp != ' ' && *cp != '\t' && *cp != '\0')
190
5.76M
    {
191
5.76M
      if ((valp != NULL) && (i < buf_size - 1))
192
520k
        valp[i++] = *cp;
193
5.76M
      ++cp;
194
5.76M
    }
195
260k
  if (i == 0 || i >= buf_size)
196
130k
    return NULL;
197
130k
  valp[i] = '\0';
198
130k
  return cp;
199
260k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:scan_string
os-linux.c:scan_string
Line
Count
Source
183
260k
{
184
260k
  size_t i = 0;
185
186
260k
  if (!(cp = skip_whitespace (cp)))
187
0
    return NULL;
188
189
6.02M
  while (*cp != ' ' && *cp != '\t' && *cp != '\0')
190
5.76M
    {
191
5.76M
      if ((valp != NULL) && (i < buf_size - 1))
192
520k
        valp[i++] = *cp;
193
5.76M
      ++cp;
194
5.76M
    }
195
260k
  if (i == 0 || i >= buf_size)
196
130k
    return NULL;
197
130k
  valp[i] = '\0';
198
130k
  return cp;
199
260k
}
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
130k
{
206
130k
  char perm[16], dash = 0, colon = 0, *cp;
207
130k
  unsigned long major, minor, inum;
208
130k
  ssize_t i, nread;
209
210
130k
  if (mi->fd < 0)
211
0
    return 0;
212
213
130k
  while (1)
214
130k
    {
215
130k
      ssize_t bytes_left = mi->buf_end - mi->buf;
216
130k
      char *eol = NULL;
217
218
9.41M
      for (i = 0; i < bytes_left; ++i)
219
9.37M
        {
220
9.37M
          if (mi->buf[i] == '\n')
221
93.0k
            {
222
93.0k
              eol = mi->buf + i;
223
93.0k
              break;
224
93.0k
            }
225
9.28M
          else if (mi->buf[i] == '\0')
226
0
            break;
227
9.37M
        }
228
130k
      if (!eol)
229
37.2k
        {
230
          /* copy down the remaining bytes, if any */
231
37.2k
          if (bytes_left > 0)
232
0
            memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
233
234
37.2k
          mi->buf = mi->buf_end - mi->buf_size;
235
37.2k
          nread = read (mi->fd, mi->buf + bytes_left,
236
37.2k
                        mi->buf_size - bytes_left);
237
37.2k
          if (nread <= 0)
238
0
            return 0;
239
37.2k
          else if ((size_t) (nread + bytes_left) < mi->buf_size)
240
37.2k
            {
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
37.2k
              memmove (mi->buf_end - nread - bytes_left, mi->buf,
245
37.2k
                       nread + bytes_left);
246
37.2k
              mi->buf = mi->buf_end - nread - bytes_left;
247
37.2k
            }
248
249
37.2k
          eol = mi->buf + bytes_left + nread - 1;
250
251
4.79M
          for (i = bytes_left; i < bytes_left + nread; ++i)
252
4.79M
            if (mi->buf[i] == '\n')
253
37.2k
              {
254
37.2k
                eol = mi->buf + i;
255
37.2k
                break;
256
37.2k
              }
257
37.2k
        }
258
130k
      cp = mi->buf;
259
130k
      mi->buf = eol + 1;
260
130k
      *eol = '\0';
261
262
      /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
263
130k
      cp = scan_hex (cp, low);
264
130k
      cp = scan_char (cp, &dash);
265
130k
      cp = scan_hex (cp, high);
266
130k
      cp = scan_string (cp, perm, sizeof (perm));
267
130k
      cp = scan_hex (cp, offset);
268
130k
      cp = scan_hex (cp, &major);
269
130k
      cp = scan_char (cp, &colon);
270
130k
      cp = scan_hex (cp, &minor);
271
130k
      cp = scan_dec (cp, &inum);
272
130k
      cp = mi->path = skip_whitespace (cp);
273
130k
      if (!cp)
274
0
        continue;
275
130k
      cp = scan_string (cp, NULL, 0);
276
130k
      if (dash != '-' || colon != ':')
277
0
        continue;       /* skip line with unknown or bad format */
278
130k
      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
130k
      return 1;
295
130k
    }
296
0
  return 0;
297
130k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:maps_next
os-linux.c:maps_next
Line
Count
Source
205
130k
{
206
130k
  char perm[16], dash = 0, colon = 0, *cp;
207
130k
  unsigned long major, minor, inum;
208
130k
  ssize_t i, nread;
209
210
130k
  if (mi->fd < 0)
211
0
    return 0;
212
213
130k
  while (1)
214
130k
    {
215
130k
      ssize_t bytes_left = mi->buf_end - mi->buf;
216
130k
      char *eol = NULL;
217
218
9.41M
      for (i = 0; i < bytes_left; ++i)
219
9.37M
        {
220
9.37M
          if (mi->buf[i] == '\n')
221
93.0k
            {
222
93.0k
              eol = mi->buf + i;
223
93.0k
              break;
224
93.0k
            }
225
9.28M
          else if (mi->buf[i] == '\0')
226
0
            break;
227
9.37M
        }
228
130k
      if (!eol)
229
37.2k
        {
230
          /* copy down the remaining bytes, if any */
231
37.2k
          if (bytes_left > 0)
232
0
            memmove (mi->buf_end - mi->buf_size, mi->buf, bytes_left);
233
234
37.2k
          mi->buf = mi->buf_end - mi->buf_size;
235
37.2k
          nread = read (mi->fd, mi->buf + bytes_left,
236
37.2k
                        mi->buf_size - bytes_left);
237
37.2k
          if (nread <= 0)
238
0
            return 0;
239
37.2k
          else if ((size_t) (nread + bytes_left) < mi->buf_size)
240
37.2k
            {
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
37.2k
              memmove (mi->buf_end - nread - bytes_left, mi->buf,
245
37.2k
                       nread + bytes_left);
246
37.2k
              mi->buf = mi->buf_end - nread - bytes_left;
247
37.2k
            }
248
249
37.2k
          eol = mi->buf + bytes_left + nread - 1;
250
251
4.79M
          for (i = bytes_left; i < bytes_left + nread; ++i)
252
4.79M
            if (mi->buf[i] == '\n')
253
37.2k
              {
254
37.2k
                eol = mi->buf + i;
255
37.2k
                break;
256
37.2k
              }
257
37.2k
        }
258
130k
      cp = mi->buf;
259
130k
      mi->buf = eol + 1;
260
130k
      *eol = '\0';
261
262
      /* scan: "LOW-HIGH PERM OFFSET MAJOR:MINOR INUM PATH" */
263
130k
      cp = scan_hex (cp, low);
264
130k
      cp = scan_char (cp, &dash);
265
130k
      cp = scan_hex (cp, high);
266
130k
      cp = scan_string (cp, perm, sizeof (perm));
267
130k
      cp = scan_hex (cp, offset);
268
130k
      cp = scan_hex (cp, &major);
269
130k
      cp = scan_char (cp, &colon);
270
130k
      cp = scan_hex (cp, &minor);
271
130k
      cp = scan_dec (cp, &inum);
272
130k
      cp = mi->path = skip_whitespace (cp);
273
130k
      if (!cp)
274
0
        continue;
275
130k
      cp = scan_string (cp, NULL, 0);
276
130k
      if (dash != '-' || colon != ':')
277
0
        continue;       /* skip line with unknown or bad format */
278
130k
      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
130k
      return 1;
295
130k
    }
296
0
  return 0;
297
130k
}
298
299
static inline void
300
maps_close (struct map_iterator *mi)
301
74.4k
{
302
74.4k
  if (mi->fd < 0)
303
37.2k
    return;
304
37.2k
  close (mi->fd);
305
37.2k
  mi->fd = -1;
306
37.2k
  if (mi->buf)
307
37.2k
    {
308
37.2k
      mi_munmap (mi->buf_end - mi->buf_size, mi->buf_size);
309
      mi->buf = mi->buf_end = NULL;
310
37.2k
    }
311
37.2k
}
Unexecuted instantiation: Lfind_proc_info-lsb.c:maps_close
os-linux.c:maps_close
Line
Count
Source
301
74.4k
{
302
74.4k
  if (mi->fd < 0)
303
37.2k
    return;
304
37.2k
  close (mi->fd);
305
37.2k
  mi->fd = -1;
306
37.2k
  if (mi->buf)
307
37.2k
    {
308
37.2k
      mi_munmap (mi->buf_end - mi->buf_size, mi->buf_size);
309
      mi->buf = mi->buf_end = NULL;
310
37.2k
    }
311
37.2k
}
312
313
#endif /* os_linux_h */