使用Windbg静态分析dump文件的一般步骤详解

目录

1、概述

2、静态分析dump文件的一般步骤

2.1、查看异常类型

2.2、使用.ecxr命令切换到发生异常的线程上下文,查看发生异常的那条汇编指令

2.3、使用kn/kv/kp命令查看异常发生时的函数调用堆栈

2.4、使用lm命令查看模块的时间戳,找到对应的pdb文件,设置到Windbg中

3、问题分析实例说明

4、使用Windbg详细分析dump文件,展现完整分析过程

4.1、查看异常类型和发生崩溃的汇编指令,初步分析

4.2、使用kn/kv/kp命令查看函数调用堆栈

4.3、找到pdb文件,设置到windbg中,查看完整的函数调用堆栈

4.4、可以将C++源代码路径设置到Windbg中,Windbg会自动跳转到源代码行号上

4.5、有时需要查看函数调用堆栈中函数的局部变量或C++类对象中变量的值

4.6、有时可能需要使用IDA去查看二进制文件的汇编代码上下文去辅助分析


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++基础与进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html       有很多正在学习C++软件调试技术的朋友或者刚入门的人,希望我能详细讲讲使用Windbg静态分析dump文件的一般步骤,他们好对照这个步骤去练习去实操。所以今天就来讲讲这个主题内容,先概要性讲述Windbg静态分析dump文件的一般步骤,然后再以一个问题分析实例详细展现分析dump文件的完整过程(与本课程相关的免费视频教程,已经发布到B站,具体地址见本文的的评论区)。

1、概述

       基本大部分软件都内置了异常捕获模块,在软件发生异常闪退崩溃时,会弹出相关的提示框,比如PC版的微信在崩溃时,其内置的异常捕获模块会捕获到异常并生成日志及dump文件,同时会弹出如下的发送错误报告的提示框:

提示框的下方会自动带上崩溃相关的文件,其中最后一个文件就是我们要讲的dump文件,点击确定则会将这些文件发送到腾讯远端的后台服务器上。腾讯后台的运维人员会收到通知,然后到服务器上将dump等文件下载下去分析。

       有些软件可能没有上传崩溃日志到服务器的功能,捕捉到异常时会自动将dump文件保存到指定的路径中,事后可以到该路径中取到对应的dump文件。如果客户机器上遇到崩溃,可以和客户联系,让客户帮忙从对应的路径中取来出dump文件发过去。

       使用Windbg分析包含异常上下文的dump文件,属于静态分析,下面就来详细讲一下拿到dump文件之后如何使用Windbg去静态分析dump文件。

      关于Windbg的下载安装及详细介绍,可以查看我之前写的文章:
Windbg使用详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/120631007关于Windbg的命令汇总,可以查看我之前写的文章:

Windbg调试命令汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/51711212

2、静态分析dump文件的一般步骤

        可以先打开Windbg,然后将dump文件拖到Windbg中。

2.1、查看异常类型

        打开dump文件,就会显示发生异常的类型,如下所示:

根据显示的异常类型(ExceptionCode值对应的含义,比如0xC0000005对应的就是Access violation内存访问违例)。

       通过这个异常类型可以初步判断出异常的种类,比如Access violation(内存访问违例)、Integer divided by zero(除0异常)、Stack overflow(线程栈溢出)等。对于Access violation内存访问违例的异常,原因比较多,很难一步确定引发异常的原因。但对于Integer divided by zero除0异常、Stack overflow线程栈溢出异常,则能给出直接的线索,可以快速地定位问题。

2.2、使用.ecxr命令切换到发生异常的线程上下文,查看发生异常的那条汇编指令

        在查看异常类型之后,我们输入.ecxr命令切换到发生异常的线程上下文,查看发生异常的那条汇编指令及相关寄存器的值:

通过查看汇编指令及各寄存器的值,可以初步判断当前发生异常的初步原因,比如可能是访问C++类空指针、野指针或者用户态的代码访问了内核态内存地址。

汇编代码能最直接、最本真的反映出崩溃的原因,程序运行时执行的都是二进制文件中的汇编代码(或者称为二进制机器码,二进制机器码和汇编代码是等价的)。

       下面讲两个典型的汇编指令中访问了不该访问的内存异常场景。

2.2.1、汇编指令中访问64KB小地址内存区,引发访问违例,导致崩溃

       在Windows系统中,64KB以内的内存地址是禁止访问的,如果程序访问这个范围内的内存,则会触发内存访问违例,系统会强行将进程终止掉。

2.2.2、汇编指令中访问了很大的内核态的内存地址,引发访问违例,导致崩溃

       对于32位程序,系统会给进程分配4GB的虚拟内存,一般情况下用户态和内核态会各占一半,即各占2GB,我们编写的代码基本都是运行在用户态的,用户态的代码时不能访问内核态内存地址的(内核态地址是供系统内核模块使用的),如果崩溃指令中访问了一个很大的内存地址,超过用户态的地址范围0-2GB,内存地址大于0x8000000,则会触发内存违例,因为用户态的代码是禁止访问内核态内存地址的。

2.3、使用kn/kv/kp命令查看异常发生时的函数调用堆栈

       接着,使用kn/kv/kp命令(三个命令,可以使用其中任何命令)去查看异常发生时的函数调用堆栈,看看是调用了什么函数触发的异常。根据函数调用堆栈,对照着源码去进行分析。    

       一般此时没有加载pdb文件,调用堆栈中不会显示具体的函数名和代码的行号:

因为Windbg没有加载包含函数及变量符号信息的pdb文件。要函数调用堆栈中显示具体的函数名和代码行号,则需要拿到pdb文件,然后将pdb文件的路径设置到Windbg中。然后Windbg记载到pdb符号库文件之后,重新输出函数调用堆栈,堆栈中就会显示出具体的函数名和代码行号了。

2.4、使用lm命令查看模块的时间戳,找到对应的pdb文件,设置到Windbg中

       函数调用堆栈在没加载pdb文件时,会显示代码所在的模块名称,我们使用lm命令查看这些模块编译生成的时间戳:

然后通过时间戳找来这些模块对应的pdb文件,然后将pdb文件的路径设置到windbg中。如何将pdb文件的路径设置到Windbg中,下面讲实例时会详细说明,此处就不再赘述了。

       Windbg加载pdb文件之后,使用kn/kv/kp命令再次输出函数调用堆栈,堆栈中就会显示具体的函数名及行号,这样对照着C++源代码去详细分析引发问题的最终原因了。

       有时候为了搞清楚发生异常的本质,我们还需要使用IDA查看相关二进制文件的汇编代码,查看一下发生异常的那条汇编指令的上下文,对照着C++源码,看看那条汇编指令为啥会出现异常。

3、问题分析实例说明

        我们为了方便展开讲解,我们特意使用VisualStudio创建了一个基于MFC对话框的exe测试程序,对话框中有个名为button1的按钮,如下所示:

我们在此按钮的响应中添加了一段引发崩溃的测试代码,故意让程序产生崩溃,测试然后拿到崩溃时的dump文件。

       具体的测试代码如下所示:

// 添加的一段测试代码
SHELLEXECUTEINFO *pShExeInfo = NULL;
int nVal = pShExeInfo->cbSize; // 通过空指针访问结构体成员,导致崩溃CString strTip;
strTip.Format( _T("nVal=%d."), nVal );
AfxMessageBox( strTip );

代码中使用到的结构体SHELLEXECUTEINFO 定义如下:

typedef struct _SHELLEXECUTEINFOW
{DWORD cbSize;               // in, required, sizeof of this structureULONG fMask;                // in, SEE_MASK_XXX valuesHWND hwnd;                  // in, optionalLPCWSTR  lpVerb;            // in, optional when unspecified the default verb is choosenLPCWSTR  lpFile;            // in, either this value or lpIDList must be specifiedLPCWSTR  lpParameters;      // in, optionalLPCWSTR  lpDirectory;       // in, optionalint nShow;                  // in, requiredHINSTANCE hInstApp;         // out when SEE_MASK_NOCLOSEPROCESS is specifiedvoid *lpIDList;             // in, valid when SEE_MASK_IDLIST is specified, PCIDLIST_ABSOLUTE, for use with SEE_MASK_IDLIST & SEE_MASK_INVOKEIDLISTLPCWSTR  lpClass;           // in, valid when SEE_MASK_CLASSNAME is specifiedHKEY hkeyClass;             // in, valid when SEE_MASK_CLASSKEY is specifiedDWORD dwHotKey;             // in, valid when SEE_MASK_HOTKEY is specifiedunion                       {                           HANDLE hIcon;           // not used
#if (NTDDI_VERSION >= NTDDI_WIN2K)HANDLE hMonitor;        // in, valid when SEE_MASK_HMONITOR specified
#endif // (NTDDI_VERSION >= NTDDI_WIN2K)} DUMMYUNIONNAME;           HANDLE hProcess;            // out, valid when SEE_MASK_NOCLOSEPROCESS specified
} SHELLEXECUTEINFOW, *LPSHELLEXECUTEINFOW;#ifdef UNICODE
typedef SHELLEXECUTEINFOW SHELLEXECUTEINFO;
typedef LPSHELLEXECUTEINFOW LPSHELLEXECUTEINFO;
#else
typedef SHELLEXECUTEINFOA SHELLEXECUTEINFO;
typedef LPSHELLEXECUTEINFOA LPSHELLEXECUTEINFO;
#endif // UNICODE

       在测试代码中定义了SHELLEXECUTEINFO结构体指针pShExeInfo,并初始化为NULL,然后并没有给该指针赋一个有效的结构体对象地址,然后使用pShExeInfo访问结构体的cbSize成员的内存,因为pShExeInfo中的值为NULL,所以结构体cbSize成员的内存地址是结构体对象起始地址的偏移,因为结构体对象地址为NULL,cbSize成员位于结构体的首位,所以cbSize成员就是结构体对象的首地址,就是NULL,所以就访问64KB小地址内存块的异常,引发内存访问违例,导致程序发生崩溃闪退。

       至于如何在程序中设置异常捕获模块去捕获异常、自动生成dump文件,可以尝试使用google开源的CrashRpt库,我们产品很早就有了。大家如果想集成异常捕获库,可以使用开源CrashRpt(开源版本有一些缺陷,我们进行了一些优化和改进),也可以使用Google浏览器开源代码中的BreakPad,网上有很多版本,可以去研究一下。

4、使用Windbg详细分析dump文件,展现完整分析过程

4.1、查看异常类型和发生崩溃的汇编指令,初步分析

       使用Windbg打开dump文件(直接将dump文件拖入到Windbg),首先我们查看一下发生的异常类型,如下:

       然后使用.excr命令切换到发生异常的线程上下文,会自动将发生异常的那条汇编指令显示出来,如下:

       我们可以先看一下这条汇编指令以及崩溃时的各个寄存器的值,可能能初步估计出发生异常的原因。如果指令中访问了一个很小的地址或者访问了一个很大的地址,都会触发内存访问违例。
从崩溃的这条汇编指令来看,是访问了小地址0x00000000地址,这是访问了小于64KB小地址内存区,这个范围的地址是禁止访问的,所以引发了内存访问违例。这个异常很可能是访问了空指针引起的。

4.2、使用kn/kv/kp命令查看函数调用堆栈

       接着输入了kn命令,将函数调用堆栈打印出来:

函数调用堆栈是从下往上看的,最上面一行就是最后调用的一个函数,也是崩溃的那条汇编指令所在的函数。从函数调用堆栈的最后一帧调用的函数来看,程序的崩溃是发生在TestDlg.exe文件模块中,不是其他的dll模块。显示的函数地址是相对TestDlg.exe文件模块起始地址的偏移,为啥看不到模块中具体函数名称呢?那是因为Windbg找不到TestDlg.exe对应的pdb文件,pdb文件中包含对应的二进制文件中的函数名称及变量等信息,Windbg加载到pdb文件才能显示完整的函数名。

       查看函数调用堆栈的命令,除了kn,还有kv和kp命令,其中kv还可以看到函数调用堆栈中调用函数时传递的参数,如下所示:

我们需要取来pdb符号库文件,去查看具体的函数名及行号的,这样才好找到直接的线索的。下面就来看看如何获取到TestDlg.exe模块的pdb文件。

4.3、找到pdb文件,设置到windbg中,查看完整的函数调用堆栈

       如何才能找到TestDlg.exe文件对应的dpb文件?我们可以通过查看TestDlg.exe文件的时间戳找到文件的编译时间,通过编译时间找到文件对应的pdb文件。在Windbg中输入lm vm TestDlg*命令,可以查看到TestDlg.exe文件的详细信息,其中就包含文件的时间戳:(当前的lm命令中使用m通配符参数,所以在TestDlg后面加上了*号)

可以看到文件是2022年6月25日8点26分23秒生成的,就可以找到对应时间点的pdb文件了。

        一般在公司正式的项目中,通过自动化软件编译系统,每天都会自动编译软件版本,并将软件的安装包及相关模块的pdb文件保存到文件服务器中,如下所示:

这样我们就可以根据模块的编译时间找到对应版本的pdb文件了。

        我们找到了TestDlg.exe对应的pdb文件TestDlg.pdb,将其所在的路径设置到Windbg中。点击Windbg菜单栏中的File->Symbol File Path...,打开设置pdb文件路径的窗口,将pdb文件的路径设置进去,如下所示:

点击OK按钮之前,最好勾选上Reload选项,这样Windbg就会去自动加载pdb文件了。但有时勾选了该选项,好像不会自动去加载,我们就需要使用.reload /f TestDlg.exe命令去让Windbg强制去加载pdb文件(命令中必须是包含文件后缀的文件全名)。

        设置完成后,我们可以再次运行lm vm TestDlg*命令去看看pdb文件有没有加载进来:

如果已经加载进来,则会在上图中的位置显示出已经加载进来的pdb文件的完整路径,如上所示。

       关于pdb符号库文件的说明,可以查看我之前写的文章:
pdb符号库文件详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125508858       加载到TestDlg.exe文件对应的pdb文件之后,我们再次执行kn命令就可以包含具体的函数名及及代码的行号信息了,如下:

 我们看到了具体的函数名CTestDlgDlg::OnBnClickedButton1,还看到了对应的代码行号312。通过这些信息,我们就能到源代码中找到对应的位置了,如下所示:

是访问了空指针产生的异常。当然上面的代码是我们故意这样写的,目的是为了构造一个异常来详细讲解如何使用Windbg进行动态调试跟踪的。

4.4、可以将C++源代码路径设置到Windbg中,Windbg会自动跳转到源代码行号上

       为了方便查看,我们可以直接在Windbg中设置C++源码路径,这样Windbg会自动跳转到源码对应的位置。点击Windbg菜单栏的File->Source File Path...,将源码路径设置进去:

然后在Windbg点击函数调用堆栈中的每一行记录前面的数字超链接,会自动跳转到对应的函数及行号上,如下所示:

上图中的函数调用堆栈中很多模块是系统库中的,比如mfc100u、User32等,这些库是系统库,是没有源码的。我们可以点击最下面的第23个链接,其位于我们应用程序的模块中,会自动跳转到对应的代码中,如下:

4.5、有时需要查看函数调用堆栈中函数的局部变量或C++类对象中变量的值

        有时我们通过查看变量的值,找到排查问题的线索,比如变量中值为0或者很大的异常值。这点我们在多次问题排查中使用到,确实能找到一些线索。可以查看函数中局部变量的值,也可以查看函数所在类对象的this指针指向的类对象中变量的值。我们要查看哪个函数,就点击函数调用堆栈中每一行前面的数字超链接,如下所示:

我们看到了局部变量pShExeInfo 的值:

struct _SHELLEXECUTEINFOW * pShExeInfo = 0x00000000

       我们可以点击this对象的超链接:

就能查看当前函数对应的C++类对象中成员变量的值,如下:

      有时函数中相关变量的值,对于分析当前的C++软件异常,可能是关键线索,比如我之前写过的两篇项目问题排查实例文章:

通过查看Windbg中的变量值去定位C++软件异常问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125731044通过查看Windbg中汇编指令及内存中的值去定位软件崩溃问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125793532       但有时不一定能查看变量的值,因为当前通过异常捕获模块自动生成的dump文件一般是minidump文件,文件也就几MB左右,不可能包含所有变量的值。所以要在minidump文件中查看变量的值,要看运气的,有时能查看到,有时是看不到的。这里要讲一下dump文件的分类,主要分为minidump文件和全dump文件。

      关于dump文件的分类与dump文件的生成方式,可以查看我之前写的文章:
dump文件类型与dump文件生成方法详解icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/127991002

4.6、有时可能需要使用IDA去查看二进制文件的汇编代码上下文去辅助分析

       有时通过函数调用堆栈和源码很难定位问题时,可能需要使用IDA打开二进制文件去查看汇编代码的上下文去辅助定位问题。关于如何使用IDA去辅助排查问题,可以参见我之前写的文章:

使用IDA查看汇编代码上下文去辅助排查C++软件异常问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/128942626

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

状态管理小能手:Cookie 和 Session

1. 引言 大家好,我是小❤,一个漂泊江湖多年的 985 非科班程序员,曾混迹于国企、互联网大厂和创业公司的后台开发攻城狮。 假期抢票的尴尬事件 最近小❤在抢出行的高铁票时,发生了一件尴尬的事情。 这不是临近假期了嘛&#xf…

WPF真入门教程26--项目案例--欧姆龙PLC通讯工具

1、案例介绍 前面已经完成了25篇的文章介绍,概括起来就是从0开始,一步步熟悉了wpf的概念,UI布局控件,资源样式文件的使用,MVVM模式介绍,命令Command等内容,这节来完成一个实际的项目开发&#…

MIB 变更周期

MIB 始终以 80 ms 的周期在 BCH 上传输并在 80 ms 内重复,并且它包括从小区获取 SIB1 所需的参数;如果 SSB 的周期大于 80 ms,则 MIB 的发送周期与 SSB 的周期相同。 在UE初始搜索时,SSB在半帧内的周期是20ms;所以对于…

鸿蒙开发基础-UIAbility内页面间的跳转

基于Stage模型下的UIAbility开发,实现UIAbility内页面间的跳转和数据传递。 创建两个页面 启动DevEco Studio,创建一个新工程。在工程pages目录中,选中Index.ets,点击鼠标右键 > Refactor > Rename,改名为Inde…

如何配置 VS Code 实现 git 密码免输入

目录 问题描述尝试过的失败方法问题分析最终采用的解决方案:利用 ssh key 提供密码免输入功能安装 git windows 命令工具在windows本地生成 ssh key将公钥安装到 git 服务器第一种方法第二种方法调试方法 参考资料: 问题描述 在 Windows 上,使用 Visual…

Redis不同环境缓存同一条数据,数据内部值不同

背景 现实中,本地环境(dev)和开发环境(feature)会共同使用相同的中间件(本篇拿Redis举例),对于不同环境中的,图片、视频、语音等资源类型的预览地址url,需要配…

Cesium笔记 初始化 使用Vue-Cesium 组件

参考 A Vue 3 based component library of CesiumJS for developers | Vue for CesiumVue for Cesium, a Vue 3.x based component library of CesiumJS for GISerhttps://zouyaoji.top/vue-cesium/#/zh-CN/component/quickstart

Spring boot 3 集成rocketmq-spring-boot-starter解决版本不一致问题

安装RocketMQ根据上篇文章使用Docker安装RocketMQ并启动之后&#xff0c;有个隐患详情见下文 Spring Boot集成 <dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2…

基于 SpringBoot + vue 的医院管理系统(含源码,数据库,文档)

基于 SpringBoot vue 的医院管理系统 †前后端分离思想&#xff0c;这个系统简直太棒了&#xff01;屯 光这个系统采用了 前后端分离思想&#xff0c;后端使用 SpringBoot和 SpringMVC框架&#xff0c;让代码更高效&#xff0c;更易于维护。前端则使用了 vue js 和ElementU…

竞赛保研 基于深度学习的动物识别 - 卷积神经网络 机器视觉 图像识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

SUDA-计算机网路-期末复习提纲

写在前面 帮苏大的同学整理的计网复习材料&#xff0c;用的是他们老师划定的范围。 1.负责互联网协议开发、标准制定、地址分配的国际组织名称及其主要职责 (1) 地址支持组织&#xff08;ASO&#xff09;负责IP地址系统的管理。 (2) 域名支持组织&#xff08;DNSO&#xff09;…

实用Unity3D Log打印工具XDebug

特点 显示时间&#xff0c;精确到毫秒显示当前帧数&#xff08;在主线程中的打印才有意义&#xff0c;非主线程显示为-1&#xff09;有三种条件编译符(如下图) 注&#xff1a;要能显示线程中的当前帧数&#xff0c;要在app启动时&#xff0c;初始化mainThreadID字段条件编译符…

开源加解密库之GmSSL

一、简介 GmSSL是由北京大学自主开发的国产商用密码开源库&#xff0c;实现了对国密算法、标准和安全通信协议的全面功能覆盖&#xff0c;支持包括移动端在内的主流操作系统和处理器&#xff0c;支持密码钥匙、密码卡等典型国产密码硬件&#xff0c;提供功能丰富的命令行工具及…

在k8s集群中部署多nginx-ingress

关于ingress的介绍&#xff0c;前面已经详细讲过了&#xff0c;参考ingress-nginx详解和部署方案。本案例ingress的部署使用deploymentLB的方式。 参考链接&#xff1a; 多个ingress部署 文章目录 1. 下载ingress的文件2. 文件资源分析3. 部署ingress3.1 部署第一套ingress3.1…

日志系统一(elasticsearch+filebeat+logstash+kibana)

目录 一、es集群部署 安装java环境 部署es集群 安装IK分词器插件 二、filebeat安装&#xff08;docker方式&#xff09; 三、logstash部署 四、kibana部署 背景&#xff1a;因业务需求需要将nginx、java、ingress日志进行收集。 架构&#xff1a;filebeatlogstasheskib…

【数据库原理】期末突击(1)

有不会的题可以后台问我的哦&#xff0c;看见了就会回。 本文章主要是选择题、填空题&#xff0c;下章将更新综合题&#xff0c;祝大家期末心想事成。 一、选择题 下列关系运算中&#xff0c;&#xff08; C &#xff09;运算不属于专门的关系运算。 A&#xff0e;选择 …

Tensorflow2.0笔记 - 创建tensor

tensor创建可以基于numpy&#xff0c;list或者tensorflow本身的API。 笔记直接上代码&#xff1a; import tensorflow as tf import numpy as np import matplotlib.pyplot as plttf.__version__#通过numpy创建tensor tensor0 tf.convert_to_tensor(np.ones([2,3])) print(te…

Mysql 分割字符串,一行变多行,@rownum,mysql.help_topic

1 前言 朋友最近遇到一个比较棘手的 sql 问题&#xff0c;让我帮忙看看&#xff1a; 他有两张表 testa 和 testb &#xff0c;一个表存的日期&#xff0c;另一个表存字符串例如 2023-11-01,2023-11-02&#xff0c;如何将这两张表关联起来&#xff0c;只查 testa 表的数据&#…

<Python>PyQt5中UI界面和逻辑函数分开写的一种方式

前言 如果经常使用PyQt5这种模块来编写带UI界面的程序&#xff0c;那么很自然的就会涉及到&#xff0c;一旦程序比较大&#xff0c;UI控件多的时候&#xff0c;需要将UI和逻辑程序分离&#xff0c;这样方便管理&#xff0c;也方便维护。 配置&#xff1a; 平台&#xff1a;win…

三、yolov8训练结果查看和模型预测

训练结果查看 1、在模型训练结束后&#xff0c;如下图所示&#xff0c;找到该文件夹。 2、然后找到weights文件夹中的best.pt文件&#xff0c;这就是该数据训练后的模型。 模型预测 1、在assets文件夹下创建FPC-2文件夹&#xff0c;放入一些同类FPC预测结果。 2、和训练…