am fa36875d: Merge "Make strerror(3) and strsignal(3) thread-safe, and add psignal(3) and psiginfo(3)."

* commit 'fa36875df423af968d053c584c0de18a2448516b':
  Make strerror(3) and strsignal(3) thread-safe, and add psignal(3) and psiginfo(3).
This commit is contained in:
Elliott Hughes 2012-09-13 15:30:34 -07:00 committed by Android Git Automerger
commit de9db8f7ef
11 changed files with 368 additions and 185 deletions

View File

@ -61,7 +61,6 @@ libc_common_src_files := \
unistd/sigwait.c \
unistd/sleep.c \
unistd/statfs.c \
unistd/strsignal.c \
unistd/syslog.c \
unistd/system.c \
unistd/tcgetpgrp.c \
@ -191,8 +190,6 @@ libc_common_src_files := \
string/strcoll.c \
string/strcspn.c \
string/strdup.c \
string/strerror.c \
string/strerror_r.c \
string/strlcat.c \
string/strlcpy.c \
string/strncat.c \
@ -291,6 +288,9 @@ libc_common_src_files := \
bionic/sched_cpucount.c \
bionic/semaphore.c \
bionic/sha1.c \
bionic/strerror.cpp \
bionic/strerror_r.cpp \
bionic/strsignal.cpp \
bionic/stubs.cpp \
bionic/system_properties.c \
bionic/tdestroy.c \
@ -332,6 +332,7 @@ libc_common_src_files := \
libc_upstream_netbsd_src_files := \
upstream-netbsd/libc/compat-43/creat.c \
upstream-netbsd/libc/gen/nice.c \
upstream-netbsd/libc/gen/psignal.c \
upstream-netbsd/libc/regex/regcomp.c \
upstream-netbsd/libc/regex/regerror.c \
upstream-netbsd/libc/regex/regexec.c \

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2012 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _BIONIC_THREAD_LOCAL_BUFFER_H_included
#define _BIONIC_THREAD_LOCAL_BUFFER_H_included
#include <malloc.h>
#include <pthread.h>
// libstdc++ currently contains __cxa_guard_acquire and __cxa_guard_release,
// so we make do with macros instead of a C++ class.
// TODO: move __cxa_guard_acquire and __cxa_guard_release into libc.
#define GLOBAL_INIT_THREAD_LOCAL_BUFFER(name) \
static pthread_once_t name ## _once; \
static pthread_key_t name ## _key; \
static void name ## _key_destroy(void* buffer) { \
free(buffer); \
} \
static void name ## _key_init() { \
pthread_key_create(&name ## _key, name ## _key_destroy); \
}
// Leaves "name_buffer" and "name_byte_count" defined and initialized.
#define LOCAL_INIT_THREAD_LOCAL_BUFFER(type, name, byte_count) \
pthread_once(&name ## _once, name ## _key_init); \
type name ## _buffer = reinterpret_cast<type>(pthread_getspecific(name ## _key)); \
if (name ## _buffer == NULL) { \
name ## _buffer = reinterpret_cast<type>(malloc(byte_count)); \
pthread_setspecific(name ## _key, name ## _buffer); \
} \
const size_t name ## _buffer_size = byte_count
#endif // _BIONIC_THREAD_LOCAL_BUFFER_H_included

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008 The Android Open Source Project
* Copyright (C) 2012 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,14 +25,14 @@
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <signal.h>
#include <string.h>
char*
strsignal(int sig)
{
if ((unsigned)sig < NSIG)
return (char*) sys_siglist[sig];
else
return "Invalid signal number";
#include <string.h>
#include "ThreadLocalBuffer.h"
GLOBAL_INIT_THREAD_LOCAL_BUFFER(strerror);
char* strerror(int error_number) {
LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, strerror, NL_TEXTMAX);
strerror_r(error_number, strerror_buffer, strerror_buffer_size);
return strerror_buffer;
}

View File

@ -0,0 +1,71 @@
/* $OpenBSD: strerror_r.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */
/* Public Domain <marc@snafu.org> */
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
struct Pair {
int code;
const char* msg;
};
static const char* __code_string_lookup(const Pair* strings, int code) {
for (size_t i = 0; strings[i].msg != NULL; ++i) {
if (strings[i].code == code) {
return strings[i].msg;
}
}
return NULL;
}
static const Pair _sys_error_strings[] = {
#define __BIONIC_ERRDEF(x,y,z) { x, z },
#include <sys/_errdefs.h>
{ 0, NULL }
};
int strerror_r(int error_number, char* buf, size_t buf_len) {
int saved_errno = errno;
size_t length;
const char* error_name = __code_string_lookup(_sys_error_strings, error_number);
if (error_name != NULL) {
length = snprintf(buf, buf_len, "%s", error_name);
} else {
length = snprintf(buf, buf_len, "Unknown error %u", error_number);
}
if (length >= buf_len) {
errno = ERANGE;
return -1;
}
errno = saved_errno;
return 0;
}
static const Pair _sys_signal_strings[] = {
#define __BIONIC_SIGDEF(x,y,z) { y, z },
#include <sys/_sigdefs.h>
{ 0, NULL }
};
extern "C" const char* __strsignal(int signal_number, char* buf, size_t buf_len) {
const char* signal_name = __code_string_lookup(_sys_signal_strings, signal_number);
if (signal_name != NULL) {
return signal_name;
}
const char* prefix = "Unknown";
if (signal_number >= SIGRTMIN && signal_number <= SIGRTMAX) {
prefix = "Real-time";
signal_number -= SIGRTMIN;
}
size_t length = snprintf(buf, buf_len, "%s signal %d", prefix, signal_number);
if (length >= buf_len) {
return NULL;
}
return buf;
}

39
libc/bionic/strsignal.cpp Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2012 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <string.h>
#include "ThreadLocalBuffer.h"
extern "C" const char* __strsignal(int, char*, size_t);
GLOBAL_INIT_THREAD_LOCAL_BUFFER(strsignal);
char* strsignal(int signal_number) {
LOCAL_INIT_THREAD_LOCAL_BUFFER(char*, strsignal, NL_TEXTMAX);
return const_cast<char*>(__strsignal(signal_number, strsignal_buffer, strsignal_buffer_size));
}

View File

@ -125,6 +125,8 @@ extern int kill(pid_t, int);
extern int killpg(int pgrp, int sig);
extern int sigaltstack(const stack_t *ss, stack_t *oss);
extern void psiginfo(const siginfo_t* si, const char* message);
extern void psignal(int signal_number, const char* message);
__END_DECLS

View File

@ -1,163 +0,0 @@
/* $OpenBSD: strerror_r.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */
/* Public Domain <marc@snafu.org> */
#define sys_siglist _sys_siglist
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <string.h>
typedef struct {
int code;
const char* msg;
} CodeString;
static const char*
__code_string_lookup( const CodeString* strings,
int code )
{
int nn = 0;
for (;;)
{
if (strings[nn].msg == NULL)
break;
if (strings[nn].code == code)
return strings[nn].msg;
nn++;
}
return NULL;
}
static const CodeString _sys_error_strings[] =
{
#define __BIONIC_ERRDEF(x,y,z) { x, z },
#include <sys/_errdefs.h>
{ 0, NULL }
};
static size_t
__digits10(unsigned int num)
{
size_t i = 0;
do {
num /= 10;
i++;
} while (num != 0);
return i;
}
static int
__itoa(int num, int sign, char *buffer, size_t start, size_t end)
{
size_t pos;
unsigned int a;
int neg;
if (sign && num < 0) {
a = -num;
neg = 1;
}
else {
a = num;
neg = 0;
}
pos = start + __digits10(a);
if (neg)
pos++;
if (pos < end)
buffer[pos] = '\0';
else
return ERANGE;
pos--;
do {
buffer[pos] = (a % 10) + '0';
pos--;
a /= 10;
} while (a != 0);
if (neg)
buffer[pos] = '-';
return 0;
}
int
strerror_r(int errnum, char *strerrbuf, size_t buflen)
{
int save_errno;
int len, ret = 0;
const char* msg;
save_errno = errno;
msg = __code_string_lookup( _sys_error_strings, errnum );
if (msg != NULL) {
len = strlcpy(strerrbuf, msg, buflen);
if ((size_t)len >= buflen)
ret = ERANGE;
} else {
len = strlcpy(strerrbuf, "Unknown error: ", buflen);
if ((size_t)len >= buflen)
ret = ERANGE;
else {
int ret = __itoa(errnum, 1, strerrbuf, len, buflen);
if (ret == 0)
ret = EINVAL;
}
}
return ret;
}
#if 0
static const CodeString _sys_signal_strings[] =
{
#define SIGDEF(x,y,z) { y, z },
#include <sys/_sigdefs.h>
};
static int
__num2string(int num, int sign, int setid, char *buf, size_t buflen,
char * list[], size_t max, const char *def)
{
int ret = 0;
size_t len;
if (0 <= num && num < max) {
len = strlcpy(buf, def, buflen);
if (len >= buflen)
ret = ERANGE;
} else {
len = strlcpy(buf, def, buflen);
if (len >= buflen)
ret = ERANGE;
else {
ret = __itoa(num, sign, buf, len, buflen);
if (ret == 0)
ret = EINVAL;
}
}
return ret;
}
#define USIGPREFIX "Unknown signal: "
char *
__strsignal(int num, char *buf)
{
__num2string(num, 0, 2, buf, NL_TEXTMAX, (char **)sys_siglist, NSIG,
USIGPREFIX);
return buf;
}
#endif

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _BIONIC_NETBSD_EXTERN_H_included
#define _BIONIC_NETBSD_EXTERN_H_included
// Placeholder.
#endif

View File

@ -1,7 +1,8 @@
/* $OpenBSD: strerror.c,v 1.7 2005/08/08 08:05:37 espie Exp $ */
/* $NetBSD: psignal.c,v 1.23 2012/03/13 21:13:36 christos Exp $ */
/*
* Copyright (c) 1988 Regents of the University of California.
* All rights reserved.
* Copyright (c) 1983, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -28,14 +29,57 @@
* SUCH DAMAGE.
*/
#include <string.h>
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)psignal.c 8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: psignal.c,v 1.23 2012/03/13 21:13:36 christos Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
#include "namespace.h"
#include <sys/types.h>
#include <sys/uio.h>
#include <limits.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
char *
strerror(int num)
#include "extern.h"
#ifdef __weak_alias
__weak_alias(psignal,_psignal)
#endif
void
psignal(int sig, const char *s)
{
static char buf[256];
struct iovec *v;
struct iovec iov[4];
char buf[NL_TEXTMAX];
(void)strerror_r(num, buf, sizeof(buf));
return (buf);
v = iov;
if (s && *s) {
v->iov_base = __UNCONST(s);
v->iov_len = strlen(s);
v++;
v->iov_base = __UNCONST(": ");
v->iov_len = 2;
v++;
}
v->iov_base = __UNCONST(__strsignal((int)sig, buf, sizeof(buf)));
v->iov_len = strlen(v->iov_base);
v++;
v->iov_base = __UNCONST("\n");
v->iov_len = 1;
(void)writev(STDERR_FILENO, iov, (int)((v - iov) + 1));
}
void
psiginfo(const siginfo_t *si, const char *s)
{
psignal(si->si_signo, s);
}

View File

@ -22,6 +22,7 @@ test_src_files = \
getcwd_test.cpp \
pthread_test.cpp \
regex_test.cpp \
string_test.cpp \
test_dynamic_ldflags = -Wl,--export-dynamic -Wl,-u,DlSymTestFunction
test_dynamic_src_files = \

107
tests/string_test.cpp Normal file
View File

@ -0,0 +1,107 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <errno.h>
#include <string.h>
TEST(string, strerror) {
// Valid.
ASSERT_STREQ("Success", strerror(0));
ASSERT_STREQ("Operation not permitted", strerror(1));
// Invalid.
ASSERT_STREQ("Unknown error 4294967295", strerror(-1));
ASSERT_STREQ("Unknown error 1234", strerror(1234));
}
void* ConcurrentStrErrorFn(void* arg) {
bool equal = (strcmp("Unknown error 2002", strerror(2002)) == 0);
return reinterpret_cast<void*>(equal);
}
#if __BIONIC__ // glibc's strerror isn't thread safe, only its strsignal.
TEST(string, strerror_concurrent) {
const char* strerror1001 = strerror(1001);
ASSERT_STREQ("Unknown error 1001", strerror1001);
pthread_t t;
ASSERT_EQ(0, pthread_create(&t, NULL, ConcurrentStrErrorFn, NULL));
void* result;
ASSERT_EQ(0, pthread_join(t, &result));
ASSERT_TRUE(static_cast<bool>(result));
ASSERT_STREQ("Unknown error 1001", strerror1001);
}
#endif
#if __BIONIC__ // glibc's strerror_r doesn't even have the same signature as the POSIX one.
TEST(string, strerror_r) {
char buf[256];
// Valid.
ASSERT_EQ(0, strerror_r(0, buf, sizeof(buf)));
ASSERT_STREQ("Success", buf);
ASSERT_EQ(0, strerror_r(1, buf, sizeof(buf)));
ASSERT_STREQ("Operation not permitted", buf);
// Invalid.
ASSERT_EQ(0, strerror_r(-1, buf, sizeof(buf)));
ASSERT_STREQ("Unknown error 4294967295", buf);
ASSERT_EQ(0, strerror_r(1234, buf, sizeof(buf)));
ASSERT_STREQ("Unknown error 1234", buf);
// Buffer too small.
ASSERT_EQ(-1, strerror_r(0, buf, 2));
ASSERT_EQ(ERANGE, errno);
}
#endif
TEST(string, strsignal) {
// A regular signal.
ASSERT_STREQ("Hangup", strsignal(1));
// A real-time signal.
#ifdef __GLIBC__ // glibc reserves real-time signals for internal use, and doesn't count those.
ASSERT_STREQ("Real-time signal 14", strsignal(48));
#else
ASSERT_STREQ("Real-time signal 16", strsignal(48));
#endif
// Errors.
ASSERT_STREQ("Unknown signal -1", strsignal(-1)); // Too small.
ASSERT_STREQ("Unknown signal 0", strsignal(0)); // Still too small.
ASSERT_STREQ("Unknown signal 1234", strsignal(1234)); // Too large.
}
void* ConcurrentStrSignalFn(void* arg) {
bool equal = (strcmp("Unknown signal 2002", strsignal(2002)) == 0);
return reinterpret_cast<void*>(equal);
}
TEST(string, strsignal_concurrent) {
const char* strsignal1001 = strsignal(1001);
ASSERT_STREQ("Unknown signal 1001", strsignal1001);
pthread_t t;
ASSERT_EQ(0, pthread_create(&t, NULL, ConcurrentStrSignalFn, NULL));
void* result;
ASSERT_EQ(0, pthread_join(t, &result));
ASSERT_TRUE(static_cast<bool>(result));
ASSERT_STREQ("Unknown signal 1001", strsignal1001);
}