[vm/bin] Improve fallback error message for process creation

Instead of just giving up try to give as much information as possible
including error code, error returned by Dart_StringFromUtf8 and
ascii portion of the OS error message.

We are making this change because we are getting reports of
ProcessException being thrown with "OS error message was a
not a utf8 string." message. There is no way to reproduce
these situations so instead we are going to add additional
information into the fallback message to aid the investigation.

TEST=tested manually by simulating fallback to the newly added code

Bug: b/178383611
Change-Id: Idf528dc762723694145a15aa07320220e8af9bfc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/181200
Commit-Queue: Vyacheslav Egorov <vegorov@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
This commit is contained in:
Vyacheslav Egorov 2021-01-26 12:56:49 +00:00 committed by commit-bot@chromium.org
parent 0f371f2dba
commit aed72ae8c8
4 changed files with 52 additions and 17 deletions

View file

@ -758,6 +758,22 @@ Dart_Handle DartUtils::NewInternalError(const char* message) {
return NewDartExceptionWithMessage(kCoreLibURL, "_InternalError", message);
}
Dart_Handle DartUtils::NewStringFormatted(const char* format, ...) {
va_list args;
va_start(args, format);
intptr_t len = vsnprintf(NULL, 0, format, args);
va_end(args);
char* buffer = reinterpret_cast<char*>(Dart_ScopeAllocate(len + 1));
MSAN_UNPOISON(buffer, (len + 1));
va_list args2;
va_start(args2, format);
vsnprintf(buffer, (len + 1), format, args2);
va_end(args2);
return NewString(buffer);
}
bool DartUtils::SetOriginalWorkingDirectory() {
// If we happen to re-initialize the Dart VM multiple times, make sure to free
// the old string (allocated by getcwd()) before setting a new one.

View file

@ -195,13 +195,16 @@ class DartUtils {
const char* message,
Dart_Handle os_error);
// Create a new Dart String object from a C String.
// Create a new Dart String object from a UTF8 encoded C String.
static Dart_Handle NewString(const char* str) {
ASSERT(str != NULL);
return Dart_NewStringFromUTF8(reinterpret_cast<const uint8_t*>(str),
strlen(str));
}
// Create a new Dart String object from a formatted string.
static Dart_Handle NewStringFormatted(const char* format, ...);
// Allocate length bytes for a C string with Dart_ScopeAllocate.
static char* ScopedCString(intptr_t length) {
char* result = NULL;

View file

@ -164,13 +164,29 @@ void FUNCTION_NAME(Process_Start)(Dart_NativeArguments args) {
result =
DartUtils::SetIntegerField(status_handle, "_errorCode", error_code);
ThrowIfError(result);
Dart_Handle val = DartUtils::NewString(os_error_message != NULL
? os_error_message
: "Cannot get error message");
const char* error_message = (os_error_message != NULL)
? os_error_message
: "Failed to get error message";
Dart_Handle val = DartUtils::NewString(error_message);
if (Dart_IsError(val)) {
// If conversion of the OS error message to a Dart string fails, fall back
// on a stock message.
val = DartUtils::NewString("OS error message was a not a utf8 string.");
// Try to clean the message from non-ASCII characters.
const intptr_t len = strlen(error_message);
char* ascii_message =
reinterpret_cast<char*>(Dart_ScopeAllocate(len + 1));
for (intptr_t i = 0; i < len; i++) {
if (static_cast<uint8_t>(error_message[i]) < 0x80) {
ascii_message[i] = error_message[i];
} else {
ascii_message[i] = '?';
}
}
ascii_message[len] = '\0';
val = DartUtils::NewStringFormatted(
"Failed to start %s. OS returned an error (code %d) which can't be "
"fully converted to Dart string (%s): %s",
path, error_code, Dart_GetError(val), ascii_message);
}
result = Dart_SetField(status_handle, DartUtils::NewString("_errorMessage"),
val);

View file

@ -399,6 +399,8 @@ class ProcessStarter {
}
private:
static constexpr int kErrorBufferSize = 1024;
int CreatePipes() {
int result;
result = TEMP_FAILURE_RETRY(pipe2(exec_control_, O_CLOEXEC));
@ -705,9 +707,8 @@ class ProcessStarter {
}
void SetChildOsErrorMessage() {
const int kBufferSize = 1024;
char* error_message = DartUtils::ScopedCString(kBufferSize);
Utils::StrError(errno, error_message, kBufferSize);
char* error_message = DartUtils::ScopedCString(kErrorBufferSize);
Utils::StrError(errno, error_message, kErrorBufferSize);
*os_error_message_ = error_message;
}
@ -715,9 +716,9 @@ class ProcessStarter {
// In the case of failure in the child process write the errno and
// the OS error message to the exec control pipe and exit.
int child_errno = errno;
const int kBufferSize = 1024;
char error_buf[kBufferSize];
char* os_error_message = Utils::StrError(errno, error_buf, kBufferSize);
char error_buf[kErrorBufferSize];
char* os_error_message =
Utils::StrError(errno, error_buf, kErrorBufferSize);
int bytes_written = FDUtils::WriteToBlocking(exec_control_[1], &child_errno,
sizeof(child_errno));
if (bytes_written == sizeof(child_errno)) {
@ -741,11 +742,10 @@ class ProcessStarter {
}
void ReadChildError() {
const int kMaxMessageSize = 256;
char* message = DartUtils::ScopedCString(kMaxMessageSize);
char* message = DartUtils::ScopedCString(kErrorBufferSize);
if (message != NULL) {
FDUtils::ReadFromBlocking(exec_control_[0], message, kMaxMessageSize);
message[kMaxMessageSize - 1] = '\0';
FDUtils::ReadFromBlocking(exec_control_[0], message, kErrorBufferSize);
message[kErrorBufferSize - 1] = '\0';
*os_error_message_ = message;
} else {
// Could not get error message. It will be NULL.