这是indexloc提供的服务,不要输入任何密码
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
# <https://www.gnu.org/licenses/>.

SUBDIRS = tests
AM_CXXFLAGS = -std=c++11 -Wall -Wextra -pedantic
AM_CXXFLAGS = $(PTHREAD_CFLAGS) -std=c++11 -Wall -Wextra -pedantic
LIBS = $(PTHREAD_LIBS)

bin_PROGRAMS = termux-elf-cleaner

termux_elf_cleaner_SOURCES = elf-cleaner.cpp arghandling.c
termux_elf_cleaner_SOURCES = elf-cleaner.cpp arghandling.c cpucount.c
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Options:

SPDX-License-Identifier: [GPL-3.0-or-later](https://spdx.org/licenses/GPL-3.0-or-later.html)

License is GPL-3.0-or-later in all source files unless specified otherwise in file header.

## See also

* [Bionic's related docs](https://android.googlesource.com/platform/bionic/+/refs/heads/master/android-changes-for-ndk-developers.md)
4 changes: 4 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ AC_PROG_CC
AC_PROG_CXX
AC_LANG(C++)

AC_CONFIG_MACRO_DIR([m4])

AX_PTHREAD

copyright="Copyright (C) 2022 Termux."
AC_DEFINE_UNQUOTED(COPYRIGHT, ["$copyright"],
[Short copyright string for this version of termux-elf-cleaner.])
Expand Down
76 changes: 76 additions & 0 deletions cpucount.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: CC0-1.0

/* Based on cpucount.c from github.com/cathugger/mkp224o */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

static int parsecpuinfo(void)
{
unsigned char cpubitmap[128];

memset(cpubitmap,0,sizeof(cpubitmap));

FILE *f = fopen("/proc/cpuinfo","r");
if (!f)
return -1;

char buf[8192];
while (fgets(buf,sizeof(buf),f)) {
// we don't like newlines
for (char *p = buf;*p;++p) {
if (*p == '\n') {
*p = 0;
break;
}
}
// split ':'
char *v = 0;
for (char *p = buf;*p;++p) {
if (*p == ':') {
*p = 0;
v = p + 1;
break;
}
}
// key padding
size_t kl = strlen(buf);
while (kl > 0 && (buf[kl - 1] == '\t' || buf[kl - 1] == ' ')) {
--kl;
buf[kl] = 0;
}
// space before value
if (v) {
while (*v && (*v == ' ' || *v == '\t'))
++v;
}
// check what we need
if (strcasecmp(buf,"processor") == 0 && v) {
char *endp = 0;
long n = strtol(v,&endp,10);
if (endp && endp > v && n >= 0 && (size_t)n < sizeof(cpubitmap) * 8)
cpubitmap[n / 8] |= 1 << (n % 8);
}
}

fclose(f);

// count bits in bitmap
int ncpu = 0;
for (size_t n = 0;n < sizeof(cpubitmap) * 8;++n)
if (cpubitmap[n / 8] & (1 << (n % 8)))
++ncpu;

return ncpu;
}

int cpucount(void)
{
int ncpu;
ncpu = parsecpuinfo();
if (ncpu > 0)
return ncpu;
return -1;
}
2 changes: 2 additions & 0 deletions cpucount.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
extern "C" int
cpucount(void);
63 changes: 59 additions & 4 deletions elf-cleaner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ along with termux-elf-cleaner. If not, see

#include <algorithm>
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>

#include "arghandling.h"

#include "cpucount.h"
// Include a local elf.h copy as not all platforms have it.
#include "elf.h"

Expand All @@ -55,6 +57,11 @@ int api_level = 21;
bool dry_run = false;
bool quiet = false;

struct thread_data {
int argc = 0;
char **argv;
};

static char const *const usage_message[] =
{ "\
\n\
Expand All @@ -65,6 +72,7 @@ Options:\n\
\n\
--api-level NN choose target api level, i.e. 21, 24, ..\n\
--dry-run print info but but do not remove entries\n\
--jobs N allow N jobs, defaults to $(ncproc)\n\
--quiet do not print info about removed entries\n\
--help display this help and exit\n\
--version output version information and exit\n"
Expand Down Expand Up @@ -299,6 +307,18 @@ int parse_file(const char *file_name)
return 0;
}

void *file_parse_loop(void *td)
{
struct thread_data *data = (struct thread_data *) td;
for (int i = 0; i < data->argc; i++) {
char *file_name = (*data).argv[i];
int ret = parse_file(file_name);
if (ret != 0)
break;
}
return NULL;
}

int main(int argc, char **argv)
{
int skip_args = 0;
Expand Down Expand Up @@ -328,15 +348,50 @@ int main(int argc, char **argv)
supported_dt_flags_1 = (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE);
}

int num_threads = cpucount();
argmatch(argv, argc, "-jobs", "--jobs", 1, &num_threads, &skip_args);
if (num_threads <= 0)
num_threads = 1;

if (argmatch(argv, argc, "-dry-run", "--dry-run", 3, NULL, &skip_args))
dry_run = true;

if (argmatch(argv, argc, "-quiet", "--quiet", 3, NULL, &skip_args))
quiet = true;

for (int i = skip_args+1; i < argc; i++) {
if (parse_file(argv[i]) != 0)
return 1;
std::vector<pthread_t> thread(num_threads);
std::vector<struct thread_data> data(num_threads);
int num_files = argc - (skip_args+1);
/* Tasks per thread, rounded down */
int tasks_per_thread = (num_files / num_threads);

for (int i = 0; i<num_threads; i++) {
if (i < num_files % num_threads) {
/* Distribute 'extra' jobs (since we rounded
down tasks_per_thread) */
data[i].argv = &argv[skip_args+1];
data[i].argc = tasks_per_thread + 1;
skip_args += tasks_per_thread + 1;
} else {
data[i].argv = &argv[skip_args+1];
data[i].argc = tasks_per_thread;
skip_args += tasks_per_thread;
}
}

/* Launch Threads */
for (int i = 0; i<num_threads; i++) {
if (data[i].argc == 0)
break;
pthread_create(&thread[i], NULL, &file_parse_loop, &data[i]);
Copy link
Member

@thunder-coding thunder-coding Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is too ugly. We probably should be relying on C++11's std::thread implementation instead of creating the thread ourselves using the lower level pthread library. Also instead of going this way and dividing all the input files equally among the available CPUs, it would be much better if we:

  1. First spawn a number of threads (either as specified by the user, or upon number of CPUs).
  2. Have a sort of atomic object which the called thread will call std::atomic::notify_all() just before completing
  3. The main thread will be waiting for threads to finish using std::atomic::wait, and spawn a new thread as soon as one finishes.

This should also increase the performance improvements in your benchmarks as not all CPUs might perform in the same fashion I guess. And also will definitely improve in conditions where the user input contains a lot of small binaries with a large binary.

I don't know whether that's the optimal approach, but I've used this in one of my personal projects'test runner, and it works pretty well. Although I haven't battle tested it. In case anyone else has a better approach feel free to comment

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the multithreaded implementation with std::thread.

Here's the repo: https://github.com/XniceCraft/termux-elf-cleaner/tree/multithreading

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@XniceCraft that is great! Would you like to open a pull request to replace this one?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Grimler91 Okay. #27

}

/* Wait for Threads to Finish */
for (int i = 0; i<num_threads; i++) {
if (data[i].argc == 0)
break;
pthread_join(thread[i], NULL);
}

return 0;
}
Loading