Skip to content

feat(events): GPU interface #10162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Oct 18, 2018
Merged
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
9 changes: 9 additions & 0 deletions src/sentry/interfaces/contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ def __init__(self, alias, data):
super(OsContextType, self).__init__(alias, data)


@contexttype
class GpuContextType(ContextType):
type = 'gpu'
indexed_fields = {
'name': u'{name}',
'vendor': u'{vendor_name}',
}


class Contexts(Interface):
"""
This interface stores context specific information.
Expand Down
41 changes: 41 additions & 0 deletions src/sentry/static/sentry/app/components/events/contextSummary.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,46 @@ class DeviceSummary extends React.Component {
}
}

export class GpuSummary extends React.Component {
static propTypes = {
data: PropTypes.object.isRequired,
};

render() {
let data = this.props.data;

if (objectIsEmpty(data) || !data.name) {
return <NoSummary title={t('Unknown GPU')} />;
}

let className = generateClassName(data.name);
let versionElement = null;

if (data.vendor_name) {
className = generateClassName(data.vendor_name);
versionElement = (
<p>
<strong>{t('Vendor:')}</strong> {data.vendor_name}
</p>
);
} else {
versionElement = (
<p>
<strong>{t('Vendor:')}</strong> {t('Unknown')}
</p>
);
}

return (
<div className={`context-item ${className}`}>
<span className="context-item-icon" />
<h3>{data.name}</h3>
{versionElement}
</div>
);
}
}

const MIN_CONTEXTS = 3;
const MAX_CONTEXTS = 4;
const KNOWN_CONTEXTS = [
Expand All @@ -197,6 +237,7 @@ const KNOWN_CONTEXTS = [
{key: 'runtime', Component: GenericSummary, unknownTitle: t('Unknown Runtime')},
{key: 'os', Component: OsSummary},
{key: 'device', Component: DeviceSummary},
{key: 'gpu', Component: GpuSummary},
];

class EventContextSummary extends React.Component {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const CONTEXT_TYPES = {
os: require('app/components/events/contexts/os').default,
runtime: require('app/components/events/contexts/runtime').default,
user: require('app/components/events/contexts/user').default,
gpu: require('app/components/events/contexts/gpu').default,
};

function getContextComponent(type) {
Expand Down
65 changes: 65 additions & 0 deletions src/sentry/static/sentry/app/components/events/contexts/gpu.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import PropTypes from 'prop-types';
import React from 'react';

import ContextBlock from 'app/components/events/contexts/contextBlock';
import {formatBytes} from 'app/utils';

const megaByteInBytes = 1048576;

class GpuContextType extends React.Component {
static propTypes = {
alias: PropTypes.string.isRequired,
data: PropTypes.object.isRequired,
};

formatMemory = memory_size => {
if (!Number.isInteger(memory_size) || memory_size <= 0) {
return null;
}

// 'usable_memory' is in defined in MB
return formatBytes(memory_size * megaByteInBytes);
};

render() {
let {
id,
name,
version,
vendor_name,
vendor_id,
memory_size,
npot_support,
multi_threaded_rendering,
api_type,
...data
} = this.props.data;

let memory = this.formatMemory(memory_size);
let knownData = [
['?Name', name],
['?Version', version],
['?Vendor', vendor_name],
['?Memory', memory],
['?NPOT Support', npot_support],
['?Multi-Thread rendering', multi_threaded_rendering],
['?API Type', api_type],
];

if (vendor_id > 0) {
knownData.unshift(['?Vendor Id', vendor_id]);
}

if (id > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This condition could be combined with the one above it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You just caught a bug there! I should be checking for: vendor_id .
This is due to Unity serialization not handling int?, so type is not nullable and 0 will be sent :
It will be the case until we get away from Unity.JsonUtility

knownData.unshift(['?GPU Id', id]);
}

return <ContextBlock data={data} knownData={knownData} alias={this.props.alias} />;
}
}

GpuContextType.getTitle = function(value) {
return 'GPU';
};

export default GpuContextType;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions src/sentry/static/sentry/less/group-detail.less
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,21 @@
}
}

&.arm .context-item-icon {
background-image: url(../images/icons/context/arm.png);
background-size: contain;
}

&.amd .context-item-icon {
background-image: url(../images/icons/context/amd.png);
background-size: contain;
}

&.nvidia .context-item-icon {
background-image: url(../images/icons/context/nvidia.png);
background-size: contain;
}

&.electron .context-item-icon {
background-image: url(../images/icons/context/electron.png);
}
Expand Down Expand Up @@ -979,6 +994,7 @@
&.tvos,
&.mac-os-x,
&.mac,
&.apple,
&.watchos {
.context-item-icon {
background-image: url(../images/icons/context/apple-ios.svg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,46 @@ exports[`ContextSummary render() should skip non-default named contexts 1`] = `
</div>
`;

exports[`GpuSummary render() should render name and vendor 1`] = `
<div
className="context-item arm"
>
<span
className="context-item-icon"
/>
<h3>
Mali-T880
</h3>
<p>
<strong>
Vendor:
</strong>

ARM
</p>
</div>
`;

exports[`GpuSummary render() should render unknown when no vendor 1`] = `
<div
className="context-item apple-a"
>
<span
className="context-item-icon"
/>
<h3>
Apple A8 GPU
</h3>
<p>
<strong>
Vendor:
</strong>

Unknown
</p>
</div>
`;

exports[`OsSummary render() should render the kernel version when no version 1`] = `
<div
className="context-item mac-os-x"
Expand Down
30 changes: 29 additions & 1 deletion tests/js/spec/components/events/contextSummary.spec.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import React from 'react';
import {shallow} from 'enzyme';

import ContextSummary, {OsSummary} from 'app/components/events/contextSummary';
import ContextSummary, {
OsSummary,
GpuSummary,
} from 'app/components/events/contextSummary';

const CONTEXT_USER = {
email: '[email protected]',
Expand Down Expand Up @@ -172,3 +175,28 @@ describe('OsSummary', function() {
});
});
});

describe('GpuSummary', function() {
describe('render()', function() {
it('should render name and vendor', () => {
const gpu = {
name: 'Mali-T880',
vendor_name: 'ARM',
version: 'OpenGL ES 3.2 v1.r22p0-01rel0.f294e54ceb2cb2d81039204fa4b0402e',
};

const wrapper = shallow(<GpuSummary data={gpu} />);
expect(wrapper).toMatchSnapshot();
});

it('should render unknown when no vendor', () => {
const gpu = {
type: 'gpu',
name: 'Apple A8 GPU',
};

const wrapper = shallow(<GpuSummary data={gpu} />);
expect(wrapper).toMatchSnapshot();
});
});
});
21 changes: 21 additions & 0 deletions tests/sentry/interfaces/test_contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,24 @@ def test_app(self):
'device_app_hash': '5678',
}
}

def test_gpu(self):
ctx = Contexts.to_python({
'gpu': {
'name': 'AMD Radeon Pro 560',
'vendor_name': 'Apple',
'version': 'Metal'
},
})
assert sorted(ctx.iter_tags()) == [
('gpu.name', 'AMD Radeon Pro 560'),
('gpu.vendor', 'Apple'),
]
assert ctx.to_json() == {
'gpu': {
'type': 'gpu',
'name': 'AMD Radeon Pro 560',
'vendor_name': 'Apple',
'version': 'Metal'
}
}