溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java運行時如何使用追蹤工具BTrace

發布時間:2021-09-27 09:47:59 來源:億速云 閱讀:263 作者:柒染 欄目:編程語言

這期內容當中小編將會給大家帶來有關Java運行時如何使用追蹤工具BTrace,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

1. BTrace是什么?

Btrace是由sundararajan在2009年6月開發的一個開源項目,是一種動態跟蹤分析一個運行中的Java應用程序的工具。 BTrace是一個為Java平臺開發的安全、動態的追蹤工具。BTrace動態地向目標應用程序的字節碼注入追蹤代碼(字節碼追蹤),這些追蹤字節碼追蹤代碼使用Java語言表達,也就是BTrace的腳本。

2. BTrace約束

為了保證追蹤動作是“只讀”的(也就是這些動作不可以修改被追蹤程序的狀態)和有限度的(比如在固定時間里結束)。一個BTrace程序只允許完成一些指定的動作。下面是BTrace一些不可以完成的事情:

  • 不能創建新的對象

  • 不能創建新的數組

  • 不能拋出異常

  • 不能捕獲異常

  • 不能進行任何的實例函數或者靜態函數 – 只有com.sun.btrace.BTraceUtils類中的靜態函數或者BTrace程序自己聲明的函數才可以被BTrace調用

  • 不可以在目標程序的類,或者對象的靜態或者實例級別的field進行賦值。但是,BTrace自身的類是可以給它的靜態field進行賦值的

  • 不能有outer,inner,嵌套的或者本地類。

  • 不能有同步代碼塊或者同步的函數

  • 不能有循環語句(for,while, do..while)

  • 不能繼承其它類(父類只能是java.lang.Object)

  • 不能實現接口

  • 不能包含斷言(assert)語句

  • 不能使用類字面值

這上面的種種限制可以通過一個配置改變:unsafe=true,在使用BTrace注解時修改該屬性的默認值(false)為true,即@BTrace(unsafe=true);也可以啟動選項中顯式聲明-Dcom.sun.btrace.unsafe=true(響應也有-u參數);現在你可以為所欲為了。BUT,這樣做之前最好考慮好風險并再三檢查腳本,請斟酌使用!

3. BTrace安裝

btrace git下載地址 ,下載release版本下來直接解壓就可以使用。

3.1 基本語法
btrace <pid> <btrace-script>腳本

btrace命令行工具運行命令如下:

btrace <options> <pid> <btrace source or .class file> <btrace arguments>
常用選項:
[-I <include-path>] [-p <port>] [-cp <classpath>]

參數說明:

where possible options include:
  --version             Show the version
  -v                    Run in verbose mode
  -o <file>             The path to store the probe output (will disable showing the output in console)
  -u                    Run in trusted mode
  -d <path>             Dump the instrumented classes to the specified path
  -pd <path>            The search path for the probe XML descriptors
  -classpath <path>     Specify where to find user class files and annotation processors
  -cp <path>            Specify where to find user class files and annotation processors
  -I <path>             Specify where to find include files
  -p <port>             Specify port to which the btrace agent listens for clients
  -statsd <host[:port]> Specify the statsd server, if any
  • include-path : 是一些用來查找頭文件的目錄。BTrace包含一個簡單的預處理,支持# define,# + include和條件編譯。它不像一個完整的C / c++預處理器–而是一個有用的子集。詳見demo代碼“ThreadBean.java”,如果沒有顯式的聲明選項-I,Btrace跳過預處理程序調用步驟。

  • port: BTrace代理程序所偵聽的端口,這是可選的選項。默認是2020

  • classpath: 是一些用來查找jar文件的目錄。默認是當前目錄”.”

  • pid:是要追蹤目標程序id

  • btrace-script: 就是追蹤程序本身。如果這是個java文件,那么提交前會進行編譯。否則,它被認為已預編譯(即它必須是一個類)并提交

  • arguments: 這是傳遞給BTrace程序的參數。BTrace程序可以通過內置的符號來引用這些參數,length是這些參數的個數。

在samples目錄下有很多示例,并且有的跟蹤很有用可直接使用.

4. BTrace的注解

4.1 方法注解
  • @com.sun.btrace.annotations.OnMethod 該注解可用來指定目標類,目標方法,以及目標方法里的“位置”。加了該注解后的操作方法會在對應的方法運行到指定的“位置”時被執行。該注解中,目標類用“clazz”屬性來指定,而目標方法用“method”屬性來指定?!眂lazz”可以是類的全路徑(比如java.awt.Component或者用兩個反斜杠中間的正則表達式,參考例子NewComponent.javaClassload.java來看它們的用法,正則表達式可以匹配0個或多個目標類,這個時候多個類都會被進行動態指令更換。如/java.awt.+/匹配java.awt包下的所有類)。方法名也可以用這樣的正則表達式 來匹配零個或者多個多個方法。參考例子MultiClass.java來查看用法。還有一種方法來指定追蹤類和函數。被追蹤的類和函數可以用注解來指定。比如,如果”clazz”屬性是@javax.jws.Webservice.那么BTrace會會把所有注解是這個的函數都進行動態指令更換。類似地,方法級別的注解也可以用來執行方法。參看例子WebServiceTracker.java來了解如何使用??梢园颜齽t表達式和注解放在一起用,比如@/com.acme..+/可以匹配任何類,只要這個類的注解能跟那段正則表達式匹配即可??梢酝ㄟ^指定父類來匹配多個類名,比如+java.lang.Runnable就可以匹配所有實現了java.lang.Runnable這個接口的類。參考例子SubtypeTracer.java來看它的用法。

  • @com.sun.btrace.annotations.OnTimer 該注解可以用來執行那些需要周期性(間隔是毫秒)的追蹤操作。參考Histogram.java來看它的用法。

  • @com.sun.btrace.annotations.OnError 該注解可以用來指定當任何異常拋出時需要執行的操作。被該注解修飾后的BTrace函數會在同一個BTrace類的其他操作方法拋出異常時執行。

  • @com.sun.btrace.annotations.OnExit 該注解用來執行黨BTrace代碼調用了exit(int)結束追蹤會話后需要執行的操作。參考例子ProbeExit.java來了解如何使用。

  • @com.sun.btrace.annotations.OnEvent 該注解用來追蹤函數與”外部”的事件關聯起來。當BTrace客戶端發送了一個“事件”后,該注解里的操作就會被執行??蛻舳税l送的事件可能是由用戶觸發的(比如按下Ctrl-C)。事件的名字是個字符串,這樣追蹤操作就只會在對應的事件觸發后被執行。到目標為止,BTrace命令行客戶端會在用戶按下Ctrl-C后發送事件,參考例子HistoOnEvent.java來了解用法。

  • @com.sun.btrace.annotations.OnLowMemory 該注解可以用來追蹤特定內存閾值被用光的事件。參看例子MemAlerter.java了解用法。

  • @com.sun.btrace.annotations.OnProbe 該注解可以用來避免使用BTrace腳本的內部類。@OnProbe探測點被映射到一個或多個@OnMethod上。目前這個映射是通過一個XML探測描述文件類指定的(這個文件會被BTrace代理所使用)。參考例子SocketTracker1.java和對應的描述文件java.net.socket.xml.當運行這個例子時,xml文件需要放在目標JVM所有運行的目錄下(或者修改btracer.bat中的probeDescPath選項來指向任意的xml文件)。

  • @com.sun.btrace.annotations.Location:該注解在一個traced/probed方法中指定一個特定的“位置”

  • @com.sun.btrace.annotations.Simpled:標記@OnMethod注解處理器采樣。采樣處理程序時并不是所有的事件將被追蹤,只有一個統計樣品與給定的意思。在默認情況下使用一種自適應采樣。BTrace將增加或減少樣品之間的調用數量保持平均時間窗口,因此減少整體的開銷。

4.2 參數相關的注解
  • @com.sun.btrace.annotations.Self:該注解把一個參數標識為保留了目標函數所指向的this的值。參考例子AWTEventTracer.javaAllCalls1.java.

  • @com.sun.btrace.annotations.Return:該注解說明這個參數保存目標函數的返回值。參考例子Classload.java

  • @com.sun.btrace.annotations.ProbeClassName:所修飾的參數保留了探測類的類名 。參看AllMethods.java(有多個探測類)

  • @com.sun.btrace.annotations.ProbeMethodName:所修飾的參數保留了探測函數的函數名。參考WebServiceTracker.java(多個探測函數)

  • @com.sun.btrace.annotations.TargetInstance:修飾的參數保留了被調用的實例。參考例子AllCall2.java.

  • @com.sun.btrace.annotations.TargetMethodOrField:該注解修飾的參數保存了調用的函數名。參考AllCalls1.javaAllCall2.java

  • @com.sun.btrace.annotations.Duration:探測方法參數標記為持續時間值的接收者,即目標方法執行的時間,單位納秒。只是用帶Location屬性的@OnMethod,并且需要配合Kind.ERROR或者Kind.RETURN使用

4.3 無注解的參數

沒有注解的BTrace探測函數參數是用來作簽名匹配的,因為他們必須必須在固定的位置上出現。然而,它們可以和其他的注解的參數進行交換。如果一個參數的類型是_AnyType[]_,它就會“吃”掉所所有剩下的參數。沒有注解的參數的具體含義與他們所在的位置有關:

名稱作用
Kind.ARRAY_GET數組元素加載
Kind.ARRAY_SET數組元素存儲
Kind.CALL方法調用
Kind.CATCH異常捕獲
Kind.CHECKCASTcheckcast
Kind.ENTRY方法進入。意指進入匹配probe點,跟你@Location設置的clazz和method沒有任何關系
Kind.ERROR錯誤,異常沒有捕獲,返回
Kind.FIELD_GETfield獲取
Kind.FIELD_SETfield設置
Kind.INSTANCEOF實例檢測
Kind.LINE源代碼行號
Kind.NEW創建新實例
Kind.NEWARRAY新的數組對象被創建
Kind.RETURN意指從某個匹配probe的方法中調用了匹配A class method的點,一定要和claz
Kind.SYNC_ENTRY進入一個同步方法鎖
Kind.SYNC_EXIT離開一個同步方法鎖
Kind.THROW拋出異常
4.4 字段相關的注解
  • @com.sun.btrace.annotations.Export BTrace字段使用該注解來說明它已經被映射到一個jvmstat計數器上。使用該注解,BTrace程序可以把追蹤計數器暴露給外部的jvmstat客戶端(比如jstat)。參考例子ThreadCounter.java

  • @com.sun.btrace.annotations.Property 該注解可以把一個字段標識為一個MBean屬性。如果一個BTrace類至少有一個靜態的字段使用了該注解。那么一個MBean就會被創建并且注冊到平臺MBean服務器上。JMX客戶端比如VisualVM,jconsole可以訪問這個字段來查看BTrace的MBean。在把BTrace附加到目標程序上后,你可以把VisualVM或者jconsole也附加到同一個目標程序上來查看剛創建好的MBean屬性。通過VisualVM或者jconsole,你可以通過MBeans tab頁來查看BTrace相關的域,然后查看它們的值。參考例子ThreadCounterBean.javaHistogramBean.java來了解用法

  • @com.sun.btrace.annotations.TLS BTrace字段使用該注解來說明它自己是一個線程本地字段(thread local field).注意你只能在@OnMethod注解后的函數里訪問這樣的字段。每個Java線程都有一個這個字段的拷貝。為了讓這樣的方式能夠工作,這個字段的類型只能是immutable(比如原始類型) 或者是cloneable (實現了Cloneable接口并且覆蓋了clone()函數)的。這些線程本地字段可以被BTrace程序用來識別它是否在同一個線程里執行了多個探測操作。參考例子OnThrow.javaWebServiceTracker.java

4.5 類相關的注解
  • @com.sun.btrace.annotations.DTrace該注解用來把一小段D腳本(嵌在BTrace 的java類中)和BTrace程序關聯起來。參考例子DTraceInline.java

  • @com.sun.btrace.annotations.DTraceRef 和上個注解一樣,不同的是D腳本是在獨立的文件中,不是嵌在java類中。

  • @com.sun.btrace.annotations.BTrace必須使用該注解來指定一個Java類是BTrace程序。BTrace編譯器會強制查找該注解,BTrace代理也會檢查這個是否有該注解。如果沒有,則提示錯誤,并且不會執行。

5. 使用示例

5.1 準備示例代碼
package com.gitee.funcy.jtools.btrace;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

/**
 * 該程序周期性地進行網絡讀取操作
 *
 * @author chengyan
 * @date 2019-11-03 10:41 下午
 */
public class HoldNetTask implements Runnable{

    public void visitWeb(String strUrl) {
        URL url = null;
        URLConnection urlconn = null;
        InputStream is = null;
        try {
            url = new URL(strUrl);
            urlconn = url.openConnection();
            is = urlconn.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
            StringBuffer bs = new StringBuffer();
            String l = null;
            while((l = bufferedReader.readLine()) != null) {
                bs.append(l).append("\r\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(is != null) {
                try {
                    is.close();
                } catch (Exception e) {

                }
            }
        }
    }

    @Override
    public void run() {
        while(true) {
            visitWeb("http://www.sina.com.cn");
        }
    }

    public static void main(String[] args) {
        new Thread(new HoldNetTask()).start();
    }

}
5.2 導入相關jar包
1. 直接導入jar包

BTrace對應的java包在Btrace release文件的build目錄下,一共有三個jar包:

btrace-agent.jar
btrace-boot.jar
btrace-client.jar
2. 使用maven引入jar包

也可使用maven引入依賴包,在pom文件中添加以下內容:

<properties>
	...省略其他
	<btrace.version>1.3.11.3</btrace.version>
</properties>

<dependencies>
	...省略其他
	<dependency>
		<groupId>com.sun.tools.btrace</groupId>
		<artifactId>btrace-agent</artifactId>
		<version>${btrace.version}</version>
	</dependency>
	<dependency>
		<groupId>com.sun.tools.btrace</groupId>
		<artifactId>btrace-boot</artifactId>
		<version>${btrace.version}</version>
	</dependency>
	<dependency>
		<groupId>com.sun.tools.btrace</groupId>
		<artifactId>btrace-client</artifactId>
		<version>${btrace.version}</version>
	</dependency>
</dependencies>

<repositories>
	<repository>
		<id>btrace-repo</id>
		<name>btrace-repo</name>
		<url>https://dl.bintray.com/btraceio/maven/</url>
		<releases>
			<enabled>true</enabled>
		</releases>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>
</repositories>

需要注意的是,maven中央倉庫中可能沒有btrace的最新版本,為此需要配置額外的倉庫。

5.3 監控函數的耗時

使用BTrace腳本可以通過正則表達式指定監控特定類的特定方法的耗時,以下代碼將監控所有類中名為visitWeb()的函數的執行時間。

package com.gitee.funcy.jtools.btrace;

import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
import com.sun.btrace.annotations.TLS;

import static com.sun.btrace.BTraceUtils.*;

/**
 * {這里添加描述}
 *
 * @author chengyan
 * @date 2019-11-03 12:13 上午
 */
@BTrace
public class PrintTimes {

    @TLS
    private static long startTime = 0;

    /**
     * 方法調用開始
     * clazz = "/.+/" : 監控做任意類
     * method="/visitWeb/":監控visitWeb方法
     */
    @OnMethod(clazz = "/.+/", method="/visitWeb/")
    public static void startMethod() {
        startTime = timeMillis();
    }

    @OnMethod(clazz = "/.+/", method="/visitWeb/", location=@Location(Kind.RETURN))
    public static void endMethod(@ProbeClassName String pcm, @ProbeMethodName String pmn) {
        println(pcm + "." + pmn + " [Time taken: " + str(timeMillis() - startTime) + "ms]");
    }
}

運行結果:

$ btrace 42188 PrintTimes.java
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 55ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 50ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 52ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 49ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 54ms]
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb [Time taken: 54ms]
5.4 監控函數參數

除了執行時間,BTrace也可輸出函數的參數:

package com.gitee.funcy.jtools.btrace;

import com.sun.btrace.AnyType;
import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;

/**
 * {這里添加描述}
 *
 * @author chengyan
 * @date 2019-11-03 10:43 下午
 */
@BTrace
public class PrintArgs {
    @OnMethod(clazz = "/.*HoldNetTask/",method = "/visitWeb/")
    public static void anyWriteFile(@ProbeClassName String pcn, @ProbeMethodName String pmn, AnyType[] args) {
        BTraceUtils.print(pcn + "-" + pmn);
        BTraceUtils.printArray(args);
    }
}

運行結果:

$ btrace 46877 PrintArgs.java
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
com.gitee.funcy.jtools.btrace.HoldNetTask-visitWeb[http://www.sina.com.cn, ]
5.5 取得做任意行代碼信息

通過BTrace腳本@Location注解,可以指定程序運行到某一行代碼時,觸發某一行為。下例顯示了通過BTrace腳本獲取HoldNetTask 類第27行代碼的信息(當目標程序運行到第27行時,觸發BTrace腳本)。

package com.gitee.funcy.jtools.btrace;

import static com.sun.btrace.BTraceUtils.*;

import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;

/**
 * {這里添加描述}
 *
 * @author chengyan
 * @date 2019-11-04 10:02 下午
 */
@BTrace
public class AllLines {

    @OnMethod(clazz = "/.*HoldNetTask/", location = @Location(value = Kind.LINE, line = 27))
    public static void online(@ProbeClassName String pcn, @ProbeMethodName String pmn, int line) {
        println(pcn + "." + pmn + ":" + line);
    }
}

運行結果:

$ btrace 46877 AllLines.java
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27
com.gitee.funcy.jtools.btrace.HoldNetTask.visitWeb:27

由腳本輸出可以看到,BTrace正確識別出HoldNetTask類的第27行代碼,正處于visitWeb() 函數的運行區間中。若將 @Location 中line的值設置為-1,則BTrace腳本將在每一行觸發。

5.6 定時觸發

BTrace腳本支持定時觸發??梢灾芷谛缘貓绦心骋恍袨?,獲取系統信息。下例使用@OnTimer 標記制定兩個周期性任務,分別為每秒運行一次和每3秒運行一次。

package com.gitee.funcy.jtools.btrace;

import com.sun.btrace.BTraceUtils;
import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.OnTimer;

import static com.sun.btrace.BTraceUtils.jstackAll;
import static com.sun.btrace.BTraceUtils.println;

/**
 * {這里添加描述}
 *
 * @author chengyan
 * @date 2019-11-04 10:11 下午
 */
@BTrace
public class Timers {

    @OnTimer(1000)
    public static void getUpTime() {
        println(BTraceUtils.Strings.strcat("1000 msec:", BTraceUtils.Strings.str(BTraceUtils.Sys.VM.vmUptime())));
    }

    @OnTimer(3000)
    public static void getStack() {
        jstackAll();
    }
}

運行結果:

$ btrace 46969 Timers.java
1000 msec:27654
1000 msec:28647
Thread[Attach Listener,9,system]


Thread[Signal Dispatcher,9,system]


Thread[Thread-1,9,system]

	java.net.PlainSocketImpl.socketAccept(Native Method)
	java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
	java.net.ServerSocket.implAccept(ServerSocket.java:545)
	java.net.ServerSocket.accept(ServerSocket.java:513)
	com.sun.btrace.agent.Main.startServer(Main.java:668)
	com.sun.btrace.agent.Main.access$000(Main.java:63)
	com.sun.btrace.agent.Main$2.run(Main.java:128)
	java.lang.Thread.run(Thread.java:748)

getUpTime()方法中,指定了一個每秒運行一次的任務,并打印虛擬機的啟動時間。在getStack()方法中,指定了一個每3秒運行一次的任務,每次都將導出系統的線程快照。

5.7 獲取類的屬性

在程序運行過程中,BTrace可以在指定的位置獲得對象實例的字段信息。比如,在本實例中,可以在調用URL.openConnection()時查看實際打開的URL地地址。

package com.gitee.funcy.jtools.btrace;

import com.sun.btrace.annotations.BTrace;
import com.sun.btrace.annotations.Location;
import com.sun.btrace.annotations.OnMethod;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.ProbeClassName;
import com.sun.btrace.annotations.ProbeMethodName;
import com.sun.btrace.annotations.Self;
import static com.sun.btrace.BTraceUtils.*;

/**
 * {這里添加描述}
 *
 * @author chengyan
 * @date 2019-11-04 10:20 下午
 */
@BTrace
public class PrintField {
    @OnMethod(clazz = "/.*URL/", method = "/.*openConnection/", location = @Location(value = Kind.ENTRY))
    public static void visitWebEntry(@Self Object self, @ProbeClassName String pcn, @ProbeMethodName String pmn) {
        println(pcn + "." + pmn);
        println(self);
        // 只能取值static變量
        println(get(field(classOf(self), "protocolPathProp")));
        // 獲得實例變量
        println(get(field(classOf(self), "host"), self));
        println("===============");
    }
}

運行結果:

$ btrace 46969 PrintField.java
java.net.URL.openConnection
http://www.sina.com.cn
java.protocol.handler.pkgs
www.sina.com.cn
===============

這里監控URL對象,在調用openConnection()方法時,獲得當前對象實例self,然后獲取對應實例的host等屬性。

6. 注意事項

  1. 腳本中方法參數需要跟原方法參數類型保持一致

  2. 腳本中不允許使用除btrace之外的類,拼接字符串使用BTraceUtils.strcat(),打印使用BTraceUtils.println(),獲取線程使用BTraceUtils.Threads

  3. BTrace植入過的代碼,會一直在,直到應用重啟為止。所以即使Btrace退出了,業務函數每次執行時都會執行Btrace植入的代碼

上述就是小編為大家分享的Java運行時如何使用追蹤工具BTrace了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女