LCOV - code coverage report
Current view: top level - libdwfl_stacktrace - dwflst_sample_frame.c (source / functions) Coverage Total Hit
Test: elfutils-0.196 Lines: 1.4 % 138 2
Test Date: 2026-08-14 21:41:58 Functions: 7.7 % 13 1
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 1.0 % 99 1

             Branch data     Line data    Source code
       1                 :             : /* Get Dwarf Frame state for perf stack sample data.
       2                 :             :    Copyright (C) 2025 Red Hat, Inc.
       3                 :             :    This file is part of elfutils.
       4                 :             : 
       5                 :             :    This file is free software; you can redistribute it and/or modify
       6                 :             :    it under the terms of either
       7                 :             : 
       8                 :             :      * the GNU Lesser General Public License as published by the Free
       9                 :             :        Software Foundation; either version 3 of the License, or (at
      10                 :             :        your option) any later version
      11                 :             : 
      12                 :             :    or
      13                 :             : 
      14                 :             :      * the GNU General Public License as published by the Free
      15                 :             :        Software Foundation; either version 2 of the License, or (at
      16                 :             :        your option) any later version
      17                 :             : 
      18                 :             :    or both in parallel, as here.
      19                 :             : 
      20                 :             :    elfutils is distributed in the hope that it will be useful, but
      21                 :             :    WITHOUT ANY WARRANTY; without even the implied warranty of
      22                 :             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      23                 :             :    General Public License for more details.
      24                 :             : 
      25                 :             :    You should have received copies of the GNU General Public License and
      26                 :             :    the GNU Lesser General Public License along with this program.  If
      27                 :             :    not, see <http://www.gnu.org/licenses/>.  */
      28                 :             : 
      29                 :             : #ifdef HAVE_CONFIG_H
      30                 :             : # include <config.h>
      31                 :             : #endif
      32                 :             : 
      33                 :             : #include "libdwfl_stacktraceP.h"
      34                 :             : 
      35                 :             : /* Various functions providing arch-specific info:  */
      36                 :             : 
      37                 :             : Ebl *default_ebl = NULL;
      38                 :             : GElf_Half default_ebl_machine = EM_NONE;
      39                 :             : 
      40                 :             : uint64_t
      41                 :           0 : dwflst_perf_sample_preferred_regs_mask (GElf_Half machine)
      42                 :             : {
      43                 :             :   /* XXX The most likely case is that this will only be called once,
      44                 :             :      for the current architecture.  So we keep one Ebl* around for
      45                 :             :      answering this query and replace it in the unlikely case of
      46                 :             :      getting called with different architectures.  */
      47   [ #  #  #  # ]:           0 :   if (default_ebl != NULL && default_ebl_machine != machine)
      48                 :             :     {
      49                 :           0 :       ebl_closebackend(default_ebl);
      50                 :           0 :       default_ebl = NULL;
      51                 :             :     }
      52         [ #  # ]:           0 :   if (default_ebl == NULL)
      53                 :             :     {
      54                 :           0 :       default_ebl = ebl_openbackend_machine(machine);
      55                 :           0 :       default_ebl_machine = machine;
      56                 :             :     }
      57         [ #  # ]:           0 :   if (default_ebl != NULL)
      58                 :           0 :     return ebl_perf_frame_regs_mask (default_ebl);
      59                 :             :   return 0;
      60                 :             : }
      61                 :             : 
      62                 :             : GElf_Half
      63                 :           8 : dwflst_arch_from_uname (const char *umachine)
      64                 :             : {
      65         [ -  + ]:           8 :   if (strncmp(umachine, "x86_64", 6) == 0)
      66                 :             :     return EM_X86_64;
      67         [ #  # ]:           0 :   else if (strncmp(umachine, "i686", 4) == 0
      68         [ #  # ]:           0 :            || strncmp(umachine, "i386", 4) == 0)
      69                 :             :     return EM_386;
      70         [ #  # ]:           0 :   else if (strncmp(umachine, "aarch64", 7) == 0)
      71                 :             :     return EM_AARCH64;
      72         [ #  # ]:           0 :   else if (strncmp(umachine, "armv7l", 6) == 0)
      73                 :           0 :     return EM_ARM;
      74                 :             :   /* XXX Other architectures not yet supported. */
      75                 :             :   return EM_NONE;
      76                 :             : }
      77                 :             : 
      78                 :             : /* XXX The per-machine switches suggest the following implementations
      79                 :             :    could be folded into backends/, but we would need to create a Ebl
      80                 :             :    to handle the dispatch:  */
      81                 :             : 
      82                 :             : uint32_t
      83                 :           0 : dwflst_arch_expected_frame_nregs (GElf_Half machine)
      84                 :             : {
      85                 :             :   /* For aarch64, we actually use fewer than ebl->frame_nregs to unwind:  */
      86         [ #  # ]:           0 :   if (machine == EM_AARCH64)
      87                 :             :     return 14;
      88         [ #  # ]:           0 :   if (machine == EM_ARM)
      89                 :             :     return 16;
      90                 :             :   /* On x86, expect everything except FLAGS:  */
      91         [ #  # ]:           0 :   if (machine == EM_X86_64 || machine == EM_386)
      92                 :             :     /* XXX An external user of the library can't access the Ebl, hence
      93                 :             :        can't conveniently provide it to us it here.  We provide the
      94                 :             :        constant directly rather than initializing a new Ebl.  */
      95         [ #  # ]:           0 :     return machine == EM_X86_64 ? 17 : 9;
      96                 :             :     /* return ebl_frame_nregs(ebl); */
      97                 :             :   /* XXX Other architectures are not supported yet.
      98                 :             :      In general, it's fine to be on the permissive side here
      99                 :             :      for a minimum expected number of registers.  */
     100                 :             :   return 0;
     101                 :             : }
     102                 :             : 
     103                 :             : int
     104                 :           0 : dwflst_arch_sp_dwarf_reg (GElf_Half machine, bool force_abi32)
     105                 :             : {
     106   [ #  #  #  #  :           0 :   switch (machine) {
                      # ]
     107                 :           0 :   case EM_X86_64:
     108         [ #  # ]:           0 :     return force_abi32 ? 4 : 7;
     109                 :             :   case EM_386:
     110                 :             :     return 4;
     111                 :             :   case EM_ARM:
     112                 :             :     return 13;
     113                 :           0 :   case EM_AARCH64:
     114         [ #  # ]:           0 :     return force_abi32 ? 13 : 31;
     115                 :             :   default:
     116                 :             :     /* XXX Other architectures are not supported yet.  */
     117                 :             :     return -1;
     118                 :             :   }
     119                 :             : }
     120                 :             : 
     121                 :             : int
     122                 :           0 : dwflst_arch_sp_perf_reg (GElf_Half machine,
     123                 :             :                          uint64_t perf_regs_mask, bool is_abi32)
     124                 :             : {
     125                 :           0 :   int sp_perf_index;
     126         [ #  # ]:           0 :   if (machine == EM_X86_64 || machine == EM_386)
     127                 :             :     sp_perf_index = 7; /* uniform on 32/64-bit abi */
     128                 :             :     /* compare backends/x86_initreg_sample.c;
     129                 :             :        for dwarf_index, would be (is_abi32 ? 4 : 7) */
     130         [ #  # ]:           0 :   else if (machine == EM_ARM || machine == EM_AARCH64)
     131         [ #  # ]:           0 :     sp_perf_index = (is_abi32 ? 13 : 31);
     132                 :             :     /* basic linear mapping of perf_regs<->dwarf_regs */
     133                 :             :   else
     134                 :             :     /* XXX Other architectures are not supported yet.  */
     135                 :             :     return -1;
     136                 :             : 
     137         [ #  # ]:           0 :   if (perf_regs_mask == 0)
     138                 :             :     /* Assume all registers present:  */
     139                 :             :     return sp_perf_index;
     140                 :             : 
     141                 :             :   /* Iterate perf_regs_mask to find
     142                 :             :      k == index of sp in perf_regs_mask
     143                 :             :      j == index of sp in regs[]  */
     144                 :             :   int j, k; uint64_t bit;
     145                 :             :   for (k = 0, j = -1, bit = 1;
     146         [ #  # ]:           0 :        k <= sp_perf_index; k++, bit <<= 1)
     147                 :             :     {
     148         [ #  # ]:           0 :       if ((bit & perf_regs_mask))
     149                 :           0 :         j++;
     150                 :             :       else
     151                 :           0 :         continue; /* sp may not be present */
     152         [ #  # ]:           0 :       if (k == sp_perf_index)
     153                 :             :         return j;
     154                 :             :     }
     155                 :             :   return -2;
     156                 :             : }
     157                 :             : 
     158                 :             : /* Stack sample handling:  */
     159                 :             : 
     160                 :             : struct sample_info {
     161                 :             :   pid_t pid;
     162                 :             :   pid_t tid;
     163                 :             :   Dwarf_Addr base_addr;
     164                 :             :   const uint8_t *stack;
     165                 :             :   size_t stack_size;
     166                 :             :   const Dwarf_Word *regs;
     167                 :             :   uint n_regs;
     168                 :             :   const int *regs_mapping;
     169                 :             :   size_t n_regs_mapping;
     170                 :             :   int elfclass;
     171                 :             :   Dwarf_Addr pc;
     172                 :             : };
     173                 :             : 
     174                 :             : /* The next few functions imitate the corefile interface for a single
     175                 :             :    stack sample, with very restricted access to registers and memory. */
     176                 :             : 
     177                 :             : /* Just yield the single thread id matching the sample. */
     178                 :             : static pid_t
     179                 :           0 : sample_next_thread (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg,
     180                 :             :                     void **thread_argp)
     181                 :             : {
     182                 :           0 :   struct sample_info *sample_arg =
     183                 :             :     (struct sample_info *)dwfl_arg;
     184         [ #  # ]:           0 :   if (*thread_argp == NULL)
     185                 :             :     {
     186                 :           0 :       *thread_argp = (void *)0xea7b3375;
     187                 :           0 :       return sample_arg->tid;
     188                 :             :     }
     189                 :             :   else
     190                 :             :     return 0;
     191                 :             : }
     192                 :             : 
     193                 :             : /* Just check that the thread id matches the sample. */
     194                 :             : static bool
     195                 :           0 : sample_getthread (Dwfl *dwfl __attribute__ ((unused)), pid_t tid,
     196                 :             :                   void *dwfl_arg, void **thread_argp)
     197                 :             : {
     198                 :           0 :   struct sample_info *sample_arg =
     199                 :             :     (struct sample_info *)dwfl_arg;
     200                 :           0 :   *thread_argp = (void *)sample_arg;
     201         [ #  # ]:           0 :   if (sample_arg->tid != tid)
     202                 :             :     {
     203                 :           0 :       __libdwfl_seterrno(DWFL_E_INVALID_ARGUMENT);
     204                 :           0 :       return false;
     205                 :             :     }
     206                 :             :   return true;
     207                 :             : }
     208                 :             : 
     209                 :             : #define copy_word_64(result, d) \
     210                 :             :   if ((((uintptr_t) (d)) & (sizeof (uint64_t) - 1)) == 0) \
     211                 :             :     *(result) = *(uint64_t *)(d); \
     212                 :             :   else \
     213                 :             :     memcpy ((result), (d), sizeof (uint64_t));
     214                 :             : 
     215                 :             : #define copy_word_32(result, d) \
     216                 :             :   if ((((uintptr_t) (d)) & (sizeof (uint32_t) - 1)) == 0) \
     217                 :             :     *(result) = *(uint32_t *)(d); \
     218                 :             :   else \
     219                 :             :     memcpy ((result), (d), sizeof (uint32_t));
     220                 :             : 
     221                 :             : #define copy_word(result, d, elfclass) \
     222                 :             :   if ((elfclass) == ELFCLASS64) \
     223                 :             :     { copy_word_64((result), (d)); } \
     224                 :             :   else if ((elfclass) == ELFCLASS32) \
     225                 :             :     { copy_word_32((result), (d)); } \
     226                 :             :   else \
     227                 :             :     *(result) = 0;
     228                 :             : 
     229                 :             : static bool
     230                 :           0 : elf_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
     231                 :             : {
     232                 :           0 :   struct sample_info *sample_arg =
     233                 :             :     (struct sample_info *)arg;
     234                 :           0 :   Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, addr);
     235                 :           0 :   Dwarf_Addr bias;
     236                 :           0 :   Elf_Scn *section = INTUSE(dwfl_module_address_section) (mod, &addr, &bias);
     237                 :             : 
     238         [ #  # ]:           0 :   if (!section)
     239                 :             :     {
     240                 :           0 :       __libdwfl_seterrno(DWFL_E_ADDR_OUTOFRANGE);
     241                 :           0 :       return false;
     242                 :             :     }
     243                 :             : 
     244                 :           0 :   Elf_Data *data = elf_getdata(section, NULL);
     245   [ #  #  #  #  :           0 :   if (data && data->d_buf && data->d_size > addr) {
                   #  # ]
     246                 :           0 :     uint8_t *d = ((uint8_t *)data->d_buf) + addr;
     247   [ #  #  #  #  :           0 :     copy_word(result, d, sample_arg->elfclass);
             #  #  #  # ]
     248                 :           0 :     return true;
     249                 :             :   }
     250                 :           0 :   __libdwfl_seterrno(DWFL_E_ADDR_OUTOFRANGE);
     251                 :           0 :   return false;
     252                 :             : }
     253                 :             : 
     254                 :             : static bool
     255                 :           0 : sample_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
     256                 :             : {
     257                 :           0 :   struct sample_info *sample_arg =
     258                 :             :     (struct sample_info *)arg;
     259                 :             :   /* Imitate read_cached_memory() with the stack sample data as the cache. */
     260         [ #  # ]:           0 :   if (addr < sample_arg->base_addr ||
     261         [ #  # ]:           0 :       addr - sample_arg->base_addr >= sample_arg->stack_size)
     262                 :           0 :     return elf_memory_read(dwfl, addr, result, arg);
     263                 :           0 :   const uint8_t *d = &sample_arg->stack[addr - sample_arg->base_addr];
     264   [ #  #  #  #  :           0 :   copy_word(result, d, sample_arg->elfclass);
             #  #  #  # ]
     265                 :             :   return true;
     266                 :             : }
     267                 :             : 
     268                 :             : 
     269                 :             : static bool
     270                 :           0 : sample_set_initial_registers (Dwfl_Thread *thread, void *arg)
     271                 :             : {
     272                 :           0 :   struct sample_info *sample_arg =
     273                 :             :     (struct sample_info *)arg;
     274                 :           0 :   INTUSE(dwfl_thread_state_register_pc) (thread, sample_arg->pc);
     275                 :           0 :   Dwfl_Process *process = thread->process;
     276                 :           0 :   Ebl *ebl = process->ebl;
     277                 :           0 :   return ebl_set_initial_registers_sample
     278                 :             :     (ebl, sample_arg->regs, sample_arg->n_regs,
     279                 :             :      sample_arg->regs_mapping, sample_arg->n_regs_mapping,
     280                 :             :      __libdwfl_set_initial_registers_thread, thread);
     281                 :             : }
     282                 :             : 
     283                 :             : static void
     284                 :           0 : sample_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
     285                 :             : {
     286                 :           0 :   struct sample_info *sample_arg =
     287                 :             :     (struct sample_info *)dwfl_arg;
     288                 :           0 :   free (sample_arg);
     289                 :           0 : }
     290                 :             : 
     291                 :             : static const Dwfl_Thread_Callbacks sample_thread_callbacks =
     292                 :             :   {
     293                 :             :     sample_next_thread,
     294                 :             :     sample_getthread,
     295                 :             :     sample_memory_read,
     296                 :             :     sample_set_initial_registers,
     297                 :             :     sample_detach,
     298                 :             :     NULL, /* sample_thread_detach */
     299                 :             :   };
     300                 :             : 
     301                 :             : int
     302                 :           0 : dwflst_sample_getframes (Dwfl *dwfl, Elf *elf,
     303                 :             :                          pid_t pid, pid_t tid,
     304                 :             :                          const void *stack, size_t stack_size,
     305                 :             :                          const Dwarf_Word *regs, uint n_regs,
     306                 :             :                          const int *regs_mapping, size_t n_regs_mapping,
     307                 :             :                          int (*callback) (Dwfl_Frame *state, void *arg),
     308                 :             :                          void *arg)
     309                 :             : {
     310                 :             :   /* TODO: Lock the dwfl to ensure attach_state does not interfere
     311                 :             :      with other dwfl_perf_sample_getframes calls. */
     312                 :             : 
     313                 :           0 :   struct sample_info *sample_arg;
     314                 :           0 :   bool attached = false;
     315         [ #  # ]:           0 :   if (dwfl->process != NULL)
     316                 :             :     {
     317                 :           0 :       sample_arg = dwfl->process->callbacks_arg;
     318                 :           0 :       attached = true;
     319                 :             :     }
     320                 :             :   else
     321                 :             :     {
     322                 :           0 :       sample_arg = malloc (sizeof *sample_arg);
     323         [ #  # ]:           0 :       if (sample_arg == NULL)
     324                 :             :         {
     325                 :           0 :           __libdwfl_seterrno(DWFL_E_NOMEM);
     326                 :           0 :           return -1;
     327                 :             :         }
     328                 :             :     }
     329                 :             : 
     330                 :           0 :   sample_arg->pid = pid;
     331                 :           0 :   sample_arg->tid = tid;
     332                 :           0 :   sample_arg->stack = (const uint8_t *)stack;
     333                 :           0 :   sample_arg->stack_size = stack_size;
     334                 :           0 :   sample_arg->regs = regs;
     335                 :           0 :   sample_arg->n_regs = n_regs;
     336                 :           0 :   sample_arg->regs_mapping = regs_mapping;
     337                 :           0 :   sample_arg->n_regs_mapping = n_regs_mapping;
     338                 :             : 
     339         [ #  # ]:           0 :   if (! attached
     340         [ #  # ]:           0 :       && ! INTUSE(dwfl_attach_state) (dwfl, elf, pid,
     341                 :             :                                       &sample_thread_callbacks, sample_arg))
     342                 :             :     {
     343                 :           0 :       free(sample_arg);
     344                 :           0 :       return -1;
     345                 :             :     }
     346                 :             : 
     347                 :           0 :   Dwfl_Process *process = dwfl->process;
     348                 :           0 :   Ebl *ebl = process->ebl;
     349                 :           0 :   sample_arg->elfclass = ebl_get_elfclass(ebl);
     350                 :           0 :   ebl_sample_sp_pc(ebl, regs, n_regs,
     351                 :             :                    regs_mapping, n_regs_mapping,
     352                 :           0 :                    &sample_arg->base_addr, &sample_arg->pc);
     353                 :             : 
     354                 :           0 :   return INTUSE(dwfl_getthread_frames) (dwfl, tid, callback, arg);
     355                 :             : }
     356                 :             : 
     357                 :             : int
     358                 :           0 : dwflst_perf_sample_getframes (Dwfl *dwfl, Elf *elf,
     359                 :             :                               pid_t pid, pid_t tid,
     360                 :             :                               const void *stack, size_t stack_size,
     361                 :             :                               const Dwarf_Word *regs, uint32_t n_regs,
     362                 :             :                               uint64_t perf_regs_mask, uint32_t abi,
     363                 :             :                               int (*callback) (Dwfl_Frame *state, void *arg),
     364                 :             :                               void *arg)
     365                 :             : {
     366                 :             :   /* TODO: Lock the dwfl to ensure attach_state does not interfere
     367                 :             :      with other dwfl_perf_sample_getframes calls. */
     368                 :             : 
     369                 :           0 :   struct sample_info *sample_arg;
     370                 :           0 :   bool attached = false;
     371         [ #  # ]:           0 :   if (dwfl->process != NULL)
     372                 :             :     {
     373                 :           0 :       sample_arg = dwfl->process->callbacks_arg;
     374                 :           0 :       attached = true;
     375                 :             :     }
     376                 :             :   else
     377                 :             :     {
     378                 :           0 :       sample_arg = malloc (sizeof *sample_arg);
     379         [ #  # ]:           0 :       if (sample_arg == NULL)
     380                 :             :         {
     381                 :           0 :           __libdwfl_seterrno(DWFL_E_NOMEM);
     382                 :           0 :           return -1;
     383                 :             :         }
     384                 :             :     }
     385                 :             : 
     386                 :           0 :   sample_arg->pid = pid;
     387                 :           0 :   sample_arg->tid = tid;
     388                 :           0 :   sample_arg->stack = (const uint8_t *)stack;
     389                 :           0 :   sample_arg->stack_size = stack_size;
     390                 :           0 :   sample_arg->regs = regs;
     391                 :           0 :   sample_arg->n_regs = n_regs;
     392                 :             : 
     393         [ #  # ]:           0 :   if (! attached
     394         [ #  # ]:           0 :       && ! INTUSE(dwfl_attach_state) (dwfl, elf, pid,
     395                 :             :                                       &sample_thread_callbacks, sample_arg))
     396                 :             :     {
     397                 :           0 :       free(sample_arg);
     398                 :           0 :       return -1;
     399                 :             :     }
     400                 :             : 
     401                 :             :   /* Select the regs_mapping based on architecture.  This will be
     402                 :             :      cached in ebl to avoid having to recompute the regs_mapping array
     403                 :             :      when perf_regs_mask is consistent for the entire session: */
     404                 :           0 :   Dwfl_Process *process = dwfl->process;
     405                 :           0 :   Ebl *ebl = process->ebl;
     406         [ #  # ]:           0 :   if (!ebl_sample_perf_regs_mapping(ebl,
     407                 :             :                                     perf_regs_mask, abi,
     408                 :             :                                     &sample_arg->regs_mapping, &sample_arg->n_regs_mapping))
     409                 :             :     {
     410                 :           0 :       __libdwfl_seterrno(DWFL_E_LIBEBL_BAD);
     411                 :           0 :       return -1;
     412                 :             :     }
     413                 :           0 :   sample_arg->elfclass = ebl_get_elfclass(ebl);
     414                 :           0 :   ebl_sample_sp_pc(ebl, regs, n_regs,
     415                 :             :                    sample_arg->regs_mapping, sample_arg->n_regs_mapping,
     416                 :           0 :                    &sample_arg->base_addr, &sample_arg->pc);
     417                 :             : 
     418                 :             :   /* XXX May want to check if abi matches ebl_get_elfclass(ebl). */
     419                 :           0 :   return INTUSE(dwfl_getthread_frames) (dwfl, tid, callback, arg);
     420                 :             : }
        

Generated by: LCOV version 2.0-1