This is the mail archive of the elfutils-devel@sourceware.org mailing list for the elfutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [patch v7 3/5] x86* unwinder: libdwfl/


Hi Jan,

The two files I had skipped in the last review.
Mostly nits about error paths/values. But The comment about
tids_attached/pid_memory_read might need some real reply.
Hopefully the suggestion will make that part simpler.

On Sun, Oct 13, 2013 at 12:24:55PM +0200, Jan Kratochvil wrote:
> 	* dwfl_frame_pid.c: New file.
> 	* dwfl_frame_core.c: New file.

Nitpick. Since these files don't actually contain the public
functions dwfl_frame_pid or dwfl_frame_core they might be better
named something without the dwfl_ prefix (linux-pid-attach.c and
linux-core-attach.c, or libdwfl_attach_pid/core.c maybe?)

> diff --git a/libdwfl/dwfl_frame_core.c b/libdwfl/dwfl_frame_core.c
> +struct core_arg
> +{
> +  Elf *core;
> +  Elf_Data *note_data;
> +  size_t thread_note_offset;
> +  Ebl *ebl;
> +};
> +
> +struct thread_arg
> +{
> +  struct core_arg *core_arg;
> +  size_t note_offset;
> +};
> +
> +static bool
> +core_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result,
> +		  void *dwfl_arg)
> +{
> +  Dwfl_Process *process = dwfl->process;
> +  struct core_arg *core_arg = dwfl_arg;
> +  Elf *core = core_arg->core;
> +  assert (core != NULL);
> +  static size_t phnum;
> +  if (elf_getphdrnum (core, &phnum) < 0)
> +    return false;

__libdwfl_seterrno (DWFL_E_LIBELF) ?

> +  for (size_t cnt = 0; cnt < phnum; ++cnt)
> +    {
> +      GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
> +      if (phdr == NULL || phdr->p_type != PT_LOAD)
> +	continue;
> +      /* Bias is zero here, a core file itself has no bias.  */
> +      GElf_Addr start = __libdwfl_segment_start (dwfl, phdr->p_vaddr);
> +      GElf_Addr end = __libdwfl_segment_end (dwfl,
> +					     phdr->p_vaddr + phdr->p_memsz);
> +      unsigned bytes = process->ebl->class == ELFCLASS64 ? 8 : 4;
> +      if (addr < start || addr + bytes > end)
> +	continue;
> +      Elf_Data *data;
> +      data = elf_getdata_rawchunk (core, phdr->p_offset + addr - start,
> +				   bytes, ELF_T_ADDR);
> +      if (data == NULL)
> +	return false;

__libdwfl_seterrno (DWFL_E_LIBELF) ?

> +      assert (data->d_size == bytes);

Maybe assert is too strong here? Could there be corrupt core files?

> +      /* FIXME: Currently any arch supported for unwinding supports
> +	 unaligned access.  */
> +      if (bytes == 8)
> +	*result = *(const uint64_t *) data->d_buf;
> +      else
> +	*result = *(const uint32_t *) data->d_buf;
> +      return true;
> +    }
> +  return false;
> +}

It would be nice to set dwfl_errno at the end to indicate the core file
just didn't contain that memory. DWFL_E_ADDR_OUTOFRANGE or DWFL_E_NO_MATCH?

> +static pid_t
> +core_next_thread (Dwfl *dwfl __attribute__ ((unused)),
> +		  Dwfl_Thread *nthread __attribute__ ((unused)), void *dwfl_arg,
> +		  void **thread_argp)
> +{
> +  struct core_arg *core_arg = dwfl_arg;
> +  Elf *core = core_arg->core;
> +  GElf_Nhdr nhdr;
> +  size_t name_offset;
> +  size_t desc_offset;
> +  Elf_Data *note_data = core_arg->note_data;
> +  size_t offset;
> +  while (offset = core_arg->thread_note_offset, offset < note_data->d_size
> +	 && (core_arg->thread_note_offset = gelf_getnote (note_data, offset,
> +							  &nhdr, &name_offset,
> +							  &desc_offset)) > 0)
> +    {
> +      /* Do not check NAME for now, help broken Linux kernels.  */
> +      const char *name = note_data->d_buf + name_offset;
> +      const char *desc = note_data->d_buf + desc_offset;
> +      GElf_Word regs_offset;
> +      size_t nregloc;
> +      const Ebl_Register_Location *reglocs;
> +      size_t nitems;
> +      const Ebl_Core_Item *items;
> +      if (! ebl_core_note (core_arg->ebl, &nhdr, name,
> +			   &regs_offset, &nregloc, &reglocs, &nitems, &items))
> +	{
> +	  /* This note may be just not recognized, skip it.  */
> +	  continue;
> +	}
> +      if (nhdr.n_type != NT_PRSTATUS)
> +	continue;
> +      const Ebl_Core_Item *item;
> +      for (item = items; item < items + nitems; item++)
> +	if (strcmp (item->name, "pid") == 0)
> +	  break;
> +      if (item == items + nitems)
> +	continue;
> +      uint32_t val32 = *(const uint32_t *) (desc + item->offset);
> +      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
> +		? be32toh (val32) : le32toh (val32));
> +      pid_t tid = (int32_t) val32;
> +      eu_static_assert (sizeof val32 <= sizeof tid);
> +      struct thread_arg *thread_arg = malloc (sizeof (*thread_arg));
> +      if (thread_arg == NULL)
> +	{
> +	  __libdwfl_seterrno (DWFL_E_NOMEM);
> +	  return 0;
> +	}

Error should be return -1.

> +      thread_arg->core_arg = core_arg;
> +      thread_arg->note_offset = offset;
> +      *thread_argp = thread_arg;
> +      return tid;
> +    }
> +  return 0;
> +}

> +static bool
> +core_set_initial_registers (Dwfl_Thread *thread, void *thread_arg_voidp)
> +{
> +  struct thread_arg *thread_arg = thread_arg_voidp;
> +  struct core_arg *core_arg = thread_arg->core_arg;
> +  Elf *core = core_arg->core;
> +  size_t offset = thread_arg->note_offset;
> +  GElf_Nhdr nhdr;
> +  size_t name_offset;
> +  size_t desc_offset;
> +  Elf_Data *note_data = core_arg->note_data;
> +  size_t nregs = ebl_frame_nregs (core_arg->ebl);
> +  assert (offset < note_data->d_size);
> +  size_t getnote_err = gelf_getnote (note_data, offset, &nhdr, &name_offset,
> +				     &desc_offset);
> +  assert (getnote_err != 0);

Too strong IMHO. Just __libdwfl_seterrno (DWFL_E_LIBELF) return false?
core files can be corrupted.

> +  /* Do not check NAME for now, help broken Linux kernels.  */
> +  const char *name = note_data->d_buf + name_offset;
> +  const char *desc = note_data->d_buf + desc_offset;
> +  GElf_Word regs_offset;
> +  size_t nregloc;
> +  const Ebl_Register_Location *reglocs;
> +  size_t nitems;
> +  const Ebl_Core_Item *items;
> +  int core_note_err = ebl_core_note (core_arg->ebl, &nhdr, name, &regs_offset,
> +				     &nregloc, &reglocs, &nitems, &items);
> +  assert (core_note_err != 0);
> +  assert (nhdr.n_type == NT_PRSTATUS);

As above too strong IMHO.

> +  const Ebl_Core_Item *item;
> +  for (item = items; item < items + nitems; item++)
> +    if (strcmp (item->name, "pid") == 0)
> +      break;
> +  assert (item < items + nitems);
> +  pid_t tid;
> +  {
> +    uint32_t val32 = *(const uint32_t *) (desc + item->offset);
> +    val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
> +	     ? be32toh (val32) : le32toh (val32));
> +    tid = (int32_t) val32;
> +    eu_static_assert (sizeof val32 <= sizeof tid);
> +  }
> +  assert (tid == INTUSE(dwfl_thread_tid) (thread));

Can this happen with a corrupt core file? Then it is too strong IMHO.

> +  desc += regs_offset;
> +  for (size_t regloci = 0; regloci < nregloc; regloci++)
> +    {
> +      const Ebl_Register_Location *regloc = reglocs + regloci;
> +      if (regloc->regno >= nregs)
> +	continue;
> +      assert (regloc->bits == 32 || regloc->bits == 64);
> +      const char *reg_desc = desc + regloc->offset;
> +      for (unsigned regno = regloc->regno;
> +	   regno < MIN (regloc->regno + (regloc->count ?: 1U), nregs);
> +	   regno++)
> +	{
> +	  /* PPC provides DWARF register 65 irrelevant for
> +	     CFI which clashes with register 108 (LR) we need.
> +	     LR (108) is provided earlier (in NT_PRSTATUS) than the # 65.
> +	     FIXME: It depends now on their order in core notes.
> +	     FIXME: It uses private function.  */
> +	  if (dwfl_frame_reg_get (thread->unwound, regno, NULL))
> +	    continue;

I dunno how (or if it is important) to fix this issue.
The dwfl_frame_reg_get check does look irrelevant for other arches.
But should be harmless.

> +	  Dwarf_Word val;
> +	  switch (regloc->bits)
> +	  {
> +	    case 32:;
> +	      uint32_t val32 = *(const uint32_t *) reg_desc;
> +	      reg_desc += sizeof val32;
> +	      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
> +		       ? be32toh (val32) : le32toh (val32));
> +	      /* Do a host width conversion.  */
> +	      val = val32;
> +	      break;
> +	    case 64:;
> +	      uint64_t val64 = *(const uint64_t *) reg_desc;
> +	      reg_desc += sizeof val64;
> +	      val64 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
> +		       ? be64toh (val64) : le64toh (val64));
> +	      assert (sizeof (*thread->unwound->regs) == sizeof val64);
> +	      val = val64;
> +	      break;
> +	    default:
> +	      abort ();
> +	  }
> +	  /* Registers not valid for CFI are just ignored.  */
> +	  INTUSE(dwfl_thread_state_registers) (thread, regno, 1, &val);
> +	  reg_desc += regloc->pad;
> +	}
> +    }
> +  return true;
> +}
> +
> +static void
> +core_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
> +{
> +  struct core_arg *core_arg = dwfl_arg;
> +  ebl_closebackend (core_arg->ebl);
> +  free (core_arg);
> +}
> +
> +static const Dwfl_Thread_Callbacks core_thread_callbacks = 
> +{
> +  core_next_thread,
> +  core_memory_read,
> +  core_set_initial_registers,
> +  core_detach,
> +  NULL, /* core_thread_detach */
> +};
> +
> +bool
> +internal_function
> +__libdwfl_attach_state_for_core (Dwfl *dwfl, Elf *core)
> +{
> +  Ebl *ebl = ebl_openbackend (core);
> +  if (ebl == NULL)
> +    {
> +      __libdwfl_seterrno (DWFL_E_LIBEBL);
> +      return false;
> +    }
> +  size_t nregs = ebl_frame_nregs (ebl);
> +  if (nregs == 0)
> +    {
> +      ebl_closebackend (ebl);
> +      __libdwfl_seterrno (DWFL_E_LIBEBL);
> +      return false;
> +    }
> +  GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (core, &ehdr_mem);
> +  if (ehdr == NULL)
> +    {
> +      ebl_closebackend (ebl);
> +      __libdwfl_seterrno (DWFL_E_LIBELF);
> +      return false;
> +    }
> +  assert (ehdr->e_type == ET_CORE);
> +  size_t phnum;
> +  if (elf_getphdrnum (core, &phnum) < 0)
> +    {
> +      ebl_closebackend (ebl);
> +      __libdwfl_seterrno (DWFL_E_LIBELF);
> +      return false;
> +    }
> +  pid_t pid = -1;
> +  Elf_Data *note_data = NULL;
> +  for (size_t cnt = 0; cnt < phnum; ++cnt)
> +    {
> +      GElf_Phdr phdr_mem, *phdr = gelf_getphdr (core, cnt, &phdr_mem);
> +      if (phdr != NULL && phdr->p_type == PT_NOTE)
> +	{
> +	  note_data = elf_getdata_rawchunk (core, phdr->p_offset,
> +					    phdr->p_filesz, ELF_T_NHDR);
> +	  break;
> +	}
> +    }
> +  if (note_data == NULL)
> +    {
> +      ebl_closebackend (ebl);
> +      __libdwfl_seterrno (DWFL_E_LIBELF);
> +      return NULL;
> +    }
> +  size_t offset = 0;
> +  GElf_Nhdr nhdr;
> +  size_t name_offset;
> +  size_t desc_offset;
> +  while (offset < note_data->d_size
> +	 && (offset = gelf_getnote (note_data, offset,
> +				    &nhdr, &name_offset, &desc_offset)) > 0)
> +    {
> +      /* Do not check NAME for now, help broken Linux kernels.  */
> +      const char *name = note_data->d_buf + name_offset;
> +      const char *desc = note_data->d_buf + desc_offset;
> +      GElf_Word regs_offset;
> +      size_t nregloc;
> +      const Ebl_Register_Location *reglocs;
> +      size_t nitems;
> +      const Ebl_Core_Item *items;
> +      if (! ebl_core_note (ebl, &nhdr, name,
> +			   &regs_offset, &nregloc, &reglocs, &nitems, &items))
> +	{
> +	  /* This note may be just not recognized, skip it.  */
> +	  continue;
> +	}
> +      if (nhdr.n_type != NT_PRPSINFO)
> +	continue;
> +      const Ebl_Core_Item *item;
> +      for (item = items; item < items + nitems; item++)
> +	if (strcmp (item->name, "pid") == 0)
> +	  break;
> +      if (item == items + nitems)
> +	continue;
> +      uint32_t val32 = *(const uint32_t *) (desc + item->offset);
> +      val32 = (elf_getident (core, NULL)[EI_DATA] == ELFDATA2MSB
> +		? be32toh (val32) : le32toh (val32));
> +      pid = (int32_t) val32;
> +      eu_static_assert (sizeof val32 <= sizeof pid);
> +      break;
> +    }
> +  if (pid == -1)
> +    {
> +      /* No valid NT_PRPSINFO recognized in this CORE.  */
> +      ebl_closebackend (ebl);
> +      __libdwfl_seterrno (DWFL_E_BADELF);
> +      return false;
> +    }
> +  struct core_arg *core_arg = malloc (sizeof *core_arg);
> +  if (core_arg == NULL)
> +    {
> +      ebl_closebackend (ebl);
> +      __libdwfl_seterrno (DWFL_E_NOMEM);
> +      return false;
> +    }
> +  core_arg->core = core;
> +  core_arg->note_data = note_data;
> +  core_arg->thread_note_offset = 0;
> +  core_arg->ebl = ebl;
> +  if (! INTUSE(dwfl_attach_state) (dwfl, ebl_get_elfmachine (ebl), pid,
> +				   &core_thread_callbacks, core_arg))
> +    {
> +      free (core_arg);
> +      ebl_closebackend (ebl);
> +      return false;
> +    }
> +  return true;
> +}

> diff --git a/libdwfl/dwfl_frame_pid.c b/libdwfl/dwfl_frame_pid.c
> +struct pid_arg
> +{
> +  DIR *dir;
> +  size_t tids_attached_size, tids_attached_used;
> +  pid_t *tids_attached;
> +};

This might be overkill. I think the way we use the callbacks makes
sure there is only one tid at a time attached. Except when there
is a multi-threaded app that uses the same Dwfl in multiple threads.
I am not sure we fully support that. Making this dynamic resizing
of the attached tids struct work isn't fully multi-threaded safe.
We just have to think about whether and how we should support
multi-threaded Dwfl access (our current --thread-safety support seems
not fully functional). See also the comment in pid_memory_read.

> +static bool
> +ptrace_attach (pid_t tid)
> +{
> +  if (ptrace (PTRACE_ATTACH, tid, NULL, NULL) != 0)
> +    return false;
> +  /* FIXME: Handle missing SIGSTOP on old Linux kernels.  */

How old are these kernels?
If this is before say 2.6.18 kernels (7 years ago now) then I
think it isn't too urgent.

> +  for (;;)
> +    {
> +      int status;
> +      if (waitpid (tid, &status, __WALL) != tid || !WIFSTOPPED (status))
> +	{
> +	  ptrace (PTRACE_DETACH, tid, NULL, NULL);
> +	  return false;
> +	}
> +      if (WSTOPSIG (status) == SIGSTOP)
> +	break;
> +      if (ptrace (PTRACE_CONT, tid, NULL,
> +		  (void *) (uintptr_t) WSTOPSIG (status)) != 0)
> +	{
> +	  ptrace (PTRACE_DETACH, tid, NULL, NULL);
> +	  return false;
> +	}
> +    }
> +  return true;
> +}

When used in pid_set_initial_registers the return value is also just
false to indicate failure. And dwfl_thread_getframes will then just
return -1 and not set dwfl_errno. Can we do a little better?
Maybe use __libdwfl_seterrno (DWFL_E_ERRNO) when ptrace or waitpid fails?

> +static bool
> +pid_memory_read (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
> +{
> +  struct pid_arg *pid_arg = arg;
> +  assert (pid_arg->tids_attached_used > 0);
> +  pid_t tid = pid_arg->tids_attached[0];

Aha, that works because you need an arbitrary tid of the pid that is
attached. And you know there must be at least one or this callback
wouldn't be called.

But is this really why the whole tids_attached datastructure is kept?
If so, then maybe our memory_read callback really should have a
tid or Dwfl_Thread argument to indicate for which thread the memory
read was intended. This thread must be attached already and then
you could just use it here instead of having to keep track of all
the tids_attached. In theory memory_read is generic per process,
but in practice it does matter which thread the request was for
it seems.

> +  Dwfl_Process *process = dwfl->process;
> +  if (process->ebl->class == ELFCLASS64)
> +    {
> +      errno = 0;
> +      *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL);
> +      return errno == 0;
> +    }
> +#if SIZEOF_LONG == 8
> +  /* We do not care about reads unaliged to 4 bytes boundary.
> +     But 0x...ffc read of 8 bytes could overrun a page.  */
> +  bool lowered = (addr & 4) != 0;
> +  if (lowered)
> +    addr -= 4;
> +#endif /* SIZEOF_LONG == 8 */
> +  errno = 0;
> +  *result = ptrace (PTRACE_PEEKDATA, tid, (void *) (uintptr_t) addr, NULL);
> +  if (errno != 0)
> +    return false;
> +#if SIZEOF_LONG == 8
> +# if BYTE_ORDER == BIG_ENDIAN
> +  if (! lowered)
> +    *result >>= 32;
> +# else
> +  if (lowered)
> +    *result >>= 32;
> +# endif
> +#endif /* SIZEOF_LONG == 8 */
> +  *result &= 0xffffffff;
> +  return true;
> +}

> +static pid_t
> +pid_next_thread (Dwfl *dwfl __attribute__ ((unused)),
> +		 Dwfl_Thread *nthread __attribute__ ((unused)), void *dwfl_arg,
> +		 void **thread_argp)
> +{
> +  struct pid_arg *pid_arg = dwfl_arg;
> +  struct dirent *dirent;
> +  do
> +    {
> +      errno = 0;
> +      dirent = readdir (pid_arg->dir);
> +      if (dirent == NULL)
> +	return errno == 0 ? 0 : -1;

if (errno != 0) __libdwfl_seterrno (DWFL_E_ERRNO) ?

> +    }
> +  while (strcmp (dirent->d_name, ".") == 0
> +	 || strcmp (dirent->d_name, "..") == 0);

I assume readdir makes sure there two are always first?

> +  char *end;
> +  errno = 0;
> +  long tidl = strtol (dirent->d_name, &end, 10);
> +  if (errno != 0)
> +    return -1;

 __libdwfl_seterrno (DWFL_E_ERRNO) ?

> +  pid_t tid = tidl;
> +  if (tidl <= 0 || (end && *end) || tid != tidl)
> +    return -1;

O, weird... Yeah, that would not be good, but an assert is
probably not good either in case something weird shows up in the dir.
Maybe DWFL_E_PARSE_PROC?

> +  *thread_argp = dwfl_arg;
> +  return tid;
> +}

> +static bool
> +pid_thread_state_registers_cb (const int firstreg,
> +			       unsigned nregs,
> +			       const Dwarf_Word *regs,
> +			       void *arg)
> +{
> +  Dwfl_Thread *thread = (Dwfl_Thread *) arg;
> +  return INTUSE(dwfl_thread_state_registers) (thread, firstreg, nregs, regs);
> +}

Might want to add a comment this is the ebl_set_initial_registers_tid
callback.

> +static bool
> +pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg)
> +{
> +  struct pid_arg *pid_arg = thread_arg;
> +  pid_t tid = INTUSE(dwfl_thread_tid) (thread);
> +  if (! ptrace_attach (tid))
> +    return false;
> +  if (pid_arg->tids_attached_used == pid_arg->tids_attached_size)
> +    {
> +      pid_arg->tids_attached_size *= 2;
> +      pid_arg->tids_attached_size = MAX (64, pid_arg->tids_attached_size);
> +      pid_arg->tids_attached = realloc (pid_arg->tids_attached,
> +					(pid_arg->tids_attached_size
> +					 * sizeof *pid_arg->tids_attached));

In theory the realloc could fail and return NULL.

> +    }
> +  pid_arg->tids_attached[pid_arg->tids_attached_used++] = tid;
> +  Dwfl_Process *process = thread->process;
> +  Ebl *ebl = process->ebl;
> +  return ebl_set_initial_registers_tid (ebl, tid,
> +					pid_thread_state_registers_cb, thread);
> +}

> +static void
> +pid_detach (Dwfl *dwfl __attribute__ ((unused)), void *dwfl_arg)
> +{
> +  struct pid_arg *pid_arg = dwfl_arg;
> +  closedir (pid_arg->dir);
> +  free (pid_arg->tids_attached);
> +  free (pid_arg);
> +}
> +
> +static void
> +pid_thread_detach (Dwfl_Thread *thread, void *thread_arg)
> +{
> +  struct pid_arg *pid_arg = thread_arg;
> +  pid_t tid = INTUSE(dwfl_thread_tid) (thread);
> +  size_t ix;
> +  for (ix = 0; ix < pid_arg->tids_attached_used; ix++)
> +    if (pid_arg->tids_attached[ix] == tid)
> +      break;
> +  assert (ix < pid_arg->tids_attached_used);
> +  pid_arg->tids_attached[ix]
> +    = pid_arg->tids_attached[--pid_arg->tids_attached_used];
> +  ptrace (PTRACE_DETACH, tid, NULL, NULL);
> +}
> +
> +static const Dwfl_Thread_Callbacks pid_thread_callbacks = 
> +{
> +  pid_next_thread,
> +  pid_memory_read,
> +  pid_set_initial_registers,
> +  pid_detach,
> +  pid_thread_detach,
> +};

OK.

> +bool
> +internal_function
> +__libdwfl_attach_state_for_pid (Dwfl *dwfl, pid_t pid)
> +{
> +  char dirname[64];
> +  int i = snprintf (dirname, sizeof (dirname), "/proc/%ld/task", (long) pid);
> +  assert (i > 0 && i < (ssize_t) sizeof (dirname) - 1);

30 chars is enough if pids are actually long, otherwise 21 should be enough
for everybody. (MAX_INT is 2147483647, MAX_LONG is 9223372036854775807.)

> +  DIR *dir = opendir (dirname);
> +  if (dir == NULL)
> +    {
> +      __libdwfl_seterrno (DWFL_E_PARSE_PROC);
> +      return NULL;
> +    }

That should be return false.
Do you need a special dwfl_errno for this or can you use DWFL_E_ERRNO?

> +  struct pid_arg *pid_arg = malloc (sizeof *pid_arg);
> +  if (pid_arg == NULL)
> +    {
> +      closedir (dir);
> +      __libdwfl_seterrno (DWFL_E_NOMEM);
> +      return false;
> +    }
> +  pid_arg->dir = dir;
> +  pid_arg->tids_attached_size = 0;
> +  pid_arg->tids_attached_used = 0;
> +  pid_arg->tids_attached = NULL;
> +  if (! INTUSE(dwfl_attach_state) (dwfl, EM_NONE, pid, &pid_thread_callbacks,
> +				   pid_arg))
> +    {
> +      free (pid_arg);
> +      return false;
> +    }

Missing closedir (dir)?

> +  return true;
> +}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]