Remove logic handling `Exception::$trace` as a resource
[hiphop-php.git] / hphp / runtime / ext / std / ext_std_errorfunc.cpp
blob6179c7b462f0c6b43a48a5096f079fecdcd3ea63
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1997-2010 The PHP Group |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
17 #include "hphp/runtime/ext/std/ext_std_errorfunc.h"
19 #include <iostream>
21 #include <folly/Format.h>
22 #include <folly/Likely.h>
23 #include <folly/Random.h>
25 #include "hphp/runtime/base/array-init.h"
26 #include "hphp/runtime/base/array-iterator.h"
27 #include "hphp/runtime/base/backtrace.h"
28 #include "hphp/runtime/base/bespoke-runtime.h"
29 #include "hphp/runtime/base/builtin-functions.h"
30 #include "hphp/runtime/base/exceptions.h"
31 #include "hphp/runtime/base/execution-context.h"
32 #include "hphp/runtime/base/string-buffer.h"
33 #include "hphp/runtime/base/request-info.h"
34 #include "hphp/runtime/base/backtrace.h"
35 #include "hphp/runtime/ext/std/ext_std_file.h"
36 #include "hphp/util/logger.h"
38 namespace HPHP {
40 ///////////////////////////////////////////////////////////////////////////////
42 const StaticString
43 s_file("file"),
44 s_line("line"),
45 s_function("function"),
46 s_class("class"),
47 s_type("type"),
48 s_args("args"),
49 s_message("message"),
50 s_call_user_func("call_user_func"),
51 s_call_user_func_array("call_user_func_array"),
52 s_hphp_debug_caller_info("hphp_debug_caller_info");
54 ///////////////////////////////////////////////////////////////////////////////
56 const int64_t k_DEBUG_BACKTRACE_PROVIDE_OBJECT = (1 << 0);
57 const int64_t k_DEBUG_BACKTRACE_IGNORE_ARGS = (1 << 1);
58 const int64_t k_DEBUG_BACKTRACE_PROVIDE_METADATA = (1 << 16);
60 const int64_t k_DEBUG_BACKTRACE_HASH_CONSIDER_METADATA = (1 << 0);
63 Array HHVM_FUNCTION(debug_backtrace, int64_t options /* = 1 */,
64 int64_t limit /* = 0 */) {
65 bool provide_object = options & k_DEBUG_BACKTRACE_PROVIDE_OBJECT;
66 bool provide_metadata = options & k_DEBUG_BACKTRACE_PROVIDE_METADATA;
67 bool ignore_args = options & k_DEBUG_BACKTRACE_IGNORE_ARGS;
68 return createBacktrace(BacktraceArgs()
69 .withThis(provide_object)
70 .withMetadata(provide_metadata)
71 .ignoreArgs(ignore_args)
72 .setLimit(limit));
75 ArrayData* debug_backtrace_jit(int64_t options) {
76 return HHVM_FN(debug_backtrace)(options).detach();
79 bool hphp_debug_caller_info_impl(
80 Array& result, bool& skipped, const Func* func, Offset offset) {
81 if (!skipped && func->isSkipFrame()) return false;
82 if (!skipped) {
83 skipped = true;
84 return false;
87 if (func->name()->isame(s_call_user_func.get())) return false;
88 if (func->name()->isame(s_call_user_func_array.get())) return false;
90 auto const line = func->getLineNumber(offset);
91 if (line == -1) return false;
93 static constexpr auto s_file_idx = 0;
94 static constexpr auto s_line_idx = 1;
95 static constexpr auto s_class_idx = 2;
96 static constexpr auto s_function_idx = 3;
97 static const RuntimeStruct::FieldIndexVector s_fields = {
98 {s_file_idx, s_file},
99 {s_line_idx, s_line},
100 {s_class_idx, s_class},
101 {s_function_idx, s_function},
103 static auto const s_struct =
104 RuntimeStruct::registerRuntimeStruct(s_hphp_debug_caller_info, s_fields);
106 auto const cls = func->cls();
107 auto const path = func->originalFilename() ?
108 func->originalFilename() : func->unit()->filepath();
110 auto const has_cls = cls && !func->isClosureBody();
111 StructDictInit init(s_struct, has_cls ? 4 : 3);
112 if (has_cls) {
113 init.set(s_class_idx, s_class,
114 Variant(VarNR(const_cast<StringData*>(cls->name()))));
116 init.set(s_file_idx, s_file,
117 Variant(VarNR(const_cast<StringData*>(path))));
118 init.set(s_function_idx, s_function,
119 Variant(VarNR(const_cast<StringData*>(func->name()))));
120 init.set(s_line_idx, s_line, line);
121 result = init.toArray();
122 return true;
126 * hphp_debug_caller_info - returns an array of info about the "caller"
128 * For clarity, we refer to the function that called hphp_debug_caller_info()
129 * as the "callee", and we refer to the function that called the callee as
130 * the "caller".
132 * This function returns an array containing keys "file", "function", "line" and
133 * optionally "class" which indicate the filename, function, line number and
134 * class name (if in class context) where the "caller" called the "callee".
136 Array HHVM_FUNCTION(hphp_debug_caller_info) {
137 Array result = empty_dict_array();
138 bool skipped = false;
139 walkStack([&] (const BTFrame& frm) {
140 return hphp_debug_caller_info_impl(
141 result, skipped, frm.func(), frm.bcOff());
143 return result;
146 int64_t HHVM_FUNCTION(hphp_debug_backtrace_hash, int64_t options /* = 0 */) {
147 return createBacktraceHash(
148 options & k_DEBUG_BACKTRACE_HASH_CONSIDER_METADATA
152 void HHVM_FUNCTION(debug_print_backtrace, int64_t options /* = 0 */,
153 int64_t limit /* = 0 */) {
154 bool ignore_args = options & k_DEBUG_BACKTRACE_IGNORE_ARGS;
155 g_context->write(debug_string_backtrace(false, ignore_args, limit));
158 String stringify_backtrace(const Array& bt, bool ignore_args) {
159 StringBuffer buf;
160 int i = 0;
161 for (ArrayIter it = bt.begin(); !it.end(); it.next(), i++) {
162 Array frame = it.second().toArray();
163 buf.append('#');
164 buf.append(i);
165 if (i < 10) buf.append(' ');
166 buf.append(' ');
167 if (frame.exists(s_class)) {
168 buf.append(tvCastToString(frame->get(s_class)));
169 buf.append(tvCastToString(frame->get(s_type)));
171 buf.append(tvCastToString(frame->get(s_function)));
172 buf.append("(");
173 if (!ignore_args) {
174 bool first = true;
175 for (ArrayIter argsIt(tvCastToArrayLike(frame->get(s_args)));
176 !argsIt.end();
177 argsIt.next()) {
178 if (!first) {
179 buf.append(", ");
180 } else {
181 first = false;
183 try {
184 buf.append(argsIt.second().toString());
185 } catch (FatalErrorException& fe) {
186 buf.append(fe.getMessage());
190 buf.append(")");
191 if (frame.exists(s_file)) {
192 buf.append(" called at [");
193 buf.append(tvCastToString(frame->get(s_file)));
194 buf.append(':');
195 buf.append(tvCastToString(frame->get(s_line)));
196 buf.append(']');
198 buf.append('\n');
200 return buf.detach();
203 String debug_string_backtrace(bool skip, bool ignore_args /* = false */,
204 int64_t limit /* = 0 */) {
205 Array bt;
206 bt = createBacktrace(BacktraceArgs()
207 .skipTop(skip)
208 .ignoreArgs(ignore_args)
209 .setLimit(limit));
210 return stringify_backtrace(bt, ignore_args);
213 Array HHVM_FUNCTION(error_get_last) {
214 String lastError = g_context->getLastError();
215 if (lastError.isNull()) {
216 return null_array;
218 return make_dict_array(
219 s_type, g_context->getLastErrorNumber(),
220 s_message, g_context->getLastError(),
221 s_file, g_context->getLastErrorPath(),
222 s_line, g_context->getLastErrorLine()
226 bool HHVM_FUNCTION(error_log, const String& message, int64_t message_type /* = 0 */,
227 const Variant& destination /* = null */,
228 const Variant& /*extra_headers*/ /* = null */) {
229 // error_log() should not invoke the user error handler,
230 // so we use Logger::Error() instead of raise_warning() or raise_error()
231 switch (message_type) {
232 case 0:
234 std::string line(message.data(),
235 // Truncate to 512k
236 message.size() > (1<<19) ? (1<<19) : message.size());
237 Logger::Error(line);
238 return true;
240 case 3:
242 // open for append only
243 auto outfile = HHVM_FN(fopen)(destination.toString(), "a");
244 if (outfile.isNull()) {
245 Logger::Error("can't open error_log file!\n");
246 return false;
248 HHVM_FN(fwrite)(outfile.toResource(), message);
249 HHVM_FN(fclose)(outfile.toResource());
250 return true;
252 case 2: // not used per PHP
253 default:
254 Logger::Error("error_log does not support message_type %ld!", message_type);
255 break;
257 return false;
260 int64_t HHVM_FUNCTION(error_reporting, const Variant& level /* = null */) {
261 auto& id = RequestInfo::s_requestInfo.getNoCheck()->m_reqInjectionData;
262 int oldErrorReportingLevel = id.getErrorReportingLevel();
263 if (!level.isNull()) {
264 id.setErrorReportingLevel(level.toInt32());
266 return oldErrorReportingLevel;
269 bool HHVM_FUNCTION(restore_error_handler) {
270 g_context->popUserErrorHandler();
271 return true;
274 bool HHVM_FUNCTION(restore_exception_handler) {
275 g_context->popUserExceptionHandler();
276 return true;
279 Variant HHVM_FUNCTION(set_error_handler, const Variant& error_handler,
280 int64_t error_types /* = ErrorMode::PHP_ALL | STRICT */) {
281 if (!is_null(error_handler.asTypedValue())) {
282 return g_context->pushUserErrorHandler(error_handler, error_types);
283 } else {
284 g_context->clearUserErrorHandlers();
285 return init_null_variant;
289 Variant HHVM_FUNCTION(set_exception_handler, const Variant& exception_handler) {
290 return g_context->pushUserExceptionHandler(exception_handler);
293 void HHVM_FUNCTION(hphp_set_error_page, const String& page) {
294 g_context->setErrorPage(page);
297 void HHVM_FUNCTION(hphp_throw_fatal_error, const String& error_msg) {
298 std::string msg = error_msg.data();
299 raise_error(msg);
302 void HHVM_FUNCTION(hphp_clear_unflushed) {
303 g_context->obEndAll();
304 g_context->obStart();
305 g_context->obProtect(true);
308 bool HHVM_FUNCTION(trigger_error, const String& error_msg,
309 int64_t error_type /* = ErrorMode::USER_NOTICE */) {
310 std::string msg = error_msg.data(); // not toCppString()
311 if (UNLIKELY(g_context->getThrowAllErrors())) {
312 throw Exception(folly::sformat("throwAllErrors: {}", error_type));
314 if (error_type == (int)ErrorMode::USER_ERROR) {
315 g_context->handleError(msg, error_type, true,
316 ExecutionContext::ErrorThrowMode::IfUnhandled,
317 "\nFatal error: ");
318 return true;
320 if (error_type == (int)ErrorMode::USER_WARNING) {
321 g_context->handleError(msg, error_type, true,
322 ExecutionContext::ErrorThrowMode::Never,
323 "\nWarning: ");
324 return true;
326 if (error_type == (int)ErrorMode::USER_NOTICE) {
327 g_context->handleError(msg, error_type, true,
328 ExecutionContext::ErrorThrowMode::Never,
329 "\nNotice: ");
330 return true;
332 if (error_type == (int)ErrorMode::USER_DEPRECATED) {
333 g_context->handleError(msg, error_type, true,
334 ExecutionContext::ErrorThrowMode::Never,
335 "\nDeprecated: ");
336 return true;
338 if (error_type == (int)ErrorMode::STRICT) {
339 // So that we can raise strict warnings for mismatched
340 // params in FCallBuiltin
341 raise_strict_warning(msg);
342 return true;
345 auto const f = fromCaller(
346 [] (const BTFrame& frm) { return frm.func(); }
349 if (f && f->isBuiltin()) {
350 if (error_type == (int)ErrorMode::ERROR) {
351 raise_error_without_first_frame(msg);
352 return true;
354 if (error_type == (int)ErrorMode::WARNING) {
355 raise_warning_without_first_frame(msg);
356 return true;
358 if (error_type == (int)ErrorMode::NOTICE) {
359 raise_notice_without_first_frame(msg);
360 return true;
362 if (error_type == (int)ErrorMode::PHP_DEPRECATED) {
363 raise_deprecated_without_first_frame(msg);
364 return true;
366 if (error_type == (int)ErrorMode::RECOVERABLE_ERROR) {
367 raise_recoverable_error_without_first_frame(msg);
368 return true;
371 raise_warning("Invalid error type specified");
372 return false;
375 bool HHVM_FUNCTION(trigger_sampled_error, const String& error_msg,
376 int64_t sample_rate,
377 int64_t error_type /* = (int)ErrorMode::USER_NOTICE */) {
378 if (!folly::Random::oneIn(sample_rate)) {
379 return true;
381 return HHVM_FN(trigger_error)(error_msg, error_type);
384 bool HHVM_FUNCTION(user_error, const String& error_msg,
385 int64_t error_type /* = (int)ErrorMode::USER_NOTICE */) {
386 return HHVM_FN(trigger_error)(error_msg, error_type);
389 ///////////////////////////////////////////////////////////////////////////////
391 Array HHVM_FUNCTION(HH_deferred_errors) {
392 return g_context->releaseDeferredErrors();
395 ///////////////////////////////////////////////////////////////////////////////
397 void StandardExtension::initErrorFunc() {
398 HHVM_FE(debug_backtrace);
399 HHVM_FE(hphp_debug_caller_info);
400 HHVM_FE(hphp_debug_backtrace_hash);
401 HHVM_FE(debug_print_backtrace);
402 HHVM_FE(error_get_last);
403 HHVM_FE(error_log);
404 HHVM_FE(error_reporting);
405 HHVM_FE(restore_error_handler);
406 HHVM_FE(restore_exception_handler);
407 HHVM_FE(set_error_handler);
408 HHVM_FE(set_exception_handler);
409 HHVM_FE(hphp_set_error_page);
410 HHVM_FE(hphp_throw_fatal_error);
411 HHVM_FE(hphp_clear_unflushed);
412 HHVM_FE(trigger_error);
413 HHVM_FE(trigger_sampled_error);
414 HHVM_FE(user_error);
415 HHVM_FALIAS(HH\\deferred_errors, HH_deferred_errors);
416 HHVM_RC_INT(DEBUG_BACKTRACE_PROVIDE_OBJECT, k_DEBUG_BACKTRACE_PROVIDE_OBJECT);
417 HHVM_RC_INT(DEBUG_BACKTRACE_IGNORE_ARGS, k_DEBUG_BACKTRACE_IGNORE_ARGS);
418 HHVM_RC_INT(DEBUG_BACKTRACE_PROVIDE_METADATA,
419 k_DEBUG_BACKTRACE_PROVIDE_METADATA);
420 HHVM_RC_INT(DEBUG_BACKTRACE_HASH_CONSIDER_METADATA,
421 k_DEBUG_BACKTRACE_HASH_CONSIDER_METADATA);
422 HHVM_RC_INT(E_ERROR, (int)ErrorMode::ERROR);
423 HHVM_RC_INT(E_WARNING, (int)ErrorMode::WARNING);
424 HHVM_RC_INT(E_PARSE, (int)ErrorMode::PARSE);
425 HHVM_RC_INT(E_NOTICE, (int)ErrorMode::NOTICE);
426 HHVM_RC_INT(E_CORE_ERROR, (int)ErrorMode::CORE_ERROR);
427 HHVM_RC_INT(E_CORE_WARNING, (int)ErrorMode::CORE_WARNING);
428 HHVM_RC_INT(E_COMPILE_ERROR, (int)ErrorMode::COMPILE_ERROR);
429 HHVM_RC_INT(E_COMPILE_WARNING, (int)ErrorMode::COMPILE_WARNING);
430 HHVM_RC_INT(E_USER_ERROR, (int)ErrorMode::USER_ERROR);
431 HHVM_RC_INT(E_USER_WARNING, (int)ErrorMode::USER_WARNING);
432 HHVM_RC_INT(E_USER_NOTICE, (int)ErrorMode::USER_NOTICE);
433 HHVM_RC_INT(E_STRICT, (int)ErrorMode::STRICT);
434 HHVM_RC_INT(E_RECOVERABLE_ERROR, (int)ErrorMode::RECOVERABLE_ERROR);
435 HHVM_RC_INT(E_DEPRECATED, (int)ErrorMode::PHP_DEPRECATED);
436 HHVM_RC_INT(E_USER_DEPRECATED, (int)ErrorMode::USER_DEPRECATED);
437 HHVM_RC_INT(E_ALL, (int)ErrorMode::PHP_ALL | (int)ErrorMode::STRICT);
439 HHVM_RC_INT(E_HHVM_FATAL_ERROR, (int)ErrorMode::FATAL_ERROR);
441 loadSystemlib("std_errorfunc");