Skip to content

Commit e72090c

Browse files
[libc] Implement perror
The perror function writes an error message directly to stderr. This patch adds an implementation, tests, and header generation details.
1 parent 06f6a77 commit e72090c

File tree

8 files changed

+174
-0
lines changed

8 files changed

+174
-0
lines changed

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,6 +1113,7 @@ if(LLVM_LIBC_FULL_BUILD)
11131113
libc.src.stdio.getc_unlocked
11141114
libc.src.stdio.getchar
11151115
libc.src.stdio.getchar_unlocked
1116+
libc.src.stdio.perror
11161117
libc.src.stdio.putc
11171118
libc.src.stdio.putchar
11181119
libc.src.stdio.puts

libc/include/stdio.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,12 @@ functions:
247247
- POSIX
248248
return_type: int
249249
arguments: []
250+
- name: perror
251+
standards:
252+
- stdc
253+
return_type: void
254+
arguments:
255+
- type: const char *
250256
- name: printf
251257
standards:
252258
- stdc

libc/src/stdio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ add_stdio_entrypoint_object(fopen)
221221
add_stdio_entrypoint_object(fclose)
222222
add_stdio_entrypoint_object(fread_unlocked)
223223
add_stdio_entrypoint_object(fread)
224+
add_stdio_entrypoint_object(perror)
224225
add_stdio_entrypoint_object(puts)
225226
add_stdio_entrypoint_object(fputs)
226227
add_stdio_entrypoint_object(fwrite_unlocked)

libc/src/stdio/generic/CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,21 @@ add_generic_entrypoint_object(
206206
libc.src.__support.File.platform_file
207207
)
208208

209+
add_generic_entrypoint_object(
210+
perror
211+
SRCS
212+
perror.cpp
213+
HDRS
214+
../perror.h
215+
DEPENDS
216+
libc.src.errno.errno
217+
libc.src.__support.StringUtil.error_to_string
218+
libc.src.__support.CPP.string_view
219+
libc.src.__support.File.file
220+
libc.src.__support.File.platform_file
221+
libc.src.__support.File.platform_stderr
222+
)
223+
209224
add_generic_entrypoint_object(
210225
fputs
211226
SRCS

libc/src/stdio/generic/perror.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//===-- Implementation of perror ------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/stdio/perror.h"
10+
#include "src/__support/CPP/string_view.h"
11+
#include "src/__support/File/file.h"
12+
#include "src/__support/StringUtil/error_to_string.h"
13+
#include "src/__support/macros/config.h"
14+
#include "src/errno/libc_errno.h"
15+
16+
namespace LIBC_NAMESPACE_DECL {
17+
18+
namespace {
19+
20+
// TODO: this is copied from `puts`, it should be moved to a shared utility.
21+
// Simple helper to unlock the file once destroyed.
22+
struct ScopedLock {
23+
ScopedLock(LIBC_NAMESPACE::File *stream) : stream(stream) { stream->lock(); }
24+
~ScopedLock() { stream->unlock(); }
25+
26+
private:
27+
LIBC_NAMESPACE::File *stream;
28+
};
29+
30+
int write_out(cpp::string_view str_view, File *f) {
31+
if (str_view.size() > 0) {
32+
auto result = f->write_unlocked(str_view.data(), str_view.size());
33+
if (result.has_error())
34+
return result.error;
35+
}
36+
return 0;
37+
}
38+
39+
} // namespace
40+
41+
// TODO: this seems like there should be some sort of queue system to
42+
// deduplicate this code.
43+
LLVM_LIBC_FUNCTION(void, perror, (const char *str)) {
44+
const char empty_str[1] = {'\0'};
45+
if (str == nullptr)
46+
str = empty_str;
47+
cpp::string_view str_view(str);
48+
49+
auto err_str = get_error_string(libc_errno);
50+
51+
// We need to lock the stream to ensure the newline is always appended.
52+
ScopedLock lock(LIBC_NAMESPACE::stderr);
53+
int write_err;
54+
55+
// FORMAT:
56+
// if str != nullptr and doesn't start with a null byte:
57+
// "[str]: [strerror(errno)]\n"
58+
// else
59+
// "[strerror(errno)]\n"
60+
if (str_view.size() > 0) {
61+
write_err = write_out(str_view, LIBC_NAMESPACE::stderr);
62+
if (write_err != 0) {
63+
libc_errno = write_err;
64+
return;
65+
}
66+
67+
write_err = write_out(": ", LIBC_NAMESPACE::stderr);
68+
if (write_err != 0) {
69+
libc_errno = write_err;
70+
return;
71+
}
72+
}
73+
74+
write_err = write_out(err_str, LIBC_NAMESPACE::stderr);
75+
if (write_err != 0) {
76+
libc_errno = write_err;
77+
return;
78+
}
79+
80+
write_err = write_out("\n", LIBC_NAMESPACE::stderr);
81+
if (write_err != 0) {
82+
libc_errno = write_err;
83+
return;
84+
}
85+
}
86+
87+
} // namespace LIBC_NAMESPACE_DECL

libc/src/stdio/perror.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//===-- Implementation header of perror -------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDIO_PERROR_H
10+
#define LLVM_LIBC_SRC_STDIO_PERROR_H
11+
12+
#include "src/__support/macros/config.h"
13+
14+
namespace LIBC_NAMESPACE_DECL {
15+
16+
void perror(const char *s);
17+
18+
} // namespace LIBC_NAMESPACE_DECL
19+
20+
#endif // LLVM_LIBC_SRC_STDIO_PERROR_H

libc/test/src/stdio/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,18 @@ add_libc_test(
357357
libc.src.stdio.puts
358358
)
359359

360+
add_libc_test(
361+
perror_test
362+
HERMETIC_TEST_ONLY # writes to libc's stderr
363+
SUITE
364+
libc_stdio_unittests
365+
SRCS
366+
perror_test.cpp
367+
DEPENDS
368+
libc.src.stdio.perror
369+
libc.src.errno.errno
370+
)
371+
360372
add_libc_test(
361373
fputs_test
362374
HERMETIC_TEST_ONLY # writes to libc's stdout and stderr

libc/test/src/stdio/perror_test.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===-- Unittests for perror ---------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/stdio/perror.h"
10+
11+
#include "src/errno/libc_errno.h"
12+
#include "test/UnitTest/Test.h"
13+
14+
// The standard says perror prints directly to stderr and returns nothing. This
15+
// makes it rather difficult to test automatically.
16+
17+
// TODO: figure out redirecting stderr so this test can check correctness.
18+
TEST(LlvmLibcPerrorTest, PrintOut) {
19+
LIBC_NAMESPACE::libc_errno = 0;
20+
constexpr char simple[] = "A simple string";
21+
LIBC_NAMESPACE::perror(simple);
22+
23+
// stick to stdc errno values, specifically 0, EDOM, ERANGE, and EILSEQ.
24+
LIBC_NAMESPACE::libc_errno = EDOM;
25+
LIBC_NAMESPACE::perror("Print this and an error");
26+
27+
LIBC_NAMESPACE::libc_errno = EILSEQ;
28+
LIBC_NAMESPACE::perror("\0 shouldn't print this.");
29+
30+
LIBC_NAMESPACE::libc_errno = ERANGE;
31+
LIBC_NAMESPACE::perror(nullptr);
32+
}

0 commit comments

Comments
 (0)