blob: 6e2d0bfe0ee3980f12b481035987cc8175e94858 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 "runtime/io/error-converter.h"
#include "gutil/strings/substitute.h"
#include "util/debug-util.h"
#include "util/error-util.h"
#include "util/string-parser.h"
#include "common/names.h"
namespace impala {
using std::unordered_map;
unordered_map<int, string> ErrorConverter::errno_to_error_text_map_(
{{EACCES, "Access denied for the process' user"},
{EINTR, "Internal error occured."},
{EINVAL, "Invalid inputs."},
{EMFILE, "Process level opened file descriptor count is reached."},
{ENAMETOOLONG,
"Either the path length or a path component exceeds the maximum length."},
{ENFILE, "OS level opened file descriptor count is reached."},
{ENOENT, "The given path doesn't exist."},
{ENOSPC, "No space left on device."},
{ENOTDIR, "It is not a directory."},
{EOVERFLOW, "File size can't be represented."},
{EROFS, "The file system is read only."},
{EAGAIN, "Resource temporarily unavailable."},
{EBADF, "The given file descriptor is invalid."},
{ENOMEM, "Not enough memory."},
{EFBIG, "Maximum file size reached."},
{EIO, "Disk level I/O error occured."},
{ENXIO, "Device doesn't exist."}});
Status ErrorConverter::GetErrorStatusFromErrno(const string& function_name,
const string& file_path, int err_no, const Params& params) {
return Status(ErrorMsg(TErrorCode::DISK_IO_ERROR, GetBackendString(),
GetErrorText(function_name, file_path, err_no, params)));
}
bool ErrorConverter::IsBlacklistableError(int err_no) {
// A set of disk IO related non-transient error codes that should cause failure for
// reading/writing file on local disk. These errors could be caused by incorrect
// configurations or file system errors, but are not caused by temporarily unavailable
// resource, like EAGAIN, ENOMEM, EMFILE, ENFILE and ENOSPC. Since these errors are not
// likely disappear soon, the node which frequently hits such disk errors should be
// blacklisted.
static const set<int32_t> blacklistable_disk_error_codes = {
EPERM, // Operation not permitted.
ENOENT, // No such file or directory.
ESRCH, // No such process.
EINTR, // Interrupted system call.
EIO, // Disk level I/O error.
ENXIO, // No such device or address.
E2BIG, // Argument list too long.
ENOEXEC, // Exec format error.
EBADF, // The given file descriptor is invalid.
EACCES, // Permission denied.
EFAULT, // Bad address.
ENODEV, // No such device
ENOTDIR, // It is not a directory.
EINVAL, // Invalid argument.
EFBIG, // Maximum file size reached.
ESPIPE, // Illegal seek.
EROFS, // The file system is read only.
ENAMETOOLONG, // Either the path length or a path component exceeds the max length.
EOVERFLOW}; // File size can't be represented.
// Return true if the err_no matches any of the 'blacklistable' error code.
return (blacklistable_disk_error_codes.find(err_no)
!= blacklistable_disk_error_codes.end());
}
bool ErrorConverter::IsBlacklistableError(const Status& status) {
// Return true if the error is generated by Debug Action.
// Return false if error code is not set as DISK_IO_ERROR, or there is no 'err_no' in
// error text, or 'err_no' is set in wrong format.
if (status.IsInternalError()) {
return (status.msg().msg().find("Debug Action") != string::npos);
} else if (!status.IsDiskIoError()) {
return false;
}
size_t found = status.msg().msg().find("errno=");
if (found == string::npos) return false;
size_t start_pos = found + 6;
size_t end_pos = status.msg().msg().find(",", start_pos);
string value = (end_pos != string::npos) ?
status.msg().msg().substr(start_pos, end_pos - start_pos) :
status.msg().msg().substr(start_pos);
if (value.empty()) return false;
StringParser::ParseResult result;
int err_no = StringParser::StringToInt<int32_t>(value.c_str(), value.length(), &result);
if (result != StringParser::PARSE_SUCCESS) {
return false;
} else {
return IsBlacklistableError(err_no);
}
}
string ErrorConverter::GetErrorText(const string& function_name,
const string& file_path, int err_no, Params params) {
const string* error_text_body = GetErrorTextBody(err_no);
if (error_text_body != nullptr) {
params["errno"] = SimpleItoa(err_no);
return Substitute("$0 failed for $1. $2 $3", function_name, file_path,
*error_text_body, GetParamsString(params), err_no);
}
return Substitute("$0 failed for $1. errno=$2, description=$3", function_name,
file_path, err_no, GetStrErrMsg(err_no));
}
string ErrorConverter::GetParamsString(const Params& params) {
string result = "";
bool first = true;
for (const auto& item : params) {
if (!first) result.append(", ");
result.append(item.first).append("=").append(item.second);
first = false;
}
return result;
}
const string* ErrorConverter::GetErrorTextBody(int err_no) {
auto error_mapping_it = errno_to_error_text_map_.find(err_no);
if (error_mapping_it != errno_to_error_text_map_.end()) {
return &error_mapping_it->second;
}
return nullptr;
}
}