/Users/deen/code/yugabyte-db/src/yb/common/ybc_util.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | |
13 | | #include "yb/common/ybc_util.h" |
14 | | |
15 | | #include <stdarg.h> |
16 | | |
17 | | #include <fstream> |
18 | | |
19 | | #include "yb/common/pgsql_error.h" |
20 | | #include "yb/common/transaction_error.h" |
21 | | #include "yb/common/ybc-internal.h" |
22 | | |
23 | | #include "yb/gutil/stringprintf.h" |
24 | | |
25 | | #include "yb/util/bytes_formatter.h" |
26 | | #include "yb/util/debug-util.h" |
27 | | #include "yb/util/env.h" |
28 | | #include "yb/util/flag_tags.h" |
29 | | #include "yb/util/init.h" |
30 | | #include "yb/util/logging.h" |
31 | | #include "yb/util/net/net_util.h" |
32 | | #include "yb/util/scope_exit.h" |
33 | | #include "yb/util/status_format.h" |
34 | | #include "yb/util/thread.h" |
35 | | |
36 | | using std::string; |
37 | | DEFINE_test_flag(string, process_info_dir, string(), |
38 | | "Directory where all postgres process will writes their PIDs and executable name"); |
39 | | |
40 | | bool yb_debug_log_docdb_requests = false; |
41 | | |
42 | | bool yb_non_ddl_txn_for_sys_tables_allowed = false; |
43 | | |
44 | | bool yb_format_funcs_include_yb_metadata = false; |
45 | | |
46 | | bool yb_force_global_transaction = false; |
47 | | |
48 | | bool suppress_nonpg_logs = false; |
49 | | |
50 | | bool yb_binary_restore = false; |
51 | | |
52 | | namespace yb { |
53 | | |
54 | | namespace { |
55 | | |
56 | 20.1k | void ChangeWorkingDir(const char* dir) { |
57 | 20.1k | int chdir_result = chdir(dir); |
58 | 20.1k | if (chdir_result != 0) { |
59 | 0 | LOG(WARNING) << "Failed to change working directory to " << dir << ", error was " |
60 | 0 | << errno << " " << std::strerror(errno) << "!"; |
61 | 0 | } |
62 | 20.1k | } |
63 | | |
64 | 4.89k | void WriteCurrentProcessInfo(const string& destination_dir) { |
65 | 4.89k | string executable_path; |
66 | 4.89k | if (Env::Default()->GetExecutablePath(&executable_path).ok()) { |
67 | 4.89k | const auto destination_file = Format("$0/$1", destination_dir, getpid()); |
68 | 4.89k | std::ofstream out(destination_file, std::ios_base::out); |
69 | 4.89k | out << executable_path; |
70 | 4.89k | if (out4.89k ) { |
71 | 4.89k | LOG(INFO) << "Process info is written to " << destination_file; |
72 | 4.89k | return; |
73 | 4.89k | } |
74 | 4.89k | } |
75 | 18.4E | LOG(WARNING) << "Unable to write process info to " |
76 | 18.4E | << destination_dir << " dir: error " << errno << " " << std::strerror(errno); |
77 | 18.4E | } |
78 | | |
79 | 10.0k | Status InitGFlags(const char* argv0) { |
80 | | |
81 | 10.0k | const char* executable_path = argv0; |
82 | 10.0k | std::string executable_path_str; |
83 | 10.0k | if (executable_path == nullptr) { |
84 | 1.99k | RETURN_NOT_OK(Env::Default()->GetExecutablePath(&executable_path_str)); |
85 | 1.99k | executable_path = executable_path_str.c_str(); |
86 | 1.99k | } |
87 | 10.0k | RSTATUS_DCHECK(executable_path != nullptr, RuntimeError, "Unable to get path to executable"); |
88 | | |
89 | | // Change current working directory from postgres data dir (as set by postmaster) |
90 | | // to the one from yb-tserver so that relative paths in gflags would be resolved in the same way. |
91 | 10.0k | char pg_working_dir[PATH_MAX]; |
92 | 10.0k | CHECK(getcwd(pg_working_dir, sizeof(pg_working_dir)) != nullptr); |
93 | 10.0k | const char* yb_working_dir = getenv("YB_WORKING_DIR"); |
94 | 10.0k | if (yb_working_dir) { |
95 | 10.0k | ChangeWorkingDir(yb_working_dir); |
96 | 10.0k | } |
97 | 10.0k | auto se = ScopeExit([&pg_working_dir] { |
98 | | // Restore PG data dir as current directory. |
99 | 10.0k | ChangeWorkingDir(pg_working_dir); |
100 | 10.0k | }); |
101 | | |
102 | | // Also allow overriding flags on the command line using the appropriate environment variables. |
103 | 10.0k | std::vector<google::CommandLineFlagInfo> flag_infos; |
104 | 10.0k | google::GetAllFlags(&flag_infos); |
105 | 3.16M | for (auto& flag_info : flag_infos) { |
106 | 3.16M | string env_var_name = "FLAGS_" + flag_info.name; |
107 | 3.16M | const char* env_var_value = getenv(env_var_name.c_str()); |
108 | 3.16M | if (env_var_value) { |
109 | 267k | google::SetCommandLineOption(flag_info.name.c_str(), env_var_value); |
110 | 267k | } |
111 | 3.16M | } |
112 | | |
113 | 10.0k | RETURN_NOT_OK(CheckCPUFlags()); |
114 | | // Use InitGoogleLoggingSafeBasic() instead of InitGoogleLoggingSafe() to avoid calling |
115 | | // google::InstallFailureSignalHandler(). This will prevent interference with PostgreSQL's |
116 | | // own signal handling. |
117 | | // Use InitGoogleLoggingSafeBasicSuppressNonNativePostgresLogs() over |
118 | | // InitGoogleLoggingSafeBasic() when the "suppress_nonpg_logs" flag is set. |
119 | | // This will suppress non-Postgres logs from the Postgres log file. |
120 | 10.0k | if (suppress_nonpg_logs) { |
121 | 0 | yb::InitGoogleLoggingSafeBasicSuppressNonNativePostgresLogs(executable_path); |
122 | 10.0k | } else { |
123 | 10.0k | yb::InitGoogleLoggingSafeBasic(executable_path); |
124 | 10.0k | } |
125 | | |
126 | 10.0k | if (VLOG_IS_ON(1)) { |
127 | 0 | for (auto& flag_info : flag_infos) { |
128 | 0 | string env_var_name = "FLAGS_" + flag_info.name; |
129 | 0 | const char* env_var_value = getenv(env_var_name.c_str()); |
130 | 0 | if (env_var_value) { |
131 | 0 | VLOG(1) << "Setting flag " << flag_info.name << " to the value of the env var " |
132 | 0 | << env_var_name << ": " << env_var_value; |
133 | 0 | } |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | 10.0k | return Status::OK(); |
138 | 10.0k | } |
139 | | |
140 | | } // anonymous namespace |
141 | | |
142 | | extern "C" { |
143 | | |
144 | | YBCStatus YBCStatus_OK = nullptr; |
145 | | |
146 | | // Wraps Status object created by YBCStatus. |
147 | | // Uses trick with AddRef::kFalse and DetachStruct, to avoid incrementing and decrementing |
148 | | // ref counter. |
149 | | class StatusWrapper { |
150 | | public: |
151 | 1.47M | explicit StatusWrapper(YBCStatus s) : status_(s, AddRef::kFalse) {} |
152 | | |
153 | 1.47M | ~StatusWrapper() { |
154 | 1.47M | status_.DetachStruct(); |
155 | 1.47M | } |
156 | | |
157 | 1.42M | Status* operator->() { |
158 | 1.42M | return &status_; |
159 | 1.42M | } |
160 | | |
161 | 121k | Status& operator*() { |
162 | 121k | return status_; |
163 | 121k | } |
164 | | |
165 | | private: |
166 | | Status status_; |
167 | | }; |
168 | | |
169 | 862k | bool YBCStatusIsOK(YBCStatus s) { |
170 | 862k | return StatusWrapper(s)->IsOk(); |
171 | 862k | } |
172 | | |
173 | 4.18k | bool YBCStatusIsNotFound(YBCStatus s) { |
174 | 4.18k | return StatusWrapper(s)->IsNotFound(); |
175 | 4.18k | } |
176 | | |
177 | 0 | bool YBCStatusIsDuplicateKey(YBCStatus s) { |
178 | 0 | return StatusWrapper(s)->IsAlreadyPresent(); |
179 | 0 | } |
180 | | |
181 | 121k | uint32_t YBCStatusPgsqlError(YBCStatus s) { |
182 | 121k | StatusWrapper wrapper(s); |
183 | 121k | const uint8_t* pg_err_ptr = wrapper->ErrorData(PgsqlErrorTag::kCategory); |
184 | | // If we have PgsqlError explicitly set, we decode it |
185 | 121k | YBPgErrorCode result = pg_err_ptr != nullptr ? PgsqlErrorTag::Decode(pg_err_ptr)50.3k |
186 | 121k | : YBPgErrorCode::YB_PG_INTERNAL_ERROR71.6k ; |
187 | | |
188 | | // If the error is the default generic YB_PG_INTERNAL_ERROR (as we also set in AsyncRpc::Failed) |
189 | | // then we try to deduce it from a transaction error. |
190 | 121k | if (result == YBPgErrorCode::YB_PG_INTERNAL_ERROR) { |
191 | 73.0k | const uint8_t* txn_err_ptr = wrapper->ErrorData(TransactionErrorTag::kCategory); |
192 | 73.0k | if (txn_err_ptr != nullptr) { |
193 | 72.5k | switch (TransactionErrorTag::Decode(txn_err_ptr)) { |
194 | 5.97k | case TransactionErrorCode::kAborted: FALLTHROUGH_INTENDED; |
195 | 7.35k | case TransactionErrorCode::kReadRestartRequired: FALLTHROUGH_INTENDED; |
196 | 72.4k | case TransactionErrorCode::kConflict: |
197 | 72.4k | result = YBPgErrorCode::YB_PG_T_R_SERIALIZATION_FAILURE; |
198 | 72.4k | break; |
199 | 0 | case TransactionErrorCode::kSnapshotTooOld: |
200 | 0 | result = YBPgErrorCode::YB_PG_SNAPSHOT_TOO_OLD; |
201 | 0 | break; |
202 | 0 | case TransactionErrorCode::kNone: FALLTHROUGH_INTENDED; |
203 | 38 | default: |
204 | 38 | result = YBPgErrorCode::YB_PG_INTERNAL_ERROR; |
205 | 72.5k | } |
206 | 72.5k | } |
207 | 73.0k | } |
208 | 121k | return static_cast<uint32_t>(result); |
209 | 121k | } |
210 | | |
211 | 121k | uint16_t YBCStatusTransactionError(YBCStatus s) { |
212 | 121k | const TransactionError txn_err(*StatusWrapper(s)); |
213 | 121k | return static_cast<uint16_t>(txn_err.value()); |
214 | 121k | } |
215 | | |
216 | 121k | void YBCFreeStatus(YBCStatus s) { |
217 | 121k | FreeYBCStatus(s); |
218 | 121k | } |
219 | | |
220 | 121k | size_t YBCStatusMessageLen(YBCStatus s) { |
221 | 121k | return StatusWrapper(s)->message().size(); |
222 | 121k | } |
223 | | |
224 | 121k | const char* YBCStatusMessageBegin(YBCStatus s) { |
225 | 121k | return StatusWrapper(s)->message().cdata(); |
226 | 121k | } |
227 | | |
228 | 121k | const char* YBCStatusCodeAsCString(YBCStatus s) { |
229 | 121k | return StatusWrapper(s)->CodeAsCString(); |
230 | 121k | } |
231 | | |
232 | 121k | char* DupYBStatusMessage(YBCStatus status, bool message_only) { |
233 | 121k | const char* const code_as_cstring = YBCStatusCodeAsCString(status); |
234 | 121k | const size_t code_strlen = strlen(code_as_cstring); |
235 | 121k | const size_t status_len = YBCStatusMessageLen(status); |
236 | 121k | size_t sz = code_strlen + status_len + 3; |
237 | 121k | if (message_only) { |
238 | 11.1k | sz -= 2 + code_strlen; |
239 | 11.1k | } |
240 | 121k | char* const msg_buf = reinterpret_cast<char*>(YBCPAlloc(sz)); |
241 | 121k | char* pos = msg_buf; |
242 | 121k | if (!message_only) { |
243 | 110k | memcpy(msg_buf, code_as_cstring, code_strlen); |
244 | 110k | pos += code_strlen; |
245 | 110k | *pos++ = ':'; |
246 | 110k | *pos++ = ' '; |
247 | 110k | } |
248 | 121k | memcpy(pos, YBCStatusMessageBegin(status), status_len); |
249 | 121k | pos[status_len] = 0; |
250 | 121k | return msg_buf; |
251 | 121k | } |
252 | | |
253 | 196k | bool YBCIsRestartReadError(uint16_t txn_errcode) { |
254 | 196k | return txn_errcode == static_cast<uint16_t>(TransactionErrorCode::kReadRestartRequired); |
255 | 196k | } |
256 | | |
257 | 1.99k | YBCStatus YBCInitGFlags(const char* argv0) { |
258 | 1.99k | return ToYBCStatus(yb::InitGFlags(argv0)); |
259 | 1.99k | } |
260 | | |
261 | 198k | bool YBCIsTxnConflictError(uint16_t txn_errcode) { |
262 | 198k | return txn_errcode == to_underlying(TransactionErrorCode::kConflict); |
263 | 198k | } |
264 | | |
265 | 38 | bool YBCIsTxnSkipLockingError(uint16_t txn_errcode) { |
266 | 38 | return txn_errcode == to_underlying(TransactionErrorCode::kSkipLocking); |
267 | 38 | } |
268 | | |
269 | 2.83k | uint16_t YBCGetTxnConflictErrorCode() { |
270 | 2.83k | return to_underlying(TransactionErrorCode::kConflict); |
271 | 2.83k | } |
272 | | |
273 | | YBCStatus YBCInit(const char* argv0, |
274 | | YBCPAllocFn palloc_fn, |
275 | 8.08k | YBCCStringToTextWithLenFn cstring_to_text_with_len_fn) { |
276 | 8.08k | YBCSetPAllocFn(palloc_fn); |
277 | 8.09k | if (cstring_to_text_with_len_fn8.08k ) { |
278 | 8.09k | YBCSetCStringToTextWithLenFn(cstring_to_text_with_len_fn); |
279 | 8.09k | } |
280 | 8.08k | auto status = yb::InitGFlags(argv0); |
281 | 8.09k | if (status.ok()8.08k && !FLAGS_TEST_process_info_dir.empty()) { |
282 | 4.89k | WriteCurrentProcessInfo(FLAGS_TEST_process_info_dir); |
283 | 4.89k | } |
284 | 8.08k | return ToYBCStatus(status); |
285 | 8.08k | } |
286 | | |
287 | | void YBCLogImpl( |
288 | | google::LogSeverity severity, |
289 | | const char* file, |
290 | | int line, |
291 | | bool with_stack_trace, |
292 | | const char* format, |
293 | 5.08k | ...) { |
294 | 5.08k | va_list argptr; |
295 | 5.08k | va_start(argptr, format); \ |
296 | 5.08k | string buf; |
297 | 5.08k | StringAppendV(&buf, format, argptr); |
298 | 5.08k | va_end(argptr); |
299 | 5.08k | google::LogMessage log_msg(file, line, severity); |
300 | 5.08k | log_msg.stream() << buf; |
301 | 5.08k | if (with_stack_trace) { |
302 | 0 | log_msg.stream() << "\n" << yb::GetStackTrace(); |
303 | 0 | } |
304 | 5.08k | } |
305 | | |
306 | 0 | const char* YBCFormatBytesAsStr(const char* data, size_t size) { |
307 | 0 | return YBCPAllocStdString(FormatBytesAsStr(data, size)); |
308 | 0 | } |
309 | | |
310 | 6 | const char* YBCGetStackTrace() { |
311 | 6 | return YBCPAllocStdString(yb::GetStackTrace()); |
312 | 6 | } |
313 | | |
314 | 1.99k | void YBCResolveHostname() { |
315 | 1.99k | string fqdn; |
316 | 1.99k | auto status = GetFQDN(&fqdn); |
317 | 1.99k | if (!status.ok()) { |
318 | 0 | LOG(WARNING) << "Failed to get fully qualified domain name of the local hostname: " |
319 | 0 | << status; |
320 | 0 | } |
321 | 1.99k | } |
322 | | |
323 | 65 | inline double YBCGetNumHashBuckets() { |
324 | 65 | return 64.0; |
325 | 65 | } |
326 | | |
327 | | /* Gets the number of hash buckets for a DocDB table */ |
328 | 130 | inline double YBCGetHashBucketFromValue(uint32_t hash_val) { |
329 | | /* |
330 | | * Since hash values are 16 bit for now and there are (1 << 6) |
331 | | * buckets, we must right shift a hash value by 16 - 6 = 10 to |
332 | | * obtain its bucket number |
333 | | */ |
334 | 130 | return hash_val >> 10; |
335 | 130 | } |
336 | | |
337 | 65 | double YBCEvalHashValueSelectivity(int32_t hash_low, int32_t hash_high) { |
338 | 65 | hash_high = hash_high <= USHRT_MAX ? hash_high52 : USHRT_MAX; |
339 | 65 | hash_high = hash_high >= 0 ? hash_high : 00 ; |
340 | 65 | hash_low = hash_low >= 0 ? hash_low15 : 050 ; |
341 | 65 | hash_low = hash_low <= USHRT_MAX ? hash_low : USHRT_MAX; |
342 | | |
343 | 65 | uint32_t greatest_bucket = YBCGetHashBucketFromValue(hash_high); |
344 | 65 | uint32_t lowest_bucket = YBCGetHashBucketFromValue(hash_low); |
345 | 65 | return hash_high >= hash_low ? |
346 | 65 | ((greatest_bucket - lowest_bucket + 1.0) / YBCGetNumHashBuckets()) |
347 | 65 | : 0.00 ; |
348 | 65 | } |
349 | | |
350 | 1.99k | void YBCInitThreading() { |
351 | 1.99k | InitThreading(); |
352 | 1.99k | } |
353 | | |
354 | | } // extern "C" |
355 | | |
356 | | } // namespace yb |