Coverage Report

Created: 2025-11-13 06:32

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