blob: 5b80a84d08dbd6f80ee1ba1f47282c8d9fbff09d [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2015 Travis Geiselbrecht
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <assert.h>
#include <ctype.h>
#include <debug.h>
#include <lib/debuglog.h>
#include <lib/io.h>
#include <lib/persistent-debuglog.h>
#include <lib/zircon-internal/macros.h>
#include <platform.h>
#include <string.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <arch/ops.h>
#include <kernel/lockdep.h>
#include <kernel/thread.h>
#include <platform/debug.h>
#include <vm/vm.h>
namespace {
enum class SkipPersistedDebuglog { No = 0, Yes };
DECLARE_SINGLETON_SPINLOCK_WITH_TYPE(dputc_spin_lock, MonitoredSpinLock);
} // namespace
void serial_write(ktl::string_view str) {
Guard<MonitoredSpinLock, IrqSave> guard{dputc_spin_lock::Get(), SOURCE_TAG};
// Write out the serial port.
platform_dputs_irq(str.data(), str.size());
}
static void stdout_write(ktl::string_view str, SkipPersistedDebuglog skip_pdlog) {
if (skip_pdlog == SkipPersistedDebuglog::No) {
persistent_dlog_write(str);
}
if (dlog_bypass() == false) {
if (dlog_write(DEBUGLOG_INFO, 0, str) == ZX_OK)
return;
}
serial_write(str);
}
static void stdout_write_buffered(ktl::string_view str, SkipPersistedDebuglog skip_pdlog) {
Thread* t = Thread::Current::Get();
if (unlikely(t == nullptr)) {
stdout_write(str, skip_pdlog);
return;
}
// Look for corruption and don't continue.
Thread::Linebuffer& lb = t->linebuffer();
if (unlikely(!is_kernel_address((uintptr_t)lb.buffer.data()) || lb.pos >= lb.buffer.size())) {
stdout_write("<linebuffer corruption>\n"sv, skip_pdlog);
return;
}
while (!str.empty()) {
size_t remaining = lb.buffer.size() - lb.pos;
auto substring = str.substr(0, remaining);
size_t newline_pos = substring.find_first_of('\n');
size_t size;
bool inject;
bool flush;
if (newline_pos != substring.npos) {
// A newline that fits in our remaining buffer.
size = newline_pos + 1;
inject = false;
flush = true;
} else if (substring.size() == remaining) {
// We fill the buffer, injecting a newline.
size = remaining - 1;
inject = true;
flush = true;
} else {
// We only add to the buffer.
size = substring.size();
inject = false;
flush = false;
}
memcpy(&lb.buffer[lb.pos], substring.data(), size);
str.remove_prefix(size);
lb.pos += size;
if (inject) {
lb.buffer[lb.pos] = '\n';
lb.pos += 1;
}
if (flush) {
stdout_write({lb.buffer.data(), lb.pos}, skip_pdlog);
lb.pos = 0;
}
}
}
// This is what printf calls. Really this could and should be const.
// But all the stdio function signatures require non-const `FILE*`.
FILE FILE::stdout_{[](void*, ktl::string_view str) {
stdout_write_buffered(str, SkipPersistedDebuglog::No);
return static_cast<int>(str.size());
},
nullptr};
FILE gSerialFile{[](void*, ktl::string_view str) {
serial_write(str);
return static_cast<int>(str.size());
},
nullptr};
FILE gStdoutUnbuffered{[](void*, ktl::string_view str) {
stdout_write(str, SkipPersistedDebuglog::No);
return static_cast<int>(str.size());
},
nullptr};
FILE gStdoutNoPersist{[](void*, ktl::string_view str) {
stdout_write_buffered(str, SkipPersistedDebuglog::Yes);
return static_cast<int>(str.size());
},
nullptr};