<commit_before>//
// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.Microsoft::WR::
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

// debug.cpp: Debugging utilities.

#include "common/winrtutils.h"

#include <wrl\implements.h>
#include <wrl\module.h>
#include <wrl\event.h>
#include <wrl\wrappers\corewrappers.h>
#include <windows.applicationmodel.core.h>
#include <windows.graphics.display.h>
#include <math.h>

#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE)
#include <windows.ui.xaml.media.dxinterop.h>
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE)

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Storage;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::ApplicationModel;
using namespace ABI::Windows::Graphics::Display;
using namespace ABI::Windows::UI::Core;

namespace winrt
{

static float GetLogicalDpi()
{
  ComPtr<IDisplayPropertiesStatics> dp;
  FLOAT dpi = 1.0;

  if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), dp.GetAddressOf()))) {
    if (SUCCEEDED(dp->get_LogicalDpi(&dpi))) {
      return dpi;
    }
  }
  return 1.0;
}

// Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels.
float convertDipsToPixels(float dips)
{
   static const float dipsPerInch = 96.0f;
   return floor(dips * GetLogicalDpi() / dipsPerInch + 0.5f); // Round to nearest integer.
}

bool isSwapChainBackgroundPanel(ComPtr<IUnknown> window)
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE)
    return FALSE;
#else
    ComPtr<ISwapChainBackgroundPanelNative> panelNative;
    return S_OK == (window.Get())->QueryInterface(IID_PPV_ARGS(&panelNative));
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE)
}

ComPtr<ICoreWindow> getCurrentWindowForThread()
{
    HRESULT result = S_OK;
    ComPtr<ICoreWindow> window;
    ComPtr<ICoreWindowStatic> staticWindow;
    result = GetActivationFactory(HStringReference(L"Windows.UI.Core.CoreWindow").Get(), staticWindow.GetAddressOf());
    if(SUCCEEDED(S_OK))
    {
        result = staticWindow->GetForCurrentThread(window.GetAddressOf());
    }
    if(SUCCEEDED(S_OK))
    {
        return window;
    }
    return nullptr;
}

HRESULT getWindowDimensions(ComPtr<ICoreWindow> window, int& width, int& height)
{
    width = 0;
    height = 0;
    Rect bounds;
    HRESULT result = window->get_Bounds(&bounds);

    if(SUCCEEDED(result))
    {
        width = static_cast<int>(convertDipsToPixels(bounds.Width));
        height = static_cast<int>(convertDipsToPixels(bounds.Height));
    }
    return result;
}

HRESULT getCurrentWindowDimensions(int& width, int& height)
{
    width = 0;
    height = 0;
    HRESULT result = -1;

    ComPtr<ICoreWindow> window = getCurrentWindowForThread();
    if(window.Get() != nullptr)
    {
        result = getWindowDimensions(window,width, height);
    }

    return result;
}

std::string getTemporaryFilePath()
{
    HString hstrAppData;
    hstrAppData.Set(RuntimeClass_Windows_ApplicationModel_Package);
    ComPtr<IActivationFactory> pActivationFactory;
    HRESULT hr = GetActivationFactory(hstrAppData.Get(), &pActivationFactory);
    std::string result = "";

    ComPtr<IPackageStatics> packageStatics;
    hr = pActivationFactory.As(&packageStatics);
    if(SUCCEEDED(hr))
    {
        ComPtr<IPackage> package;
        hr = packageStatics->get_Current(&package);
        if(SUCCEEDED(hr))
        {
            ComPtr<IStorageFolder> storageFolder;
            hr = package->get_InstalledLocation(&storageFolder);
            if(SUCCEEDED(hr))
            {
                ComPtr<IStorageItem> storageItem;
                hr = storageFolder.As(&storageItem);
                if(SUCCEEDED(hr))
                {
                    HSTRING hsPath;
                    storageItem->get_Path(&hsPath);
                    std::wstring t = std::wstring(WindowsGetStringRawBuffer(hsPath,0));
                    result = std::string(t.begin(),t.end());
                }
            }
        }
    }

    return result;
}

}
<commit_msg>fixed check for windows phone<commit_after>//
// Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved.Microsoft::WR::
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

// debug.cpp: Debugging utilities.

#include "common/winrtutils.h"

#include <wrl\implements.h>
#include <wrl\module.h>
#include <wrl\event.h>
#include <wrl\wrappers\corewrappers.h>
#include <windows.applicationmodel.core.h>
#include <windows.graphics.display.h>
#include <math.h>

#if defined(WINAPI_FAMILY)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
#define ANGLE_PLATFORM_WINRT
#endif // #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)

#if defined(WINAPI_PARTITION_PHONE) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE)
#define ANGLE_PLATFORM_WP8
#endif // #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE)
#endif // #if defined(WINAPI_FAMILY)

#if !defined(ANGLE_PLATFORM_WP8)
#include <windows.ui.xaml.media.dxinterop.h>
#endif // !defined(ANGLE_PLATFORM_WP8)

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Storage;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::ApplicationModel;
using namespace ABI::Windows::Graphics::Display;
using namespace ABI::Windows::UI::Core;

namespace winrt
{

static float GetLogicalDpi()
{
  ComPtr<IDisplayPropertiesStatics> dp;
  FLOAT dpi = 1.0;

  if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), dp.GetAddressOf()))) {
    if (SUCCEEDED(dp->get_LogicalDpi(&dpi))) {
      return dpi;
    }
  }
  return 1.0;
}

// Method to convert a length in device-independent pixels (DIPs) to a length in physical pixels.
float convertDipsToPixels(float dips)
{
   static const float dipsPerInch = 96.0f;
   return floor(dips * GetLogicalDpi() / dipsPerInch + 0.5f); // Round to nearest integer.
}

bool isSwapChainBackgroundPanel(ComPtr<IUnknown> window)
{
#if defined(ANGLE_PLATFORM_WP8)
    return FALSE;
#else
    ComPtr<ISwapChainBackgroundPanelNative> panelNative;
    return S_OK == (window.Get())->QueryInterface(IID_PPV_ARGS(&panelNative));
#endif // #if defined(ANGLE_PLATFORM_WP8)
}

ComPtr<ICoreWindow> getCurrentWindowForThread()
{
    HRESULT result = S_OK;
    ComPtr<ICoreWindow> window;
    ComPtr<ICoreWindowStatic> staticWindow;
    result = GetActivationFactory(HStringReference(L"Windows.UI.Core.CoreWindow").Get(), staticWindow.GetAddressOf());
    if(SUCCEEDED(S_OK))
    {
        result = staticWindow->GetForCurrentThread(window.GetAddressOf());
    }
    if(SUCCEEDED(S_OK))
    {
        return window;
    }
    return nullptr;
}

HRESULT getWindowDimensions(ComPtr<ICoreWindow> window, int& width, int& height)
{
    width = 0;
    height = 0;
    Rect bounds;
    HRESULT result = window->get_Bounds(&bounds);

    if(SUCCEEDED(result))
    {
        width = static_cast<int>(convertDipsToPixels(bounds.Width));
        height = static_cast<int>(convertDipsToPixels(bounds.Height));
    }
    return result;
}

HRESULT getCurrentWindowDimensions(int& width, int& height)
{
    width = 0;
    height = 0;
    HRESULT result = -1;

    ComPtr<ICoreWindow> window = getCurrentWindowForThread();
    if(window.Get() != nullptr)
    {
        result = getWindowDimensions(window,width, height);
    }

    return result;
}

std::string getTemporaryFilePath()
{
    HString hstrAppData;
    hstrAppData.Set(RuntimeClass_Windows_ApplicationModel_Package);
    ComPtr<IActivationFactory> pActivationFactory;
    HRESULT hr = GetActivationFactory(hstrAppData.Get(), &pActivationFactory);
    std::string result = "";

    ComPtr<IPackageStatics> packageStatics;
    hr = pActivationFactory.As(&packageStatics);
    if(SUCCEEDED(hr))
    {
        ComPtr<IPackage> package;
        hr = packageStatics->get_Current(&package);
        if(SUCCEEDED(hr))
        {
            ComPtr<IStorageFolder> storageFolder;
            hr = package->get_InstalledLocation(&storageFolder);
            if(SUCCEEDED(hr))
            {
                ComPtr<IStorageItem> storageItem;
                hr = storageFolder.As(&storageItem);
                if(SUCCEEDED(hr))
                {
                    HSTRING hsPath;
                    storageItem->get_Path(&hsPath);
                    std::wstring t = std::wstring(WindowsGetStringRawBuffer(hsPath,0));
                    result = std::string(t.begin(),t.end());
                }
            }
        }
    }

    return result;
}

}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Andrei Pangin
 *
 * Licensed 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 <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include "vmEntry.h"
#include "arguments.h"
#include "javaApi.h"
#include "os.h"
#include "profiler.h"
#include "instrument.h"
#include "lockTracer.h"
#include "log.h"
#include "vmStructs.h"

// JVM TI agent return codes
const int ARGUMENTS_ERROR = 100;
const int COMMAND_ERROR = 200;

static Arguments _agent_args;

JavaVM* VM::_vm;
jvmtiEnv* VM::_jvmti = NULL;
int VM::_hotspot_version = 0;
void* VM::_libjvm;
void* VM::_libjava;
AsyncGetCallTrace VM::_asyncGetCallTrace;
JVM_GetManagement VM::_getManagement;
jvmtiError (JNICALL *VM::_orig_RedefineClasses)(jvmtiEnv*, jint, const jvmtiClassDefinition*);
jvmtiError (JNICALL *VM::_orig_RetransformClasses)(jvmtiEnv*, jint, const jclass* classes);
jvmtiError (JNICALL *VM::_orig_GenerateEvents)(jvmtiEnv* jvmti, jvmtiEvent event_type);
volatile int VM::_in_redefine_classes = 0;

static void wakeupHandler(int signo) {
    // Dummy handler for interrupting syscalls
}

bool VM::init(JavaVM* vm, bool attach) {
    if (_jvmti != NULL) return true;

    _vm = vm;
    if (_vm->GetEnv((void**)&_jvmti, JVMTI_VERSION_1_0) != 0) {
        return false;
    }

    char* prop;
    if (_jvmti->GetSystemProperty("java.vm.name", &prop) == 0) {
        bool is_hotspot = strstr(prop, "OpenJDK") != NULL ||
                          strstr(prop, "HotSpot") != NULL ||
                          strstr(prop, "GraalVM") != NULL;
        _jvmti->Deallocate((unsigned char*)prop);

        if (is_hotspot && _jvmti->GetSystemProperty("java.vm.version", &prop) == 0) {
            if (strncmp(prop, "25.", 3) == 0) {
                _hotspot_version = 8;
            } else if (strncmp(prop, "24.", 3) == 0) {
                _hotspot_version = 7;
            } else if (strncmp(prop, "20.", 3) == 0) {
                _hotspot_version = 6;
            } else if ((_hotspot_version = atoi(prop)) < 9) {
                _hotspot_version = 9;
            }
            _jvmti->Deallocate((unsigned char*)prop);
        }
    }

    _libjvm = getLibraryHandle("libjvm.so");
    _asyncGetCallTrace = (AsyncGetCallTrace)dlsym(_libjvm, "AsyncGetCallTrace");
    _getManagement = (JVM_GetManagement)dlsym(_libjvm, "JVM_GetManagement");

    if (attach) {
        ready();
    }

    jvmtiCapabilities capabilities = {0};
    capabilities.can_generate_all_class_hook_events = 1;
    capabilities.can_retransform_classes = 1;
    capabilities.can_retransform_any_class = 1;
    capabilities.can_get_bytecodes = 1;
    capabilities.can_get_constant_pool = 1;
    capabilities.can_get_source_file_name = 1;
    capabilities.can_get_line_numbers = 1;
    capabilities.can_generate_compiled_method_load_events = 1;
    capabilities.can_generate_monitor_events = 1;
    capabilities.can_tag_objects = 1;
    _jvmti->AddCapabilities(&capabilities);

    jvmtiEventCallbacks callbacks = {0};
    callbacks.VMInit = VMInit;
    callbacks.VMDeath = VMDeath;
    callbacks.ClassLoad = ClassLoad;
    callbacks.ClassPrepare = ClassPrepare;
    callbacks.ClassFileLoadHook = Instrument::ClassFileLoadHook;
    callbacks.CompiledMethodLoad = Profiler::CompiledMethodLoad;
    callbacks.CompiledMethodUnload = Profiler::CompiledMethodUnload;
    callbacks.DynamicCodeGenerated = Profiler::DynamicCodeGenerated;
    callbacks.ThreadStart = Profiler::ThreadStart;
    callbacks.ThreadEnd = Profiler::ThreadEnd;
    callbacks.MonitorContendedEnter = LockTracer::MonitorContendedEnter;
    callbacks.MonitorContendedEntered = LockTracer::MonitorContendedEntered;
    _jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));

    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);

    if (attach) {
        loadAllMethodIDs(jvmti(), jni());
        DisableSweeper ds;
        _jvmti->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
        _jvmti->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
    }

    if (hotspot_version() > 0 && hotspot_version() < 11) {
        // Avoid GenerateEvents conflict with another agent
        JVMTIFunctions* functions = *(JVMTIFunctions**)_jvmti;
        _orig_GenerateEvents = functions->GenerateEvents;
        functions->GenerateEvents = GenerateEventsHook;
    }

    OS::installSignalHandler(WAKEUP_SIGNAL, NULL, wakeupHandler);

    return true;
}

// Run late initialization when JVM is ready
void VM::ready() {
    Profiler* profiler = Profiler::instance();
    profiler->updateSymbols(false);
    NativeCodeCache* libjvm = profiler->findNativeLibrary((const void*)_asyncGetCallTrace);
    if (libjvm != NULL) {
        JitWriteProtection jit(true);  // workaround for JDK-8262896
        VMStructs::init(libjvm);
    }

    profiler->setupTrapHandler();

    _libjava = getLibraryHandle("libjava.so");

    // Make sure we reload method IDs upon class retransformation
    JVMTIFunctions* functions = *(JVMTIFunctions**)_jvmti;
    _orig_RedefineClasses = functions->RedefineClasses;
    _orig_RetransformClasses = functions->RetransformClasses;
    functions->RedefineClasses = RedefineClassesHook;
    functions->RetransformClasses = RetransformClassesHook;
}

void* VM::getLibraryHandle(const char* name) {
    if (!OS::isJavaLibraryVisible()) {
        void* handle = dlopen(name, RTLD_LAZY);
        if (handle != NULL) {
            return handle;
        }
        Log::warn("Failed to load %s: %s", name, dlerror());
    }
    return RTLD_DEFAULT;
}

void VM::loadMethodIDs(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
    if (VMStructs::hasClassLoaderData()) {
        VMKlass* vmklass = VMKlass::fromJavaClass(jni, klass);
        int method_count = vmklass->methodCount();
        if (method_count > 0) {
            ClassLoaderData* cld = vmklass->classLoaderData();
            cld->lock();
            // Workaround for JVM bug: preallocate space for jmethodIDs
            // at the beginning of the list (rather than at the end)
            for (int i = 0; i < method_count; i += MethodList::SIZE) {
                *cld->methodList() = new MethodList(*cld->methodList());
            }
            cld->unlock();
        }
    }

    jint method_count;
    jmethodID* methods;
    if (jvmti->GetClassMethods(klass, &method_count, &methods) == 0) {
        jvmti->Deallocate((unsigned char*)methods);
    }
}

void VM::loadAllMethodIDs(jvmtiEnv* jvmti, JNIEnv* jni) {
    jint class_count;
    jclass* classes;
    if (jvmti->GetLoadedClasses(&class_count, &classes) == 0) {
        for (int i = 0; i < class_count; i++) {
            loadMethodIDs(jvmti, jni, classes[i]);
        }
        jvmti->Deallocate((unsigned char*)classes);
    }
}

void JNICALL VM::VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
    ready();
    loadAllMethodIDs(jvmti, jni);

    // Delayed start of profiler if agent has been loaded at VM bootstrap
    Error error = Profiler::instance()->run(_agent_args);
    if (error) {
        Log::error("%s", error.message());
    }
}

void JNICALL VM::VMDeath(jvmtiEnv* jvmti, JNIEnv* jni) {
    Profiler::instance()->shutdown(_agent_args);
}

jvmtiError VM::RedefineClassesHook(jvmtiEnv* jvmti, jint class_count, const jvmtiClassDefinition* class_definitions) {
    atomicInc(_in_redefine_classes);
    jvmtiError result = _orig_RedefineClasses(jvmti, class_count, class_definitions);

    if (result == 0) {
        // jmethodIDs are invalidated after RedefineClasses
        JNIEnv* env = jni();
        for (int i = 0; i < class_count; i++) {
            if (class_definitions[i].klass != NULL) {
                loadMethodIDs(jvmti, env, class_definitions[i].klass);
            }
        }
    }

    atomicInc(_in_redefine_classes, -1);
    return result;
}

jvmtiError VM::RetransformClassesHook(jvmtiEnv* jvmti, jint class_count, const jclass* classes) {
    atomicInc(_in_redefine_classes);
    jvmtiError result = _orig_RetransformClasses(jvmti, class_count, classes);

    if (result == 0) {
        // jmethodIDs are invalidated after RetransformClasses
        JNIEnv* env = jni();
        for (int i = 0; i < class_count; i++) {
            if (classes[i] != NULL) {
                loadMethodIDs(jvmti, env, classes[i]);
            }
        }
    }

    atomicInc(_in_redefine_classes, -1);
    return result;
}

jvmtiError VM::GenerateEventsHook(jvmtiEnv* jvmti, jvmtiEvent event_type) {
    if (event_type == JVMTI_EVENT_COMPILED_METHOD_LOAD) {
        // Workaround for JDK-8222072: prepare to receive events designated for another agent
        Log::warn("async-profiler conflicts with another agent calling GenerateEvents()");
        Profiler::instance()->resetJavaMethods();
    }
    return _orig_GenerateEvents(jvmti, event_type);
}

extern "C" JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    Error error = _agent_args.parse(options);
    Log::open(_agent_args._log);
    if (error) {
        Log::error("%s", error.message());
        return ARGUMENTS_ERROR;
    }

    if (!VM::init(vm, false)) {
        Log::error("JVM does not support Tool Interface");
        return COMMAND_ERROR;
    }

    return 0;
}

extern "C" JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
    Arguments args;
    Error error = args.parse(options);
    Log::open(args._log);
    if (error) {
        Log::error("%s", error.message());
        return ARGUMENTS_ERROR;
    }

    if (!VM::init(vm, true)) {
        Log::error("JVM does not support Tool Interface");
        return COMMAND_ERROR;
    }

    // Save the arguments in case of shutdown
    if (args._action == ACTION_START || args._action == ACTION_RESUME) {
        _agent_args.save(args);
    }

    error = Profiler::instance()->run(args);
    if (error) {
        Log::error("%s", error.message());
        return COMMAND_ERROR;
    }

    return 0;
}

extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved) {
    if (!VM::init(vm, true)) {
        return 0;
    }

    JavaAPI::registerNatives(VM::jvmti(), VM::jni());
    return JNI_VERSION_1_6;
}
<commit_msg>`DCEVM` is also hotspot (#457)<commit_after>/*
 * Copyright 2016 Andrei Pangin
 *
 * Licensed 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 <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include "vmEntry.h"
#include "arguments.h"
#include "javaApi.h"
#include "os.h"
#include "profiler.h"
#include "instrument.h"
#include "lockTracer.h"
#include "log.h"
#include "vmStructs.h"

// JVM TI agent return codes
const int ARGUMENTS_ERROR = 100;
const int COMMAND_ERROR = 200;

static Arguments _agent_args;

JavaVM* VM::_vm;
jvmtiEnv* VM::_jvmti = NULL;
int VM::_hotspot_version = 0;
void* VM::_libjvm;
void* VM::_libjava;
AsyncGetCallTrace VM::_asyncGetCallTrace;
JVM_GetManagement VM::_getManagement;
jvmtiError (JNICALL *VM::_orig_RedefineClasses)(jvmtiEnv*, jint, const jvmtiClassDefinition*);
jvmtiError (JNICALL *VM::_orig_RetransformClasses)(jvmtiEnv*, jint, const jclass* classes);
jvmtiError (JNICALL *VM::_orig_GenerateEvents)(jvmtiEnv* jvmti, jvmtiEvent event_type);
volatile int VM::_in_redefine_classes = 0;

static void wakeupHandler(int signo) {
    // Dummy handler for interrupting syscalls
}

bool VM::init(JavaVM* vm, bool attach) {
    if (_jvmti != NULL) return true;

    _vm = vm;
    if (_vm->GetEnv((void**)&_jvmti, JVMTI_VERSION_1_0) != 0) {
        return false;
    }

    char* prop;
    if (_jvmti->GetSystemProperty("java.vm.name", &prop) == 0) {
        bool is_hotspot = strstr(prop, "OpenJDK") != NULL ||
                          strstr(prop, "HotSpot") != NULL ||
                          strstr(prop, "GraalVM") != NULL ||
                          strstr(prop, "Dynamic Code Evolution") != NULL;
        _jvmti->Deallocate((unsigned char*)prop);

        if (is_hotspot && _jvmti->GetSystemProperty("java.vm.version", &prop) == 0) {
            if (strncmp(prop, "25.", 3) == 0) {
                _hotspot_version = 8;
            } else if (strncmp(prop, "24.", 3) == 0) {
                _hotspot_version = 7;
            } else if (strncmp(prop, "20.", 3) == 0) {
                _hotspot_version = 6;
            } else if ((_hotspot_version = atoi(prop)) < 9) {
                _hotspot_version = 9;
            }
            _jvmti->Deallocate((unsigned char*)prop);
        }
    }

    _libjvm = getLibraryHandle("libjvm.so");
    _asyncGetCallTrace = (AsyncGetCallTrace)dlsym(_libjvm, "AsyncGetCallTrace");
    _getManagement = (JVM_GetManagement)dlsym(_libjvm, "JVM_GetManagement");

    if (attach) {
        ready();
    }

    jvmtiCapabilities capabilities = {0};
    capabilities.can_generate_all_class_hook_events = 1;
    capabilities.can_retransform_classes = 1;
    capabilities.can_retransform_any_class = 1;
    capabilities.can_get_bytecodes = 1;
    capabilities.can_get_constant_pool = 1;
    capabilities.can_get_source_file_name = 1;
    capabilities.can_get_line_numbers = 1;
    capabilities.can_generate_compiled_method_load_events = 1;
    capabilities.can_generate_monitor_events = 1;
    capabilities.can_tag_objects = 1;
    _jvmti->AddCapabilities(&capabilities);

    jvmtiEventCallbacks callbacks = {0};
    callbacks.VMInit = VMInit;
    callbacks.VMDeath = VMDeath;
    callbacks.ClassLoad = ClassLoad;
    callbacks.ClassPrepare = ClassPrepare;
    callbacks.ClassFileLoadHook = Instrument::ClassFileLoadHook;
    callbacks.CompiledMethodLoad = Profiler::CompiledMethodLoad;
    callbacks.CompiledMethodUnload = Profiler::CompiledMethodUnload;
    callbacks.DynamicCodeGenerated = Profiler::DynamicCodeGenerated;
    callbacks.ThreadStart = Profiler::ThreadStart;
    callbacks.ThreadEnd = Profiler::ThreadEnd;
    callbacks.MonitorContendedEnter = LockTracer::MonitorContendedEnter;
    callbacks.MonitorContendedEntered = LockTracer::MonitorContendedEntered;
    _jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));

    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);

    if (attach) {
        loadAllMethodIDs(jvmti(), jni());
        DisableSweeper ds;
        _jvmti->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
        _jvmti->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
    }

    if (hotspot_version() > 0 && hotspot_version() < 11) {
        // Avoid GenerateEvents conflict with another agent
        JVMTIFunctions* functions = *(JVMTIFunctions**)_jvmti;
        _orig_GenerateEvents = functions->GenerateEvents;
        functions->GenerateEvents = GenerateEventsHook;
    }

    OS::installSignalHandler(WAKEUP_SIGNAL, NULL, wakeupHandler);

    return true;
}

// Run late initialization when JVM is ready
void VM::ready() {
    Profiler* profiler = Profiler::instance();
    profiler->updateSymbols(false);
    NativeCodeCache* libjvm = profiler->findNativeLibrary((const void*)_asyncGetCallTrace);
    if (libjvm != NULL) {
        JitWriteProtection jit(true);  // workaround for JDK-8262896
        VMStructs::init(libjvm);
    }

    profiler->setupTrapHandler();

    _libjava = getLibraryHandle("libjava.so");

    // Make sure we reload method IDs upon class retransformation
    JVMTIFunctions* functions = *(JVMTIFunctions**)_jvmti;
    _orig_RedefineClasses = functions->RedefineClasses;
    _orig_RetransformClasses = functions->RetransformClasses;
    functions->RedefineClasses = RedefineClassesHook;
    functions->RetransformClasses = RetransformClassesHook;
}

void* VM::getLibraryHandle(const char* name) {
    if (!OS::isJavaLibraryVisible()) {
        void* handle = dlopen(name, RTLD_LAZY);
        if (handle != NULL) {
            return handle;
        }
        Log::warn("Failed to load %s: %s", name, dlerror());
    }
    return RTLD_DEFAULT;
}

void VM::loadMethodIDs(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
    if (VMStructs::hasClassLoaderData()) {
        VMKlass* vmklass = VMKlass::fromJavaClass(jni, klass);
        int method_count = vmklass->methodCount();
        if (method_count > 0) {
            ClassLoaderData* cld = vmklass->classLoaderData();
            cld->lock();
            // Workaround for JVM bug: preallocate space for jmethodIDs
            // at the beginning of the list (rather than at the end)
            for (int i = 0; i < method_count; i += MethodList::SIZE) {
                *cld->methodList() = new MethodList(*cld->methodList());
            }
            cld->unlock();
        }
    }

    jint method_count;
    jmethodID* methods;
    if (jvmti->GetClassMethods(klass, &method_count, &methods) == 0) {
        jvmti->Deallocate((unsigned char*)methods);
    }
}

void VM::loadAllMethodIDs(jvmtiEnv* jvmti, JNIEnv* jni) {
    jint class_count;
    jclass* classes;
    if (jvmti->GetLoadedClasses(&class_count, &classes) == 0) {
        for (int i = 0; i < class_count; i++) {
            loadMethodIDs(jvmti, jni, classes[i]);
        }
        jvmti->Deallocate((unsigned char*)classes);
    }
}

void JNICALL VM::VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
    ready();
    loadAllMethodIDs(jvmti, jni);

    // Delayed start of profiler if agent has been loaded at VM bootstrap
    Error error = Profiler::instance()->run(_agent_args);
    if (error) {
        Log::error("%s", error.message());
    }
}

void JNICALL VM::VMDeath(jvmtiEnv* jvmti, JNIEnv* jni) {
    Profiler::instance()->shutdown(_agent_args);
}

jvmtiError VM::RedefineClassesHook(jvmtiEnv* jvmti, jint class_count, const jvmtiClassDefinition* class_definitions) {
    atomicInc(_in_redefine_classes);
    jvmtiError result = _orig_RedefineClasses(jvmti, class_count, class_definitions);

    if (result == 0) {
        // jmethodIDs are invalidated after RedefineClasses
        JNIEnv* env = jni();
        for (int i = 0; i < class_count; i++) {
            if (class_definitions[i].klass != NULL) {
                loadMethodIDs(jvmti, env, class_definitions[i].klass);
            }
        }
    }

    atomicInc(_in_redefine_classes, -1);
    return result;
}

jvmtiError VM::RetransformClassesHook(jvmtiEnv* jvmti, jint class_count, const jclass* classes) {
    atomicInc(_in_redefine_classes);
    jvmtiError result = _orig_RetransformClasses(jvmti, class_count, classes);

    if (result == 0) {
        // jmethodIDs are invalidated after RetransformClasses
        JNIEnv* env = jni();
        for (int i = 0; i < class_count; i++) {
            if (classes[i] != NULL) {
                loadMethodIDs(jvmti, env, classes[i]);
            }
        }
    }

    atomicInc(_in_redefine_classes, -1);
    return result;
}

jvmtiError VM::GenerateEventsHook(jvmtiEnv* jvmti, jvmtiEvent event_type) {
    if (event_type == JVMTI_EVENT_COMPILED_METHOD_LOAD) {
        // Workaround for JDK-8222072: prepare to receive events designated for another agent
        Log::warn("async-profiler conflicts with another agent calling GenerateEvents()");
        Profiler::instance()->resetJavaMethods();
    }
    return _orig_GenerateEvents(jvmti, event_type);
}

extern "C" JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    Error error = _agent_args.parse(options);
    Log::open(_agent_args._log);
    if (error) {
        Log::error("%s", error.message());
        return ARGUMENTS_ERROR;
    }

    if (!VM::init(vm, false)) {
        Log::error("JVM does not support Tool Interface");
        return COMMAND_ERROR;
    }

    return 0;
}

extern "C" JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
    Arguments args;
    Error error = args.parse(options);
    Log::open(args._log);
    if (error) {
        Log::error("%s", error.message());
        return ARGUMENTS_ERROR;
    }

    if (!VM::init(vm, true)) {
        Log::error("JVM does not support Tool Interface");
        return COMMAND_ERROR;
    }

    // Save the arguments in case of shutdown
    if (args._action == ACTION_START || args._action == ACTION_RESUME) {
        _agent_args.save(args);
    }

    error = Profiler::instance()->run(args);
    if (error) {
        Log::error("%s", error.message());
        return COMMAND_ERROR;
    }

    return 0;
}

extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved) {
    if (!VM::init(vm, true)) {
        return 0;
    }

    JavaAPI::registerNatives(VM::jvmti(), VM::jni());
    return JNI_VERSION_1_6;
}
<|endoftext|>


<commit_before>#include <assert.h>
#include "wpkt_call.h"
#include "code_builder.h"
#include "evalctx.h"
#include "eval.h"
#include "util.h"
#include "struct_writer.h"

extern CodeBuilder	*code_builder;
extern writepkt_map	writepkts_map;

using namespace std;

void WritePktCall::printExterns(TableGen* tg) const
{
	const string args_w2w[] = {"const uint64_t*", "uint64_t*"};
	const string args_c[] = {"const uint64_t*"};

	tg->printExternFunc(
		getFuncName(), "void", vector<string>(args_w2w, args_w2w+2));
	tg->printExternFunc(
		getCondFuncName(), "bool", vector<string>(args_c, args_c+1));
}

/**
 * need to generate protos for
 *	1. wpkt2wpkt param conversion
 *	2. conditional (if none exists, ignore it)
 */
void WritePktCall::genProto(void) const
{
	/* two args: params in, params out */
	code_builder->genProto(
		getFuncName(),
		NULL,
		llvm::Type::getInt64PtrTy(llvm::getGlobalContext()),
		llvm::Type::getInt64PtrTy(llvm::getGlobalContext()));

	/* one arg: just the params */
	code_builder->genProto(
		getCondFuncName(),
		llvm::Type::getInt1Ty(llvm::getGlobalContext()),
		llvm::Type::getInt64PtrTy(llvm::getGlobalContext()));
}

std::string WritePktCall::getCondFuncName(void) const
{
	return getFuncName() + "_cond";
}

void WritePktCall::genTableInstance(class TableGen* tg) const
{
	StructWriter	sw(tg->getOS());
	sw.write("w2w_params_f", getFuncName());
	sw.write("w2w_cond_f", getCondFuncName());
	sw.write("w2w_wpkt",  string("&wpkt_")+name->getName()+int_to_string(0));
}

void WritePktCall::genCodeWpkt2Wpkt(void) const
{
	llvm::Function			*f;
	llvm::BasicBlock		*entry_bb;
	llvm::IRBuilder<>		*builder;
	llvm::Function::arg_iterator	arg_it;
	llvm::AllocaInst		*pb_out_ptr;
	const ArgsList			*args_out;
	WritePkt			*call_pkt;
	unsigned int			pb_idx, arg_idx;
	EvalCtx				ectx(
		getParent()->getParent()->getVarScope());

	if (writepkts_map.count(name->getName()) == 0) {
		cerr << "NO WRITE PACKET BY NAME OF " <<
			name->getName() << endl;
		return;
	}
	call_pkt = writepkts_map[name->getName()];

	args_out = call_pkt->getArgs();
	f = code_builder->getModule()->getFunction(getFuncName());
	if (f == NULL) {
		cerr	<< "Could not find function prototype for "
			<< getFuncName() << endl;
		return;
	}

	entry_bb = llvm::BasicBlock::Create(llvm::getGlobalContext(), "entry", f);
	builder = code_builder->getBuilder();
	builder->SetInsertPoint(entry_bb);

	/* 1. extract and name arguments from argument array */
	getParent()->getParent()->genLoadArgs(f);

	/* 2. load up output parambuf */
	arg_it = f->arg_begin();
	arg_it++;
	pb_out_ptr = code_builder->createTmpI64Ptr();
	builder->CreateStore(arg_it, pb_out_ptr);

	/* 3. dump expressions into parambuf */
	pb_idx = 0;
	arg_idx = 0;
	for (	ExprList::const_iterator it = exprs->begin();
		it != exprs->end();
		it++, arg_idx++)
	{
		const Expr	*cur_expr = *it;
		Expr		*evaled_cexpr;
		int		elems_stored;

		evaled_cexpr = eval(ectx, cur_expr);
		elems_stored = code_builder->storeExprIntoParamBuf(
			args_out->get(arg_idx), evaled_cexpr,
			pb_out_ptr, pb_idx);
		delete evaled_cexpr;
		if (elems_stored <= 0) {
			assert (0 == 1 && "FAILED TO STORE");
			return;
		}
		pb_idx += elems_stored;
	}

	builder->CreateRetVoid();
}

void WritePktCall::genCodeCond(void) const
{
	llvm::Function			*f;
	llvm::BasicBlock		*bb_then, *bb_else, *entry_bb;
	llvm::IRBuilder<>		*builder;

	builder = code_builder->getBuilder();
	f = code_builder->getModule()->getFunction(getCondFuncName());

	entry_bb = llvm::BasicBlock::Create(
		llvm::getGlobalContext(), "entry", f);

	bb_then = llvm::BasicBlock::Create(
		llvm::getGlobalContext(), "wpktc_then", f);
	builder->SetInsertPoint(bb_then);
	builder->CreateRet(
		llvm::ConstantInt::get(
			llvm::getGlobalContext(), llvm::APInt(1, 1)));

	bb_else = llvm::BasicBlock::Create(
		llvm::getGlobalContext(), "wpktc_else", f);
	builder->SetInsertPoint(bb_else);
	builder->CreateRet(
		llvm::ConstantInt::get(
			llvm::getGlobalContext(), llvm::APInt(1,0)));

	WritePktStmt::genCodeHeader(f, entry_bb, bb_then, bb_else);
}

void WritePktCall::genCode(void) const
{
	genCodeCond();
	genCodeWpkt2Wpkt();
}

string WritePktCall::getFuncName(void) const
{
	return  "__wpktcall_"+parent->getParent()->getName() +
		"_b" + int_to_string(parent->getBlkNum()) +
		"_s" + int_to_string(stmt_num);
}

std::ostream& WritePktCall::print(std::ostream& out) const
{
	out << "(writepkt-call ";
	name->print(out);
	out << " ";
	exprs->print(out);
	out << ")";
	return out;
}

WritePktCall::WritePktCall(Id* in_name, ExprList* in_exprs)
: name(in_name), exprs(in_exprs)
{ }
<commit_msg>update wpkt_call to use code_builder param buf store<commit_after>#include <assert.h>
#include "wpkt_call.h"
#include "code_builder.h"
#include "evalctx.h"
#include "eval.h"
#include "util.h"
#include "struct_writer.h"

extern CodeBuilder	*code_builder;
extern writepkt_map	writepkts_map;

using namespace std;

void WritePktCall::printExterns(TableGen* tg) const
{
	const string args_w2w[] = {"const uint64_t*", "uint64_t*"};
	const string args_c[] = {"const uint64_t*"};

	tg->printExternFunc(
		getFuncName(), "void", vector<string>(args_w2w, args_w2w+2));
	tg->printExternFunc(
		getCondFuncName(), "bool", vector<string>(args_c, args_c+1));
}

/**
 * need to generate protos for
 *	1. wpkt2wpkt param conversion
 *	2. conditional (if none exists, ignore it)
 */
void WritePktCall::genProto(void) const
{
	/* two args: params in, params out */
	code_builder->genProto(
		getFuncName(),
		NULL,
		llvm::Type::getInt64PtrTy(llvm::getGlobalContext()),
		llvm::Type::getInt64PtrTy(llvm::getGlobalContext()));

	/* one arg: just the params */
	code_builder->genProto(
		getCondFuncName(),
		llvm::Type::getInt1Ty(llvm::getGlobalContext()),
		llvm::Type::getInt64PtrTy(llvm::getGlobalContext()));
}

std::string WritePktCall::getCondFuncName(void) const
{
	return getFuncName() + "_cond";
}

void WritePktCall::genTableInstance(class TableGen* tg) const
{
	StructWriter	sw(tg->getOS());
	sw.write("w2w_params_f", getFuncName());
	sw.write("w2w_cond_f", getCondFuncName());
	sw.write("w2w_wpkt",  string("&wpkt_")+name->getName()+int_to_string(0));
}

void WritePktCall::genCodeWpkt2Wpkt(void) const
{
	llvm::Function			*f;
	llvm::BasicBlock		*entry_bb;
	llvm::IRBuilder<>		*builder;
	llvm::Function::arg_iterator	arg_it;
	llvm::AllocaInst		*pb_out;
	const ArgsList			*args_out;
	WritePkt			*call_pkt;
	EvalCtx				ec(
		getParent()->getParent()->getVarScope());

	if (writepkts_map.count(name->getName()) == 0) {
		cerr << "NO WRITE PACKET BY NAME OF " <<
			name->getName() << endl;
		return;
	}
	call_pkt = writepkts_map[name->getName()];

	args_out = call_pkt->getArgs();
	f = code_builder->getModule()->getFunction(getFuncName());
	if (f == NULL) {
		cerr	<< "Could not find function prototype for "
			<< getFuncName() << endl;
		return;
	}

	entry_bb = llvm::BasicBlock::Create(llvm::getGlobalContext(), "entry", f);
	builder = code_builder->getBuilder();
	builder->SetInsertPoint(entry_bb);

	/* 1. extract and name arguments from argument array */
	getParent()->getParent()->genLoadArgs(f);

	/* 2. load up output parambuf */
	arg_it = f->arg_begin();
	arg_it++;
	pb_out = code_builder->createTmpI64Ptr();
	builder->CreateStore(arg_it, pb_out);

	/* 3. dump expressions into parambuf */
	code_builder->storeExprListIntoParamBuf(&ec, args_out, exprs, pb_out);

	builder->CreateRetVoid();
}

void WritePktCall::genCodeCond(void) const
{
	llvm::Function			*f;
	llvm::BasicBlock		*bb_then, *bb_else, *entry_bb;
	llvm::IRBuilder<>		*builder;

	builder = code_builder->getBuilder();
	f = code_builder->getModule()->getFunction(getCondFuncName());

	entry_bb = llvm::BasicBlock::Create(
		llvm::getGlobalContext(), "entry", f);

	bb_then = llvm::BasicBlock::Create(
		llvm::getGlobalContext(), "wpktc_then", f);
	builder->SetInsertPoint(bb_then);
	builder->CreateRet(
		llvm::ConstantInt::get(
			llvm::getGlobalContext(), llvm::APInt(1, 1)));

	bb_else = llvm::BasicBlock::Create(
		llvm::getGlobalContext(), "wpktc_else", f);
	builder->SetInsertPoint(bb_else);
	builder->CreateRet(
		llvm::ConstantInt::get(
			llvm::getGlobalContext(), llvm::APInt(1,0)));

	WritePktStmt::genCodeHeader(f, entry_bb, bb_then, bb_else);
}

void WritePktCall::genCode(void) const
{
	genCodeCond();
	genCodeWpkt2Wpkt();
}

string WritePktCall::getFuncName(void) const
{
	return  "__wpktcall_"+parent->getParent()->getName() +
		"_b" + int_to_string(parent->getBlkNum()) +
		"_s" + int_to_string(stmt_num);
}

std::ostream& WritePktCall::print(std::ostream& out) const
{
	return out <<
		"(writepkt-call " << name->print(out) << ' ' <<
		exprs->print(out) << ')';
}

WritePktCall::WritePktCall(Id* in_name, ExprList* in_exprs)
: name(in_name), exprs(in_exprs)
{ }
<|endoftext|>


<commit_before>// This file is part of Poseidon.
// Copyleft 2020, LH_Mouse. All wrongs reserved.

#include "../precompiled.hpp"
#include "config_file.hpp"
#include "../utils.hpp"
#include <asteria/library/system.hpp>

namespace poseidon {
namespace {

struct Implode
  {
    const char* const* psegs;
    size_t nsegs;

    constexpr
    Implode(const char* const* p, size_t n)
      noexcept
      : psegs(p), nsegs(n)
      { }
  };

tinyfmt&
operator<<(tinyfmt& fmt, const Implode& imp)
  {
    if(imp.nsegs == 0)
      return fmt;

    fmt << imp.psegs[0];
    for(size_t k = 1;  k < imp.nsegs;  ++k)
      fmt << '.' << imp.psegs[k];
    return fmt;
  }

}  // namespace

Config_File::
~Config_File()
  {
  }

Config_File&
Config_File::
reload(const cow_string& path)
  {
    this->m_root = ::asteria::std_system_conf_load_file(path);
    return *this;
  }

const ::asteria::Value&
Config_File::
get_value(const char* const* psegs, size_t nsegs)
  const
  {
    auto qobj = &(this->m_root);
    size_t icur = 0;
    if(icur == nsegs)
      POSEIDON_THROW("Empty path not valid");

    for(;;) {
      // Find the child denoted by `*sptr`.
      // Return null if no such child exists or if an explicit null
      // is found.
      auto qchild = qobj->ptr(::rocket::sref(psegs[icur]));
      if(!qchild) {
        POSEIDON_LOG_WARN(
            "Undefined value `$2`\n"
            "[in configuration file '$1']",
            this->m_abspath, Implode(psegs, nsegs));

        return ::asteria::null_value;
      }
      else if(qchild->is_null())
        return ::asteria::null_value;

      // Advance to the next segment.
      // If the end of `path` is reached, we are done.
      if(++icur == nsegs)
        return *qchild;

      // If more segments follow, the child must be an object.
      if(!qchild->is_object())
        POSEIDON_THROW(
            "Unexpected type of `$2` (expecting `object`, got `$3`)\n"
            "[in configuration file '$1']",
            this->m_abspath, Implode(psegs, nsegs),
            ::asteria::describe_type(qchild->type()));

      qobj = &(qchild->as_object());
    }
  }

opt<bool>
Config_File::
get_bool_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_boolean())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `boolean`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_boolean();
  }

opt<int64_t>
Config_File::
get_int64_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_integer())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `integer`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_integer();
  }

opt<double>
Config_File::
get_double_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_convertible_to_real())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `number`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.convert_to_real();
  }

opt<cow_string>
Config_File::
get_string_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_string())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `string`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_string();
  }

opt<::asteria::V_array>
Config_File::
get_array_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_array())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `array`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_array();
  }

opt<::asteria::V_object>
Config_File::
get_object_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_object())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `object`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_object();
  }

}  // namespace poseidon
<commit_msg>core/config_file: Don't treat value-not-found as a warning<commit_after>// This file is part of Poseidon.
// Copyleft 2020, LH_Mouse. All wrongs reserved.

#include "../precompiled.hpp"
#include "config_file.hpp"
#include "../utils.hpp"
#include <asteria/library/system.hpp>

namespace poseidon {
namespace {

struct Implode
  {
    const char* const* psegs;
    size_t nsegs;

    constexpr
    Implode(const char* const* p, size_t n)
      noexcept
      : psegs(p), nsegs(n)
      { }
  };

tinyfmt&
operator<<(tinyfmt& fmt, const Implode& imp)
  {
    if(imp.nsegs == 0)
      return fmt;

    fmt << imp.psegs[0];
    for(size_t k = 1;  k < imp.nsegs;  ++k)
      fmt << '.' << imp.psegs[k];
    return fmt;
  }

}  // namespace

Config_File::
~Config_File()
  {
  }

Config_File&
Config_File::
reload(const cow_string& path)
  {
    this->m_root = ::asteria::std_system_conf_load_file(path);
    return *this;
  }

const ::asteria::Value&
Config_File::
get_value(const char* const* psegs, size_t nsegs)
  const
  {
    auto qobj = &(this->m_root);
    size_t icur = 0;
    if(icur == nsegs)
      POSEIDON_THROW("Empty path not valid");

    for(;;) {
      // Find the child denoted by `*sptr`.
      // Return null if no such child exists or if an explicit null
      // is found.
      auto qchild = qobj->ptr(::rocket::sref(psegs[icur]));
      if(!qchild) {
        POSEIDON_LOG_DEBUG(
            "Undefined value `$2`\n"
            "[in configuration file '$1']",
            this->m_abspath, Implode(psegs, nsegs));

        return ::asteria::null_value;
      }
      else if(qchild->is_null())
        return ::asteria::null_value;

      // Advance to the next segment.
      // If the end of `path` is reached, we are done.
      if(++icur == nsegs)
        return *qchild;

      // If more segments follow, the child must be an object.
      if(!qchild->is_object())
        POSEIDON_THROW(
            "Unexpected type of `$2` (expecting `object`, got `$3`)\n"
            "[in configuration file '$1']",
            this->m_abspath, Implode(psegs, nsegs),
            ::asteria::describe_type(qchild->type()));

      qobj = &(qchild->as_object());
    }
  }

opt<bool>
Config_File::
get_bool_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_boolean())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `boolean`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_boolean();
  }

opt<int64_t>
Config_File::
get_int64_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_integer())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `integer`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_integer();
  }

opt<double>
Config_File::
get_double_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_convertible_to_real())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `number`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.convert_to_real();
  }

opt<cow_string>
Config_File::
get_string_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_string())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `string`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_string();
  }

opt<::asteria::V_array>
Config_File::
get_array_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_array())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `array`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_array();
  }

opt<::asteria::V_object>
Config_File::
get_object_opt(const char* const* psegs, size_t nsegs)
  const
  {
    const auto& value = this->get_value(psegs, nsegs);
    if(value.is_null())
      return nullopt;

    if(!value.is_object())
      POSEIDON_THROW(
          "Unexpected type of `$2` (expecting `object`, got `$3`)\n"
          "[in configuration file '$1']",
          this->m_abspath, Implode(psegs, nsegs),
          ::asteria::describe_type(value.type()));

    return value.as_object();
  }

}  // namespace poseidon
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed 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 "walltime.h"

#include <sys/time.h>

#include <cstdio>
#include <cstdint>
#include <cstring>
#include <ctime>

#include <atomic>
#include <chrono>
#include <limits>
#include <type_traits>

#include "arraysize.h"
#include "check.h"
#include "cycleclock.h"
#include "log.h"
#include "sysinfo.h"

namespace benchmark {
namespace walltime {

namespace {

#if defined(HAVE_STEADY_CLOCK)
template <bool HighResIsSteady = std::chrono::high_resolution_clock::is_steady>
struct ChooseSteadyClock {
    typedef std::chrono::high_resolution_clock type;
};

template <>
struct ChooseSteadyClock<false> {
    typedef std::chrono::steady_clock type;
};
#endif

struct ChooseClockType {
#if defined(HAVE_STEADY_CLOCK)
  typedef typename ChooseSteadyClock<>::type type;
#else
  typedef std::chrono::high_resolution_clock type;
#endif
};

class WallTimeImp
{
public:
  WallTime Now();

  static WallTimeImp& GetWallTimeImp() {
    static WallTimeImp imp;
#if __cplusplus >= 201103L
    static_assert(std::is_trivially_destructible<WallTimeImp>::value,
                  "WallTimeImp must be trivially destructible to prevent "
                  "issues with static destruction");
#endif
    return imp;
  }

private:
  WallTimeImp();
  // Helper routines to load/store a float from an AtomicWord. Required because
  // g++ < 4.7 doesn't support std::atomic<float> correctly. I cannot wait to
  // get rid of this horror show.
  void SetDrift(float f) {
    int32_t w;
    memcpy(&w, &f, sizeof(f));
    std::atomic_store(&drift_adjust_, w);
  }

  float GetDrift() const {
    float f;
    int32_t w = std::atomic_load(&drift_adjust_);
    memcpy(&f, &w, sizeof(f));
    return f;
  }

  WallTime Slow() const {
    struct timeval tv;
    gettimeofday(&tv, nullptr);
    return tv.tv_sec + tv.tv_usec * 1e-6;
  }

private:
  static_assert(sizeof(float) <= sizeof(int32_t),
               "type sizes don't allow the drift_adjust hack");

  static constexpr double kMaxErrorInterval = 100e-6;

  WallTime base_walltime_;
  int64_t base_cycletime_;
  int64_t cycles_per_second_;
  double seconds_per_cycle_;
  uint32_t last_adjust_time_;
  std::atomic<int32_t> drift_adjust_;
  int64_t max_interval_cycles_;

  BENCHMARK_DISALLOW_COPY_AND_ASSIGN(WallTimeImp);
};

WallTime WallTimeImp::Now() {
  WallTime now = 0.0;
  WallTime result = 0.0;
  int64_t ct = 0;
  uint32_t top_bits = 0;
  do {
    ct = cycleclock::Now();
    int64_t cycle_delta = ct - base_cycletime_;
    result = base_walltime_ + cycle_delta * seconds_per_cycle_;

    top_bits = static_cast<uint32_t>(uint64_t(ct) >> 32);
    // Recompute drift no more often than every 2^32 cycles.
    // I.e., @2GHz, ~ every two seconds
    if (top_bits == last_adjust_time_) {  // don't need to recompute drift
      return result + GetDrift();
    }

    now = Slow();
  } while (cycleclock::Now() - ct > max_interval_cycles_);
  // We are now sure that "now" and "result" were produced within
  // kMaxErrorInterval of one another.

  SetDrift(now - result);
  last_adjust_time_ = top_bits;
  return now;
}

WallTimeImp::WallTimeImp()
    : base_walltime_(0.0), base_cycletime_(0),
      cycles_per_second_(0), seconds_per_cycle_(0.0),
      last_adjust_time_(0), drift_adjust_(0),
      max_interval_cycles_(0) {
  cycles_per_second_ = static_cast<int64_t>(CyclesPerSecond());
  CHECK(cycles_per_second_ != 0);
  seconds_per_cycle_ = 1.0 / cycles_per_second_;
  max_interval_cycles_ =
      static_cast<int64_t>(cycles_per_second_ * kMaxErrorInterval);
  do {
    base_cycletime_ = cycleclock::Now();
    base_walltime_ = Slow();
  } while (cycleclock::Now() - base_cycletime_ > max_interval_cycles_);
  // We are now sure that "base_walltime" and "base_cycletime" were produced
  // within kMaxErrorInterval of one another.

  SetDrift(0.0);
  last_adjust_time_ = static_cast<uint32_t>(uint64_t(base_cycletime_) >> 32);
}

WallTime CPUWalltimeNow() {
  static WallTimeImp& imp = WallTimeImp::GetWallTimeImp();
  return imp.Now();
}

WallTime ChronoWalltimeNow() {
  typedef ChooseClockType::type Clock;
  typedef std::chrono::duration<WallTime, std::chrono::seconds::period>
          FPSeconds;
  static_assert(std::chrono::treat_as_floating_point<WallTime>::value,
                "This type must be treated as a floating point type.");
  auto now = Clock::now().time_since_epoch();
  return std::chrono::duration_cast<FPSeconds>(now).count();
}

bool UseCpuCycleClock() {
    bool useWallTime = !CpuScalingEnabled();
    if (useWallTime) {
        VLOG(1) << "Using the CPU cycle clock to provide walltime::Now().\n";
    } else {
        VLOG(1) << "Using std::chrono to provide walltime::Now().\n";
    }
    return useWallTime;
}

} // end anonymous namespace

// WallTimeImp doesn't work when CPU Scaling is enabled. If CPU Scaling is
// enabled at the start of the program then std::chrono::system_clock is used
// instead.
WallTime Now()
{
  static bool useCPUClock = UseCpuCycleClock();
  if (useCPUClock) {
    return CPUWalltimeNow();
  } else {
    return ChronoWalltimeNow();
  }
}

}  // end namespace walltime

namespace {

std::string DateTimeString(bool local) {
  typedef std::chrono::system_clock Clock;
  std::time_t now = Clock::to_time_t(Clock::now());
  char storage[128];

  std::tm timeinfo;
  std::memset(&timeinfo, 0, sizeof(std::tm));
  if (local) {
    localtime_r(&now, &timeinfo);
  } else {
    gmtime_r(&now, &timeinfo);
  }
  std::size_t written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo);
  CHECK(written < arraysize(storage));
  ((void)written); // prevent unused variable in optimized mode.
  return std::string(storage);
}

} // end namespace

std::string LocalDateTimeString() {
  return DateTimeString(true);
}

}  // end namespace benchmark
<commit_msg>Remove unnecessary `typename'.<commit_after>// Copyright 2015 Google Inc. All rights reserved.
//
// Licensed 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 "walltime.h"

#include <sys/time.h>

#include <cstdio>
#include <cstdint>
#include <cstring>
#include <ctime>

#include <atomic>
#include <chrono>
#include <limits>
#include <type_traits>

#include "arraysize.h"
#include "check.h"
#include "cycleclock.h"
#include "log.h"
#include "sysinfo.h"

namespace benchmark {
namespace walltime {

namespace {

#if defined(HAVE_STEADY_CLOCK)
template <bool HighResIsSteady = std::chrono::high_resolution_clock::is_steady>
struct ChooseSteadyClock {
    typedef std::chrono::high_resolution_clock type;
};

template <>
struct ChooseSteadyClock<false> {
    typedef std::chrono::steady_clock type;
};
#endif

struct ChooseClockType {
#if defined(HAVE_STEADY_CLOCK)
  typedef ChooseSteadyClock<>::type type;
#else
  typedef std::chrono::high_resolution_clock type;
#endif
};

class WallTimeImp
{
public:
  WallTime Now();

  static WallTimeImp& GetWallTimeImp() {
    static WallTimeImp imp;
#if __cplusplus >= 201103L
    static_assert(std::is_trivially_destructible<WallTimeImp>::value,
                  "WallTimeImp must be trivially destructible to prevent "
                  "issues with static destruction");
#endif
    return imp;
  }

private:
  WallTimeImp();
  // Helper routines to load/store a float from an AtomicWord. Required because
  // g++ < 4.7 doesn't support std::atomic<float> correctly. I cannot wait to
  // get rid of this horror show.
  void SetDrift(float f) {
    int32_t w;
    memcpy(&w, &f, sizeof(f));
    std::atomic_store(&drift_adjust_, w);
  }

  float GetDrift() const {
    float f;
    int32_t w = std::atomic_load(&drift_adjust_);
    memcpy(&f, &w, sizeof(f));
    return f;
  }

  WallTime Slow() const {
    struct timeval tv;
    gettimeofday(&tv, nullptr);
    return tv.tv_sec + tv.tv_usec * 1e-6;
  }

private:
  static_assert(sizeof(float) <= sizeof(int32_t),
               "type sizes don't allow the drift_adjust hack");

  static constexpr double kMaxErrorInterval = 100e-6;

  WallTime base_walltime_;
  int64_t base_cycletime_;
  int64_t cycles_per_second_;
  double seconds_per_cycle_;
  uint32_t last_adjust_time_;
  std::atomic<int32_t> drift_adjust_;
  int64_t max_interval_cycles_;

  BENCHMARK_DISALLOW_COPY_AND_ASSIGN(WallTimeImp);
};

WallTime WallTimeImp::Now() {
  WallTime now = 0.0;
  WallTime result = 0.0;
  int64_t ct = 0;
  uint32_t top_bits = 0;
  do {
    ct = cycleclock::Now();
    int64_t cycle_delta = ct - base_cycletime_;
    result = base_walltime_ + cycle_delta * seconds_per_cycle_;

    top_bits = static_cast<uint32_t>(uint64_t(ct) >> 32);
    // Recompute drift no more often than every 2^32 cycles.
    // I.e., @2GHz, ~ every two seconds
    if (top_bits == last_adjust_time_) {  // don't need to recompute drift
      return result + GetDrift();
    }

    now = Slow();
  } while (cycleclock::Now() - ct > max_interval_cycles_);
  // We are now sure that "now" and "result" were produced within
  // kMaxErrorInterval of one another.

  SetDrift(now - result);
  last_adjust_time_ = top_bits;
  return now;
}

WallTimeImp::WallTimeImp()
    : base_walltime_(0.0), base_cycletime_(0),
      cycles_per_second_(0), seconds_per_cycle_(0.0),
      last_adjust_time_(0), drift_adjust_(0),
      max_interval_cycles_(0) {
  cycles_per_second_ = static_cast<int64_t>(CyclesPerSecond());
  CHECK(cycles_per_second_ != 0);
  seconds_per_cycle_ = 1.0 / cycles_per_second_;
  max_interval_cycles_ =
      static_cast<int64_t>(cycles_per_second_ * kMaxErrorInterval);
  do {
    base_cycletime_ = cycleclock::Now();
    base_walltime_ = Slow();
  } while (cycleclock::Now() - base_cycletime_ > max_interval_cycles_);
  // We are now sure that "base_walltime" and "base_cycletime" were produced
  // within kMaxErrorInterval of one another.

  SetDrift(0.0);
  last_adjust_time_ = static_cast<uint32_t>(uint64_t(base_cycletime_) >> 32);
}

WallTime CPUWalltimeNow() {
  static WallTimeImp& imp = WallTimeImp::GetWallTimeImp();
  return imp.Now();
}

WallTime ChronoWalltimeNow() {
  typedef ChooseClockType::type Clock;
  typedef std::chrono::duration<WallTime, std::chrono::seconds::period>
          FPSeconds;
  static_assert(std::chrono::treat_as_floating_point<WallTime>::value,
                "This type must be treated as a floating point type.");
  auto now = Clock::now().time_since_epoch();
  return std::chrono::duration_cast<FPSeconds>(now).count();
}

bool UseCpuCycleClock() {
    bool useWallTime = !CpuScalingEnabled();
    if (useWallTime) {
        VLOG(1) << "Using the CPU cycle clock to provide walltime::Now().\n";
    } else {
        VLOG(1) << "Using std::chrono to provide walltime::Now().\n";
    }
    return useWallTime;
}

} // end anonymous namespace

// WallTimeImp doesn't work when CPU Scaling is enabled. If CPU Scaling is
// enabled at the start of the program then std::chrono::system_clock is used
// instead.
WallTime Now()
{
  static bool useCPUClock = UseCpuCycleClock();
  if (useCPUClock) {
    return CPUWalltimeNow();
  } else {
    return ChronoWalltimeNow();
  }
}

}  // end namespace walltime

namespace {

std::string DateTimeString(bool local) {
  typedef std::chrono::system_clock Clock;
  std::time_t now = Clock::to_time_t(Clock::now());
  char storage[128];

  std::tm timeinfo;
  std::memset(&timeinfo, 0, sizeof(std::tm));
  if (local) {
    localtime_r(&now, &timeinfo);
  } else {
    gmtime_r(&now, &timeinfo);
  }
  std::size_t written = std::strftime(storage, sizeof(storage), "%F %T", &timeinfo);
  CHECK(written < arraysize(storage));
  ((void)written); // prevent unused variable in optimized mode.
  return std::string(storage);
}

} // end namespace

std::string LocalDateTimeString() {
  return DateTimeString(true);
}

}  // end namespace benchmark
<|endoftext|>


<commit_before>/* Copyright (c) 2016, 2017, 2018 Dennis Wölfing
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* kernel/src/initrd.cpp
 * Initial RAM disk.
 */

#include <libgen.h>
#include <stdlib.h>
#include <string.h>
#include <tar.h>
#include <dennix/kernel/file.h>
#include <dennix/kernel/initrd.h>
#include <dennix/kernel/log.h>
#include <dennix/kernel/symlink.h>

struct TarHeader {
    char name[100];
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];
    char mtime[12];
    char checksum[8];
    char typeflag;
    char linkname[100];
    char magic[6];
    char version[2];
    char uname[32];
    char gname[32];
    char devmajor[8];
    char devminor[8];
    char prefix[155];
    char padding[12];
};

Reference<DirectoryVnode> Initrd::loadInitrd(vaddr_t initrd) {
    Reference<DirectoryVnode> root = new DirectoryVnode(nullptr, 0755, 0);
    TarHeader* header = (TarHeader*) initrd;

    while (strcmp(header->magic, TMAGIC) == 0) {
        size_t size = (size_t) strtoul(header->size, nullptr, 8);
        char* path;

        if (header->prefix[0]) {
            path = (char*) malloc(strlen(header->name) +
                    strlen(header->prefix) + 2);

            stpcpy(stpcpy(stpcpy(path, header->prefix), "/"), header->name);
        } else {
            path = strdup(header->name);
        }

        char* path2 = strdup(path);
        char* dirName = dirname(path);
        char* fileName = basename(path2);

        Reference<DirectoryVnode> directory =
                (Reference<DirectoryVnode>) resolvePath(root, dirName);

        if (!directory) {
            Log::printf("Could not add '%s' to nonexistent directory '%s'.\n",
                    fileName, dirName);
            return root;
        }

        Reference<Vnode> newFile;
        mode_t mode = (mode_t) strtol(header->mode, nullptr, 8);

        if (header->typeflag == REGTYPE || header->typeflag == AREGTYPE) {
            newFile = new FileVnode(header + 1, size, mode,
                    directory->stats.st_dev);
            header += 1 + ALIGNUP(size, 512) / 512;
        } else if (header->typeflag == DIRTYPE) {
            newFile = new DirectoryVnode(directory, mode,
                    directory->stats.st_dev);
            header++;
        } else if (header->typeflag == SYMTYPE) {
            newFile = new SymlinkVnode(header->linkname,
                    sizeof(header->linkname), directory->stats.st_dev);
            header++;
        } else {
            Log::printf("Unknown typeflag '%c'\n", header->typeflag);
            return root;
        }

        directory->link(fileName, newFile);
        free(path);
        free(path2);
    }

    return root;
}
<commit_msg>Load last modification time from the initrd.<commit_after>/* Copyright (c) 2016, 2017, 2018 Dennis Wölfing
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* kernel/src/initrd.cpp
 * Initial RAM disk.
 */

#include <libgen.h>
#include <stdlib.h>
#include <string.h>
#include <tar.h>
#include <dennix/kernel/file.h>
#include <dennix/kernel/initrd.h>
#include <dennix/kernel/log.h>
#include <dennix/kernel/symlink.h>

struct TarHeader {
    char name[100];
    char mode[8];
    char uid[8];
    char gid[8];
    char size[12];
    char mtime[12];
    char checksum[8];
    char typeflag;
    char linkname[100];
    char magic[6];
    char version[2];
    char uname[32];
    char gname[32];
    char devmajor[8];
    char devminor[8];
    char prefix[155];
    char padding[12];
};

Reference<DirectoryVnode> Initrd::loadInitrd(vaddr_t initrd) {
    Reference<DirectoryVnode> root = new DirectoryVnode(nullptr, 0755, 0);
    TarHeader* header = (TarHeader*) initrd;

    while (strcmp(header->magic, TMAGIC) == 0) {
        char* path;
        if (header->prefix[0]) {
            path = (char*) malloc(strlen(header->name) +
                    strlen(header->prefix) + 2);

            stpcpy(stpcpy(stpcpy(path, header->prefix), "/"), header->name);
        } else {
            path = strdup(header->name);
        }

        char* path2 = strdup(path);
        char* dirName = dirname(path);
        char* fileName = basename(path2);

        Reference<DirectoryVnode> directory =
                (Reference<DirectoryVnode>) resolvePath(root, dirName);

        if (!directory) {
            Log::printf("Could not add '%s' to nonexistent directory '%s'.\n",
                    fileName, dirName);
            return root;
        }

        Reference<Vnode> newFile;
        mode_t mode = (mode_t) strtol(header->mode, nullptr, 8);
        size_t size = (size_t) strtoul(header->size, nullptr, 8);
        struct timespec mtime;
        mtime.tv_sec = (time_t) strtoll(header->mtime, nullptr, 8);
        mtime.tv_nsec = 0;

        if (header->typeflag == REGTYPE || header->typeflag == AREGTYPE) {
            newFile = new FileVnode(header + 1, size, mode,
                    directory->stats.st_dev);
            header += 1 + ALIGNUP(size, 512) / 512;
        } else if (header->typeflag == DIRTYPE) {
            newFile = new DirectoryVnode(directory, mode,
                    directory->stats.st_dev);
            header++;
        } else if (header->typeflag == SYMTYPE) {
            newFile = new SymlinkVnode(header->linkname,
                    sizeof(header->linkname), directory->stats.st_dev);
            header++;
        } else {
            Log::printf("Unknown typeflag '%c'\n", header->typeflag);
            return root;
        }

        newFile->stats.st_atim = mtime;
        newFile->stats.st_mtim = mtime;

        directory->link(fileName, newFile);
        free(path);
        free(path2);
    }

    return root;
}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
//  imgui-d3d11.cc
//  Dear ImGui integration sample with D3D11 backend.
//------------------------------------------------------------------------------
#include "d3d11entry.h"
#define SOKOL_IMPL
#define SOKOL_D3D11
#define SOKOL_LOG(s) OutputDebugStringA(s)
#include "sokol_gfx.h"
#include "sokol_time.h"
#include "imgui.h"

static const int Width = 1024;
static const int Height = 768;
static const int MaxVertices = (1<<16);
static const int MaxIndices = MaxVertices * 3;

static uint64_t last_time = 0;
static bool show_test_window = true;
static bool show_another_window = false;

static sg_pipeline pip;
static sg_bindings bind;
static sg_pass_action pass_action;

typedef struct {
    ImVec2 disp_size;
} vs_params_t;

static void draw_imgui(ImDrawData*);

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
    // setup d3d11 app wrapper, sokol_gfx, sokol_time
    d3d11_init(Width, Height, 1, L"Sokol Dear ImGui D3D11");
    sg_desc desc = { };
    desc.d3d11_device = d3d11_device();
    desc.d3d11_device_context = d3d11_device_context();
    desc.d3d11_render_target_view_cb = d3d11_render_target_view;
    desc.d3d11_depth_stencil_view_cb = d3d11_depth_stencil_view;
    sg_setup(&desc);
    stm_setup();

    // input forwarding
    d3d11_mouse_pos([] (float x, float y)   { ImGui::GetIO().MousePos = ImVec2(x, y); });
    d3d11_mouse_btn_down([] (int btn)       { ImGui::GetIO().MouseDown[btn] = true; });
    d3d11_mouse_btn_up([] (int btn)         { ImGui::GetIO().MouseDown[btn] = false; });
    d3d11_mouse_wheel([](float v)           { ImGui::GetIO().MouseWheel = v; });
    d3d11_char([] (wchar_t c)               { ImGui::GetIO().AddInputCharacter(c); });
    d3d11_key_down([] (int key)             { if (key < 512) ImGui::GetIO().KeysDown[key] = true; });
    d3d11_key_up([] (int key)               { if (key < 512) ImGui::GetIO().KeysDown[key] = false; });

    // setup Dear Imgui
    ImGui::CreateContext();
    ImGui::StyleColorsDark();
    ImGuiIO& io = ImGui::GetIO();
    io.IniFilename = nullptr;
    io.Fonts->AddFontDefault();
    io.KeyMap[ImGuiKey_Tab] = VK_TAB;
    io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
    io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
    io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
    io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
    io.KeyMap[ImGuiKey_Home] = VK_HOME;
    io.KeyMap[ImGuiKey_End] = VK_END;
    io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
    io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
    io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
    io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
    io.KeyMap[ImGuiKey_A] = 'A';
    io.KeyMap[ImGuiKey_C] = 'C';
    io.KeyMap[ImGuiKey_V] = 'V';
    io.KeyMap[ImGuiKey_X] = 'X';
    io.KeyMap[ImGuiKey_Y] = 'Y';
    io.KeyMap[ImGuiKey_Z] = 'Z';

    // dynamic vertex- and index-buffers for imgui-generated geometry
    sg_buffer_desc vbuf_desc = { };
    vbuf_desc.usage = SG_USAGE_STREAM;
    vbuf_desc.size = MaxVertices * sizeof(ImDrawVert);
    bind.vertex_buffers[0] = sg_make_buffer(&vbuf_desc);

    sg_buffer_desc ibuf_desc = { };
    ibuf_desc.type = SG_BUFFERTYPE_INDEXBUFFER;
    ibuf_desc.usage = SG_USAGE_STREAM;
    ibuf_desc.size = MaxIndices * sizeof(ImDrawIdx);
    bind.index_buffer = sg_make_buffer(&ibuf_desc);

    // font texture for imgui's default font
    unsigned char* font_pixels;
    int font_width, font_height;
    io.Fonts->GetTexDataAsRGBA32(&font_pixels, &font_width, &font_height);
    sg_image_desc img_desc = { };
    img_desc.width = font_width;
    img_desc.height = font_height;
    img_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
    img_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE;
    img_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
    img_desc.content.subimage[0][0].ptr = font_pixels;
    img_desc.content.subimage[0][0].size = font_width * font_height * 4;
    bind.fs_images[0] = sg_make_image(&img_desc);

    // shader object for imgui rendering
    sg_shader_desc shd_desc = { };
    auto& ub = shd_desc.vs.uniform_blocks[0];
    ub.size = sizeof(vs_params_t);
    shd_desc.attrs[0].sem_name = "POSITION";
    shd_desc.attrs[1].sem_name = "TEXCOORD";
    shd_desc.attrs[2].sem_name = "COLOR";
    shd_desc.vs.source =
        "cbuffer params {\n"
        "  float2 disp_size;\n"
        "};\n"
        "struct vs_in {\n"
        "  float2 pos: POSITION;\n"
        "  float2 uv: TEXCOORD0;\n"
        "  float4 color: COLOR0;\n"
        "};\n"
        "struct vs_out {\n"
        "  float2 uv: TEXCOORD0;\n"
        "  float4 color: COLOR0;\n"
        "  float4 pos: SV_Position;\n"
        "};\n"
        "vs_out main(vs_in inp) {\n"
        "  vs_out outp;\n"
        "  outp.pos = float4(((inp.pos/disp_size)-0.5)*float2(2.0,-2.0), 0.5, 1.0);\n"
        "  outp.uv = inp.uv;\n"
        "  outp.color = inp.color;\n"
        "  return outp;\n"
        "}\n";
    shd_desc.fs.images[0].type = SG_IMAGETYPE_2D;
    shd_desc.fs.source =
        "Texture2D<float4> tex: register(t0);\n"
        "sampler smp: register(s0);\n"
        "float4 main(float2 uv: TEXCOORD0, float4 color: COLOR0): SV_Target0 {\n"
        "  return tex.Sample(smp, uv) * color;\n"
        "}\n";
    sg_shader shd = sg_make_shader(&shd_desc);

    // pipeline object for imgui rendering
    sg_pipeline_desc pip_desc = { };
    pip_desc.layout.buffers[0].stride = sizeof(ImDrawVert);
    auto& attrs = pip_desc.layout.attrs;
    attrs[0].offset=offsetof(ImDrawVert, pos); attrs[0].format=SG_VERTEXFORMAT_FLOAT2;
    attrs[1].offset=offsetof(ImDrawVert, uv); attrs[1].format=SG_VERTEXFORMAT_FLOAT2;
    attrs[2].offset=offsetof(ImDrawVert, col); attrs[2].format=SG_VERTEXFORMAT_UBYTE4N;
    pip_desc.shader = shd;
    pip_desc.index_type = SG_INDEXTYPE_UINT16;
    pip_desc.blend.enabled = true;
    pip_desc.blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
    pip_desc.blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
    pip_desc.blend.color_write_mask = SG_COLORMASK_RGB;
    pip = sg_make_pipeline(&pip_desc);

    // initial clear color
    pass_action.colors[0].action = SG_ACTION_CLEAR;
    pass_action.colors[0].val[0] = 0.0f;
    pass_action.colors[0].val[1] = 0.5f;
    pass_action.colors[0].val[2] = 0.7f;
    pass_action.colors[0].val[3] = 1.0f;

    // draw loop
    while (d3d11_process_events()) {
        const int cur_width = d3d11_width();
        const int cur_height = d3d11_height();

        // this is standard ImGui demo code
        ImGuiIO& io = ImGui::GetIO();
        io.DisplaySize = ImVec2(float(cur_width), float(cur_height));
        io.DeltaTime = (float) stm_sec(stm_laptime(&last_time));
        ImGui::NewFrame();

        // 1. Show a simple window
        // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets appears in a window automatically called "Debug"
        static float f = 0.0f;
        ImGui::Text("Hello, world!");
        ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
        ImGui::ColorEdit3("clear color", &pass_action.colors[0].val[0]);
        if (ImGui::Button("Test Window")) show_test_window ^= 1;
        if (ImGui::Button("Another Window")) show_another_window ^= 1;
        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);

        // 2. Show another simple window, this time using an explicit Begin/End pair
        if (show_another_window) {
            ImGui::SetNextWindowSize(ImVec2(200,100), ImGuiSetCond_FirstUseEver);
            ImGui::Begin("Another Window", &show_another_window);
            ImGui::Text("Hello");
            ImGui::End();
        }

        // 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowTestWindow()
        if (show_test_window) {
            ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiSetCond_FirstUseEver);
            ImGui::ShowTestWindow();
        }

        // the sokol_gfx draw pass
        sg_begin_default_pass(&pass_action, cur_width, cur_height);
        ImGui::Render();
        draw_imgui(ImGui::GetDrawData());
        sg_end_pass();
        sg_commit();
        d3d11_present();
    }
    ImGui::DestroyContext();
    sg_shutdown();
    d3d11_shutdown();
}

// render ImGui draw lists through sokol-gfx
void draw_imgui(ImDrawData* draw_data) {
    assert(draw_data);
    if (draw_data->CmdListsCount == 0) {
        return;
    }

    // render the command list
    vs_params_t vs_params;
    vs_params.disp_size.x = ImGui::GetIO().DisplaySize.x;
    vs_params.disp_size.y = ImGui::GetIO().DisplaySize.y;
    sg_apply_pipeline(pip);
    sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &vs_params, sizeof(vs_params));
    for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++) {
        const ImDrawList* cl = draw_data->CmdLists[cl_index];

        // append vertices and indices to buffers, record start offsets in bindings struct
        const int vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
        const int idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
        const int vb_offset = sg_append_buffer(bind.vertex_buffers[0], &cl->VtxBuffer.front(), vtx_size);
        const int ib_offset = sg_append_buffer(bind.index_buffer, &cl->IdxBuffer.front(), idx_size);
        /* don't render anything if the buffer is in overflow state (this is also
            checked internally in sokol_gfx, draw calls that attempt from
            overflowed buffers will be silently dropped)
        */
        if (sg_query_buffer_overflow(bind.vertex_buffers[0]) ||
            sg_query_buffer_overflow(bind.index_buffer))
        {
            continue;
        }

        bind.vertex_buffer_offsets[0] = vb_offset;
        bind.index_buffer_offset = ib_offset;
        sg_apply_bindings(&bind);

        int base_element = 0;
        for (const ImDrawCmd& pcmd : cl->CmdBuffer) {
            if (pcmd.UserCallback) {
                pcmd.UserCallback(cl, &pcmd);
            }
            else {
                const int scissor_x = (int) (pcmd.ClipRect.x);
                const int scissor_y = (int) (pcmd.ClipRect.y);
                const int scissor_w = (int) (pcmd.ClipRect.z - pcmd.ClipRect.x);
                const int scissor_h = (int) (pcmd.ClipRect.w - pcmd.ClipRect.y);
                sg_apply_scissor_rect(scissor_x, scissor_y, scissor_w, scissor_h, true);
                sg_draw(base_element, pcmd.ElemCount, 1);
            }
            base_element += pcmd.ElemCount;
        }
    }
}
<commit_msg>d3d11 samples: ImGuiSetCond -> ImGuiCond<commit_after>//------------------------------------------------------------------------------
//  imgui-d3d11.cc
//  Dear ImGui integration sample with D3D11 backend.
//------------------------------------------------------------------------------
#include "d3d11entry.h"
#define SOKOL_IMPL
#define SOKOL_D3D11
#define SOKOL_LOG(s) OutputDebugStringA(s)
#include "sokol_gfx.h"
#include "sokol_time.h"
#include "imgui.h"

static const int Width = 1024;
static const int Height = 768;
static const int MaxVertices = (1<<16);
static const int MaxIndices = MaxVertices * 3;

static uint64_t last_time = 0;
static bool show_test_window = true;
static bool show_another_window = false;

static sg_pipeline pip;
static sg_bindings bind;
static sg_pass_action pass_action;

typedef struct {
    ImVec2 disp_size;
} vs_params_t;

static void draw_imgui(ImDrawData*);

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
    // setup d3d11 app wrapper, sokol_gfx, sokol_time
    d3d11_init(Width, Height, 1, L"Sokol Dear ImGui D3D11");
    sg_desc desc = { };
    desc.d3d11_device = d3d11_device();
    desc.d3d11_device_context = d3d11_device_context();
    desc.d3d11_render_target_view_cb = d3d11_render_target_view;
    desc.d3d11_depth_stencil_view_cb = d3d11_depth_stencil_view;
    sg_setup(&desc);
    stm_setup();

    // input forwarding
    d3d11_mouse_pos([] (float x, float y)   { ImGui::GetIO().MousePos = ImVec2(x, y); });
    d3d11_mouse_btn_down([] (int btn)       { ImGui::GetIO().MouseDown[btn] = true; });
    d3d11_mouse_btn_up([] (int btn)         { ImGui::GetIO().MouseDown[btn] = false; });
    d3d11_mouse_wheel([](float v)           { ImGui::GetIO().MouseWheel = v; });
    d3d11_char([] (wchar_t c)               { ImGui::GetIO().AddInputCharacter(c); });
    d3d11_key_down([] (int key)             { if (key < 512) ImGui::GetIO().KeysDown[key] = true; });
    d3d11_key_up([] (int key)               { if (key < 512) ImGui::GetIO().KeysDown[key] = false; });

    // setup Dear Imgui
    ImGui::CreateContext();
    ImGui::StyleColorsDark();
    ImGuiIO& io = ImGui::GetIO();
    io.IniFilename = nullptr;
    io.Fonts->AddFontDefault();
    io.KeyMap[ImGuiKey_Tab] = VK_TAB;
    io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT;
    io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT;
    io.KeyMap[ImGuiKey_UpArrow] = VK_UP;
    io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN;
    io.KeyMap[ImGuiKey_Home] = VK_HOME;
    io.KeyMap[ImGuiKey_End] = VK_END;
    io.KeyMap[ImGuiKey_Delete] = VK_DELETE;
    io.KeyMap[ImGuiKey_Backspace] = VK_BACK;
    io.KeyMap[ImGuiKey_Enter] = VK_RETURN;
    io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE;
    io.KeyMap[ImGuiKey_A] = 'A';
    io.KeyMap[ImGuiKey_C] = 'C';
    io.KeyMap[ImGuiKey_V] = 'V';
    io.KeyMap[ImGuiKey_X] = 'X';
    io.KeyMap[ImGuiKey_Y] = 'Y';
    io.KeyMap[ImGuiKey_Z] = 'Z';

    // dynamic vertex- and index-buffers for imgui-generated geometry
    sg_buffer_desc vbuf_desc = { };
    vbuf_desc.usage = SG_USAGE_STREAM;
    vbuf_desc.size = MaxVertices * sizeof(ImDrawVert);
    bind.vertex_buffers[0] = sg_make_buffer(&vbuf_desc);

    sg_buffer_desc ibuf_desc = { };
    ibuf_desc.type = SG_BUFFERTYPE_INDEXBUFFER;
    ibuf_desc.usage = SG_USAGE_STREAM;
    ibuf_desc.size = MaxIndices * sizeof(ImDrawIdx);
    bind.index_buffer = sg_make_buffer(&ibuf_desc);

    // font texture for imgui's default font
    unsigned char* font_pixels;
    int font_width, font_height;
    io.Fonts->GetTexDataAsRGBA32(&font_pixels, &font_width, &font_height);
    sg_image_desc img_desc = { };
    img_desc.width = font_width;
    img_desc.height = font_height;
    img_desc.pixel_format = SG_PIXELFORMAT_RGBA8;
    img_desc.wrap_u = SG_WRAP_CLAMP_TO_EDGE;
    img_desc.wrap_v = SG_WRAP_CLAMP_TO_EDGE;
    img_desc.content.subimage[0][0].ptr = font_pixels;
    img_desc.content.subimage[0][0].size = font_width * font_height * 4;
    bind.fs_images[0] = sg_make_image(&img_desc);

    // shader object for imgui rendering
    sg_shader_desc shd_desc = { };
    auto& ub = shd_desc.vs.uniform_blocks[0];
    ub.size = sizeof(vs_params_t);
    shd_desc.attrs[0].sem_name = "POSITION";
    shd_desc.attrs[1].sem_name = "TEXCOORD";
    shd_desc.attrs[2].sem_name = "COLOR";
    shd_desc.vs.source =
        "cbuffer params {\n"
        "  float2 disp_size;\n"
        "};\n"
        "struct vs_in {\n"
        "  float2 pos: POSITION;\n"
        "  float2 uv: TEXCOORD0;\n"
        "  float4 color: COLOR0;\n"
        "};\n"
        "struct vs_out {\n"
        "  float2 uv: TEXCOORD0;\n"
        "  float4 color: COLOR0;\n"
        "  float4 pos: SV_Position;\n"
        "};\n"
        "vs_out main(vs_in inp) {\n"
        "  vs_out outp;\n"
        "  outp.pos = float4(((inp.pos/disp_size)-0.5)*float2(2.0,-2.0), 0.5, 1.0);\n"
        "  outp.uv = inp.uv;\n"
        "  outp.color = inp.color;\n"
        "  return outp;\n"
        "}\n";
    shd_desc.fs.images[0].type = SG_IMAGETYPE_2D;
    shd_desc.fs.source =
        "Texture2D<float4> tex: register(t0);\n"
        "sampler smp: register(s0);\n"
        "float4 main(float2 uv: TEXCOORD0, float4 color: COLOR0): SV_Target0 {\n"
        "  return tex.Sample(smp, uv) * color;\n"
        "}\n";
    sg_shader shd = sg_make_shader(&shd_desc);

    // pipeline object for imgui rendering
    sg_pipeline_desc pip_desc = { };
    pip_desc.layout.buffers[0].stride = sizeof(ImDrawVert);
    auto& attrs = pip_desc.layout.attrs;
    attrs[0].offset=offsetof(ImDrawVert, pos); attrs[0].format=SG_VERTEXFORMAT_FLOAT2;
    attrs[1].offset=offsetof(ImDrawVert, uv); attrs[1].format=SG_VERTEXFORMAT_FLOAT2;
    attrs[2].offset=offsetof(ImDrawVert, col); attrs[2].format=SG_VERTEXFORMAT_UBYTE4N;
    pip_desc.shader = shd;
    pip_desc.index_type = SG_INDEXTYPE_UINT16;
    pip_desc.blend.enabled = true;
    pip_desc.blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA;
    pip_desc.blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
    pip_desc.blend.color_write_mask = SG_COLORMASK_RGB;
    pip = sg_make_pipeline(&pip_desc);

    // initial clear color
    pass_action.colors[0].action = SG_ACTION_CLEAR;
    pass_action.colors[0].val[0] = 0.0f;
    pass_action.colors[0].val[1] = 0.5f;
    pass_action.colors[0].val[2] = 0.7f;
    pass_action.colors[0].val[3] = 1.0f;

    // draw loop
    while (d3d11_process_events()) {
        const int cur_width = d3d11_width();
        const int cur_height = d3d11_height();

        // this is standard ImGui demo code
        ImGuiIO& io = ImGui::GetIO();
        io.DisplaySize = ImVec2(float(cur_width), float(cur_height));
        io.DeltaTime = (float) stm_sec(stm_laptime(&last_time));
        ImGui::NewFrame();

        // 1. Show a simple window
        // Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets appears in a window automatically called "Debug"
        static float f = 0.0f;
        ImGui::Text("Hello, world!");
        ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
        ImGui::ColorEdit3("clear color", &pass_action.colors[0].val[0]);
        if (ImGui::Button("Test Window")) show_test_window ^= 1;
        if (ImGui::Button("Another Window")) show_another_window ^= 1;
        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);

        // 2. Show another simple window, this time using an explicit Begin/End pair
        if (show_another_window) {
            ImGui::SetNextWindowSize(ImVec2(200,100), ImGuiCond_FirstUseEver);
            ImGui::Begin("Another Window", &show_another_window);
            ImGui::Text("Hello");
            ImGui::End();
        }

        // 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowTestWindow()
        if (show_test_window) {
            ImGui::SetNextWindowPos(ImVec2(460, 20), ImGuiCond_FirstUseEver);
            ImGui::ShowTestWindow();
        }

        // the sokol_gfx draw pass
        sg_begin_default_pass(&pass_action, cur_width, cur_height);
        ImGui::Render();
        draw_imgui(ImGui::GetDrawData());
        sg_end_pass();
        sg_commit();
        d3d11_present();
    }
    ImGui::DestroyContext();
    sg_shutdown();
    d3d11_shutdown();
}

// render ImGui draw lists through sokol-gfx
void draw_imgui(ImDrawData* draw_data) {
    assert(draw_data);
    if (draw_data->CmdListsCount == 0) {
        return;
    }

    // render the command list
    vs_params_t vs_params;
    vs_params.disp_size.x = ImGui::GetIO().DisplaySize.x;
    vs_params.disp_size.y = ImGui::GetIO().DisplaySize.y;
    sg_apply_pipeline(pip);
    sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &vs_params, sizeof(vs_params));
    for (int cl_index = 0; cl_index < draw_data->CmdListsCount; cl_index++) {
        const ImDrawList* cl = draw_data->CmdLists[cl_index];

        // append vertices and indices to buffers, record start offsets in bindings struct
        const int vtx_size = cl->VtxBuffer.size() * sizeof(ImDrawVert);
        const int idx_size = cl->IdxBuffer.size() * sizeof(ImDrawIdx);
        const int vb_offset = sg_append_buffer(bind.vertex_buffers[0], &cl->VtxBuffer.front(), vtx_size);
        const int ib_offset = sg_append_buffer(bind.index_buffer, &cl->IdxBuffer.front(), idx_size);
        /* don't render anything if the buffer is in overflow state (this is also
            checked internally in sokol_gfx, draw calls that attempt from
            overflowed buffers will be silently dropped)
        */
        if (sg_query_buffer_overflow(bind.vertex_buffers[0]) ||
            sg_query_buffer_overflow(bind.index_buffer))
        {
            continue;
        }

        bind.vertex_buffer_offsets[0] = vb_offset;
        bind.index_buffer_offset = ib_offset;
        sg_apply_bindings(&bind);

        int base_element = 0;
        for (const ImDrawCmd& pcmd : cl->CmdBuffer) {
            if (pcmd.UserCallback) {
                pcmd.UserCallback(cl, &pcmd);
            }
            else {
                const int scissor_x = (int) (pcmd.ClipRect.x);
                const int scissor_y = (int) (pcmd.ClipRect.y);
                const int scissor_w = (int) (pcmd.ClipRect.z - pcmd.ClipRect.x);
                const int scissor_h = (int) (pcmd.ClipRect.w - pcmd.ClipRect.y);
                sg_apply_scissor_rect(scissor_x, scissor_y, scissor_w, scissor_h, true);
                sg_draw(base_element, pcmd.ElemCount, 1);
            }
            base_element += pcmd.ElemCount;
        }
    }
}
<|endoftext|>