Nanolog起步笔记-10-log解压过程(4)寻找meta续2

news2024/12/25 12:43:33

Nanolog起步笔记-10-log解压过程4寻找meta续2

  • 写在前面
    • 重新开始trace
  • readDictionaryFragment
    • 读取meta头部
    • 读入每个记录
    • createMicroCode
      • 读入头部,和文件名
    • 切分format字符串
      • PrintFragment
  • 后记

写在前面

前面的工作,已做打下令人有信心的基础。

重新开始trace

之前我们起步就看到了 metadata ,显然这前就已加载了。
所以,只需要重走一遍代码,就能得到了。
目前的代码相当简练。走过几步后看到:

在这里插入图片描述
显然我们接近了。
这个第一个得到的,就是我们想要的。
先得到类型:
在这里插入图片描述
type=73
结转为
struct UnknownHeader {
uint8_t entryType:2;
uint8_t other:6;
};
得到:
LOG_MSGS_OR_DIC = 1,
在这里插入图片描述
然后我们详细分析readDictionaryFragment.

readDictionaryFragment

bool
Log::Decoder::readDictionaryFragment(FILE *fd) {

读取meta头部

在这里我们稍作停留:
在这里插入图片描述
能够看到一些信息。特别是,DictionaryFragment 的定义

    /**
     * A DictionaryFragment contains a partial mapping of unique identifiers to
     * static log information on disk. Following this structure is one or more
     * CompressedLogInfo. The order in which these log infos appear determine
     * its unique identifier (i.e. by order of appearance starting at 0).
     */
    NANOLOG_PACK_PUSH
    struct DictionaryFragment {
        // Byte representation of an EntryType::LOG_MSG_OR_DIC to indicate
        // the start of a dictionary fragment.
        uint32_t entryType:2;

        // Number of bytes for this fragment (including all CompressedLogInfo)
        uint32_t newMetadataBytes:30;

        // Total number of FormatMetadata encountered so far in the log
        // including this fragment (used as a sanity check only).
        uint32_t totalMetadataEntries;
    };
    NANOLOG_PACK_POP

翻译一下:
DictionaryFragment 包含唯一标识符到磁盘上的静态日志信息的部分映射。此结构后面是一个或多个CompressedLogInfo。
这些日志信息的出现顺序,取决于其唯一标识符(即按从 0开始的出现顺序)。

这是比较容易理解的。因为logid或者fragment_id,就是这个id。也就是我们要找的meta record 的id.

在这里插入图片描述
这里得到了上图的信息。
最明显的是:(df).totalMetadataEntries=2
这与预期一致,因为我们的确,只有两次写入log。

读入每个记录

    /**
     * Stores the static log information associated with a log message on disk.
     * Following this structure are the filename and format string.
     */
    NANOLOG_PACK_PUSH
    struct CompressedLogInfo {
        // LogLevel severity of the original log invocation
        uint8_t severity;

        // File line number in which the original log invocation appeared
        uint32_t linenum;

        // Length of the filename that is associated with this log invocation
        // and follows after this structure.
        uint16_t filenameLength;

        // Length of the format string that is associated with this log
        // invocation and comes after filename.
        uint16_t formatStringLength;
    };
    NANOLOG_PACK_POP

CompressedLogInfo的信息很明确。
首先,它没有数组之类的信息的位置。
其次,4个信息,简单明了:级别;行号,文件名长,format长。
所以,它是每个meta的record的头部。并不是全部的信息。

读入过程如下:

        CompressedLogInfo cli;
        newBytesRead += fread(&cli, 1, sizeof(CompressedLogInfo), fd);
       newBytesRead += fread(filename, 1, cli.filenameLength, fd);
        newBytesRead += fread(format, 1, cli.formatStringLength, fd);

然后入队和创建:

        fmtId2metadata.push_back(endOfRawMetadata);
        fmtId2fmtString.push_back(format);
        createMicroCode(&endOfRawMetadata,
                            format,
                            filename,
                            cli.linenum,
                            cli.severity);

createMicroCode

读入头部,和文件名

在这里插入图片描述
跳过头部:*microCode += sizeof(FormatMetadata);
*microCode = stpcpy(*microCode, filename) + 1;

     /**
     * Describes a unique log message within the user sources. The order in
     * which this structure appears in the log file determines the associated
     * logId. Following this structure are the source filename and
     * PrintFragments required for this message.
     */
    NANOLOG_PACK_PUSH
    struct FormatMetadata {
        // Number of nibbles in the dynamic data stream used to pack() arguments
        uint8_t numNibbles;

        // Number of PrintFragments following this data structure
        uint8_t numPrintFragments;

        // Log level of the LOG statement in the original source file
        uint8_t logLevel;

        // Line number of the LOG statement in the original source file
        uint32_t lineNumber;

        // Number of bytes in filename[] (including the null character)
        uint16_t filenameLength;

        // Filename for the original source file containing the LOG statement
        char filename[];
    };
    NANOLOG_PACK_POP

FormatMetadata中的信息,是前面的CompressedLogInfo 超集。因为二者作用域不同。CompressedLogInfo 更加倾向于是序列化之后的域中。
FormatMetadata相当于内存中的数据域。
一般而言元数据在程序中,有三层:最上层的有函数的类级;没有函数,但有指针的内存级;用于存盘的序列化级,最低级,要么没有指针,要么指针是转义的(例如转为相对偏移量,从文件的某个位置,一般是零)。

切分format字符串

看到这个
定义正则表达式,准备切分format
我们知道要切分字符串了。
首先,定位到下一个’%’
定位到下一个啊,这里感慨一下,连boost中的正则表达式,也成为std的一部分了。
过正则匹配split后,(这里注意google的工程师,手工将字符串定位到指定位置,然后再进行正则匹配,而不是直接用正则表达式进行查找,可见他们对效率的追求达到了极致)。
在这里插入图片描述
得到
type=NanoLogInternal::Log::const_char_ptr_t
代码如下,这些代码很繁琐,但实际上,如果写底层工程,复用最多的反而是这些底层代码。


/**
 * Parses the <length> and <specifier> components of a printf format sub-string
 * according to http://www.cplusplus.com/reference/cstdio/printf/ and returns
 * a corresponding FormatType.
 *
 * \param length
 *      Length component of the printf format string
 * \param specifier
 *      Specifier component of the printf format string
 * @return
 *      The FormatType corresponding to the length and specifier. A value of
 *      MAX_FORMAT_TYPE is returned in case of error.
 */
static NanoLogInternal::Log::FormatType
getFormatType(std::string length, char specifier)
{
    using namespace NanoLogInternal::Log;

    // Signed Integers
    if (specifier == 'd' || specifier == 'i') {
        if (length.empty())
            return int_t;

        if (length.size() == 2) {
            if (length[0] == 'h') return signed_char_t;
            if (length[0] == 'l') return long_long_int_t;
        }

        switch(length[0]) {
            case 'h': return short_int_t;
            case 'l': return long_int_t;
            case 'j': return intmax_t_t;
            case 'z': return size_t_t;
            case 't': return ptrdiff_t_t;
            default : break;
        }
    }

    // Unsigned integers
    if (specifier == 'u' || specifier == 'o'
            || specifier == 'x' || specifier == 'X')
    {
        if (length.empty())
            return unsigned_int_t;

        if (length.size() == 2) {
            if (length[0] == 'h') return unsigned_char_t;
            if (length[0] == 'l') return unsigned_long_long_int_t;
        }

        switch(length[0]) {
            case 'h': return unsigned_short_int_t;
            case 'l': return unsigned_long_int_t;
            case 'j': return uintmax_t_t;
            case 'z': return size_t_t;
            case 't': return ptrdiff_t_t;
            default : break;
        }
    }

    // Strings
    if (specifier == 's') {
        if (length.empty()) return const_char_ptr_t;
        if (length[0] == 'l') return const_wchar_t_ptr_t;
    }

    // Pointer
    if (specifier == 'p') {
        if (length.empty()) return const_void_ptr_t;
    }


    // Floating points
    if (specifier == 'f' || specifier == 'F'
            || specifier == 'e' || specifier == 'E'
            || specifier == 'g' || specifier == 'G'
            || specifier == 'a' || specifier == 'A')
    {
        if (length.size() == 1 && length[0] == 'L' )
            return long_double_t;
        else
            return double_t;
    }

    if (specifier == 'c') {
        if (length.empty()) return int_t;
        if (length[0] == 'l') return wint_t_t;
    }

    fprintf(stderr, "Attempt to decode format specifier failed: %s%c\r\n",
            length.c_str(), specifier);
    return MAX_FORMAT_TYPE;
}

从我们看到,其实数据是紧密排列的:
即当前文件名"main.cc"的’\0’之后,紧跟着格式信息。
在这里插入图片描述

稍等啊,这里我有些偏离当前正在分析的问题的主题了,基实我要分析的是
在这里插入图片描述
然后根据formatString,自动换算出一个唯一标识符,作为CTF meta的event的name.
当然,也不完全偏离,解析数据时,还是要走现在的部分。

PrintFragment

    /**
     * Describes how to interpret the dynamic log stream and partially
     * reconstruct the original log message.
     */
    NANOLOG_PACK_PUSH
    struct PrintFragment {
        // The type of the argument to pull from the dynamic buffer to the
        // partial format string (formatFragment)
        uint8_t argType:5;

        // Indicates that the fragment requires a dynamic width/precision
        // argument in addition to one required by the format specifier.
        bool hasDynamicWidth:1;
        bool hasDynamicPrecision:1;

        //TODO(syang0) is this necessary? The format fragment is null-terminated
        // Length of the format fragment
        uint16_t fragmentLength;

        // A fragment of the original LOG statement that contains at most
        // one format specifier.
        char formatFragment[];
    };
    NANOLOG_PACK_POP

这里稍作笔记:
在这里插入图片描述((NanoLogInternal::Log::PrintFragment *)0x7fffb70a2021)->fragmentLength=42
这里的42是指从format头到现在的位置。
可以,想象,下一次memory copy就短得多了,因为这次,包括了当前这个%号的所有的format字符串。

本轮最后,更新了父node的信息:
在这里插入图片描述
之后是循环遍历完后面的3个%,略。

后记

暂时本系列告一段落的。占用了大概有3天的时间。
后面我需要准备开始转为CTF的meta的工作。
在开始之前,需要先研究一下我们自己的魔改版本的nanolog,具体细节有哪些。
还要看一下业务的代码,哪些位置,是我关注的。
我们做性能分析时,是划格子,涉及到的probe点,不是越多越好,正相反,每个主动对象,我们只需要关注收跳(TTI),和每slot的所有的task的generate,push to Queuq, scheduled, finish。
以及,在processing过程中,被OS重新调度该线程的情况。

后面的工作,许多内容,与本系列关系不太大了。
当然,个人还是有所收获。特别是,以后改为内存形态的log,以及定位当前log在解压时,有错误发生的bug,有了足够的信心和了解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2257653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

编译问题 fatal error: rpc/rpc.h: No such file or directory

在编译一些第三方软件的时候&#xff0c;会经常遇到一些文件识别不到的问题&#xff0c;这里整理下做个归总。 目前可能的原因有&#xff08;排序分先后&#xff09;&#xff1a; 文件不存在&#xff1b;文件存在但路径识别不了&#xff1b;…… 这次以常见的编译lmbench测试…

【OpenCV】Canny边缘检测

理论 Canny 边缘检测是一种流行的边缘检测算法。它是由 John F. Canny 在 1986 年提出。 这是一个多阶段算法&#xff0c;我们将介绍算法的每一个步骤。 降噪 由于边缘检测易受图像中的噪声影响&#xff0c;因此第一步是使用 5x5 高斯滤波器去除图像中的噪声。我们在前面的章…

记录:ubuntu 使用chattts的过程。

你知道什么是穷人吗&#xff1f;穷人就是没钱还想学习。 git GitHub - 2noise/ChatTTS: A generative speech model for daily dialogue. 因为所以。cosyvoice&#xff0c;gpt-s . 0.先找一个目录吧。 1.命令行模式 duyichengduyicheng-computer:~/gitee$ git clone https:…

鸿蒙实现应用通知

目录&#xff1a; 1、应用通知的表现形式2、应用通知消息的实现1、发布普通文本类型通知2、发布进度类型通知3、更新通知4、移除通知 3、设置通知道通展示不同形式通知4、设置通知组5、为通知添加行为意图1、导入模块2、创建WantAgentInfo信息3、创建WantAgent对象4、构造Notif…

Redis篇-6--原理篇5--单线程模型

1、概述 Redis 采用单线程模型来处理客户端请求&#xff0c;这意味着在任意时刻只有一个命令被执行。这种设计简化了 Redis 的实现&#xff0c;并确保了高并发环境下的数据一致性。尽管 Redis 是单线程的&#xff0c;但它通过高效的内存管理和网络 I/O 操作&#xff0c;仍然能…

stm32 GPIO8种输入输出模式

1、分类&#xff1a; 2、输出详解 2.1 推挽输出、复用推挽输出 GPIO_Mode_Out_PP (Output Push-Pull)、GPIO_Mode_AF_PP (Alternate Function Push-Pull) 推挽模式下&#xff0c;内部供电&#xff0c;电路主要控制P-MOS、N-MOS通、断电流实现I/O高、低电平输出 -->应用&…

MongoDB 建模调优change stream实战

MongoDB开发规范 &#xff08;1&#xff09;命名原则。数据库、集合命名需要简单易懂&#xff0c;数据库名使用小写字符&#xff0c;集合名称使用统一命名风格&#xff0c;可以统一大小写或使用驼峰式命名。数据库名和集合名称均不能超过64个字符。 &#xff08;2&#xff09…

【漫话机器学习系列】003.Agglomerative聚类

Agglomerative 聚类&#xff08;层次聚类中的自底向上方法&#xff09; Agglomerative 聚类是一种层次聚类&#xff08;Hierarchical Clustering&#xff09;算法&#xff0c;采用自底向上的策略&#xff0c;将每个数据点看作一个单独的簇&#xff0c;然后逐步将相近的簇合并…

docker的网络类型和使用方式

docker的网络类型 5种网络类型 bridge 默认类型&#xff0c;桥接到宿主机docker0的网络&#xff0c;有点类似于VM虚拟机的NAT网络模型。 案例: docker run --rm -itd --network bridge --name wzy666wzy-bridge alpine host host类型&#xff0c;共享宿主机的网络空间&#…

Java版-图论-最小生成树-Prim算法

实现描述 如图&#xff1a; Prim算法的基本思想是从一个顶点开始&#xff0c;逐步构建最小生成树。具体步骤如下&#xff1a; 随机选取一个顶点作为起始点&#xff0c;并将其加入最小生成树的集合中。从该顶点出发&#xff0c;选择一条边连接到其他未被访问的顶点中的最小权…

ZooKeeper节点扩容

新节点的准备工作&#xff08;这里由hadoop05节点&#xff0c;IP地址为192.168.46.131充当&#xff09; 配置新节点的主机域名映射&#xff0c;并将其通告给集群中的其他节点配置主机间免密登录关闭防火墙并将其加入到开机不启动项同步hadoop01节点的时间将所需要的文件分发给新…

HTML前端开发-- Iconfont 矢量图库使用简介

一、SVG 简介及基础语法 1. SVG 简介 SVG&#xff08;Scalable Vector Graphics&#xff09;是一种基于 XML 的矢量图形格式&#xff0c;用于在网页上显示二维图形。SVG 图形可以无限缩放而不会失真&#xff0c;非常适合用于图标、图表和复杂图形。SVG 文件是文本文件&#x…

厦门凯酷全科技有限公司抖音电商服务的卓越典范

在短视频和直播带货迅速崛起的时代&#xff0c;厦门凯酷全科技有限公司&#xff08;以下简称“凯酷全科技”&#xff09;以其专业的服务、创新的精神以及对市场的深刻理解&#xff0c;在抖音电商领域中脱颖而出&#xff0c;成为众多品牌商家信赖的选择。本文将深入探讨凯酷全科…

电脑运行时提示“0x80240037”错误,提示安装ie插件或其他微软程序时,报错提示“未指定的错误”是什么原因?以及要怎么解决和预防?

电脑运行时0x80240037错误解析&#xff1a;未指定的错误在安装IE插件或微软程序中的原因、解决与预防 作为一名经验丰富的软件开发从业者&#xff0c;我深知电脑在日常使用中可能遇到的各种问题&#xff0c;尤其是安装或更新软件时出现的错误。今天&#xff0c;我们将聚焦于一…

【C++】输入三个整数,输出最大值的高级分析

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C 文章目录 &#x1f4af;前言&#x1f4af;问题描述&#x1f4af;解题思路&#x1f4af;实现与分析方法一&#xff1a;三元运算符的直接应用详细分析&#xff1a;优缺点剖析&#xff1a; 方法二&#xff1a;显式条件…

类和对象一

目录 1.类的引入 2.类的定义 3.访问限定符 4.类的作用域 5.类对象模型 6.类的大小 1.类的引入 C语言结构体中只能定义变量&#xff0c;在C中&#xff0c;结构体不仅可以定义变量&#xff0c;也可以定义函数。 C兼容C语言&#xff0c;结构用法可以继续使用 同时sruct也升…

Python爬虫——HTML中Xpath定位

Xpath是一种路径查询语言。利用一个路径表达式从html文档中找到我们需要的数据位置&#xff0c;进而将其写入到本地或者数据库中。 学习Xpath爬虫&#xff0c;我们首先学习一下python中lxml库 关于库 lxml 终端下载Xpath需要用到的模块 pip install lxml 关于HTML 超文本标…

vulnhub靶场【hacksudo】之LPE的后续提权方法学习

前言 靶场&#xff1a;hacksudo-lpe的后几个challenge 基于上篇靶场hacksudo-ple的sudo提权 SUID文件提权 ar文件提权 使用find寻找具有SUID权限的文件 find / -perm -us -type f 2>/dev/null查看ar的SUID用法 sudo install -m xs $(which ar) .TF$(mktemp -u) LFILE&…

uniapp -- 实现页面滚动触底加载数据

效果 首选,是在pages.json配置开启下拉刷新 {"path": "pages/my/document/officialDocument","style": {"navigationStyle":</

【Windows11系统局域网共享文件数据】

【Windows11系统局域网共享文件数据】 1. 引言1. 规划网络2. 获取必要的硬件3. 设置网络4. 配置网络设备5. 测试网络连接6. 安全性和维护7. 扩展和优化 2. 准备工作2.1: 启用网络发现和文件共享2.2: 设置共享文件夹 3. 访问共享文件夹4. 小贴士5. 总结 1. 引言 随着家庭和小型办…