blob: e12fd850f156ceeca25a612130cdc1ec02a3a81e [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/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 {
DECLARE_SINGLETON_SPINLOCK_WITH_TYPE(dputc_spin_lock, MonitoredSpinLock);
DECLARE_SINGLETON_SPINLOCK_WITH_TYPE(print_spin_lock, MonitoredSpinLock);
static fbl::DoublyLinkedList<PrintCallback*> print_callbacks TA_GUARDED(print_spin_lock::Get());
} // 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());
}
void console_write(ktl::string_view str) {
// Print to any registered console loggers.
Guard<MonitoredSpinLock, IrqSave> guard{print_spin_lock::Get(), SOURCE_TAG};
for (PrintCallback& print_callback : print_callbacks) {
print_callback.Print(str);
}
}
static void stdout_write(ktl::string_view str) {
if (dlog_bypass() == false) {
if (dlog_write(DEBUGLOG_INFO, 0, str) == ZX_OK)
return;
}
console_write(str);
serial_write(str);
}
static void stdout_write_buffered(ktl::string_view str) {
Thread* t = Thread::Current::Get();
if (unlikely(t == nullptr)) {
stdout_write(str);
return;
}
t->linebuffer().Write(str);
}
void Linebuffer::Write(ktl::string_view str) {
// Look for corruption and don't continue.
if (unlikely(!is_kernel_address((uintptr_t)buffer_.data()) || pos_ >= buffer_.size())) {
stdout_write("<linebuffer corruption>\n"sv);
return;
}
while (!str.empty()) {
size_t remaining = buffer_.size() - 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(&buffer_[pos_], substring.data(), size);
str.remove_prefix(size);
pos_ += size;
if (inject) {
buffer_[pos_] = '\n';
pos_ += 1;
}
if (flush) {
stdout_write({buffer_.data(), pos_});
pos_ = 0;
}
}
}
void register_print_callback(PrintCallback* cb) {
Guard<MonitoredSpinLock, IrqSave> guard{print_spin_lock::Get(), SOURCE_TAG};
print_callbacks.push_front(cb);
}
void unregister_print_callback(PrintCallback* cb) {
Guard<MonitoredSpinLock, IrqSave> guard{print_spin_lock::Get(), SOURCE_TAG};
print_callbacks.erase(*cb);
}
// 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);
return static_cast<int>(str.size());
},
nullptr};
FILE gConsoleFile{[](void*, ktl::string_view str) {
console_write(str);
return static_cast<int>(str.size());
},
nullptr};
FILE gSerialFile{[](void*, ktl::string_view str) {
serial_write(str);
return static_cast<int>(str.size());
},
nullptr};