Skip to content

Commit 5a6a4b6

Browse files
[libc] Implement perror (#143624)
The perror function writes an error message directly to stderr. This patch adds an implementation, tests, and header generation details.
1 parent 4e765b7 commit 5a6a4b6

File tree

10 files changed

+170
-0
lines changed

10 files changed

+170
-0
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,7 @@ if(LLVM_LIBC_FULL_BUILD)
972972
libc.src.stdio.getc_unlocked
973973
libc.src.stdio.getchar
974974
libc.src.stdio.getchar_unlocked
975+
libc.src.stdio.perror
975976
libc.src.stdio.putc
976977
libc.src.stdio.putchar
977978
libc.src.stdio.puts

libc/config/linux/riscv/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,7 @@ if(LLVM_LIBC_FULL_BUILD)
10981098
libc.src.stdio.getc_unlocked
10991099
libc.src.stdio.getchar
11001100
libc.src.stdio.getchar_unlocked
1101+
libc.src.stdio.perror
11011102
libc.src.stdio.putc
11021103
libc.src.stdio.putchar
11031104
libc.src.stdio.puts

libc/config/linux/x86_64/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,7 @@ if(LLVM_LIBC_FULL_BUILD)
11161116
libc.src.stdio.getc_unlocked
11171117
libc.src.stdio.getchar
11181118
libc.src.stdio.getchar_unlocked
1119+
libc.src.stdio.perror
11191120
libc.src.stdio.putc
11201121
libc.src.stdio.putchar
11211122
libc.src.stdio.puts

libc/include/stdio.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,12 @@ functions:
249249
- POSIX
250250
return_type: int
251251
arguments: []
252+
- name: perror
253+
standards:
254+
- stdc
255+
return_type: void
256+
arguments:
257+
- type: const char *
252258
- name: printf
253259
standards:
254260
- 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: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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/libc_errno.h"
14+
#include "src/__support/macros/config.h"
15+
16+
namespace LIBC_NAMESPACE_DECL {
17+
18+
static int write_out(cpp::string_view str_view, File *f) {
19+
if (str_view.size() > 0) {
20+
auto result = f->write_unlocked(str_view.data(), str_view.size());
21+
if (result.has_error())
22+
return result.error;
23+
}
24+
return 0;
25+
}
26+
27+
// separate function so that we can return early on error but still get the
28+
// unlock. This function sets errno and should not be called elsewhere.
29+
static void write_sequence(cpp::string_view str_view,
30+
cpp::string_view err_str) {
31+
int write_err;
32+
// TODO: this seems like there should be some sort of queue system to
33+
// deduplicate this code.
34+
35+
// FORMAT:
36+
// if str != nullptr and doesn't start with a null byte:
37+
// "[str]: [strerror(errno)]\n"
38+
// else
39+
// "[strerror(errno)]\n"
40+
if (str_view.size() > 0) {
41+
write_err = write_out(str_view, LIBC_NAMESPACE::stderr);
42+
if (write_err != 0) {
43+
libc_errno = write_err;
44+
return;
45+
}
46+
47+
write_err = write_out(": ", LIBC_NAMESPACE::stderr);
48+
if (write_err != 0) {
49+
libc_errno = write_err;
50+
return;
51+
}
52+
}
53+
54+
write_err = write_out(err_str, LIBC_NAMESPACE::stderr);
55+
if (write_err != 0) {
56+
libc_errno = write_err;
57+
return;
58+
}
59+
60+
write_err = write_out("\n", LIBC_NAMESPACE::stderr);
61+
if (write_err != 0) {
62+
libc_errno = write_err;
63+
return;
64+
}
65+
}
66+
67+
LLVM_LIBC_FUNCTION(void, perror, (const char *str)) {
68+
const char empty_str[1] = {'\0'};
69+
if (str == nullptr)
70+
str = empty_str;
71+
cpp::string_view str_view(str);
72+
73+
cpp::string_view err_str = get_error_string(libc_errno);
74+
75+
// We need to lock the stream to ensure the newline is always appended.
76+
LIBC_NAMESPACE::stderr->lock();
77+
write_sequence(str_view, err_str);
78+
LIBC_NAMESPACE::stderr->unlock();
79+
}
80+
81+
} // 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/__support/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)