Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions nodescraper/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@
# SOFTWARE.
#
###############################################################################
from .platforms.mobile_apu.amd_mobile_apu_plugin import AmdMobileApuPlugin

__all__ = [
"AmdMobileApuPlugin",
]
Comment on lines +26 to +30
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from typing import Optional

from nodescraper.enums import ExecutionStatus
from nodescraper.interfaces import DataAnalyzer
from nodescraper.models import TaskResult

from .amd_mobile_apu_analyzer_args import AmdMobileApuAnalyzerArgs
from .amd_mobile_apu_data_model import AmdMobileApuDataModel


class AmdMobileApuAnalyzer(DataAnalyzer[AmdMobileApuDataModel, AmdMobileApuAnalyzerArgs]):
DATA_MODEL = AmdMobileApuDataModel

def analyze(
self,
data_model: AmdMobileApuDataModel,
analyzer_args: AmdMobileApuAnalyzerArgs | None = None,
):
if data_model.pl1_power_limit_mw is None:
Comment on lines +39 to +44
return {
"status": "WARN",
"messages": ["Powercap sensor unavailable (likely WSL or missing kernel module)."],
}

actual = data_model.pl1_power_limit_mw
if analyzer_args and analyzer_args.exp_pl1_power_limit_mw is not None:
expected = analyzer_args.exp_pl1_power_limit_mw
if abs(actual - expected) > 1000:
return {
"status": "FAIL",
"messages": [f"PL1 mismatch: expected {expected} mW, got {actual} mW"],
}

return {"status": "PASS", "messages": []}

def analyze_data(
self,
data: AmdMobileApuDataModel,
args: Optional[AmdMobileApuAnalyzerArgs] = None,
) -> TaskResult:
result = self.analyze(data, args)
status_map = {
"PASS": ExecutionStatus.OK,
"WARN": ExecutionStatus.WARNING,
"FAIL": ExecutionStatus.ERROR,
}
return TaskResult(status=status_map[result["status"]], message="; ".join(result["messages"]))
Comment on lines +66 to +72
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@


###############################################################################
#
# MIT License
Comment on lines +1 to +5
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from pydantic import Field

from nodescraper.models import AnalyzerArgs


class AmdMobileApuAnalyzerArgs(AnalyzerArgs):
exp_pl1_power_limit_mw: int | None = Field(
None, description="Expected sustained power limit in milliwatts"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from pathlib import Path
from typing import Optional

from nodescraper.base import InBandDataCollector
from nodescraper.enums import ExecutionStatus
from nodescraper.models import TaskResult

from .amd_mobile_apu_data_model import AmdMobileApuDataModel


class AmdMobileApuCollector(InBandDataCollector[AmdMobileApuDataModel, None]):
DATA_MODEL = AmdMobileApuDataModel

def _read_int(self, path: str) -> int | None:
try:
return int(Path(path).read_text().strip())
except (OSError, IOError, ValueError):
return None
Comment on lines +26 to +43

def collect(self) -> AmdMobileApuDataModel:
pl1_power_limit_mw = self._read_int(
"/sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw"
)
if pl1_power_limit_mw is not None:
pl1_power_limit_mw //= 1000

cpu_temp_millidegree = self._read_int("/sys/class/thermal/thermal_zone0/temp")

return AmdMobileApuDataModel(
pl1_power_limit_mw=pl1_power_limit_mw,
cpu_temp_millidegree=cpu_temp_millidegree,
)

def collect_data(
self, args: Optional[None] = None
) -> tuple[TaskResult, Optional[AmdMobileApuDataModel]]:
return TaskResult(status=ExecutionStatus.OK), self.collect()
Comment on lines +59 to +62
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from pydantic import BaseModel, Field

from nodescraper.models import DataModel


class AmdMobileApuDataModel(DataModel):
pl1_power_limit_mw: int | None = Field(None, description="Sustained power limit in milliwatts")
cpu_temp_millidegree: int | None = Field(None, description="CPU temperature in millidegrees Celsius")
38 changes: 38 additions & 0 deletions nodescraper/plugins/platforms/mobile_apu/amd_mobile_apu_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
###############################################################################
#
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
###############################################################################
from nodescraper.base import InBandDataPlugin

from .amd_mobile_apu_analyzer import AmdMobileApuAnalyzer
from .amd_mobile_apu_analyzer_args import AmdMobileApuAnalyzerArgs
from .amd_mobile_apu_collector import AmdMobileApuCollector
from .amd_mobile_apu_data_model import AmdMobileApuDataModel


class AmdMobileApuPlugin(InBandDataPlugin[AmdMobileApuDataModel, None, AmdMobileApuAnalyzerArgs]):
DATA_MODEL = AmdMobileApuDataModel
COLLECTOR = AmdMobileApuCollector
ANALYZER = AmdMobileApuAnalyzer
ANALYZER_ARGS = AmdMobileApuAnalyzerArgs
Loading