Line data Source code
1 : /* Copyright (C) 2019 Wildfire Games.
2 : *
3 : * Permission is hereby granted, free of charge, to any person obtaining
4 : * a copy of this software and associated documentation files (the
5 : * "Software"), to deal in the Software without restriction, including
6 : * without limitation the rights to use, copy, modify, merge, publish,
7 : * distribute, sublicense, and/or sell copies of the Software, and to
8 : * permit persons to whom the Software is furnished to do so, subject to
9 : * the following conditions:
10 : *
11 : * The above copyright notice and this permission notice shall be included
12 : * in all copies or substantial portions of the Software.
13 : *
14 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 : * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 : * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 : * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 : * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 : * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 : */
22 :
23 : /*
24 : * IO event recording
25 : */
26 :
27 : #include "precompiled.h"
28 : #include "lib/file/common/trace.h"
29 :
30 : #include <cstdio>
31 : #include <sstream>
32 :
33 : #include "lib/allocators/pool.h"
34 : #include "lib/timer.h" // timer_Time
35 : #include "lib/sysdep/sysdep.h" // sys_OpenFile
36 :
37 :
38 83 : /*virtual*/ ITrace::~ITrace()
39 : {
40 :
41 83 : }
42 :
43 :
44 : //-----------------------------------------------------------------------------
45 :
46 2 : TraceEntry::TraceEntry(EAction action, const Path& pathname, size_t size)
47 2 : : m_timestamp((float)timer_Time())
48 : , m_action(action)
49 : , m_pathname(pathname)
50 2 : , m_size(size)
51 : {
52 2 : }
53 :
54 :
55 3 : TraceEntry::TraceEntry(const std::wstring& text)
56 : {
57 : // swscanf is far too awkward to get working cross-platform,
58 : // so use iostreams here instead
59 :
60 : wchar_t dummy;
61 : wchar_t action;
62 :
63 6 : std::wstringstream stream(text);
64 3 : stream >> m_timestamp;
65 :
66 3 : stream >> dummy;
67 3 : ENSURE(dummy == ':');
68 :
69 3 : stream >> action;
70 3 : ENSURE(action == 'L' || action == 'S');
71 3 : m_action = (EAction)action;
72 :
73 3 : stream >> dummy;
74 3 : ENSURE(dummy == '"');
75 :
76 6 : Path::String pathname;
77 3 : std::getline(stream, pathname, L'"');
78 3 : m_pathname = Path(pathname);
79 :
80 3 : stream >> m_size;
81 :
82 3 : ENSURE(stream.get() == '\n');
83 : // NOTE: Don't use good() here - it fails due to a bug in older libc++ versions
84 3 : ENSURE(!stream.bad() && !stream.fail());
85 3 : ENSURE(stream.get() == WEOF);
86 3 : }
87 :
88 :
89 2 : std::wstring TraceEntry::EncodeAsText() const
90 : {
91 : // Ensure timestamp gets correctly encoded.
92 2 : char* oldLocale = setlocale(LC_ALL, "C");
93 :
94 2 : const wchar_t action = (wchar_t)m_action;
95 : wchar_t buf[1000];
96 2 : swprintf_s(buf, ARRAY_SIZE(buf), L"%#010f: %c \"%ls\" %lu\n", m_timestamp, action, m_pathname.string().c_str(), (unsigned long)m_size);
97 2 : setlocale(LC_ALL, oldLocale);
98 2 : return buf;
99 : }
100 :
101 :
102 : //-----------------------------------------------------------------------------
103 :
104 166 : class Trace_Dummy : public ITrace
105 : {
106 : public:
107 83 : Trace_Dummy(size_t UNUSED(maxSize))
108 83 : {
109 :
110 83 : }
111 :
112 2836 : virtual void NotifyLoad(const Path& UNUSED(pathname), size_t UNUSED(size))
113 : {
114 2836 : }
115 :
116 96 : virtual void NotifyStore(const Path& UNUSED(pathname), size_t UNUSED(size))
117 : {
118 96 : }
119 :
120 0 : virtual Status Load(const OsPath& UNUSED(pathname))
121 : {
122 0 : return INFO::OK;
123 : }
124 :
125 0 : virtual Status Store(const OsPath& UNUSED(pathname)) const
126 : {
127 0 : return INFO::OK;
128 : }
129 :
130 0 : virtual const TraceEntry* Entries() const
131 : {
132 0 : return 0;
133 : }
134 :
135 0 : virtual size_t NumEntries() const
136 : {
137 0 : return 0;
138 : }
139 : };
140 :
141 :
142 : //-----------------------------------------------------------------------------
143 :
144 : class Trace : public ITrace
145 : {
146 : public:
147 0 : Trace(size_t maxSize)
148 0 : {
149 0 : (void)pool_create(&m_pool, maxSize, sizeof(TraceEntry));
150 0 : }
151 :
152 0 : virtual ~Trace()
153 0 : {
154 0 : for(size_t i = 0; i < NumEntries(); i++)
155 : {
156 0 : TraceEntry* entry = (TraceEntry*)(uintptr_t(m_pool.da.base) + i*m_pool.el_size);
157 0 : entry->~TraceEntry();
158 : }
159 :
160 0 : (void)pool_destroy(&m_pool);
161 0 : }
162 :
163 0 : virtual void NotifyLoad(const Path& pathname, size_t size)
164 : {
165 0 : new(Allocate()) TraceEntry(TraceEntry::Load, pathname, size);
166 0 : }
167 :
168 0 : virtual void NotifyStore(const Path& pathname, size_t size)
169 : {
170 0 : new(Allocate()) TraceEntry(TraceEntry::Store, pathname, size);
171 0 : }
172 :
173 0 : virtual Status Load(const OsPath& pathname)
174 : {
175 0 : pool_free_all(&m_pool);
176 :
177 0 : errno = 0;
178 0 : FILE* file = sys_OpenFile(pathname, "rt");
179 0 : if(!file)
180 0 : WARN_RETURN(StatusFromErrno());
181 :
182 : for(;;)
183 : {
184 : wchar_t text[500];
185 0 : if(!fgetws(text, ARRAY_SIZE(text)-1, file))
186 0 : break;
187 0 : new(Allocate()) TraceEntry(text);
188 0 : }
189 0 : fclose(file);
190 :
191 0 : return INFO::OK;
192 : }
193 :
194 0 : virtual Status Store(const OsPath& pathname) const
195 : {
196 0 : errno = 0;
197 0 : FILE* file = sys_OpenFile(pathname, "at");
198 0 : if(!file)
199 0 : WARN_RETURN(StatusFromErrno());
200 0 : for(size_t i = 0; i < NumEntries(); i++)
201 : {
202 0 : std::wstring text = Entries()[i].EncodeAsText();
203 0 : fputws(text.c_str(), file);
204 : }
205 0 : (void)fclose(file);
206 0 : return INFO::OK;
207 : }
208 :
209 0 : virtual const TraceEntry* Entries() const
210 : {
211 0 : return (const TraceEntry*)m_pool.da.base;
212 : }
213 :
214 0 : virtual size_t NumEntries() const
215 : {
216 0 : return m_pool.da.pos / m_pool.el_size;
217 : }
218 :
219 : private:
220 0 : void* Allocate()
221 : {
222 0 : void* p = pool_alloc(&m_pool, 0);
223 0 : ENSURE(p);
224 0 : return p;
225 : }
226 :
227 : Pool m_pool;
228 : };
229 :
230 :
231 83 : PITrace CreateDummyTrace(size_t maxSize)
232 : {
233 83 : return PITrace(new Trace_Dummy(maxSize));
234 : }
235 :
236 0 : PITrace CreateTrace(size_t maxSize)
237 : {
238 0 : return PITrace(new Trace(maxSize));
239 3 : }
|