Asan基本原理及试用
概述
Asan是Google专门为C/C++开发的内存错误探测工具,其具有如下功能
- 使用已释放内存(野指针)√
 - 堆内存越界(读写)√
 - 栈内存越界(读写)√
 - 全局变量越界(读写)×
 - 函数返回局部变量 ×
 - 内存泄漏 √
 
注:本文中测试使用的系统为Ubuntu 16.04,GCC版本为5.4.0,上面功能打√表示该版本GCC支持的功能,打×的表示该版本GCC不支持的功能。
Asan 运行十分的快,被测程序的运行速度一般只会降低2倍(相比于valgrind动辄10~20倍的性能下降来说,已经非常快了)。
Asan包括编译器指令模块和运行时库两部分组成:
-  
编译器插桩模块
在程序编译时加入控制指令,用于监测程序的所有内存使用行为,代码中每一次的内存访问操作都会被编译器修改如下方式:原始代码:
*address = ...; // or: ... = *address;编译后代码:
if (IsPoisoned(address)) {ReportError(address, kAccessSize, kIsWrite); } *address = ...; // or: ... = *address; 
-* 运行时库*
 用于替换glibc库的malloc/free函数,实现内存的分配和释放操作。malloc执行完后,已分配内存的前后(称为“红区”)会被标记为“中毒”状态,而释放的内存则会被隔离起来(暂时不会分配出去)且也会被标记为“中毒”状态。
Asan起初作为LLVM的一部分,后来其被集成到了4.8版本的GCC中,其可以运行在X86、ARM、MIPS、PowerPC等平台,操作系统支持Linux、OS X、iOS、Android、FreeBSD。
编译配置
GCC编译选项
-  
-fsanitize=address:开启内存越界检测
 -  
-fsanitize=leak:开启内存泄漏检测
 -  
-fsanitize-recover=address:一般后台程序为保证稳定性,不能遇到错误就简单退出,而是继续运行,采用该选项支持内存出错之后程序继续运行,需要叠加设置环境变量ASAN_
OPTIONS=halt_on_error=0才会生效;若未设置此选项,则内存出错即报错退出 -  
-fno-stack-protector:去使能栈溢出保护
 -  
-fno-omit-frame-pointer:去使能栈溢出保护
 -  
-fno-var-tracking:默认选项为-fvar-tracking,会导致运行非常慢
 -  
-g1:表示最小调试信息,通常debug版本用-g即-g2
 
在Makefile中可以通过设置类似于下面的编译选项控制GCC的关于Asan的选项。
ASAN_CFLAGS += -fsanitize=address -fsanitize-recover=address
 
Asan运行选项
ASAN_OPTIONS是Address-Sanitizier的运行选项环境变量。
- halt_on_error=0:检测内存错误后继续运行
 - detect_leaks=1:使能内存泄露检测
 - malloc_context_size=15:内存错误发生时,显示的调用栈层数为15
 - log_path=/var/log/asan.log:内存检查问题日志存放文件路径
 - suppressions=$SUPP_FILE:屏蔽打印某些内存错误
 
下面是设置ASAN_OPTIONS环境变量:
export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/var/log/asan.log:suppressions=$SUPP_FILE
 
LSAN_OPTIONS是内存泄漏检测模块LeakSanitizier的环境变量,常用的运行选项如下:
-  
exitcode=0:设置内存泄露退出码为0,默认情况内存泄露退出码0x16
 -  
use_unaligned=4:4字节对齐
 
下面是设置LSAN_OPTIONS环境变量:
export LSAN_OPTIONS=exitcode=0:use_unaligned=4
 
Asan运行库配置
用于Asan接管了应用程序内存的申请和释放操作,其替换了原有malloc和free的操作,比如glibc库,所以在程序运行前需要使用LD_PRELOAD指定下libasan.so的位置。
示例
Asan除了内存泄漏之外,默认情况下,其监测到内存问题后,程序就会立即退出,并且打印出相关的内存问题日志。对于内存泄漏问题,当程序正常退出时,才会检测程序是否存在内存泄漏,这里说的正常退出,对于C/C++程序有几种情况:
- main函数,return退出
 - 调用了exit函数退出
 
如果进程由于信号而退出的话,Asan不能检测是否存在内存泄漏。
堆访问越界(heap-buffer-overflow)
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{int *array = malloc(sizeof (int) * 100);array[0] = 0;int res = array[1 + 100]; //array访问越界free(array);pause();//程序等待,不退出return 0;
}
 
编译命令:
gcc heapOOB.c -o heapOOB -g -fsanitize=address -fsanitize=leak
 
执行情况:
==3653==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61400000ffd4 at pc 0x000000400871 bp 0x7ffe50cde9c0 sp 0x7ffe50cde9b0
READ of size 4 at 0x61400000ffd4 thread T0
#0 0x400870 in main /home/jetpack/work/4G/test/asan/heapOOB.c:7
#1 0x7f30b337a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapOOB+0x400708)
0x61400000ffd4 is located 4 bytes to the right of 400-byte region [0x61400000fe40,0x61400000ffd0)
allocated by thread T0 here:
#0 0x7f30b37bc602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x4007ee in main /home/jetpack/work/4G/test/asan/heapOOB.c:5
#2 0x7f30b337a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
 
栈访问越界(stack-buffer-overflow)
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) 
{int stack_array[100];stack_array[100] = 0;//栈访问越界pause();return 0; 
}
 
编译命令:
gcc stackOOB.c -o stackOOB -g -fsanitize=address -fsanitize=leak
 
执行结果:
==3952==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffdb72e830 at pc 0x0000004008ef bp 0x7fffdb72e660 sp 0x7fffdb72e650
WRITE of size 4 at 0x7fffdb72e830 thread T0
#0 0x4008ee in main /home/jetpack/work/4G/test/asan/stackOOB.c:6
#1 0x7f0c47a8e83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400748 in _start (/home/jetpack/work/4G/test/asan/stackOOB+0x400748)
Address 0x7fffdb72e830 is located in stack of thread T0 at offset 432 in frame
#0 0x400825 in main /home/jetpack/work/4G/test/asan/stackOOB.c:4
This frame has 1 object(s):
[32, 432) 'stack_array' <== Memory access at offset 432 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
 
使用已经释放的内存(UseAfterFree)
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) 
{int *array = malloc(sizeof (int) * 100);array[0] = 0;free(array);int res = array[0]; //使用已经释放的内存pause();return 0;
}
 
编译命令:
gcc heapUAF.c -o heapUAF -g -fsanitize=address -fsanitize=leak
 
执行结果:
==4385==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe40 at pc 0x000000400877 bp 0x7ffdc9019c20 sp 0x7ffdc9019c10
READ of size 4 at 0x61400000fe40 thread T0
#0 0x400876 in main /home/jetpack/work/4G/test/asan/heapUAF.c:8
#1 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapUAF+0x400708)
0x61400000fe40 is located 0 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
freed by thread T0 here:
#0 0x7f93f91982ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
#1 0x40083f in main /home/jetpack/work/4G/test/asan/heapUAF.c:7
#2 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
previously allocated by thread T0 here:
#0 0x7f93f9198602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x4007ee in main /home/jetpack/work/4G/test/asan/heapUAF.c:5
#2 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
 
内存泄漏 (Memory Leak)
#include <string.h>
#include <stdlib.h>
#include <unistd.h>int main(int argc, char **argv) 
{int *array = malloc(sizeof (int) * 100);memset(array, 0, 100 * 4);return 0;
}
 
编译命令:
gcc heapLeak.c -o heapLeak -g -fsanitize=address -fsanitize=leak
 
执行结果:
==3120==ERROR: LeakSanitizer: detected memory leaksDirect leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x7f412d5b7602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x40073e in main /home/jetpack/work/4G/test/asan/heapLeak.c:7
#2 0x7f412d17583f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)SUMMARY: AddressSanitizer: 400 byte(s) leaked in 1 allocation(s). 
实际应用中,特别是嵌入式设备,程序会一直运行,直到程序遇到问题,比如,内存访问越界导致段错误。所以,如果想随时使程序正常退出,检测程序是否存在内存泄漏的话,可以通过向进程发送特定的信号,
 进程接收到信号后主动退出程序,从而触发Asan内存泄漏检测。
//heapLeak.c#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>static int is_exit = 0;void sig_exit(int num)
{if (num == SIGUSR1) {//接收到SIGUSR1后,设置is_exit为1printf("SIGKILL\n");is_exit = 1;}   
}int main(int argc, char **argv) 
{struct sigaction action;sigemptyset(&action.sa_mask);action.sa_flags = 0;action.sa_handler = sig_exit;sigaction(SIGUSR1, &action, NULL);int *array = malloc(sizeof (int) * 100);memset(array, 0, 100 * 4); while(1) {                                                                                                                                                                                                                               if (is_exit) {//如果is_exit为1,退出循环,从使程序正常退出break;}   sleep(1);}   return 0;
}
 
编译命令:
gcc heapLeak.c -o heapLeak -g -fsanitize=address -fsanitize=leak
 
测试过程,运行heapLeak
./heapLeak //等待接收SIGUSR1
 
//查询进程id
pidof heapLeak
4347//发送SIGUSR1信号
kill -SIGUSR1 4347
 
heapLeak收到SIGUSR1之后,退出,并输出内存泄漏统计信息:
==4347==ERROR: LeakSanitizer: detected memory leaksDirect leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x7fbcd578d602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x400b31 in main /home/jetpack/work/4G/test/asan/heapLeak.c:26
#2 0x7fbcd534b83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)SUMMARY: AddressSanitizer: 400 byte(s) leaked in 1 allocation(s). 
函数返回的局部变量访问
int *ptr;
void usr_func(void)
{    //申请栈内存int local[100] = {0};ptr = local;
}    int main(void)
{    //使用函数栈返回的内存*ptr = 0;return 0;                                                                                                                                                                                                                                
}
 
全局变量访问越界
int global_array[100] = {0};                                                                                                                                                                                                                 int main(int argc, char **argv) 
{//全局变量global_array访问越界 global_array[101];return 0;
} 
注:全局变量访问越界(GlobalOutOfBounds)和函数返回的局部变量访问(UseAfterReturn),gcc-5.4.0暂时不支持,所以暂时不能测试。
日志记录内存问题
如果出现问题时,不是直接退出程序,而是继续运行,并且把内存问题输出到文件中的话,可以通过使用-fsanitize-recover=address编译选项,配合ASAN_OPTIONS变量实现,运行程序之前声明该变量即可。
编译时,加上-fsanitize-recover=address选项:
gcc heapOOB.c -o heapOOB -g -fsanitize=address -fsanitize=leak -fsanitize-recover=address运行时,先声明ASAN_OPTIONS再运行程序:
export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/var/log/asan.log:suppressions=$SUPP_FILE
 
如果gcc不支持-fsanitize-recover=address编译选项,比如,我现在使用的gcc-5.4.0,那么程序在遇到问题的时候,还是自动退出,但,日志会输出到log_path指定的目录,比如
asan.log.5089  asan.log.5090  asan.log.5348 //其中,文件后面的数字表示程序的进程号
 
番外篇
说一个最近使用Asan的经历,过程比较心酸,但是,也许有些意义,拿来和大家分享下。
其实,之前我是从来没有用Asan的,某天看到后,觉得这厮功能十分的强大,可以拿来玩玩。于是,写了个几个常见的内存使用问题,发现确实可以在第一时间就能把问题揪出来。正巧我当时,正在做一个嵌入式Linux的项目,程序规模属于中等,基本功能当时开发完了,想着正好可以使用Asan测试下这个项目是不是存在内存问题。记得当时还查阅了一些资料,分析了如何在项目里面使用Asan。之后,就把Asan用上了,当时确实找出来一个错误,然后,就没有再出现啥问题了(现在想想,当时最大的疏忽就是没有看使用Asan工具会给应用带来多大额外的内存耗损,就不会后来一直冤枉人家应用程序存在内存泄漏了;))。
这样相安无事的过了一段时间后,有一天在我发现系统的内存剩余很少了,第一反应就是应用出现了内存泄漏,通过长时间的监测,确定应用程序内存使用量存在不断增长的问题,进一步印证了内存泄漏的猜想。之后通过各种分析,甚至使用了valgrind工具,都没有检测到内存泄漏,但,应用内存使用量确实在不断增长,最后搞的十分的郁闷。
后来,网络上有说,glibc自带的内存分配器(ptmalloc)存在问题,很容易造成内存碎片,导致很多内存即使被free了,也只能被进程占着,不能返还给OS。我觉得,这不是正和我遇到的问题一样嘛,于是乎,我查阅了大量的资料,学习glibc ptmalloc的实现机制,以及如何造成内存碎片,多核、多线程场景下,性能如何低的讨论,甚至分析了另外两种内存分配器jemalloc(Facebook家的)、tcmalloc(Google家的)的性能如何如何厉害。
最后,实在是走投无路了,干脆直接把ptmalloc换了,正好buildroot支持jemalloc编译,顺其自然的,我就把jemalloc换上了。不换不知道,一换吓一跳,应用虚拟内存使用直接从500M降到了60M,当时直接给jemalloc跪了。
后来,才知道虚拟内存大小降了那么多,不是jemalloc的功能,而是由于使用了Asan虚拟内存使用量才上来的,也正是由于Asan需要实时监测内存使用情况,才最终导致了内存的不断增长。
哈哈,彻底理解了程序猿经常为了体验新的技术,自己挖坑,自己跳,自己埋的故事了。
总结几点经验:
- 项目开发过程中,新技术、新工具要慎用,用之前,要充分了解,过程中要做好使用记录,待问题出现时,可以反过来找问题的可能原因。
 - 对于新技术、新工具要有一点免疫力,不要头脑发热,看到它鼓吹的功能、性能,就恨不得马上应用到项目里,这样其实往往是在给自己挖坑,最后自己怎么死的都不知道。
 - 最后,新技术、新工具不是不学,不用了,反而要时刻保持一颗积极探索的心态,碰到好的东西,要积极学习,等待想法成熟之后,再应用到实际工作中去。
 
当然,从这次事件中也学到了很多,比如大概了解了常用的几款内存分配器的原理和使用方式,了解了Asan和valgrind的基本原理和使用方式,这些对以后的工作都是大有用处的,算是因祸得福
 吧,哈哈。
相关文章:
Asan基本原理及试用
概述 Asan是Google专门为C/C开发的内存错误探测工具,其具有如下功能 使用已释放内存(野指针)√堆内存越界(读写)√栈内存越界(读写)√全局变量越界(读写)函数返回局部变…...
深度学习应用技巧4-模型融合:投票法、加权平均法、集成模型法
大家好,我是微学AI,今天给大家介绍一下,深度学习中的模型融合。它是将多个深度学习模型或其预测结果结合起来,以提高模型整体性能的一种技术。 深度学习中的模型融合技术,也叫做集成学习,是指同时使用多个…...
【并发编程】深入理解Java内存模型及相关面试题
文章目录优秀引用1、引入2、概述3、JMM内存模型的实现3.1、简介3.2、原子性3.3、可见性3.4、有序性4、相关面试题4.1、你知道什么是Java内存模型JMM吗?4.2、JMM和volatile他们两个之间的关系是什么?4.3、JMM有哪些特性/能说说JMM的三大特性吗?…...
C++编程语言STL之queue介绍
本文主要介绍C编程语言的STL(Standard Template Library)中queue(队列)的相关知识,同时通过示例代码介绍queue的常见用法。1 概述适配器(adaptor)是STL中的一个通用概念。容器、迭代器和函数都有…...
ACO优化蚁群算法
%% 蚁群算法(ant colony optimization,ACO) %清空变量 clear close all clc [ graph ] createGraph(); figure subplot(1,3,1) drawGraph( graph); %% 初始化参数 maxIter 100; antNo 50; tau0 10 * 1 / ( graph.n * mean( graph.edges(:) …...
SwiftUI 常用组件和属性(SwiftUI初学笔记)
本文为初学SwiftUI笔记。记录SwiftUI常用的组件和属性。 组件 共有属性(View的属性) Image("toRight").resizable().background(.red) // 背景色.shadow(color: .black, radius: 2, x: 9, y: 15) //阴影.frame(width: 30, height: 30) // 宽高 可以只设置宽或者高.…...
Centos 中设置代理的两种方法
Centos 中设置代理的两种方法 在使用局域网时,有时在局域网内只有一台电脑可以进行上网,其他电脑只能通过配置代理的方式来上网,在Windows系统中设置代理上网相对简单,如果只需上网的话,只需在浏览器中找到网络连接&am…...
高速PCB设计指南系列(一)
第一篇 PCB布线 在PCB设计中,布线是完成产品设计的重要步骤,可以说前面的准备工作都是为它而做的, 在整个PCB中,以布线的设计过程限定最高,技巧最细、工作量最大。PCB布线有单面布线、 双面布线及多层布线。布线的方…...
云端IDE:TitanIDE v2.6.0 正式发布
原文作者:行云创新技术总监 邓冰寒 引言 云原生集成开发环境 TitanIDE v2.6.0 正式发布了,一起来看看都有那些全新的体验吧! TitanIDE 是一款云IDE, 也称 CloudIDE,作为数字化时代研发体系不可或缺的一环,和企业建设…...
【Python】tqdm 模块
import mathfrom tqdm import tqdm, trange# 计算阶乘 results_1 []for i in range(6666):results_1.append(math.factorial(i))这是一个循环计算阶乘的程序,我们不知道程序运行的具体情况,如果能加上一个程序运行过程的进度条,那可就太有趣…...
论文阅读:Adversarial Cross-Modal Retrieval对抗式跨模式检索
Adversarial Cross-Modal Retrieval 对抗式跨模式检索 跨模态检索研究的核心是学习一个共同的子空间,不同模态的数据可以直接相互比较。本文提出了一种新的对抗性跨模态检索(ACMR)方法,它在对抗性学习的基础上寻求有效的共同子空间…...
计算机网络复习
什么是DHCP和DNS DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的IP数串。通过主机名,最终得到该主机名对应的…...
unity动画--动画绑定,转换,用脚本触发
文章目录如何制作和添加动画大概过程示例图将多组图片转化为动画放在对象身上实现动画之间的切换使用脚本触发Parameters(Trigger)如何制作和添加动画 大概过程示例图 将多组图片转化为动画放在对象身上 首先,我们要为我们要对象添加animator 然后我们要设置对应的…...
车载汽车充气泵PCBA方案
汽车为什么会需要充气泵呢?其实是由于乘用车中没有供气源,所以就必需充气泵来给避震器供气。充气泵是为了保障汽车车胎对汽车的行驶安全所配备的,防止遇上紧急问题时没有解决方案,同时也可以检测轮胎胎压。现阶段的充气泵方案&…...
Android 连接 MySQL 数据库教程
在 Android 应用程序中连接 MySQL 数据库可以帮助开发人员实现更丰富的数据管理功能。本教程将介绍如何在 Android 应用程序中使用低版本的 MySQL Connector/J 驱动程序来连接 MySQL 数据库。 步骤一:下载 MySQL Connector/J 驱动程序 首先,我们需要下…...
tmall.item.update.schema.get( 天猫编辑商品规则获取 )
¥开放平台免费API必须用户授权 Schema方式编辑天猫商品时,编辑商品规则获取 公共参数 请求地址: HTTP地址 http://gw.api.taobao.com/router/rest 公共请求参数: 公共响应参数: 点击获取key和secret 请求示例 TaobaoClient client new DefaultTaobao…...
Leetcode 2379. 得到 K 个黑块的最少涂色次数
目录 一、题目内容和对应链接 1.题目对应链接 2.题目内容 二、我的想法 三、其他人的题解 一、题目内容和对应链接 1.题目对应链接 Leetcode 2379. 得到 K 个黑块的最少涂色次数 2.题目内容 给你一个长度为 n 下标从 0 开始的字符串 blocks ,blocks[i] 要…...
[深入理解SSD系列 闪存实战2.1.3] 固态硬盘闪存的物理学原理_NAND Flash 的读、写、擦工作原理
2.1.3.1 Flash 的物理学原理与发明历程 经典物理学认为 物体越过势垒,有一阈值能量;粒子能量小于此能量则不能越过,大于此能 量则可以越过。例如骑自行车过小坡,先用力骑,如果坡很低,不蹬自行车也能 靠惯性过去。如果坡很高,不蹬自行车,车到一半就停住,然后退回去。 …...
总结:Linux内核相关
一、介绍看eBPF和Cilium相关内容时,碰到Cilium是运行在第 3/4 层,不明白怎么做到的,思考原理的时候就想到了内容,本文记录下内核相关知识。https://www.oschina.net/p/cilium?hmsraladdin1e1二、Linux内核主要由哪几个部分组成Li…...
flutter工程创建过程中遇到一些问题。
安装环境版本:JDK7.-JDK 8 Andriod SDK 10 flutter 版本 3.0 1.当创建完后flutter工程后会遇到 run gradle task assemlble Debug 的问题,需要设置远程仓库,共需要修改三个地方build.gradle两处以及flutter 下面的D:\FVM\versions\3.0.0\pac…...
Ubuntu系统下交叉编译openssl
一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机:Ubuntu 20.04.6 LTSHost:ARM32位交叉编译器:arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...
【JavaEE】-- HTTP
1. HTTP是什么? HTTP(全称为"超文本传输协议")是一种应用非常广泛的应用层协议,HTTP是基于TCP协议的一种应用层协议。 应用层协议:是计算机网络协议栈中最高层的协议,它定义了运行在不同主机上…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
