OSATE总线延迟的源码分析与模型修复——针对 Latency-case-study项目 端到端流延迟分析过程中空指针异常的解决

一、背景

 在文章AADL 端到端流延迟分析示例项目 Latency-case-study 简述的 “第八章 进行系统的端到端流延迟分析” 中,遇到了这样的一个问题:对分布式系统的端到端流延迟进行分析时,没有生成流延迟分析报告,并且错误日志提示,出现内部错误“空指针异常”。

上述文章给出的解决方案是使用旧版本的OSATE,但是这并未真正地解决问题。将OSATE项目的源代码下载,进行调试和分析,结果如下:

在涉及到总线的端到端延迟分析过程中,只将“在end to end flow中涉及的连接” 添加到指定数据结构connectionsToContributors。如果一个连接绑定到了某总线,却未在某个end to end flow中声明,那么该连接便不会添加到connectionsToContributors中,由此导致connectionsToContributors缺少关于该连接的数据,导致空指针异常。

本文在最后给出了修改后的代码,以下为详细内容:

二、问题回顾

对于Latency-case-study项目,在生成integration.software_distributed实例之后,启用流延迟分析,发现并未有任何报告被生成:

错误日志(Error Log)显示如下:

java.lang.NullPointerException: Cannot invoke "org.osate.analysis.flows.model.LatencyContributor.addSubContributor(org.osate.analysis.flows.model.LatencyContributor)" because "latencyContributor" is null

at org.osate.analysis.flows.FlowLatencyAnalysisSwitch.fillInQueuingTimes(FlowLatencyAnalysisSwitch.java:1465)

at org.osate.analysis.flows.FlowLatencyAnalysisSwitch.invokeOnSOM(FlowLatencyAnalysisSwitch.java:1052)

at org.osate.analysis.flows.handlers.CheckFlowLatency.analyzeInstanceModel(CheckFlowLatency.java:158)

at org.osate.ui.handlers.AbstractInstanceOrDeclarativeModelReadOnlyHandler.analyzeInstanceModelInMode(AbstractInstanceOrDeclarativeModelReadOnlyHandler.java:126)

at org.osate.ui.handlers.AbstractInstanceOrDeclarativeModelReadOnlyHandler.doAaxlAction(AbstractInstanceOrDeclarativeModelReadOnlyHandler.java:101)

at org.osate.ui.handlers.AbstractInstanceOrDeclarativeModelModifyHandler.processAaxlAction(AbstractInstanceOrDeclarativeModelModifyHandler.java:58)

at org.osate.ui.handlers.AbstractAaxlHandler.actionBody(AbstractAaxlHandler.java:181)

at org.osate.ui.handlers.AaxlReadOnlyHandlerAsJob$ActionAsJob.runInWorkspace(AaxlReadOnlyHandlerAsJob.java:115)

at org.eclipse.core.internal.resources.InternalWorkspaceJob.run(InternalWorkspaceJob.java:43)

at org.eclipse.core.internal.jobs.Worker.run(Worker.java:63)

可以看到,内部错误的主要原因是因为“ "latencyContributor" is null 

三、问题详细分析

为什么会产生空指针异常,接下来将自上而下地对问题进行分析:

在整个分析过程中,对于连接延迟的计算,涉及到了一个Map对象connectionsToContributors,其定义如下:(FlowLatencyAnalysisSwitch.java : line 111)

/*

 * Map from (bus component, connection instance) -> latency contributor. We need this because the queuing latency is computed at the end of process

 * after all the transmission times are computed. So we add the queuing latency to this latency contributor for the connection instance

 * bound to the given bus. This may contain entries that in the end are not interesting because virtual buses can be bound to

 * other virtual buses that are eventually bound to real bus. We chases down all those layers.

 */

private final Map<Pair<ComponentInstance, ConnectionInstance>, LatencyContributor> connectionsToContributors = new HashMap<>();

在流延迟分析过程中,会将端到端流中的元素按照类型依次添加到对应的Map中(其中一个Map用来存储关于连接的组件,即上面提到的connectionsToContributors),代码如下:

(FlowLatencyAnalysisSwitch.java : mapFlowElementInstance() : line 187)

public LatencyReportEntry analyzeLatency(EndToEndFlowInstance etef, SystemOperationMode som,boolean asynchronousSystem) {
		LatencyReportEntry entry = new LatencyReportEntry(etef, som, asynchronousSystem, 
			report.isMajorFrameDelay());
		for (FlowElementInstance fei : etef.getFlowElements()) {
			mapFlowElementInstance(etef, fei, entry);
		}
		// Issue 1148 moved this somewhere else
		// entry.finalizeReportEntry();
		return entry;
}

上述代码中,会遍历所有的端到端流etef(end to end flow),将每个端到端流fei(Flow Element Instance)作为参数传递给函数mapFlowElementInstance(etef, fei, entry),而该函数会间接调用processSamplingAndQueuingTimes(),从而根据端到端流的组成元素,向Map connectionsToContributors 中添加元素(只有在端到端流中声明的元素才会被添加),代码如下:(FlowLatencyAnalysisSwitch.java : processSamplingAndQueuingTimes():line 896)

	//通过下面函数获取总线的周期,对于周期性总线和非周期性总线,延迟的计算方式不同。
	double period = hasPeriod.contains(cc) ? PropertyUtils.getScaled(
	    TimingProperties::getPeriod, boundBusOrRequiredClassifier, TimeUnits.MS).orElse(0.0)    :   0.0;
	
	// 如果是周期性总线,增加采样延迟,将排队延迟设为0
	if (period > 0) {
	    // add sampling latency due to the protocol or bus being periodic
	    LatencyContributor samplingLatencyContributor = new LatencyContributorComponent(
	        boundBusOrRequiredClassifier, report.isMajorFrameDelay());
	    samplingLatencyContributor.setBestCaseMethod(LatencyContributorMethod.SAMPLED_PROTOCOL);
	    samplingLatencyContributor.setWorstCaseMethod(LatencyContributorMethod.SAMPLED_PROTOCOL);
	    samplingLatencyContributor.setSamplingPeriod(period);
	    latencyContributor.addSubContributor(samplingLatencyContributor);
	
	    // add queuing latency: always zero in this case
	    LatencyContributor queuingLatencyContributor = new LatencyContributorComponent(
	        boundBusOrRequiredClassifier, report.isMajorFrameDelay());
	    queuingLatencyContributor.setBestCaseMethod(LatencyContributorMethod.QUEUED);
	    queuingLatencyContributor.setWorstCaseMethod(LatencyContributorMethod.QUEUED);
	    queuingLatencyContributor.setMinimum(0.0);
	    queuingLatencyContributor.setMaximum(0.0);
	    latencyContributor.addSubContributor(queuingLatencyContributor);
	} 
	//如果是非周期性总线,需要在后续过程中计算排队延迟,将其存储于Map中
	else {
	    /*
		* Issue 1148
		*
		* if "boundBus" is really a bound component instance, and not a required component classifier,
		* then we remember the bus as asynchronous. Later in fillInQueuingTimes() we go through this list,
		* and then find all the connection instances bound to this bus. For each connection,
		* we compute the sum of the max transmission times of the OTHER connections bound to the bus. This
		* we set as the worse case queuing time. (Best case is 0.)
		*
		* We also remember the bus--connection pair that needs the queuing latency by storing its latency contributor.
		*/
	    if (bindingConnection != null) {
	        final ComponentInstance boundBus = (ComponentInstance) boundBusOrRequiredClassifier;
	
	        /* Set the bus order and then add it to the ordered set */
	        if (!busOrder.containsKey(boundBus)) {
	            busOrder.put(boundBus, nextBusId++);
	            asyncBuses.add(boundBus);
	        }
	        connectionsToContributors.put(new Pair<>(boundBus, bindingConnection), latencyContributor);
	}

根据上述代码:对于周期性总线和非周期性总线,其延迟的计算方式不同。

对于周期性总线,计算它的采样延迟,范围为[0, period]。

对于非周期性总线,其最小延迟为0,最大延迟按照绑定到该总线的其他连接的数据传输时间之和进行计算。

因此,对于端到端流延迟分析函数的调用代码,涉及到了一个名为 fillInQueuingTimes()的函数,其源码如下:(FlowLatencyAnalysisSwitch.java : fillInQueuingTimes():line 1412)

	private void fillInQueuingTimes(final SystemInstance system) {
		// Nothing to do if there are no asynchronous buses
		if (!asyncBuses.isEmpty()) {
			// Get all the connections bound to a bus and group them together by the bus they are bound to
			final Map<ComponentInstance, Set<ConnectionInstance>> sortedConnections = sortBoundConnections(system);
			/*
			 * Go through the list of all the asynchronous buses
			 */
			for (final NamedElement ne : asyncBuses) {
				// only proceed if it is a bus instance and not a classifier (from Required_Virtual_Bus_Class)
				if (ne instanceof ComponentInstance) {
					final ComponentInstance bus = (ComponentInstance) ne;

					// Get all the connections bound to that bus
					final Set<ConnectionInstance> boundConnections = sortedConnections.getOrDefault(bus,Collections.emptySet());
					// Get all the transmission times and compute the total
					double totalTime = 0.0;
					final Map<ConnectionInstance, Double> transmissionTimes = new HashMap<>();
					for (final ConnectionInstance ci : boundConnections) {
						final Double time = computedMaxTransmissionLatencies
								.getOrDefault(new Pair<ComponentInstance, ConnectionInstance>(bus, ci), 0.0);
						transmissionTimes.put(ci, time);
						totalTime += time;
					}

					/*
					 * Go through the list of connections again, and subtract the time associated
					 * with the current connection to find the max waiting time for each connection.
					 * (That each for each connection ci, we will have the sum of all the times
					 * for the _other_ connections bound to same bus. This gives us the max
					 * time that connection ci may have to wait to use the bus.)
					 */
					for (final ConnectionInstance ci : boundConnections) {
						final Double ciTime = transmissionTimes.get(ci);
						final double maxWaitingTime = totalTime - ciTime;

						// Finally we can stick this into the latency contributor
						final LatencyContributor latencyContributor = connectionsToContributors.get(new Pair<>(bus, ci));
						final LatencyContributor queuingLatencyContributor = new LatencyContributorComponent(bus,report.isMajorFrameDelay());
						queuingLatencyContributor.setBestCaseMethod(LatencyContributorMethod.QUEUED);
						queuingLatencyContributor.setWorstCaseMethod(LatencyContributorMethod.QUEUED);
						queuingLatencyContributor.setMinimum(0.0);
						if (report.isDisableQueuingLatency()) {
							// Hide the queuing time
							queuingLatencyContributor.setMaximum(0.0);
							queuingLatencyContributor.reportInfo("Ignoring queuing time of " + maxWaitingTime + "ms");
						} else {
							// Report the queuing time
							queuingLatencyContributor.setMaximum(maxWaitingTime);
						}
						latencyContributor.addSubContributor(queuingLatencyContributor);

						// add the sampling latency
						LatencyContributor samplingLatencyContributor = new LatencyContributorComponent(
								bus, report.isMajorFrameDelay());
						samplingLatencyContributor.setBestCaseMethod(LatencyContributorMethod.SAMPLED_PROTOCOL);
						samplingLatencyContributor.setWorstCaseMethod(LatencyContributorMethod.SAMPLED_PROTOCOL);
						samplingLatencyContributor.setSamplingPeriod(0.0);
						latencyContributor.addSubContributor(samplingLatencyContributor);
					}
				}
			}
		}
	}

在上述代码中,首先会获取一条总线上绑定的所有连接。

final Set<ConnectionInstance> boundConnections = sortedConnections.getOrDefault(bus,Collections.emptySet());

然后依次遍历每条连接,在Map connectionsToContributors中定位到这个连接并向其中添加延迟贡献。

final LatencyContributor latencyContributor = connectionsToContributors.get(new Pair<>(bus, ci));

......

latencyContributor.addSubContributor(queuingLatencyContributor);

异常出现的原因:如果一个连接没有被包含于某个端到端流,但是却被绑定到总线上,那么Map connectionsToContributors将不会添加此连接,故而在遍历绑定到总线的连接时,会出现空指针异常。

四、解决方案

修复此问题的方法有2种:

1. 完整地建模,声明系统中存在的所有端到端流,以保证不会有连接被遗漏。

2. 设定总线周期,避免排队延迟情况的出现(即绕过排队延迟计算)。

五、修复模型并测试

两种方案的代码如下:(在 integration.aadl 的 integration.software_distributed 部分)

system implementation integration.software_distributed extends integration.software_generic
	subcomponents
		s1_cpu 	: processor latency_cs::platform::generic_cpu;
		s2_cpu 	: processor latency_cs::platform::generic_cpu;
		p_cpu 	: processor latency_cs::platform::generic_cpu;
		a_cpu 	: processor latency_cs::platform::generic_cpu;
		s_p_bus: bus latency_cs::platform::generic_bus;
		p_a_bus : bus latency_cs::platform::generic_bus;
	connections
		b0 : bus access s1_cpu.net <-> s_p_bus;
		b1 : bus access s2_cpu.net <-> s_p_bus;
		b2 : bus access p_cpu.net <-> s_p_bus;
		b3 : bus access p_cpu.net <-> p_a_bus;
		b4 : bus access a_cpu.net <-> p_a_bus;
	
	-- 为解决流延迟分析过程中出现的空指针异常而增添的代码--------------
	-- 方法1.完整地建模,声明系统中存在的所有端到端流(在flows中声明)
	flows
		etef2 : end to end flow s1.sensor_source -> c0 -> p.sink0; 
		etef3 : end to end flow s2.sensor_source -> c1 -> p.sink1;
	-- ---------------------------------------------------------------
	
	properties
		-- 为解决流延迟分析过程中出现的空指针异常而增添的代码----------
		-- 方法2.设定总线周期(在properties中设定)
		--	Period => 5ms applies to s_p_bus, p_a_bus;
		-- -----------------------------------------------------------
		
		actual_processor_binding => (reference (s1_cpu)) applies to s1;
		actual_processor_binding => (reference (s2_cpu)) applies to s2;
		actual_processor_binding => (reference (p_cpu)) applies to p;
		actual_processor_binding => (reference (a_cpu)) applies to a;
		
		actual_connection_binding => (reference (s_p_bus)) applies to c0;
		actual_connection_binding => (reference (s_p_bus)) applies to c1;
		actual_connection_binding => (reference (p_a_bus)) applies to c2;
		
		-- protocol that applies to the connections
		required_virtual_bus_class => (classifier (latency_cs::platform::generic_protocol)) applies to c0, c1, c2;
end integration.software_distributed;

1. 完整地建模,声明系统中存在的所有端到端流,实例化后进行分析:

成功生成流延迟分析报告:

报告内容如下所示:

2. 设定总线周期,避免排队延迟情况的出现

成功生成流延迟分析报告:

报告内容如下所示:

六、相关链接

 OSATE 开发者文档,展示了如何部署环境并拉取OSATE源码,如下:

Setting up an OSATE development environment — OSATE 2.13.0 documentation

OSATE 项目的源码也可以直接在github下载:

osate/osate2: Open Source AADL2 Tool Environment (github.com)

latency-case-study项目的源码也可以直接在github下载:

examples/latency-case-study at master · osate/examples (github.com)

 修改后的完整的项目代码可在此处下载。

【免费】latency-case-study项目修改版(资源-CSDN文库)

如有不当或错误之处,恳请您的指正,谢谢!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python数据分析(四)-- 操作Excel文件

1 操作Excel文件-多种实现方式 在实际生产中&#xff0c;经常会用到excel来处理数据&#xff0c;虽然excel有强大的公式&#xff0c;但是很多工作也只能半自动化&#xff0c;配合Python使用可以自动化部分日常工作&#xff0c;大大提升工作效率。 openpyxl&#xff1a;只允许读…

初识JavaScript(一)

文章目录 一、JavaScript介绍二、JavaScript简介1.ECMAScript和JavaScript的关系2.ECMAScript的历史3.什么是Javascript&#xff1f;4.JavaScript的作用?5.JavaScript的特点 三、JavaScript基础1.注释语法2.JavaScript的使用 四、JavaScript变量与常量变量关键字var和let的区别…

Android广播BroadcastReceiver

BroadcastReceiver组件 BroadcastReceiver是Android中的一个组件&#xff0c;用于接收和处理系统广播或应用内广播。它可以监听系统事件或应用内自定义的广播&#xff0c;并在接收到广播时执行相应的操作。 广播是一种用于在应用组件之间传递消息的机制。通过发送广播&#x…

RT-DERT:在实时目标检测上,DETRs打败了yolo

文章目录 摘要1、简介2. 相关研究2.1、实时目标检测器2.2、端到端目标检测器2.3、用于目标检测的多尺度特征 3、检测器的端到端速度3.1、 NMS分析3.2、端到端速度基准测试 4、实时DETR4.1、模型概述4.2、高效的混合编码器4.3、IoU-aware查询选择4.4、RT-DETR的缩放 5、实验5.1、…

Microsoft SQL Server 缓冲区错误漏洞(CVE-2018-8273)解决方法

前言&#xff1a; 在一次漏洞扫描中&#xff0c;扫描出紧急漏洞Microsoft SQL Server 缓冲区错误漏洞(CVE-2018-8273) 根据修复建议找补丁。 一、漏洞详情 二、寻找补丁 根据漏洞修复建议去下载补丁 目前厂商已发布升级补丁以修复漏洞&#xff0c;补丁获取链接&#xff1a;h…

k8s中kubectl命令式对象、命令式对象配置、声明式对象配置管理资源介绍

目录 一.kubernetes资源管理简介 二.三种资源管理方式优缺点比较 三.命令式对象管理介绍 1.kubectl命令语法格式 2.资源类型 &#xff08;1&#xff09;通过“kubectl api-resources”来查看所有的资源 &#xff08;2&#xff09;每列含义 &#xff08;3&#xff09;常…

【Docker】十分钟完成mysql8安装,你也可以的!!!

十分钟完成mysql8安装&#xff0c;你也可以的 前言安装步骤1.创建安装目录2.创建docker-compose.yml3.启动容器4.mysql开启远程连接5.连接mysql 总结 前言 本文基于Docker安装mysql:8.0.29&#xff0c;首先确保系统安装了docker和docker-compose。 没有使用过docker朋友可以去…

leetcode-哈希表

1. 理论 从哈希表的概念、哈希碰撞、哈希表的三种实现方式进行学习 哈希表&#xff1a;用来快速判断一个元素是否出现集合里。也就是查值就能快速判断&#xff0c;O&#xff08;1&#xff09;复杂度&#xff1b; 哈希碰撞&#xff1a;拉链法&#xff0c;线性探测法等。只是一种…

【年终特惠】基于最新导则下生态环评报告编制技术暨报告篇、制图篇、指数篇、综合应用篇系统性实践技能提升

根据生态环评内容庞杂、综合性强的特点&#xff0c;依据生态环评最新导则&#xff0c;将内容分为4大篇章(报告篇、制图篇、指数篇、综合篇)、10大专题(生态环评报告编制、土地利用图的制作、植被类型及植被覆盖度图的制作、物种适宜生境分布图的制作、生物多样性测定、生物量及…

高压放大器在电火花加工中的作用是什么

高压放大器在电火花加工中扮演着至关重要的角色。下面安泰电子将详细介绍高压放大器在电火花加工中的作用。 电火花加工是一种精密加工技术&#xff0c;广泛应用于制造业的模具制造、航空航天、汽车零部件等领域。它通过在工件表面产生高频电火花放电的方式&#xff0c;实现对材…

uniapp实现路线规划

UniApp是一个基于Vue.js框架开发的跨平台应用开发框架&#xff0c;可以同时构建iOS、Android、H5等多个平台的应用。它使用了基于前端技术栈的Web开发方式&#xff0c;通过编写一套代码&#xff0c;即可在不同平台上运行和发布应用。 UniApp具有以下特点&#xff1a; 跨平台开…

Unity内打开网页的两种方式(自带浏览器、内嵌浏览器)

1.自带浏览器 这个比较简单&#xff0c;直接调用unity官方的API即可&#xff0c;会直接使用默认浏览器打开网页&#xff0c;这里就不多做解释了。 代码 public InputField input;private void OpenUrlByUnity(){string inputStr input.text;if (!string.IsNullOrEmpty(input…

单链表练习

单链表练习 相关内容&#xff1a; 1.再理解&#xff1a;LNode、*LinkList 2.单链表的整表创建&#xff08;头插法和尾插法&#xff09; 3.单链表的读取、插入、删除 4.单链表的整表删除 //单链表的初始化、创建、插入、删除、查找 //结点的结构体&#xff1a;数据域、指针域 …

开放式耳机能保护听力吗,开放式耳机跟骨传导耳机哪个更好?

如果从严格意义上来讲的话&#xff0c;开放式耳机中的骨传导耳机是能保护听力&#xff0c;现如今的开放式耳机是一个统称&#xff0c;将所有不入耳的类目全部规划到一块。因此在开放式耳机中存在着一些耳机是只能够保持周边环境音&#xff0c;而不是保护听力的。 下面让我来给…

linux下的程序环境和预处理(gcc演示)

1. 程序的翻译环境和执行环境 在ANSI C的任何一种实现中&#xff0c;存在两个不同的环境。 第1种是翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境&#xff0c;它用于实际执行代码。 2. 详解编译链接 2.1 翻译环境 组成一个程序的…

MySQL连接的原理⭐️4种优化连接的手段性能提升240%

MySQL连接的原理⭐️4种优化连接的手段性能提升240%&#x1f680; 前言 上两篇文章我们说到MySQL优化回表的三种方式&#xff1a;索引条件下推ICP、多范围读取MRR与覆盖索引 MySQL的优化利器⭐️索引条件下推&#xff0c;千万数据下性能提升273%&#x1f680; MySQL的优化…

函数式接口详解(Java)

函数式接口详解&#xff08;Java&#xff09;_函数式接口作为参数_凯凯凯凯.的博客-CSDN博客 函数式接口&#xff1a;有且仅有一个抽象方法的接口 Java中函数式编程体现就是Lambda表达式&#xff0c;所以函数式接口就是可以适用于Lambda使用的接口 只有确保接口中仅有一个抽…

【设计模式】第7节:创建型模式之“建造者模式”

Builder模式&#xff0c;中文翻译为建造者模式或者构建者模式&#xff0c;也有人叫它生成器模式。 在创建对象时&#xff0c;一般可以通过构造函数、set()方法等设置初始化参数&#xff0c;但当参数比较多&#xff0c;或者参数之间有依赖关系&#xff0c;需要进行复杂校验时&a…

Springboot 集成 Seata

Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式解决方案。Seata官网 1.找到适合的Seata版本 参考&#xff1a;SpringCloudAlibaba S…

Linux学习之进程二

目录 进程状态 R (running)运行状态与s休眠状态&#xff1a; disk sleep&#xff08;深度睡眠状态&#xff09; T (stopped)&#xff08;暂停状态&#xff09; t----tracing stop(追踪状态) X死亡状态&#xff08;dead&#xff09; Z(zombie)-僵尸进程 孤儿进程 进程优…