307d23028b
Resolves: #2218039 Performance issue in elf_getdata_rawchunk
225 lines
7.0 KiB
Diff
225 lines
7.0 KiB
Diff
From 3aca5b5f1f1617db2220022d9061dcaf129e54c4 Mon Sep 17 00:00:00 2001
|
|
From: Mark Wielaard <mark@klomp.org>
|
|
Date: Wed, 21 Jun 2023 18:05:12 +0200
|
|
Subject: [PATCH] libelf: Replace list of elf_getdata_rawchunk results with a
|
|
tree
|
|
|
|
elf_getdata_rawchunks did a linear search to see if a chunk was
|
|
already fetched. Replace this list with a binary search tree to make
|
|
lookup faster when a lot of Elf_Data_Chunk were created.
|
|
|
|
* libelf/libelfP.h (Elf_Data_Chunk): Remove next field.
|
|
(struct Elf): Change the rawchunks type from Elf_Data_Chunk *
|
|
to void *.
|
|
* elf_getdata_rawchunk.c (chunk_compare): New static function.
|
|
(elf_getdata_rawchunk): Use tsearch instead of a manual linked
|
|
list.
|
|
* elf_end.c (free_chunk): New static function.
|
|
(elf_end): Call tdestroy instead of walking linked list.
|
|
|
|
Signed-off-by: Mark Wielaard <mark@klomp.org>
|
|
---
|
|
libelf/elf_end.c | 22 +++++++++-------
|
|
libelf/elf_getdata_rawchunk.c | 47 +++++++++++++++++++++++++----------
|
|
libelf/libelfP.h | 13 ++++------
|
|
3 files changed, 52 insertions(+), 30 deletions(-)
|
|
|
|
diff --git a/libelf/elf_end.c b/libelf/elf_end.c
|
|
index 5c451f36..3e5d4c86 100644
|
|
--- a/libelf/elf_end.c
|
|
+++ b/libelf/elf_end.c
|
|
@@ -1,5 +1,6 @@
|
|
/* Free resources associated with Elf descriptor.
|
|
Copyright (C) 1998,1999,2000,2001,2002,2004,2005,2007,2015,2016 Red Hat, Inc.
|
|
+ Copyright (C) 2023 Mark J. Wielaard <mark@klomp.org>
|
|
This file is part of elfutils.
|
|
Written by Ulrich Drepper <drepper@redhat.com>, 1998.
|
|
|
|
@@ -32,12 +33,22 @@
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
+#include <search.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "libelfP.h"
|
|
|
|
|
|
+static void
|
|
+free_chunk (void *n)
|
|
+{
|
|
+ Elf_Data_Chunk *rawchunk = (Elf_Data_Chunk *)n;
|
|
+ if (rawchunk->dummy_scn.flags & ELF_F_MALLOCED)
|
|
+ free (rawchunk->data.d.d_buf);
|
|
+ free (rawchunk);
|
|
+}
|
|
+
|
|
int
|
|
elf_end (Elf *elf)
|
|
{
|
|
@@ -112,20 +123,13 @@ elf_end (Elf *elf)
|
|
|
|
case ELF_K_ELF:
|
|
{
|
|
- Elf_Data_Chunk *rawchunks
|
|
+ void *rawchunks
|
|
= (elf->class == ELFCLASS32
|
|
|| (offsetof (struct Elf, state.elf32.rawchunks)
|
|
== offsetof (struct Elf, state.elf64.rawchunks))
|
|
? elf->state.elf32.rawchunks
|
|
: elf->state.elf64.rawchunks);
|
|
- while (rawchunks != NULL)
|
|
- {
|
|
- Elf_Data_Chunk *next = rawchunks->next;
|
|
- if (rawchunks->dummy_scn.flags & ELF_F_MALLOCED)
|
|
- free (rawchunks->data.d.d_buf);
|
|
- free (rawchunks);
|
|
- rawchunks = next;
|
|
- }
|
|
+ tdestroy (rawchunks, free_chunk);
|
|
|
|
Elf_ScnList *list = (elf->class == ELFCLASS32
|
|
|| (offsetof (struct Elf, state.elf32.scns)
|
|
diff --git a/libelf/elf_getdata_rawchunk.c b/libelf/elf_getdata_rawchunk.c
|
|
index 5a35ccdc..cfd40396 100644
|
|
--- a/libelf/elf_getdata_rawchunk.c
|
|
+++ b/libelf/elf_getdata_rawchunk.c
|
|
@@ -1,6 +1,6 @@
|
|
/* Return converted data from raw chunk of ELF file.
|
|
Copyright (C) 2007, 2014, 2015 Red Hat, Inc.
|
|
- Copyright (C) 2022 Mark J. Wielaard <mark@klomp.org>
|
|
+ Copyright (C) 2022, 2023 Mark J. Wielaard <mark@klomp.org>
|
|
This file is part of elfutils.
|
|
|
|
This file is free software; you can redistribute it and/or modify
|
|
@@ -33,12 +33,28 @@
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
+#include <search.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "libelfP.h"
|
|
#include "common.h"
|
|
|
|
+static int
|
|
+chunk_compare (const void *a, const void *b)
|
|
+{
|
|
+ Elf_Data_Chunk *da = (Elf_Data_Chunk *)a;
|
|
+ Elf_Data_Chunk *db = (Elf_Data_Chunk *)b;
|
|
+
|
|
+ if (da->offset != db->offset)
|
|
+ return da->offset - db->offset;
|
|
+
|
|
+ if (da->data.d.d_size != db->data.d.d_size)
|
|
+ return da->data.d.d_size - db->data.d.d_size;
|
|
+
|
|
+ return da->data.d.d_type - db->data.d.d_type;
|
|
+}
|
|
+
|
|
Elf_Data *
|
|
elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type)
|
|
{
|
|
@@ -75,19 +91,25 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type)
|
|
rwlock_rdlock (elf->lock);
|
|
|
|
/* Maybe we already got this chunk? */
|
|
- Elf_Data_Chunk *rawchunks = elf->state.elf.rawchunks;
|
|
- while (rawchunks != NULL)
|
|
+ Elf_Data_Chunk key;
|
|
+ key.offset = offset;
|
|
+ key.data.d.d_size = size;
|
|
+ key.data.d.d_type = type;
|
|
+ Elf_Data_Chunk **found = tsearch (&key, &elf->state.elf.rawchunks,
|
|
+ &chunk_compare);
|
|
+ if (found == NULL)
|
|
+ goto nomem;
|
|
+
|
|
+ /* Existing entry. */
|
|
+ if (*found != &key && *found != NULL)
|
|
{
|
|
- if ((rawchunks->offset == offset || size == 0)
|
|
- && rawchunks->data.d.d_size == size
|
|
- && rawchunks->data.d.d_type == type)
|
|
- {
|
|
- result = &rawchunks->data.d;
|
|
- goto out;
|
|
- }
|
|
- rawchunks = rawchunks->next;
|
|
+ result = &(*found)->data.d;
|
|
+ goto out;
|
|
}
|
|
|
|
+ /* New entry. */
|
|
+ *found = NULL;
|
|
+
|
|
size_t align = __libelf_type_align (elf->class, type);
|
|
if (elf->map_address != NULL)
|
|
{
|
|
@@ -189,8 +211,7 @@ elf_getdata_rawchunk (Elf *elf, int64_t offset, size_t size, Elf_Type type)
|
|
rwlock_unlock (elf->lock);
|
|
rwlock_wrlock (elf->lock);
|
|
|
|
- chunk->next = elf->state.elf.rawchunks;
|
|
- elf->state.elf.rawchunks = chunk;
|
|
+ *found = chunk;
|
|
result = &chunk->data.d;
|
|
|
|
out:
|
|
diff --git a/libelf/libelfP.h b/libelf/libelfP.h
|
|
index 6624f38a..d3c241e5 100644
|
|
--- a/libelf/libelfP.h
|
|
+++ b/libelf/libelfP.h
|
|
@@ -1,5 +1,6 @@
|
|
/* Internal interfaces for libelf.
|
|
Copyright (C) 1998-2010, 2015, 2016 Red Hat, Inc.
|
|
+ Copyright (C) 2023 Mark J. Wielaard <mark@klomp.org>
|
|
This file is part of elfutils.
|
|
Contributed by Ulrich Drepper <drepper@redhat.com>, 1998.
|
|
|
|
@@ -262,11 +263,7 @@ typedef struct Elf_ScnList
|
|
typedef struct Elf_Data_Chunk
|
|
{
|
|
Elf_Data_Scn data;
|
|
- union
|
|
- {
|
|
- Elf_Scn dummy_scn;
|
|
- struct Elf_Data_Chunk *next;
|
|
- };
|
|
+ Elf_Scn dummy_scn;
|
|
int64_t offset; /* The original raw offset in the Elf image. */
|
|
} Elf_Data_Chunk;
|
|
|
|
@@ -324,7 +321,7 @@ struct Elf
|
|
Elf_ScnList *scns_last; /* Last element in the section list.
|
|
If NULL the data has not yet been
|
|
read from the file. */
|
|
- Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results. */
|
|
+ void *rawchunks; /* Tree of elf_getdata_rawchunk results. */
|
|
unsigned int scnincr; /* Number of sections allocate the last
|
|
time. */
|
|
int ehdr_flags; /* Flags (dirty) for ELF header. */
|
|
@@ -343,7 +340,7 @@ struct Elf
|
|
Elf_ScnList *scns_last; /* Last element in the section list.
|
|
If NULL the data has not yet been
|
|
read from the file. */
|
|
- Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results. */
|
|
+ void *rawchunks; /* Tree of elf_getdata_rawchunk results. */
|
|
unsigned int scnincr; /* Number of sections allocate the last
|
|
time. */
|
|
int ehdr_flags; /* Flags (dirty) for ELF header. */
|
|
@@ -368,7 +365,7 @@ struct Elf
|
|
Elf_ScnList *scns_last; /* Last element in the section list.
|
|
If NULL the data has not yet been
|
|
read from the file. */
|
|
- Elf_Data_Chunk *rawchunks; /* List of elf_getdata_rawchunk results. */
|
|
+ void *rawchunks; /* Tree of elf_getdata_rawchunk results. */
|
|
unsigned int scnincr; /* Number of sections allocate the last
|
|
time. */
|
|
int ehdr_flags; /* Flags (dirty) for ELF header. */
|
|
--
|
|
2.40.1
|
|
|